diff --git a/invenio_cli/commands/assets.py b/invenio_cli/commands/assets.py index 5cc13d5..d766294 100644 --- a/invenio_cli/commands/assets.py +++ b/invenio_cli/commands/assets.py @@ -45,20 +45,6 @@ def _watch_js_module(self, pkg): status_code=status_code, ) - @staticmethod - def _run_script(module_pkg): - """Run script and return a ProcessResponse.""" - status_code = module_pkg.run_script("link-dist") - if status_code == 0: - return ProcessResponse( - output="Module linked correctly to global", status_code=0 - ) - else: - return ProcessResponse( - error=f"Unable to link-dist. Got error code {status_code}", - status_code=status_code, - ) - def _npm_install_command(self, path, module_pkg): """Run command and return a ProcessResponse.""" install_args = self.cli_config.javascript_package_manager.install_local_package( @@ -88,28 +74,6 @@ def _build_script(module_pkg): status_code=status_code, ) - @staticmethod - def _assets_link(assets_pkg, module_pkg): - try: - module_name = module_pkg.package_json["name"] - except FileNotFoundError as e: - return ProcessResponse( - error="No module found on the specified path. " - f"File not found {e.filename}", - status_code=1, - ) - - status_code = assets_pkg.link(module_name) - if status_code == 0: - return ProcessResponse( - output="Global module linked correctly to local folder", status_code=0 - ) - else: - return ProcessResponse( - error=f"Unable to link module. Got error code {status_code}", - status_code=status_code, - ) - def watch_assets(self): """High-level command to watch assets for changes.""" watch_cmd = self.cli_config.python_package_manager.run_command( @@ -142,17 +106,19 @@ def link_js_module(self, path): args={"module_pkg": module_pkg}, message="Building...", ), - FunctionStep( # Create link to global folder - func=self._run_script, - args={"module_pkg": module_pkg}, - message="Linking module to global dist...", - ), - FunctionStep( # Link the global folder to the assets folder. - func=self._assets_link, + ] + + # The commands necessary for linking local JS packages vary by package manager + js_package_manager = self.cli_config.javascript_package_manager + link_steps = [ + FunctionStep( + func=step.function, args={"assets_pkg": assets_pkg, "module_pkg": module_pkg}, - message="Linking module to assets...", - ), + message=step.message, + ) + for step in js_package_manager.package_linking_steps() ] + steps.extend(link_steps) return steps diff --git a/invenio_cli/helpers/package_managers.py b/invenio_cli/helpers/package_managers.py index f5cf151..5bc98fe 100644 --- a/invenio_cli/helpers/package_managers.py +++ b/invenio_cli/helpers/package_managers.py @@ -9,11 +9,15 @@ import os from abc import ABC +from collections.abc import Callable +from dataclasses import dataclass from pathlib import Path from typing import Dict, List, Union from pynpm import NPMPackage, PNPMPackage +from ..helpers.process import ProcessResponse + class PythonPackageManager(ABC): """Interface for creating tool-specific Python package management commands.""" @@ -176,6 +180,14 @@ def start_activated_subshell(self) -> List[str]: return [shell, "-c", f"source .venv/bin/activate; exec {shell} -i"] +@dataclass +class AssetsFunction: + """Function to run an assets command with `assets_pkg` and `module_pkg`.""" + + function: Callable[[NPMPackage, NPMPackage], ProcessResponse] + message: str + + class JavascriptPackageManager(ABC): """Interface for creating tool-specific JS package management commands.""" @@ -193,6 +205,10 @@ def env_overrides(self) -> Dict[str, str]: """Provide environment overrides for building Invenio assets.""" return {} + def package_linking_steps(self) -> List[AssetsFunction]: + """Generate steps to link the target package to the project.""" + raise NotImplementedError() + class NPM(JavascriptPackageManager): """Generate ``npm`` commands for managing JS packages.""" @@ -211,6 +227,52 @@ def env_overrides(self): """Provide environment overrides for building Invenio assets.""" return {"INVENIO_WEBPACKEXT_NPM_PKG_CLS": "pynpm:NPMPackage"} + def package_linking_steps(self): + """Generate steps to link the target package to the project.""" + + def _link_package_to_global( + assets_pkg: NPMPackage, module_pkg: NPMPackage + ) -> ProcessResponse: + status_code = module_pkg.run_script("link-dist") + if status_code == 0: + return ProcessResponse( + output="Module linked correctly to global", status_code=0 + ) + else: + return ProcessResponse( + error=f"Unable to link-dist. Got error code {status_code}", + status_code=status_code, + ) + + def _link_global_package( + assets_pkg: NPMPackage, module_pkg: NPMPackage + ) -> ProcessResponse: + try: + module_name = module_pkg.package_json["name"] + except FileNotFoundError as e: + return ProcessResponse( + error="No module found on the specified path. " + f"File not found {e.filename}", + status_code=1, + ) + + status_code = assets_pkg.link(module_name) + if status_code == 0: + return ProcessResponse( + output="Global module linked correctly to local folder", + status_code=0, + ) + else: + return ProcessResponse( + error=f"Unable to link module. Got error code {status_code}", + status_code=status_code, + ) + + return [ + AssetsFunction(_link_package_to_global, "Linking module to global dist..."), + AssetsFunction(_link_global_package, "Linking module to assets..."), + ] + class PNPM(JavascriptPackageManager): """Generate ``pnpm`` commands for managing JS packages.""" @@ -228,3 +290,74 @@ def install_local_package(self, path): def env_overrides(self): """Provide environment overrides for building Invenio assets.""" return {"INVENIO_WEBPACKEXT_NPM_PKG_CLS": "pynpm:PNPMPackage"} + + def package_linking_steps(self): + """Generate steps to link the target package to the project.""" + + def _prelink_dist( + assets_pkg: NPMPackage, module_pkg: NPMPackage + ) -> ProcessResponse: + """Execute the "prelink-dist" script.""" + status_code = module_pkg.run_script("prelink-dist") + if status_code == 0: + return ProcessResponse( + output="Successfully ran prelink-dist script.", + status_code=0, + ) + else: + return ProcessResponse( + error=f"Unable to prelink-dist. Got error code {status_code}", + status_code=status_code, + ) + + def _link_package_single_step( + assets_pkg: NPMPackage, module_pkg: NPMPackage + ) -> ProcessResponse: + """Execute the PNPM single-step package linking.""" + try: + # Accessing `package_json` fails if the file can't be found + module_pkg.package_json["name"] + + # This is geared towards Invenio JS packages... + # But so are all the other steps + status_code = assets_pkg.link( + str(module_pkg.package_json_path.parent / "dist") + ) + except FileNotFoundError as e: + return ProcessResponse( + error="No module found on the specified path. " + f"File not found {e.filename}", + status_code=1, + ) + if status_code == 0: + return ProcessResponse( + output="Module linked successfully to assets", + status_code=0, + ) + else: + return ProcessResponse( + error=f"Unable to link module. Got error code {status_code}", + status_code=status_code, + ) + + def _postlink_dist( + assets_pkg: NPMPackage, module_pkg: NPMPackage + ) -> ProcessResponse: + """Execute the "postlink-dist" script.""" + status_code = module_pkg.run_script("postlink-dist") + if status_code == 0: + return ProcessResponse( + output="Successfully ran postlink-dist script.", + status_code=0, + ) + else: + return ProcessResponse( + error=f"Unable to run postlink-dist. Got error code {status_code}", + status_code=status_code, + ) + + return [ + AssetsFunction(_prelink_dist, "Executing prelink-dist script..."), + AssetsFunction(_link_package_single_step, "Linking module to assets..."), + AssetsFunction(_postlink_dist, "Executing postlink-dist script..."), + ]