Source code for dazzler.system._requirements

import pathlib
import re
import os
import warnings

# Create a data dir for the requirements once and for all.
import typing

from dazzler.tools import format_tag

from dazzler.errors import (
    InvalidRequirementError, InvalidRequirementKindError
)


[docs]class RequirementWarning(UserWarning): pass
[docs]class Requirement: """Represents a static asset to include as dependency for rendering."""
[docs] def __init__( self, internal: str = None, kind: str = None, name: str = None, package: str = None, dev: bool = None, external: str = None, page: str = None, integrity: str = None ): """ :param internal: Local path of the requirement to include on the page. :param kind: The file extension. :param name: The file name. :param package: The package related to this requirement. :param dev: If the requirement is a dev requirement to serve only when in debug mode. :param external: URL to serve instead when running with ``prefer_external`` mode enable in the configs. :param page: The page related to this requirement. :param integrity: The integrity hash. (sri) """ if internal is None and external is None: raise InvalidRequirementError( 'Please give either an internal or external file' ) self.internal = internal self.name = name or os.path.basename(internal or external) self.kind = kind or self.name.split(os.extsep)[-1] self.dev = dev self.external = external self.external_only = external and not internal self.internal_only = internal and not external self.integrity = integrity paths = ['dist', self.name] url = ['/dazzler', 'requirements', 'static', 'dist', self.name] if package: paths.insert(0, package) url.insert(3, package) if page: paths.insert(0, page) url.insert(3, page) if dev: paths[paths.index('dist')] = 'dev' self.dev_static = os.path.join(*paths) url[url.index('dist')] = 'dev' self.dev_url = '/'.join(url) self.internal_static = os.path.join(*paths) self.internal_url = '/'.join(url) if self.external_only: warnings.warn( f'No local file for requirement: {self.external}', RequirementWarning )
[docs] def prepare(self, external=False) -> dict: attributes = {} if self.kind == 'js': uri_key = 'src' else: uri_key = 'href' if self.kind == 'css': attributes['rel'] = 'stylesheet' attributes['type'] = 'text/css' if self.external_only: uri = self.external elif external and self.external: uri = self.external attributes['crossorigin'] = ('{key}', 'crossorigin') else: uri = self.internal_url attributes[uri_key] = uri if self.integrity and not self.dev: attributes['integrity'] = self.integrity return { 'kind': self.kind, 'url': uri, 'attributes': attributes, 'key': self.name, }
[docs] def tag(self, external=False): prepared = self.prepare(external) if self.kind == 'css': return format_tag( 'link', prepared['attributes'], opened=True, close=False ) if self.kind == 'js': return format_tag('script', prepared['attributes']) if self.kind == 'map': return '' raise InvalidRequirementKindError( f'Invalid requirement kind: {self.kind}' )
def __str__(self): return self.internal or self.external def __repr__(self): return self.tag()
[docs]def assets_to_requirements( path: str, data: dict, dev_data: dict = None, dev_path: str = None, package_name: str = None, external: str = None ) -> typing.List[Requirement]: """ Turns the output of webpack-assets-tracker into a list of Requirements :param path: Path where production assets are located :param data: Bundle tracker data.' :param dev_data: Bundle tracker dev data, if not provided take data. :param dev_path: Path where dev assets are located. :param package_name: The name of the package if used for packages. :param external: External base path for the requirement. :return: """ dev_data = dev_data or {} dev_path = dev_path or path return [ Requirement( internal=os.path.join(path, internal['name']), package=package_name, external=f'{external}/{internal["name"]}' if external else None, integrity=internal.get('integrity') ) for internal in data ] + [ Requirement( internal=os.path.join(dev_path, dev['name']), package=package_name, dev=True, external=f'{external}/{dev["name"]}' if external else None, ) for dev in dev_data ]
[docs]def collect_requirements(directory: str, page: str = None): """ Collect all js/css files in the directory and map them as requirement :param directory: :param page: :return: """ requirements = [] for current, _, files in os.walk(directory): for file in sorted( ( x for x in files if x.endswith('.js') or x.endswith('.css') ), key=lambda k: [ int(x) if x.isdigit() else x for x in re.split('([0-9]+)', k) ] ): path = os.path.join(current, file) requirements.append( Requirement( internal=path, page=page, name=str(pathlib.Path(path).relative_to(directory)) ) ) return requirements
[docs]def filter_dev_requirements(requirements, dev): return ( x for x in requirements if (x.dev and dev) or (not dev and not x.dev) )