2024-07-29 01:45:10 +02:00
|
|
|
from functools import lru_cache
|
2020-01-13 21:57:18 +01:00
|
|
|
from importlib import import_module
|
2024-07-29 02:56:37 +02:00
|
|
|
from typing import Optional, OrderedDict
|
|
|
|
|
2017-01-07 13:56:49 +01:00
|
|
|
from django.conf import settings
|
2024-07-29 02:56:37 +02:00
|
|
|
from django.http.request import HttpRequest
|
|
|
|
|
2020-01-13 21:57:18 +01:00
|
|
|
from .config import load_config
|
2024-07-29 02:56:37 +02:00
|
|
|
from .loaders import WebpackLoader
|
2015-05-16 19:10:03 +02:00
|
|
|
|
2015-09-10 02:44:41 +02:00
|
|
|
|
2020-01-13 21:57:18 +01:00
|
|
|
def import_string(dotted_path):
|
|
|
|
'''
|
|
|
|
This is a rough copy of django's import_string, which wasn't introduced until Django 1.7
|
|
|
|
|
|
|
|
Once this package's support for Django 1.6 has been removed, this can be safely replaced with
|
|
|
|
`from django.utils.module_loading import import_string`
|
|
|
|
'''
|
|
|
|
try:
|
|
|
|
module_path, class_name = dotted_path.rsplit('.', 1)
|
|
|
|
module = import_module(module_path)
|
|
|
|
return getattr(module, class_name)
|
|
|
|
except (ValueError, AttributeError, ImportError):
|
|
|
|
raise ImportError('%s doesn\'t look like a valid module path' % dotted_path)
|
|
|
|
|
|
|
|
|
2024-07-29 01:45:10 +02:00
|
|
|
@lru_cache(maxsize=None)
|
2024-07-29 02:56:37 +02:00
|
|
|
def get_loader(config_name) -> WebpackLoader:
|
2024-07-29 01:45:10 +02:00
|
|
|
config = load_config(config_name)
|
|
|
|
loader_class = import_string(config['LOADER_CLASS'])
|
|
|
|
return loader_class(config_name, config)
|
2017-01-07 13:56:49 +01:00
|
|
|
|
|
|
|
|
2022-05-26 21:00:51 +02:00
|
|
|
def get_skip_common_chunks(config_name):
|
|
|
|
loader = get_loader(config_name)
|
|
|
|
# The global default is currently False, whenever that is changed, change
|
|
|
|
# this fallback value as well which is present to provide backwards
|
|
|
|
# compatibility.
|
|
|
|
return loader.config.get('SKIP_COMMON_CHUNKS', False)
|
|
|
|
|
|
|
|
|
2017-01-07 13:56:49 +01:00
|
|
|
def _filter_by_extension(bundle, extension):
|
|
|
|
'''Return only files with the given extension'''
|
|
|
|
for chunk in bundle:
|
|
|
|
if chunk['name'].endswith('.{0}'.format(extension)):
|
|
|
|
yield chunk
|
|
|
|
|
|
|
|
|
2022-02-17 17:46:49 +01:00
|
|
|
def _get_bundle(loader, bundle_name, extension):
|
|
|
|
bundle = loader.get_bundle(bundle_name)
|
2017-01-07 13:56:49 +01:00
|
|
|
if extension:
|
|
|
|
bundle = _filter_by_extension(bundle, extension)
|
|
|
|
return bundle
|
|
|
|
|
|
|
|
|
|
|
|
def get_files(bundle_name, extension=None, config='DEFAULT'):
|
|
|
|
'''Returns list of chunks from named bundle'''
|
2022-02-17 17:46:49 +01:00
|
|
|
loader = get_loader(config)
|
|
|
|
return list(_get_bundle(loader, bundle_name, extension))
|
2017-01-07 13:56:49 +01:00
|
|
|
|
|
|
|
|
2024-11-09 16:46:17 +01:00
|
|
|
def get_as_url_to_tag_dict(
|
2024-07-29 02:56:37 +02:00
|
|
|
bundle_name, request: Optional[HttpRequest] = None, extension=None,
|
|
|
|
config='DEFAULT', suffix='', attrs='', is_preload=False
|
|
|
|
) -> OrderedDict[str, str]:
|
2017-01-07 13:56:49 +01:00
|
|
|
'''
|
2024-01-15 21:06:45 +01:00
|
|
|
Get a dict of URLs to formatted <script> & <link> tags for the assets in the
|
2017-01-07 13:56:49 +01:00
|
|
|
named bundle.
|
|
|
|
|
|
|
|
:param bundle_name: The name of the bundle
|
|
|
|
:param extension: (optional) filter by extension, eg. 'js' or 'css'
|
|
|
|
:param config: (optional) the name of the configuration
|
2024-01-15 21:06:45 +01:00
|
|
|
:return: a dict of URLs to formatted tags as strings
|
2017-01-07 13:56:49 +01:00
|
|
|
'''
|
|
|
|
|
2022-02-16 13:08:14 +01:00
|
|
|
loader = get_loader(config)
|
2022-02-17 17:46:49 +01:00
|
|
|
bundle = _get_bundle(loader, bundle_name, extension)
|
2024-07-29 02:56:37 +02:00
|
|
|
result = OrderedDict[str, str]()
|
2024-07-29 01:45:10 +02:00
|
|
|
attrs_l = attrs.lower()
|
2022-02-16 13:08:14 +01:00
|
|
|
|
2017-01-07 13:56:49 +01:00
|
|
|
for chunk in bundle:
|
|
|
|
if chunk['name'].endswith(('.js', '.js.gz')):
|
2021-08-16 23:40:50 +02:00
|
|
|
if is_preload:
|
2023-12-29 19:04:15 +01:00
|
|
|
result[chunk['url']] = (
|
2021-08-16 23:40:50 +02:00
|
|
|
'<link rel="preload" as="script" href="{0}" {1}/>'
|
2023-12-29 19:04:15 +01:00
|
|
|
).format(''.join([chunk['url'], suffix]), attrs)
|
2021-08-16 23:40:50 +02:00
|
|
|
else:
|
2023-12-29 19:04:15 +01:00
|
|
|
result[chunk['url']] = (
|
2024-07-29 01:45:10 +02:00
|
|
|
'<script src="{0}"{2}{3}{1}></script>'
|
2022-02-16 13:08:14 +01:00
|
|
|
).format(
|
|
|
|
''.join([chunk['url'], suffix]),
|
|
|
|
attrs,
|
2024-07-29 01:45:10 +02:00
|
|
|
loader.get_integrity_attr(chunk, request, attrs_l),
|
|
|
|
loader.get_nonce_attr(chunk, request, attrs_l),
|
2023-12-29 19:04:15 +01:00
|
|
|
)
|
2017-01-07 13:56:49 +01:00
|
|
|
elif chunk['name'].endswith(('.css', '.css.gz')):
|
2023-12-29 19:04:15 +01:00
|
|
|
result[chunk['url']] = (
|
2024-07-29 01:45:10 +02:00
|
|
|
'<link href="{0}" rel={2}{3}{4}{1}/>'
|
2022-02-16 13:08:14 +01:00
|
|
|
).format(
|
|
|
|
''.join([chunk['url'], suffix]),
|
|
|
|
attrs,
|
|
|
|
'"stylesheet"' if not is_preload else '"preload" as="style"',
|
2024-07-29 01:45:10 +02:00
|
|
|
loader.get_integrity_attr(chunk, request, attrs_l),
|
|
|
|
loader.get_nonce_attr(chunk, request, attrs_l),
|
2023-12-29 19:04:15 +01:00
|
|
|
)
|
|
|
|
return result
|
2017-01-07 13:56:49 +01:00
|
|
|
|
|
|
|
|
2024-11-09 16:46:17 +01:00
|
|
|
def get_as_tags(
|
|
|
|
bundle_name, request=None, extension=None, config='DEFAULT', suffix='',
|
|
|
|
attrs='', is_preload=False):
|
2024-01-15 21:06:45 +01:00
|
|
|
'''
|
|
|
|
Get a list of formatted <script> & <link> tags for the assets in the
|
|
|
|
named bundle.
|
|
|
|
|
|
|
|
:param bundle_name: The name of the bundle
|
|
|
|
:param extension: (optional) filter by extension, eg. 'js' or 'css'
|
|
|
|
:param config: (optional) the name of the configuration
|
|
|
|
:return: a list of formatted tags as strings
|
|
|
|
'''
|
2024-11-09 16:46:17 +01:00
|
|
|
return list(get_as_url_to_tag_dict(bundle_name, request, extension, config, suffix, attrs, is_preload).values())
|
2024-01-15 21:06:45 +01:00
|
|
|
|
|
|
|
|
2017-01-07 13:56:49 +01:00
|
|
|
def get_static(asset_name, config='DEFAULT'):
|
|
|
|
'''
|
|
|
|
Equivalent to Django's 'static' look up but for webpack assets.
|
|
|
|
|
|
|
|
:param asset_name: the name of the asset
|
|
|
|
:param config: (optional) the name of the configuration
|
|
|
|
:return: path to webpack asset as a string
|
|
|
|
'''
|
2023-12-01 19:15:56 +01:00
|
|
|
public_path = get_loader(config).get_assets().get('publicPath')
|
|
|
|
if not public_path or public_path == 'auto':
|
|
|
|
public_path = getattr(settings, 'STATIC_URL')
|
|
|
|
|
|
|
|
return '{0}{1}'.format(public_path, asset_name)
|
2024-03-29 09:17:49 +01:00
|
|
|
|
2024-07-29 02:56:37 +02:00
|
|
|
|
2024-03-29 09:17:49 +01:00
|
|
|
def get_asset(source_filename, config='DEFAULT'):
|
|
|
|
'''
|
|
|
|
Equivalent to Django's 'static' look up but for webpack assets, given its original filename.
|
|
|
|
Allow handling files whose path has been modified by Webpack processing, such as including content hash to filename.
|
|
|
|
|
|
|
|
:param source_filename: the source filename of the asset
|
|
|
|
:param config: (optional) the name of the configuration
|
|
|
|
:return: path to webpack asset as a string
|
|
|
|
'''
|
|
|
|
loader = get_loader(config)
|
|
|
|
asset = loader.get_asset_by_source_filename(source_filename)
|
2024-07-29 02:56:37 +02:00
|
|
|
if not asset:
|
|
|
|
return None
|
2024-03-29 09:17:49 +01:00
|
|
|
|
|
|
|
return get_static(asset['name'], config)
|