Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support pnpm.onlyBuiltDependencies
Browse files Browse the repository at this point in the history
jbedard committed May 14, 2024

Verified

This commit was signed with the committer’s verified signature.
jbedard Jason Bedard
1 parent 322f833 commit aa4d0ce
Showing 6 changed files with 105 additions and 104 deletions.
12 changes: 11 additions & 1 deletion docs/pnpm.md
Original file line number Diff line number Diff line change
@@ -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
```
@@ -258,7 +260,15 @@ 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
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).
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.

When a package has lifecycle hooks the `lifecycle_*` attributes are applied to filter which hooks are run and how they are run.

You can disable lifecycle hooks completely by setting all packages to have no hooks, using

> `lifecycle_hooks = { "*": [] }` in `npm_translate_lock`.
Because rules_js models the execution of these hooks as build actions, rather than repository rules,
40 changes: 11 additions & 29 deletions npm/extensions.bzl
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ load("//npm/private:npm_import.bzl", "npm_import_lib", "npm_import_links_lib")
load("//npm/private:npm_translate_lock.bzl", "npm_translate_lock", "npm_translate_lock_lib")
load("//npm/private:npm_translate_lock_helpers.bzl", npm_translate_lock_helpers = "helpers")
load("//npm/private:npm_translate_lock_macro_helpers.bzl", macro_helpers = "helpers")
load("//npm/private:npm_translate_lock_state.bzl", "npm_translate_lock_state")
load("//npm/private:npmrc.bzl", "parse_npmrc")
load("//npm/private:transitive_closure.bzl", "translate_to_transitive_closure")
load("//npm/private:utils.bzl", "utils")
@@ -75,36 +76,16 @@ def _npm_translate_lock_bzlmod(attr):
)

def _npm_lock_imports_bzlmod(module_ctx, attr):
lock_importers = {}
lock_packages = {}
lock_patched_dependencies = {}
lock_parse_err = None
state = npm_translate_lock_state.new(attr.name, module_ctx, attr, True)

is_windows = repo_utils.is_windows(module_ctx)
host_yq = Label("@{}_{}//:yq{}".format(attr.yq_toolchain_prefix, repo_utils.platform(module_ctx), ".exe" if is_windows else ""))
yq_args = [
str(module_ctx.path(host_yq)),
str(module_ctx.path(attr.pnpm_lock)),
"-o=json",
]
result = module_ctx.execute(yq_args)
if result.return_code:
lock_parse_err = "failed to parse pnpm lock file with yq. '{}' exited with {}: \nSTDOUT:\n{}\nSTDERR:\n{}".format(" ".join(yq_args), result.return_code, result.stdout, result.stderr)
else:
lock_importers, lock_packages, lock_patched_dependencies, lock_parse_err = utils.parse_pnpm_lock_json(result.stdout if result.stdout != "null" else None) # NB: yq will return the string "null" if the yaml file is empty

if lock_parse_err != None:
msg = """
{type}: pnpm-lock.yaml parse error: {error}`.
""".format(type = "WARNING" if attr.update_pnpm_lock else "ERROR", error = lock_parse_err)

# buildifier: disable=print
if attr.update_pnpm_lock:
print(msg)
else:
fail(msg)
importers, packages = translate_to_transitive_closure(
state.importers(),
state.packages(),
attr.prod,
attr.dev,
attr.no_optional,
)

importers, packages = translate_to_transitive_closure(lock_importers, lock_packages, attr.prod, attr.dev, attr.no_optional)
registries = {}
npm_auth = {}
if attr.npmrc:
@@ -137,7 +118,8 @@ WARNING: Cannot determine home directory in order to load home `.npmrc` file in
imports = npm_translate_lock_helpers.get_npm_imports(
importers = importers,
packages = packages,
patched_dependencies = lock_patched_dependencies,
patched_dependencies = state.patched_dependencies(),
only_built_dependencies = state.only_built_dependencies(),
root_package = attr.pnpm_lock.package,
rctx_name = attr.name,
attr = attr,
3 changes: 2 additions & 1 deletion npm/private/npm_translate_lock.bzl
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ npm_translate_lock_lib = struct(
def _npm_translate_lock_impl(rctx):
rctx.report_progress("Initializing")

state = npm_translate_lock_state.new(rctx)
state = npm_translate_lock_state.new(rctx.name, rctx, rctx.attr, rctx.attr.bzlmod)

# If a pnpm lock file has not been specified then we need to bootstrap by running `pnpm
# import` in the user's repository
@@ -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(),
4 changes: 2 additions & 2 deletions npm/private/npm_translate_lock_generate.bzl
Original file line number Diff line number Diff line change
@@ -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()]

5 changes: 3 additions & 2 deletions npm/private/npm_translate_lock_helpers.bzl
Original file line number Diff line number Diff line change
@@ -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)
145 changes: 76 additions & 69 deletions npm/private/npm_translate_lock_state.bzl

Large diffs are not rendered by default.

0 comments on commit aa4d0ce

Please sign in to comment.