From 195f3a5376bf63e78f98df63b14c8271701412e0 Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Thu, 2 May 2024 17:38:13 -0700 Subject: [PATCH] feat: support pnpm.onlyBuiltDependencies --- docs/pnpm.md | 17 +++++++++++++++-- npm/extensions.bzl | 1 + npm/private/npm_translate_lock.bzl | 1 + npm/private/npm_translate_lock_generate.bzl | 4 ++-- npm/private/npm_translate_lock_helpers.bzl | 5 +++-- npm/private/npm_translate_lock_state.bzl | 8 ++++++++ 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/docs/pnpm.md b/docs/pnpm.md index 7abb2f73d..d34c276c3 100644 --- a/docs/pnpm.md +++ b/docs/pnpm.md @@ -179,11 +179,13 @@ See our [blog post](https://blog.aspect.dev/easier-merges-on-lockfiles) for a lo First, mark the `npm_translate_lock_` file (with `` replaced with the hash generated in your workspace) to use a custom custom merge driver, in this example named `ours`: + ``` .aspect/rules/external_repository_action_cache/npm_translate_lock_= merge=ours ``` Second, developers must define the `ours` custom merge driver in their git configuration to always accept local change: + ``` git config --global merge.ours.driver true ``` @@ -258,8 +260,19 @@ npm packages have "lifecycle scripts" such as `postinstall` which are documented We refer to these as "lifecycle hooks". -> You can disable this feature completely by setting all packages to have no hooks, using -> `lifecycle_hooks = { "*": [] }` in `npm_translate_lock`. +Determining which packages have lifecycle hooks is determined by, in priority order: + +1. The workspace `package.json` [`pnpm.onlyBuiltDependencies` attribute](https://pnpm.io/package_json#pnpmonlybuiltdependencies) if present. +2. The pnpm-lock.yaml `requiresBuild` attribute. Note this is only available in pnpm _before_ v9, see [pnpm #7707](https://github.com/pnpm/pnpm/issues/7707) for reasons why this attribute was removed. + +If the `pnpm.onlyBuiltDependencies` attribute is set, even when empty, it is considered to be the full list of +all packages containing lifecycle hooks. This is recommended going forward for pnpm v9+ compatibility. + +When a package has lifecycle hooks the `lifecycle_*` attributes are applied to filter which hooks are run and how they are run. + +For example, you can restrict lifecycle hooks across all packages to only run `postinstall`: + +> `lifecycle_hooks = { "*": ["postinstall"] }` in `npm_translate_lock`. Because rules_js models the execution of these hooks as build actions, rather than repository rules, the result can be stored in the remote cache and shared between developers. diff --git a/npm/extensions.bzl b/npm/extensions.bzl index 925c02613..2b15f526a 100644 --- a/npm/extensions.bzl +++ b/npm/extensions.bzl @@ -119,6 +119,7 @@ WARNING: Cannot determine home directory in order to load home `.npmrc` file in importers = importers, packages = packages, patched_dependencies = state.patched_dependencies(), + only_built_dependencies = state.only_built_dependencies(), root_package = attr.pnpm_lock.package, rctx_name = attr.name, attr = attr, diff --git a/npm/private/npm_translate_lock.bzl b/npm/private/npm_translate_lock.bzl index f58df9b9d..d6d584351 100644 --- a/npm/private/npm_translate_lock.bzl +++ b/npm/private/npm_translate_lock.bzl @@ -132,6 +132,7 @@ See https://github.com/aspect-build/rules_js/issues/1445 importers, packages, state.patched_dependencies(), + state.only_built_dependencies(), state.root_package(), state.default_registry(), state.npm_registries(), diff --git a/npm/private/npm_translate_lock_generate.bzl b/npm/private/npm_translate_lock_generate.bzl index a889a5eeb..d6050931b 100644 --- a/npm/private/npm_translate_lock_generate.bzl +++ b/npm/private/npm_translate_lock_generate.bzl @@ -80,11 +80,11 @@ _PACKAGE_JSON_BZL_FILENAME = "package_json.bzl" _RESOLVED_JSON_FILENAME = "resolved.json" # buildifier: disable=function-docstring -def generate_repository_files(rctx, pnpm_lock_label, importers, packages, patched_dependencies, root_package, default_registry, npm_registries, npm_auth, link_workspace): +def generate_repository_files(rctx, pnpm_lock_label, importers, packages, patched_dependencies, only_built_dependencies, root_package, default_registry, npm_registries, npm_auth, link_workspace): # empty line after bzl docstring since buildifier expects this if this file is vendored in generated_by_prefix = "\"\"\"@generated by npm_translate_lock(name = \"{}\", pnpm_lock = \"{}\")\"\"\"\n".format(helpers.to_apparent_repo_name(rctx.name), str(pnpm_lock_label)) - npm_imports = helpers.get_npm_imports(importers, packages, patched_dependencies, root_package, rctx.name, rctx.attr, rctx.attr.lifecycle_hooks, rctx.attr.lifecycle_hooks_execution_requirements, rctx.attr.lifecycle_hooks_use_default_shell_env, npm_registries, default_registry, npm_auth) + npm_imports = helpers.get_npm_imports(importers, packages, patched_dependencies, only_built_dependencies, root_package, rctx.name, rctx.attr, rctx.attr.lifecycle_hooks, rctx.attr.lifecycle_hooks_execution_requirements, rctx.attr.lifecycle_hooks_use_default_shell_env, npm_registries, default_registry, npm_auth) link_packages = [helpers.link_package(root_package, import_path) for import_path in importers.keys()] diff --git a/npm/private/npm_translate_lock_helpers.bzl b/npm/private/npm_translate_lock_helpers.bzl index f2265cb94..3d0ebaee1 100644 --- a/npm/private/npm_translate_lock_helpers.bzl +++ b/npm/private/npm_translate_lock_helpers.bzl @@ -236,7 +236,7 @@ def _select_npm_auth(url, npm_auth): return npm_auth_bearer, npm_auth_basic, npm_auth_username, npm_auth_password ################################################################################ -def _get_npm_imports(importers, packages, patched_dependencies, root_package, rctx_name, attr, all_lifecycle_hooks, all_lifecycle_hooks_execution_requirements, all_lifecycle_hooks_use_default_shell_env, registries, default_registry, npm_auth): +def _get_npm_imports(importers, packages, patched_dependencies, only_built_dependencies, root_package, rctx_name, attr, all_lifecycle_hooks, all_lifecycle_hooks_execution_requirements, all_lifecycle_hooks_use_default_shell_env, registries, default_registry, npm_auth): "Converts packages from the lockfile to a struct of attributes for npm_import" if attr.prod and attr.dev: fail("prod and dev attributes cannot both be set to true") @@ -393,7 +393,8 @@ ERROR: can not apply both `pnpm.patchedDependencies` and `npm_translate_lock(pat elif name not in link_packages[public_hoist_package]: link_packages[public_hoist_package].append(name) - if requires_build: + run_lifecycle_hooks = name in only_built_dependencies if only_built_dependencies != None else requires_build + if run_lifecycle_hooks: lifecycle_hooks, _ = _gather_values_from_matching_names(False, all_lifecycle_hooks, "*", name, friendly_name, unfriendly_name) lifecycle_hooks_env, _ = _gather_values_from_matching_names(True, attr.lifecycle_hooks_envs, "*", name, friendly_name, unfriendly_name) lifecycle_hooks_execution_requirements, _ = _gather_values_from_matching_names(False, all_lifecycle_hooks_execution_requirements, "*", name, friendly_name, unfriendly_name) diff --git a/npm/private/npm_translate_lock_state.bzl b/npm/private/npm_translate_lock_state.bzl index d3a2d5b00..4e4098fc7 100644 --- a/npm/private/npm_translate_lock_state.bzl +++ b/npm/private/npm_translate_lock_state.bzl @@ -531,6 +531,9 @@ def _packages(priv): def _patched_dependencies(priv): return priv["patched_dependencies"] +def _only_built_dependencies(priv): + return _root_package_json(priv).get("pnpm", {}).get("onlyBuildDependencies", None) + def _num_patches(priv): return priv["num_patches"] @@ -543,6 +546,9 @@ def _npm_auth(priv): def _root_package(priv): return priv["root_package"] +def _root_package_json(priv): + return priv["root_package_json"] + ################################################################################ def _new(rctx_name, rctx, attr, bzlmod): label_store = repository_label_store.new(rctx.path) @@ -564,6 +570,7 @@ def _new(rctx_name, rctx, attr, bzlmod): "npm_registries": {}, "packages": {}, "root_package": None, + "root_package_json": {}, "patched_dependencies": {}, "should_update_pnpm_lock": should_update_pnpm_lock, } @@ -578,6 +585,7 @@ def _new(rctx_name, rctx, attr, bzlmod): importers = lambda: _importers(priv), packages = lambda: _packages(priv), patched_dependencies = lambda: _patched_dependencies(priv), + only_built_dependencies = lambda: _only_built_dependencies(priv), npm_registries = lambda: _npm_registries(priv), npm_auth = lambda: _npm_auth(priv), num_patches = lambda: _num_patches(priv),