from os import path
from . import renderer
from .utils import load_json
DEFAULT_SETTINGS = {
'errorOnInvalidReference': True,
'publicRoot': '/static/pack',
'manifest': 'webpack-manifest.json',
'defaultRenderer': renderer.url,
'useDefaultRenderByExt': False, # this setting is mostly experimental
'renderByExt': {
'.js': renderer.script,
'.png': renderer.image,
'.jpeg': renderer.image,
'.jpg': renderer.image,
'.gif': renderer.image,
'.css': renderer.stylesheet,
}
}
[docs]class Asset(object):
""" Asset class.
Might someday expose file access here too so can render assets
inline. For now the url is the interesting attribute """
def __init__(self, filename, url):
self.filename = filename
self.url = url
[docs]class AssetNotFoundException(Exception):
""" Thrown when an asset cannot be found,
can be disabled by settings errorOnInvalidReference = False """
pass
class EnvironmentSettings(object):
def __init__(self, **kwargs):
self.__dict__.update(DEFAULT_SETTINGS)
if not kwargs.get('useDefaultRenderByExt', self.useDefaultRenderByExt):
self.renderByExt = {}
self.__dict__.update(kwargs)
[docs]class Environment(object):
""" The webpack environment class. Loads the manifest and allows
it to be accessed.
Settings:
* **manifest** - default ``webpack-manifest.json``.
Path to the WebpackManifest file.
* **errorOnInvalidReference** - default ``True``.
True if exception should be thrown when you try to resolve an invalid
asset reference.
* **publicRoot** - default ``/static/pack``.
The public path to prepend to all asset URLs.
"""
def __init__(self, **kwargs):
self.settings = EnvironmentSettings(**kwargs)
if self.settings.manifest:
self.load_manifest(self.settings.manifest)
else:
self._manifest = {}
def _resolve_asset(self, asset):
if not self.settings.publicRoot:
url = asset
elif self.settings.publicRoot.endswith('/'):
url = self.settings.publicRoot + asset
else:
url = '%s/%s' % (self.settings.publicRoot, asset)
return Asset(filename=asset, url=url)
def _resolve_manifest(self, manifest):
result = {}
# Resolve URLs in original manifest items
for name, asset in manifest.items():
result[name] = self._resolve_asset(asset)
# Strip out the extension as well, so if the webpack output
# file is "commons.js" we can use {{ "commons" | webpack }}
for name, asset in manifest.items():
basename, ext = path.splitext(name)
if basename not in result:
result[basename] = result[name]
return result
def load_manifest(self, filename):
manifest = load_json(filename)
self._manifest = self._resolve_manifest(manifest)
[docs] def identify_assetspec(self, spec):
""" Lookup an asset from the webpack manifest.
The asset spec is processed such that you might reference an entry
with or without the extension.
Will raise an AssetNotFoundException if the errorOnInvalidReference
setting is enabled and the asset cannot be found.
Note that all files must be have globally unique names,
due to a limitation in the way that WebpackManifestPlugin writes
the data.
"""
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 result:
return result
if self.settings.errorOnInvalidReference:
raise AssetNotFoundException(spec)
[docs] def register_renderer(self, extension, renderer):
""" Register a new renderer function to the environment """
self.settings.renderByExt[extension] = renderer
def _select_renderer(self, asset):
name, ext = path.splitext(asset.filename)
return self.settings.renderByExt.get(
ext, self.settings.defaultRenderer)
[docs] def render_asset(self, asset):
""" Render an asset to a URL or something more interesting,
by looking up the extension in the registered renderers """
renderer = self._select_renderer(asset)
return renderer(asset)
__version__ = '0.2.0'