diff --git a/docs/conf.py b/docs/conf.py index 48a8c23..7909171 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -321,3 +321,5 @@ # Autodoc configuraton. autoclass_content = "both" + +nitpick_ignore = [("py:func", "flask_webpackext.project.flask_config")] diff --git a/flask_webpackext/project.py b/flask_webpackext/project.py index 530c409..ae57f69 100644 --- a/flask_webpackext/project.py +++ b/flask_webpackext/project.py @@ -2,7 +2,7 @@ # # This file is part of Flask-WebpackExt # Copyright (C) 2017, 2018 CERN. -# Copyright (C) 2024 Graz University of Technology. +# Copyright (C) 2024-2025 Graz University of Technology. # # Flask-WebpackExt is free software; you can redistribute it and/or modify # it under the terms of the Revised BSD License; see LICENSE file for @@ -14,16 +14,60 @@ from flask import current_app from flask.helpers import get_root_path +from pynpm import NPMPackage, PNPMPackage from pywebpack import WebpackBundleProject as PyWebpackBundleProject from pywebpack import WebpackTemplateProject as PyWebpackTemplateProject +from pywebpack.helpers import cached +from werkzeug.utils import import_string -from .proxies import current_webpack +class _PathStorageMixin: + """Mixin class.""" -def flask_config(): - """Flask configuration injected in Webpack. + @property + def path(self): + """Get path to project.""" + try: + return self.app.config["WEBPACKEXT_PROJECT_BUILDDIR"] + except KeyError: + return join(self.app.instance_path, "assets") - :return: Dictionary which contains the information Flask-WebpackExt knows + @property + def dist_dir(self): + """Get dist dir.""" + try: + return self.app.config["WEBPACKEXT_PROJECT_DISTDIR"] + except KeyError: + return join(self.app.static_folder, "dist") + + @property + def project(self): + project = self.app.config["WEBPACKEXT_PROJECT"] + if isinstance(project, str): + return import_string(project) + return project + + @property + def npmpkg(self): + """Get API to NPM package.""" + js_packages_manager = self.app.config.get("JAVASCRIPT_PACKAGES_MANAGER", "npm") + if js_packages_manager == "pnpm": + return PNPMPackage(self.path) + + return NPMPackage(self.path) + + @property + def storage_cls(self): + """Get storage class.""" + cls_ = self.app.config["WEBPACKEXT_STORAGE_CLS"] + if isinstance(cls_, str): + return import_string(cls_) + return cls_ + + def flask_config(self): + """Flask configuration injected in Webpack. + + :return: Dictionary which contains the information Flask-WebpackExt knows about a Webpack project and the absolute URLs for static files and assets. The dictionary consists of a key ``build`` with the following keys inside: @@ -34,54 +78,41 @@ def flask_config(): * ``assetsURL``: URL to access the built files. * ``staticPath``: Absolute path to the generated static directory. * ``staticURL``: URL to access the static files.. - """ - assets_url = current_app.config["WEBPACKEXT_PROJECT_DISTURL"] - if not assets_url.endswith("/"): - assets_url += "/" - static_url = current_app.static_url_path - if not static_url.endswith("/"): - static_url += "/" - - return { - "build": { - "debug": current_app.debug, - "context": current_webpack.project.path, - "assetsPath": current_app.config["WEBPACKEXT_PROJECT_DISTDIR"], - "assetsURL": assets_url, - "staticPath": current_app.static_folder, - "staticURL": static_url, + """ + assets_url = self.app.config["WEBPACKEXT_PROJECT_DISTURL"] + if not assets_url.endswith("/"): + assets_url += "/" + static_url = self.app.static_url_path + if not static_url.endswith("/"): + static_url += "/" + + return { + "build": { + "debug": self.app.debug, + "context": self.project.path, + "assetsPath": self.app.config["WEBPACKEXT_PROJECT_DISTDIR"], + "assetsURL": assets_url, + "staticPath": self.app.static_folder, + "staticURL": static_url, + } } - } - -def flask_allowed_copy_paths(): - """Get the allowed copy paths from the Flask application.""" - return [ - current_app.instance_path, - current_webpack.project.path, - current_app.static_folder, - current_app.config["WEBPACKEXT_PROJECT_DISTDIR"], - ] - - -class _PathStorageMixin(object): - """Mixin class.""" - - @property - def path(self): - """Get path to project.""" - return current_app.config["WEBPACKEXT_PROJECT_BUILDDIR"] - - @property - def storage_cls(self): - """Get storage class.""" - return current_webpack.storage_cls + def flask_allowed_copy_paths(self): + """Get the allowed copy paths from the Flask application.""" + return [ + self.app.instance_path, + self.path, + self.app.static_folder, + self.dist_dir, + ] class WebpackTemplateProject(_PathStorageMixin, PyWebpackTemplateProject): """Flask webpack template project.""" - def __init__(self, import_name, project_folder=None, config=None, config_path=None): + def __init__( + self, import_name, project_folder=None, config=None, config_path=None, app=None + ): """Initialize project. :param import_name: Name of the module where the @@ -96,11 +127,13 @@ def __init__(self, import_name, project_folder=None, config=None, config_path=No ``config.json``, this file is generated by :func:`flask_webpackext.project.flask_config`. """ + self.app = app or current_app project_template_dir = join(get_root_path(import_name), project_folder) + super().__init__( None, project_template_dir=project_template_dir, - config=config or flask_config, + config=config or self.flask_config, config_path=config_path, ) @@ -116,6 +149,7 @@ def __init__( config=None, config_path=None, allowed_copy_paths=None, + app=None, ): """Initialize templated folder. @@ -141,12 +175,25 @@ class is instantiated. It is used to determine the absolute path :param allowed_copy_paths: List of paths (absolute, or relative to the `config_path`) that are allowed for bundle copy instructions. """ + self.app = app or current_app project_template_dir = join(get_root_path(import_name), project_folder) + config = config or self.flask_config + allowed_copy_paths = allowed_copy_paths or self.flask_allowed_copy_paths super().__init__( None, project_template_dir=project_template_dir, bundles=bundles, - config=config or flask_config, + config=config, config_path=config_path, - allowed_copy_paths=allowed_copy_paths or flask_allowed_copy_paths, + allowed_copy_paths=allowed_copy_paths, ) + + @property + @cached + def entry(self): + """Get webpack entry points.""" + # this enables to use the bundle without the current_app context + for bundle in self.bundles: + bundle.app = self.app + + return super().entry diff --git a/tests/conftest.py b/tests/conftest.py index e63e1cc..8927b61 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ # # This file is part of Flask-WebpackExt # Copyright (C) 2017 CERN. +# Copyright (C) 2024 Graz University of Technology. # # Flask-WebpackExt is free software; you can redistribute it and/or modify # it under the terms of the Revised BSD License; see LICENSE file for @@ -128,7 +129,7 @@ def bundles(): @pytest.fixture() def projectbundle(app, bundles): """Webpack bundle project.""" - project = WebpackBundleProject(__name__, "assetsbundle", bundles=bundles) + project = WebpackBundleProject(__name__, "assetsbundle", bundles=bundles, app=app) app.config.update( { "WEBPACKEXT_PROJECT": project,