diff --git a/src/jinja2_webpack/__init__.py b/src/jinja2_webpack/__init__.py index 80c3f53..fe4f37e 100644 --- a/src/jinja2_webpack/__init__.py +++ b/src/jinja2_webpack/__init__.py @@ -7,6 +7,7 @@ 'errorOnInvalidReference': True, 'publicRoot': '/static/pack', 'manifest': 'webpack-manifest.json', + 'stats': None, 'defaultRenderer': renderer.url, 'useDefaultRenderByExt': False, # this setting is mostly experimental 'renderByExt': { @@ -51,6 +52,8 @@ class Environment(object): Settings: * **manifest** - default ``webpack-manifest.json``. Path to the WebpackManifest file. + * **stats** - default ``stats.json``. + Path to the WebpackStats file. * **errorOnInvalidReference** - default ``True``. True if exception should be thrown when you try to resolve an invalid asset reference. @@ -59,10 +62,14 @@ class Environment(object): """ def __init__(self, **kwargs): self.settings = EnvironmentSettings(**kwargs) - if self.settings.manifest: + if self.settings.manifest and not self.settings.stats: self.load_manifest(self.settings.manifest) else: self._manifest = {} + if self.settings.stats: + self.load_stats(self.settings.stats) + else: + self._stats = {} def _resolve_asset(self, asset): if not self.settings.publicRoot: @@ -88,6 +95,21 @@ def _resolve_manifest(self, manifest): return result + def _transform_stats(self, entrypoint): + return list(map(self._resolve_asset, entrypoint['assets'])) + + def _resolve_stats(self, stats): + entrypoints = stats['entrypoints'] + self.settings.publicRoot = ( + stats['publicPath'] or self.settings.publicRoot + ) + + return {k: self._transform_stats(v) for (k, v) in entrypoints.items()} + + def load_stats(self, filename): + stats = load_json(filename) + self._stats = self._resolve_stats(stats) + def load_manifest(self, filename): manifest = load_json(filename) self._manifest = self._resolve_manifest(manifest) @@ -106,9 +128,19 @@ def identify_assetspec(self, spec): """ nodir = path.basename(spec) noextension = path.splitext(nodir)[0] - result = self._manifest.get(spec) \ - or self._manifest.get(nodir) \ - or self._manifest.get(noextension) + + if self._stats: + results = (self._stats.get(spec) + or self._stats.get(nodir) + or self._stats.get(noextension)) + if results: + return results + elif self.settings.errorOnInvalidReference: + raise AssetNotFoundException(spec) + + result = (self._manifest.get(spec) + or self._manifest.get(nodir) + or self._manifest.get(noextension)) if result: return result if self.settings.errorOnInvalidReference: @@ -119,7 +151,7 @@ def register_renderer(self, extension, renderer): self.settings.renderByExt[extension] = renderer def _select_renderer(self, asset): - name, ext = path.splitext(asset.filename) + _, ext = path.splitext(asset.filename) return self.settings.renderByExt.get( ext, self.settings.defaultRenderer) @@ -129,5 +161,16 @@ def render_asset(self, asset): renderer = self._select_renderer(asset) return renderer(asset) + def render_assets(self, assets=[]): + """ Render an array of assets to a URL or something more + interesting, by looking up the extension in the registered + renderers """ + result = '\n'.join(map(self.render_asset, assets)) + + if not result: + return '' + + return result + __version__ = '0.2.0' diff --git a/src/jinja2_webpack/filter.py b/src/jinja2_webpack/filter.py index 87263a4..5338fed 100644 --- a/src/jinja2_webpack/filter.py +++ b/src/jinja2_webpack/filter.py @@ -5,6 +5,10 @@ def __init__(self, environment): self.environment = environment def __call__(self, assetspec): - asset = self.environment.identify_assetspec(assetspec) - if asset: - return self.environment.render_asset(asset) + assets = self.environment.identify_assetspec(assetspec) + + if isinstance(assets, list): + return self.environment.render_assets(assets) + + if assets: + return self.environment.render_asset(assets) diff --git a/tests/test_filter.py b/tests/test_filter.py index 572830a..936526c 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -3,6 +3,33 @@ from jinja2_webpack import AssetNotFoundException, Environment from jinja2_webpack.filter import WebpackFilter +stats = { + 'publicPath': 'https://cdn.foo.com/', + 'entrypoints': { + 'a': { + 'chunks': [ + 'a-chunk' + ], + 'assets': [ + 'a-asset-1.js' + ], + 'children': {}, + 'childAssets': {} + }, + 'b': { + 'chunks': [ + 'b-chunk' + ], + 'assets': [ + 'b-asset-1.js', + 'b-asset-2.js' + ], + 'children': {}, + 'childAssets': {} + } + } +} + def test_filter_defined(): assert WebpackFilter @@ -32,3 +59,20 @@ def test_invalid_error(): f = WebpackFilter(env) with pytest.raises(AssetNotFoundException): f('a') + + +def test_stats_lookup(): + env = Environment(stats=stats, manifest=None) + f = WebpackFilter(env) + assert f + assert f('a') == 'https://cdn.foo.com/a-asset-1.js' + assert f('b') == ( + 'https://cdn.foo.com/b-asset-1.js\nhttps://cdn.foo.com/b-asset-2.js' + ) + + +def test_invalid_stats_error(): + env = Environment(stats=stats, errorOnInvalidReference=True) + f = WebpackFilter(env) + with pytest.raises(AssetNotFoundException): + f('z')