Skip to content

Commit

Permalink
feat: support pnpm.onlyBuiltDependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
jbedard committed May 14, 2024
1 parent 245151e commit 195f3a5
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 6 deletions.
17 changes: 15 additions & 2 deletions docs/pnpm.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_<hash>` file (with `<hash>` 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_<hash>= 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
```
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions npm/extensions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions npm/private/npm_translate_lock.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
4 changes: 2 additions & 2 deletions npm/private/npm_translate_lock_generate.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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()]

Expand Down
5 changes: 3 additions & 2 deletions npm/private/npm_translate_lock_helpers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions npm/private/npm_translate_lock_state.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand All @@ -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)
Expand All @@ -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,
}
Expand All @@ -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),
Expand Down

0 comments on commit 195f3a5

Please sign in to comment.