Skip to content

Commit 7c28109

Browse files
committed
Replace Babel + AMD with Browserify + Babelify
1 parent c8109a6 commit 7c28109

File tree

15 files changed

+197
-2391
lines changed

15 files changed

+197
-2391
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Project generated files
22
*.pyc
33
/tests/test_project/compressor/
4-
/*.egg-info
4+
/*.egg-info/
5+
/.eggs/
56

67
# Vagrant
78
/.vagrant/

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ python:
55
before_install:
66
- nvm install 4.0
77
- npm install -g node-sass postcss-cli autoprefixer
8-
- npm install -g babel-cli babel-preset-es2015 babel-plugin-transform-es2015-modules-amd
8+
- npm install -g browserify babelify babel-preset-es2015
99
install:
1010
- pip install -e .[test]
1111
script:

README.md

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ It also enables Django static imports in SCSS, see the example below.
3131
// settings.py
3232

3333
COMPRESS_PRECOMPILERS = (
34-
('text/x-scss', 'compressor_toolkit.precompilers.SCSSFilter'),
34+
('text/x-scss', 'compressor_toolkit.precompilers.SCSSCompiler'),
3535
)
3636
```
3737

@@ -79,21 +79,20 @@ But there is a way to use it: transpilers that compile ES6 into good old ES5 syn
7979

8080
The add-on does next:
8181
ES6 → (
82-
[Babel](https://github.com/sass/node-sass) +
83-
[AMD](https://github.com/amdjs/amdjs-api/blob/master/AMD.md)
82+
[Babel](http://babeljs.io/) +
83+
[Browserify](http://browserify.org/)
8484
) → ES5.
8585

86-
By default, AMD module ID is generated from file URL:
87-
`{{ STATIC_URL }}app/js/module.js``app/js/module`,
88-
but you can set it explicitly - see the example below.
86+
It also enables Django static imports in ES6, see the example below.
87+
8988

9089
#### Usage
9190

9291
```py
9392
// settings.py
9493

9594
COMPRESS_PRECOMPILERS = (
96-
('module', 'compressor_toolkit.precompilers.ES6Filter'),
95+
('module', 'compressor_toolkit.precompilers.ES6Compiler'),
9796
)
9897
```
9998

@@ -102,15 +101,8 @@ COMPRESS_PRECOMPILERS = (
102101

103102
{% load compress %}
104103

105-
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.22/require.js"></script>
106-
107104
{% compress js %}
108-
<script type="module" src="{% static 'base/framework.js' %}"></script>
109-
<script type="module" data-module-id="main/scripts" src="{% static 'app/scripts.js' %}"></script>
110-
<script>
111-
// entry point
112-
require(['main/scripts']);
113-
</script>
105+
<script type="module" src="{% static 'app/scripts.js' %}"></script>
114106
{% endcompress %}
115107
```
116108

@@ -137,9 +129,8 @@ new Framework('1.0.1');
137129

138130
#### Requisites
139131

140-
You need `babel-cli`, `babel-preset-es2015` and `babel-plugin-transform-es2015-modules-amd`
141-
to be installed. Quick install:
132+
You need `babelify` and `babel-preset-es2015` to be installed. Quick install:
142133

143134
```sh
144-
npm install -g babel-cli babel-preset-es2015 babel-plugin-transform-es2015-modules-amd
135+
npm install -g browserify babelify babel-preset-es2015
145136
```

compressor_toolkit/precompilers.py

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,52 @@
33
from compressor.filters import CompilerFilter
44
from django.conf import settings
55
from django.contrib.staticfiles import finders
6-
from django.core.exceptions import ImproperlyConfigured
6+
from django.core.files.temp import NamedTemporaryFile
77

88

9-
def get_all_static():
10-
"""
11-
Get all the static files directories found by ``STATICFILES_FINDERS``
9+
class BaseCompiler(CompilerFilter):
10+
infile_ext = ''
1211

13-
:return: set of paths (top-level folders only)
14-
"""
15-
static_dirs = set()
12+
def input(self, **kwargs):
13+
"""
14+
Specify temporary input file extension.
15+
16+
Browserify requires explicit file extension (".js" or ".json" by default).
17+
https://github.com/substack/node-browserify/issues/1469
18+
"""
19+
if self.infile is None and "{infile}" in self.command:
20+
if self.filename is None:
21+
self.infile = NamedTemporaryFile(mode='wb', suffix=self.infile_ext)
22+
self.infile.write(self.content.encode(self.default_encoding))
23+
self.infile.flush()
24+
self.options += (
25+
('infile', self.infile.name),
26+
)
27+
return super(BaseCompiler, self).input(**kwargs)
28+
29+
@staticmethod
30+
def get_all_static():
31+
"""
32+
Get all the static files directories found by ``STATICFILES_FINDERS``
33+
34+
:return: set of paths (top-level folders only)
35+
"""
36+
static_dirs = set()
1637

17-
for finder in settings.STATICFILES_FINDERS:
18-
finder = finders.get_finder(finder)
38+
for finder in settings.STATICFILES_FINDERS:
39+
finder = finders.get_finder(finder)
1940

20-
if hasattr(finder, 'storages'):
21-
for storage in finder.storages.values():
22-
static_dirs.add(storage.location)
41+
if hasattr(finder, 'storages'):
42+
for storage in finder.storages.values():
43+
static_dirs.add(storage.location)
2344

24-
if hasattr(finder, 'storage'):
25-
static_dirs.add(finder.storage.location)
45+
if hasattr(finder, 'storage'):
46+
static_dirs.add(finder.storage.location)
2647

27-
return static_dirs
48+
return static_dirs
2849

2950

30-
class SCSSFilter(CompilerFilter):
51+
class SCSSCompiler(BaseCompiler):
3152
"""
3253
django-compressor pre-compiler for SCSS files.
3354
@@ -37,10 +58,12 @@ class SCSSFilter(CompilerFilter):
3758
2. ``postcss --use autoprefixer -r output.css``
3859
"""
3960
command = (
40-
'node-sass --output-style expanded {include-static} {infile} {outfile} && '
61+
'node-sass --output-style expanded {paths} {infile} {outfile} && '
4162
'postcss --use autoprefixer --autoprefixer.browsers "ie >= 9, > 5%" -r {outfile}'
4263
)
4364

65+
infile_ext = '.scss'
66+
4467
def __init__(self, content, attrs, *args, **kwargs):
4568
"""
4669
Include all available 'static' dirs:
@@ -56,47 +79,43 @@ def __init__(self, content, attrs, *args, **kwargs):
5679
font-size: $title-font-size;
5780
}
5881
"""
59-
static_dirs = get_all_static()
60-
6182
self.options += (
62-
('include-static', ' '.join(['--include-path {}'.format(s) for s in static_dirs])),
83+
('paths', ' '.join(['--include-path {}'.format(s) for s in self.get_all_static()])),
6384
)
6485

65-
super(SCSSFilter, self).__init__(content, self.command, *args, **kwargs)
86+
super(SCSSCompiler, self).__init__(content, self.command, *args, **kwargs)
6687

6788

68-
class ES6Filter(CompilerFilter):
89+
class ES6Compiler(BaseCompiler):
6990
"""
7091
django-compressor pre-compiler for ES6 files.
7192
72-
Transforms ES6 to ES5 AMD module using ``babel``.
93+
Transforms ES6 to ES5 using Browserify + Babel.
7394
"""
7495
command = (
75-
'NPM_ROOT=`npm root -g` && '
76-
'babel --presets=$NPM_ROOT/babel-preset-es2015 '
77-
'--plugins=$NPM_ROOT/babel-plugin-transform-es2015-modules-amd '
78-
'--module-id={module-id} "{infile}" -o "{outfile}"'
96+
'export NODE_PATH={paths} && '
97+
'browserify "{infile}" -o "{outfile}" --no-bundle-external --node '
98+
'-t [ {node_modules}/babelify --presets={node_modules}/babel-preset-es2015 ]'
7999
)
80100

101+
infile_ext = '.js'
102+
81103
def __init__(self, content, attrs, *args, **kwargs):
82104
"""
83-
Add extra option for compiler:
105+
Include all available 'static' dirs:
84106
85-
'module-id': 'app/script'
107+
export NODE_PATH="path/to/app-1/static/:path/to/app-2/static/" && browserify ...
86108
87-
That's AMD module ID for '/static/app/script.js' static file.
88-
"""
89-
module_id = attrs.get('data-module-id')
90-
if not module_id:
91-
module_src = attrs.get('src')
92-
if not module_src:
93-
raise ImproperlyConfigured(
94-
"Module should contain either \"data-module-id\" or \"src\" attribute"
95-
)
96-
module_id = os.path.splitext(module_src.replace(settings.STATIC_URL, ''))[0]
109+
So you can do imports inside your ES6 modules:
97110
111+
import controller from 'app-1/page-controller';
112+
import { login, signup } from 'app-2/pages';
113+
114+
controller.registerPages(login, signup);
115+
"""
98116
self.options += (
99-
('module-id', module_id),
117+
('node_modules', getattr(settings, 'COMPRESS_NODE_MODULES', '/usr/lib/node_modules')),
118+
('paths', os.pathsep.join(self.get_all_static())),
100119
)
101120

102-
super(ES6Filter, self).__init__(content, self.command, *args, **kwargs)
121+
super(ES6Compiler, self).__init__(content, self.command, *args, **kwargs)

tests/conftest.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,46 @@
11
import os
2+
import shutil
23

4+
from compressor.base import Compressor
35
from django.conf import settings
46
import pytest
57

68

9+
COMPRESSOR_HASH = 'test-hash'
10+
11+
12+
def pytest_configure():
13+
shutil.rmtree(settings.COMPRESS_ROOT, ignore_errors=True)
14+
15+
16+
@pytest.fixture(autouse=True)
17+
def fake_compressor_filename(monkeypatch):
18+
monkeypatch.setattr('compressor.base.get_hexdigest', lambda text, length: COMPRESSOR_HASH)
19+
20+
721
@pytest.fixture
8-
def assert_precompiled():
22+
def precompiled():
923
"""
1024
Fixture that does the following steps:
1125
1226
- searches given static file, e.g. 'app/layout.scss'
1327
- finds corresponding pre-compiled file generated by ``django-compressor``
14-
- asserts pre-compiled file contents with given expected contents
28+
- returns pre-compiled file contents
1529
1630
Usage:
1731
18-
assert_precompiled('app/layout.scss', 'css', '.title {\n font-size: 30px;\n}\n')
32+
assert precompiled('app/layout.scss', 'css') == '.title {\n font-size: 30px;\n}\n'
1933
"""
20-
# can't import ``Compressor`` before Django is configured
21-
from compressor.base import Compressor
22-
23-
def runner(original_file_path, file_type, compiled_file_contents):
34+
def runner(original_file_path, file_type):
2435
"""
2536
:param original_file_path: e.g. 'app/layout.scss'
2637
:param file_type: 'css' or 'js'
27-
:param compiled_file_contents: e.g. '.title {\n font-size: 30px;\n}\n'
2838
"""
2939
original_file_name = os.path.basename(original_file_path)
30-
3140
compiled_file_path = os.path.join(
3241
settings.COMPRESS_ROOT,
33-
Compressor(output_prefix=file_type).get_filepath(
34-
compiled_file_contents,
35-
original_file_name
36-
)
42+
Compressor(output_prefix=file_type).get_filepath('...', original_file_name)
3743
)
38-
39-
assert os.path.exists(compiled_file_path), \
40-
"Compiled file \"{}\" not found".format(compiled_file_path)
41-
42-
with open(compiled_file_path) as compiled_file:
43-
assert compiled_file.read() == compiled_file_contents
44-
44+
with open(compiled_file_path) as f:
45+
return f.read()
4546
return runner

0 commit comments

Comments
 (0)