diff --git a/README.md b/README.md index 5d8bb7db..2c52ad24 100644 --- a/README.md +++ b/README.md @@ -70,14 +70,16 @@ var BundleTracker = require('webpack-bundle-tracker'); module.exports = { context: __dirname, - entry: './assets/js/index', + entry: { + './assets/js/index', + './assets/exported_assets.js' output: { path: path.resolve('./assets/webpack_bundles/'), filename: "[name]-[hash].js" }, plugins: [ - new BundleTracker({filename: './webpack-stats.json'}) + new BundleTracker({filename: './webpack-stats.json', assetsIdentifier: "exported_assets"}) ] } ``` @@ -92,6 +94,7 @@ WEBPACK_LOADER = { 'CACHE': not DEBUG, 'BUNDLE_DIR_NAME': 'webpack_bundles/', # must end with slash 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'), + 'ASSETS_IDENTIFIER': 'exported_assets', 'POLL_INTERVAL': 0.1, 'TIMEOUT': None, 'IGNORE': ['.+\.hot-update.js', '.+\.map'] @@ -136,7 +139,8 @@ If the bundle generates a file called `main-cf4b5fab6e00a404e0c7.js` and your ST ```python WEBPACK_LOADER = { 'DEFAULT': { - 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json') + 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'), + 'ASSETS_IDENTIFIER': 'exported_assets', } } ``` @@ -151,6 +155,25 @@ and your webpack config is located at `/home/src/webpack.config.js`, then the va
+#### ASSETS_IDENTIFIER +```python +WEBPACK_LOADER = { + 'DEFAULT': { + 'ASSETS_IDENTIFIER': 'exported_assets', + } +} +``` + +`ASSETS_IDENTIFIER` is the key at which the assets will be generated in the `webpack-stats.json`. This is configurable on both ends, javascript being in charge of generating the right key while in python one should know where to read from. If you initialize `webpack-bundle-tracker` plugin like this + +```javacsript +new BundleTracker({filename: "webpack-stats.json", assetsIdentifier: "exported_assets"}); +``` + +you will get all your assets included through the exported_assets file on the key exported_assets in `webpack-stats.json`. + +
+ #### IGNORE `IGNORE` is a list of regular expressions. If a file generated by webpack matches one of the expressions, the file will not be included in the template. @@ -228,10 +251,12 @@ WEBPACK_LOADER = { 'DEFAULT': { 'BUNDLE_DIR_NAME': 'bundles/', 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'), + 'ASSETS_IDENTIFIER': 'exported_assets' # you can have the same name, hence different files }, 'DASHBOARD': { 'BUNDLE_DIR_NAME': 'dashboard_bundles/', 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats-dashboard.json'), + 'ASSETS_IDENTIFIER': 'exported_assets' # you can have the same name, hence different files } } ``` @@ -293,6 +318,25 @@ In the below example, `logo.png` can be any static asset shipped with any npm or ``` +### File paths from webpack's bundler + +Basically, if you need to access the public path of a file that has been bundled with webpack, you can easily do that with `webpack_asset_path`. To do so, you have to properly load them within the `exported_assets.js` file, that should be specified in the `webpack.config.js`. If that's done properly and `exported_assets.js` has + +```javascript +require('./assets/image.png'); +``` + +then you should easily refer the bundled assets as follows: + +```HTML+Django +{% load webpack_asset_path from webpack_loader %} + + + + + +
## How to use in Production diff --git a/tests/app/tests/test_webpack.py b/tests/app/tests/test_webpack.py index 6945b820..e5252d5e 100644 --- a/tests/app/tests/test_webpack.py +++ b/tests/app/tests/test_webpack.py @@ -103,6 +103,22 @@ def test_code_spliting(self): vendor = chunks['vendor'] self.assertEqual(vendor[0]['path'], os.path.join(settings.BASE_DIR, 'assets/bundles/vendor.js')) + def test_assets_extract(self): + self.compile_bundles('webpack.config.assets.js') + + assets = get_loader(DEFAULT_CONFIG).get_assets() + self.assertEqual(assets['status'], 'done') + self.assertIn('exported_assets', assets) + + assets = get_loader(DEFAULT_CONFIG).get_exported_asset('./test.png') + images_diff = call([ + 'diff', + '/Users/devil/Contributions/django-webpack-loader/tests/assets/test.png', + assets['path'] + ]) + self.assertEqual(images_diff, 0) + self.assertEqual(assets['path'], os.path.join(settings.BASE_DIR, 'assets/bundles/' + assets['name'])) + def test_templatetags(self): self.compile_bundles('webpack.config.simple.js') self.compile_bundles('webpack.config.app2.js') @@ -217,7 +233,7 @@ def test_bad_status_in_production(self): def test_request_blocking(self): # FIXME: This will work 99% time but there is no garauntee with the - # 4 second thing. Need a better way to detect if request was blocked on + # 4 second thing. Need a better way to detect if request was blocked or # not. wait_for = 3 view = TemplateView.as_view(template_name='home.html') diff --git a/tests/assets/exported_assets.js b/tests/assets/exported_assets.js new file mode 100644 index 00000000..007a8415 --- /dev/null +++ b/tests/assets/exported_assets.js @@ -0,0 +1 @@ +require('./test.png'); \ No newline at end of file diff --git a/tests/assets/test.png b/tests/assets/test.png new file mode 100644 index 00000000..c31c8e4e Binary files /dev/null and b/tests/assets/test.png differ diff --git a/tests/package.json b/tests/package.json index f94fec28..5c9f4ef2 100644 --- a/tests/package.json +++ b/tests/package.json @@ -11,11 +11,12 @@ "babel-loader": "^5.1.3", "css-loader": "^0.14.1", "extract-text-webpack-plugin": "^0.8.0", + "file-loader": "^0.9.0", "node-libs-browser": "^0.5.0", "react": "^0.13.3", "style-loader": "^0.12.3", "webpack": "^1.9.8", - "webpack-bundle-tracker": "0.0.8", + "webpack-bundle-tracker": "git+https://github.com/EmilLuta/webpack-bundle-tracker.git#5e1b73fa592aba396211c29c2f36686d403e51f4", "webpack-dev-server": "^1.9.0", "webpack-split-by-path": "0.0.1" } diff --git a/tests/webpack.config.assets.js b/tests/webpack.config.assets.js new file mode 100644 index 00000000..c966b954 --- /dev/null +++ b/tests/webpack.config.assets.js @@ -0,0 +1,30 @@ +var path = require("path"); +var webpack = require('webpack'); +var BundleTracker = require('webpack-bundle-tracker'); +var ExtractTextPlugin = require("extract-text-webpack-plugin"); + + +module.exports = { + context: __dirname, + entry: { + exported_assets: './assets/exported_assets.js' + }, + output: { + path: path.resolve('./assets/bundles/'), + filename: "[name].js" + }, + + plugins: [ + new ExtractTextPlugin("styles.css"), + new BundleTracker({filename: "webpack-stats.json", assetsIdentifier: "exported_assets"}), + ], + + module: { + loaders: [ + // we pass the output from babel loader to react-hot loader + { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel'], }, + { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }, + { test: /\.png$/, loader: "file-loader" } + ], + } +} diff --git a/webpack_loader/config.py b/webpack_loader/config.py index 39ff2b1a..a65ad38a 100644 --- a/webpack_loader/config.py +++ b/webpack_loader/config.py @@ -11,6 +11,7 @@ 'CACHE': not settings.DEBUG, 'BUNDLE_DIR_NAME': 'webpack_bundles/', 'STATS_FILE': 'webpack-stats.json', + 'ASSETS_IDENTIFIER': 'exported_assets', # FIXME: Explore usage of fsnotify 'POLL_INTERVAL': 0.1, 'TIMEOUT': None, diff --git a/webpack_loader/loader.py b/webpack_loader/loader.py index faf5fb42..4cac9d84 100644 --- a/webpack_loader/loader.py +++ b/webpack_loader/loader.py @@ -30,32 +30,18 @@ def _load_assets(self): 'the file and the path is correct?'.format( self.config['STATS_FILE'])) - def get_assets(self): - if self.config['CACHE']: - if self.name not in self._assets: - self._assets[self.name] = self._load_assets() - return self._assets[self.name] - return self._load_assets() - - def filter_chunks(self, chunks): - for chunk in chunks: - ignore = any(regex.match(chunk['name']) - for regex in self.config['ignores']) - if not ignore: - chunk['url'] = self.get_chunk_url(chunk) - yield chunk - - def get_chunk_url(self, chunk): - public_path = chunk.get('publicPath') - if public_path: - return public_path - - relpath = '{0}{1}'.format( - self.config['BUNDLE_DIR_NAME'], chunk['name'] - ) - return staticfiles_storage.url(relpath) - - def get_bundle(self, bundle_name): + def _get_bundle(self, bucket, bundle_name): + """Collects the bundle withing each bucket based on its bundle name. + + Arguments: + bucket: + The key on which you are extracting data from ('chunks', 'exported_assets') + bundle_name: + The name of the specific asset in the given bucket + + Returns: + A data dictionary containing all the data for the given chunk/ asset. + """ assets = self.get_assets() # poll when debugging and block request until bundle is compiled @@ -77,10 +63,13 @@ def get_bundle(self, bundle_name): ) if assets.get('status') == 'done': - chunks = assets['chunks'].get(bundle_name, None) + chunks = assets[bucket].get(bundle_name) if chunks is None: raise WebpackBundleLookupError('Cannot resolve bundle {0}.'.format(bundle_name)) - return self.filter_chunks(chunks) + if bucket == self.config['ASSETS_IDENTIFIER']: + return chunks + else: + return self.filter_chunks(chunks) elif assets.get('status') == 'error': if 'file' not in assets: @@ -97,3 +86,34 @@ def get_bundle(self, bundle_name): "The stats file does not contain valid data. Make sure " "webpack-bundle-tracker plugin is enabled and try to run " "webpack again.") + + def get_assets(self): + if self.config['CACHE']: + if self.name not in self._assets: + self._assets[self.name] = self._load_assets() + return self._assets[self.name] + return self._load_assets() + + def filter_chunks(self, chunks): + for chunk in chunks: + ignore = any(regex.match(chunk['name']) + for regex in self.config['ignores']) + if not ignore: + chunk['url'] = self.get_chunk_url(chunk) + yield chunk + + def get_chunk_url(self, chunk): + public_path = chunk.get('publicPath') + if public_path: + return public_path + + relpath = '{0}{1}'.format( + self.config['BUNDLE_DIR_NAME'], chunk['name'] + ) + return staticfiles_storage.url(relpath) + + def get_bundle(self, bundle_name): + return self._get_bundle('chunks', bundle_name) + + def get_exported_asset(self, bundle_name): + return self._get_bundle(self.config['ASSETS_IDENTIFIER'], bundle_name) diff --git a/webpack_loader/templatetags/webpack_loader.py b/webpack_loader/templatetags/webpack_loader.py index 002294dd..0c94e008 100644 --- a/webpack_loader/templatetags/webpack_loader.py +++ b/webpack_loader/templatetags/webpack_loader.py @@ -28,16 +28,16 @@ def render_as_tags(bundle, attrs): return mark_safe('\n'.join(tags)) -def _get_bundle(bundle_name, extension, config): - bundle = get_loader(config).get_bundle(bundle_name) +def _get_content(bundle_name, extension, config, loader_function): + content = getattr(get_loader(config), loader_function)(bundle_name) if extension: - bundle = filter_by_extension(bundle, extension) - return bundle + content = filter_by_extension(content, extension) + return content @register.simple_tag def render_bundle(bundle_name, extension=None, config='DEFAULT', attrs=''): - return render_as_tags(_get_bundle(bundle_name, extension, config), attrs) + return render_as_tags(_get_content(bundle_name, extension, config, 'get_bundle'), attrs) @register.simple_tag @@ -49,6 +49,9 @@ def webpack_static(asset_name, config='DEFAULT'): asset_name ) +@register.simple_tag +def webpack_asset_path(bundle_name, config='DEFAULT'): + return _get_content(bundle_name, None, config, 'get_exported_asset')['publicPath'] assignment_tag = register.simple_tag if VERSION >= (1, 9) else register.assignment_tag @assignment_tag @@ -65,4 +68,4 @@ def get_files(bundle_name, extension=None, config='DEFAULT'): :param config: (optional) the name of the configuration :return: a list of matching chunks """ - return list(_get_bundle(bundle_name, extension, config)) + return list(_get_content(bundle_name, extension, config, 'get_bundle'))