Skip to content

Commit 784f7b8

Browse files
committed
Add support for multiple webpack manifests
1 parent 620e050 commit 784f7b8

File tree

11 files changed

+188
-37
lines changed

11 files changed

+188
-37
lines changed

README.md

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,12 @@ module.exports = {
7878
### Default Configuration
7979
```python
8080
WEBPACK_LOADER = {
81-
'BUNDLE_DIR_NAME': 'webpack_bundles/', # must end with slash
82-
'STATS_FILE': 'webpack-stats.json',
83-
'POLL_DELAY': 0.2,
84-
'IGNORE': ['.+\.hot-update.js', '.+\.map']
81+
'DEFAULT': {
82+
'BUNDLE_DIR_NAME': 'webpack_bundles/', # must end with slash
83+
'STATS_FILE': 'webpack-stats.json',
84+
'POLL_DELAY': 0.1,
85+
'IGNORE': ['.+\.hot-update.js', '.+\.map']
86+
}
8587
}
8688
```
8789

@@ -90,7 +92,9 @@ WEBPACK_LOADER = {
9092
#### BUNDLE_DIR_NAME
9193
```python
9294
WEBPACK_LOADER = {
93-
'BUNDLE_DIR_NAME': 'bundles/' # end with slash
95+
'DEFAULT': {
96+
'BUNDLE_DIR_NAME': 'bundles/' # end with slash
97+
}
9498
}
9599
```
96100

@@ -107,7 +111,9 @@ If the bundle generates a file called `main-cf4b5fab6e00a404e0c7.js` and your ST
107111
#### STATS_FILE
108112
```python
109113
WEBPACK_LOADER = {
110-
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json')
114+
'DEFAULT': {
115+
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json')
116+
}
111117
}
112118
```
113119
@@ -142,6 +148,23 @@ and your webpack config is located at `/home/src/webpack.config.js`, then the va
142148
143149
One of the core principles of django-webpack-loader is to not manage webpack itself in order to give you the flexibility to run webpack the way you want. If you are new to webpack, check one of the [examples](https://github.com/owais/django-webpack-loader/tree/master/examples), read [my detailed blog post](http://owaislone.org/blog/webpack-plus-reactjs-and-django/) or check [webpack docs](http://webpack.github.io/).
144150
151+
#### Multiple webpack projects
152+
153+
It is possible to manage multiple webpack projects by adding extra entries in `WEBPACK_LOADER` as follows:
154+
155+
```python
156+
WEBPACK_LOADER = {
157+
'DEFAULT': {
158+
'BUNDLE_DIR_NAME': 'bundles/',
159+
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
160+
},
161+
'APP2': {
162+
'BUNDLE_DIR_NAME': 'bundles/',
163+
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats-app2.json'),
164+
}
165+
}
166+
```
167+
145168
#### Settings
146169
147170
Add `webpack_loader` to `INSTALLED_APPS`
@@ -179,6 +202,32 @@ INSTALLED_APPS = (
179202
</head>
180203
```
181204
205+
`render_bundle` third argument (defaults to `'DEFAULT'`) tells which `WEBPACK_LOADER` project contains the bundle. For example, to render bundles from several projects just define your `WEPACK_LOADER` like this,
206+
207+
```python
208+
WEBPACK_LOADER = {
209+
'APP1': {
210+
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats-app1.json'),
211+
},
212+
'APP2': {
213+
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats-app2.json'),
214+
}
215+
}
216+
```
217+
218+
then use `render_bundle` as follows
219+
220+
```HTML+Django
221+
{% load render_bundle from webpack_loader %}
222+
223+
<html>
224+
<body>
225+
....
226+
{% render_bundle 'main' 'js' 'APP1'%}
227+
{% render_bundle 'main' 'js' 'APP2'%}
228+
</body>
229+
</head>
230+
```
182231
183232
<br>
184233

tests/app/settings.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,14 @@
109109
)
110110

111111
WEBPACK_LOADER = {
112-
'BUNDLE_DIR_NAME': 'bundles/',
113-
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
112+
'DEFAULT': {
113+
'BUNDLE_DIR_NAME': 'bundles/',
114+
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
115+
},
116+
'APP2': {
117+
'BUNDLE_DIR_NAME': 'bundles/',
118+
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats-app2.json'),
119+
}
114120
}
115121

116122
from django_jinja.builtins import DEFAULT_EXTENSIONS

tests/app/templates/home.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
<meta charset="UTF-8">
66
<title>Example</title>
77
{% render_bundle 'main' 'css' %}
8+
{% render_bundle 'app2' 'css' config='APP2' %}
89
</head>
9-
10+
1011
<body>
1112
<img src="{% webpack_static 'my-image.png' %}"/>
13+
<img src="{% webpack_static 'my-image.png' 'APP2'%}"/>
1214
<div id="react-app"></div>
1315
{% render_bundle 'main' 'js' %}
16+
{% render_bundle 'app2' 'js' config='APP2' %}
1417
</body>
1518
</html>

tests/app/templates/home.jinja

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
<meta charset="UTF-8">
55
<title>Example</title>
66
{{ render_bundle('main', 'css') }}
7+
{{ render_bundle('app2', 'css', 'APP2') }}
78
</head>
89

910
<body>
1011
<div id="react-app"></div>
1112
{{ render_bundle('main', 'js') }}
13+
{{ render_bundle('app2', 'js', 'APP2') }}
1214
</body>
1315
</html>

tests/app/tests/test_webpack.py

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33
import json
44
from subprocess import call
55
from threading import Thread
6+
from unittest import skipIf
67

78
import django
89
from django.conf import settings
910
from django.test import TestCase, RequestFactory
1011
from django_jinja.builtins import DEFAULT_EXTENSIONS
1112
from django.views.generic.base import TemplateView
1213

13-
from webpack_loader.utils import get_assets, get_bundle, WebpackException
14+
from webpack_loader.utils import get_config, get_assets, get_bundle, WebpackException
1415

1516

1617
BUNDLE_PATH = os.path.join(settings.BASE_DIR, 'assets/bundles/')
17-
18+
DEFAULT_CONFIG = 'DEFAULT'
1819

1920
class LoaderTestCase(TestCase):
2021
def setUp(self):
@@ -23,16 +24,42 @@ def setUp(self):
2324
def clean_dir(self, directory):
2425
if os.path.exists(BUNDLE_PATH):
2526
[os.remove(os.path.join(BUNDLE_PATH, F)) for F in os.listdir(BUNDLE_PATH)]
26-
os.remove(settings.WEBPACK_LOADER['STATS_FILE'])
27+
os.remove(settings.WEBPACK_LOADER[DEFAULT_CONFIG]['STATS_FILE'])
2728

2829
def compile_bundles(self, config, wait=None):
2930
if wait:
3031
time.sleep(wait)
3132
call(['./node_modules/.bin/webpack', '--config', config])
3233

34+
@skipIf(django.VERSION < (1, 7),
35+
'not supported in this django version')
36+
def test_config_check(self):
37+
from django.core.checks import Error
38+
from webpack_loader.apps import webpack_cfg_check
39+
40+
with self.settings(WEBPACK_LOADER={
41+
'BUNDLE_DIR_NAME': 'bundles/',
42+
'STATS_FILE': 'webpack-stats.json',
43+
}):
44+
errors = webpack_cfg_check(None)
45+
expected_errors = [Error(
46+
'Error while parsing WEBPACK_LOADER configuration',
47+
hint='Is WEBPACK_LOADER config compliant with the new format?',
48+
obj='django.conf.settings.WEBPACK_LOADER',
49+
id='django-webpack-loader.E001',
50+
)]
51+
self.assertEqual(errors, expected_errors)
52+
53+
with self.settings(WEBPACK_LOADER={
54+
'DEFAULT': {}
55+
}):
56+
errors = webpack_cfg_check(None)
57+
expected_errors = []
58+
self.assertEqual(errors, expected_errors)
59+
3360
def test_simple_and_css_extract(self):
3461
self.compile_bundles('webpack.config.simple.js')
35-
assets = get_assets()
62+
assets = get_assets(get_config(DEFAULT_CONFIG))
3663
self.assertEqual(assets['status'], 'done')
3764
self.assertIn('chunks', assets)
3865

@@ -46,13 +73,13 @@ def test_simple_and_css_extract(self):
4673

4774
def test_static_url(self):
4875
self.compile_bundles('webpack.config.publicPath.js')
49-
assets = get_assets()
76+
assets = get_assets(get_config(DEFAULT_CONFIG))
5077
self.assertEqual(assets['status'], 'done')
5178
self.assertEqual(assets['publicPath'], 'http://custom-static-host.com/')
5279

5380
def test_code_spliting(self):
5481
self.compile_bundles('webpack.config.split.js')
55-
assets = get_assets()
82+
assets = get_assets(get_config(DEFAULT_CONFIG))
5683
self.assertEqual(assets['status'], 'done')
5784
self.assertIn('chunks', assets)
5885

@@ -68,12 +95,17 @@ def test_code_spliting(self):
6895

6996
def test_templatetags(self):
7097
self.compile_bundles('webpack.config.simple.js')
98+
self.compile_bundles('webpack.config.app2.js')
7199
view = TemplateView.as_view(template_name='home.html')
72100
request = self.factory.get('/')
73101
result = view(request)
74102
self.assertIn('<link type="text/css" href="/static/bundles/styles.css" rel="stylesheet">', result.rendered_content)
75103
self.assertIn('<script type="text/javascript" src="/static/bundles/main.js"></script>', result.rendered_content)
76104

105+
self.assertIn('<link type="text/css" href="/static/bundles/styles-app2.css" rel="stylesheet">', result.rendered_content)
106+
self.assertIn('<script type="text/javascript" src="/static/bundles/app2.js"></script>', result.rendered_content)
107+
self.assertIn('<img src="/static/my-image.png"/>', result.rendered_content)
108+
77109

78110
self.compile_bundles('webpack.config.publicPath.js')
79111
view = TemplateView.as_view(template_name='home.html')
@@ -83,6 +115,7 @@ def test_templatetags(self):
83115

84116
def test_jinja2(self):
85117
self.compile_bundles('webpack.config.simple.js')
118+
self.compile_bundles('webpack.config.app2.js')
86119
view = TemplateView.as_view(template_name='home.jinja')
87120

88121
if django.VERSION >= (1, 8):
@@ -118,16 +151,16 @@ def test_reporting_errors(self):
118151
#TODO:
119152
self.compile_bundles('webpack.config.error.js')
120153
try:
121-
get_bundle('main')
154+
get_bundle('main', DEFAULT_CONFIG)
122155
except WebpackException as e:
123156
self.assertIn("Cannot resolve module 'the-library-that-did-not-exist'", str(e))
124157

125158
def test_missing_stats_file(self):
126-
os.remove(settings.WEBPACK_LOADER['STATS_FILE'])
159+
os.remove(settings.WEBPACK_LOADER[DEFAULT_CONFIG]['STATS_FILE'])
127160
try:
128-
get_assets()
161+
get_assets(get_config(DEFAULT_CONFIG))
129162
except IOError as e:
130-
expected = 'Error reading {}. Are you sure webpack has generated the file and the path is correct?'.format(settings.WEBPACK_LOADER['STATS_FILE'])
163+
expected = 'Error reading {}. Are you sure webpack has generated the file and the path is correct?'.format(settings.WEBPACK_LOADER[DEFAULT_CONFIG]['STATS_FILE'])
131164
self.assertIn(expected, str(e))
132165

133166
def test_request_blocking(self):
@@ -138,19 +171,23 @@ def test_request_blocking(self):
138171
view = TemplateView.as_view(template_name='home.html')
139172

140173
with self.settings(DEBUG=True):
141-
open(settings.WEBPACK_LOADER['STATS_FILE'], 'w').write(json.dumps({'status': 'compiling'}))
174+
open(settings.WEBPACK_LOADER[DEFAULT_CONFIG]['STATS_FILE'], 'w').write(json.dumps({'status': 'compiling'}))
142175
then = time.time()
143176
request = self.factory.get('/')
144177
result = view(request)
145178
t = Thread(target=self.compile_bundles, args=('webpack.config.simple.js', wait_for))
179+
t2 = Thread(target=self.compile_bundles, args=('webpack.config.app2.js', wait_for))
146180
t.start()
181+
t2.start()
147182
result.rendered_content
148183
elapsed = time.time() - then
149184
t.join()
185+
t2.join()
150186
self.assertTrue(elapsed > wait_for)
151187

152188
with self.settings(DEBUG=False):
153189
self.compile_bundles('webpack.config.simple.js')
190+
self.compile_bundles('webpack.config.app2.js')
154191
then = time.time()
155192
request = self.factory.get('/')
156193
result = view(request)

tests/webpack-stats-app2.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"status":"done","chunks":{"app2":[{"name":"app2.js","path":"/Users/stefano/forks/django-webpack-loader/tests/assets/bundles/app2.js"},{"name":"styles-app2.css","path":"/Users/stefano/forks/django-webpack-loader/tests/assets/bundles/styles-app2.css"}]}}

tests/webpack.config.app2.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
var config = require("./webpack.config.simple.js");
2+
var BundleTracker = require('webpack-bundle-tracker');
3+
var ExtractTextPlugin = require("extract-text-webpack-plugin");
4+
5+
config.entry = {
6+
app2: './assets/js/index'
7+
};
8+
9+
config.plugins = [
10+
new ExtractTextPlugin("styles-app2.css"),
11+
new BundleTracker({filename: './webpack-stats-app2.json'})
12+
];
13+
14+
module.exports = config;

webpack_loader/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
11
default_app_config = 'webpack_loader.apps.WebpackLoaderConfig'
2-
3-
from .utils import get_bundle

webpack_loader/apps.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
11
from django.apps import AppConfig
22

33

4+
def webpack_cfg_check(app_configs, **kwargs):
5+
from django.conf import settings
6+
from django.core.checks import Error
7+
8+
check_failed = False
9+
user_config = getattr(settings, 'WEBPACK_LOADER', {})
10+
try:
11+
user_config = [dict({}, **cfg) for cfg in user_config.values()]
12+
except TypeError:
13+
check_failed = True
14+
15+
errors = []
16+
if check_failed:
17+
errors.append(
18+
Error(
19+
'Error while parsing WEBPACK_LOADER configuration',
20+
hint='Is WEBPACK_LOADER config compliant with the new format?',
21+
obj='django.conf.settings.WEBPACK_LOADER',
22+
id='django-webpack-loader.E001',
23+
)
24+
)
25+
return errors
26+
27+
428
class WebpackLoaderConfig(AppConfig):
529
name = 'webpack_loader'
630
verbose_name = "Webpack Loader"
731

832
def ready(self):
933
from . import signals
34+
from django.core.checks import register, Tags
35+
register(Tags.compatibility)(webpack_cfg_check)

webpack_loader/templatetags/webpack_loader.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from django import template
22
from django.conf import settings
33

4-
from ..utils import get_assets, get_bundle
4+
from ..utils import get_config, get_assets, get_bundle
55

66

77
register = template.Library()
@@ -25,16 +25,16 @@ def render_as_tags(bundle):
2525

2626

2727
@register.simple_tag
28-
def render_bundle(bundle_name, extension=None):
29-
bundle = get_bundle(bundle_name)
28+
def render_bundle(bundle_name, extension=None, config="DEFAULT"):
29+
bundle = get_bundle(bundle_name, config)
3030
if extension:
3131
bundle = filter_by_extension(bundle, extension)
3232
return render_as_tags(bundle)
3333

3434

3535
@register.simple_tag
36-
def webpack_static(asset_name):
36+
def webpack_static(asset_name, config="DEFAULT"):
3737
return "{}{}".format(
38-
get_assets().get('publicPath', getattr(settings, 'STATIC_URL')),
38+
get_assets(get_config(config)).get('publicPath', getattr(settings, 'STATIC_URL')),
3939
asset_name
4040
)

0 commit comments

Comments
 (0)