From 38c573ed16d4f5e32e1166009b7c37baa79c09ad Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Thu, 22 Feb 2024 10:42:48 -0800 Subject: [PATCH] feat: add replace_packages to npm_translate_lock (#1481) --- MODULE.bazel | 3 + WORKSPACE | 3 + WORKSPACE.bzlmod | 10 + docs/npm_import.md | 10 +- docs/npm_translate_lock.md | 5 +- js/dev_repositories.bzl | 7 + npm/extensions.bzl | 3 + npm/private/npm_import.bzl | 20 +- npm/private/npm_translate_lock.bzl | 16 ++ npm/private/npm_translate_lock_generate.bzl | 13 +- npm/private/npm_translate_lock_helpers.bzl | 8 + npm/private/test/BUILD.bazel | 1 + npm/private/test/chalk_links_defs_checked.bzl | 176 ++++++++++++++++++ npm/private/test/repositories_checked.bzl | 1 + npm/private/test/vendored/chalk-5.0.1.BUILD | 11 ++ 15 files changed, 275 insertions(+), 12 deletions(-) create mode 100644 npm/private/test/chalk_links_defs_checked.bzl create mode 100644 npm/private/test/vendored/chalk-5.0.1.BUILD diff --git a/MODULE.bazel b/MODULE.bazel index 7cbcfdc9c..480c2c018 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -189,6 +189,9 @@ npm.npm_translate_lock( # other direct dependencies in the `examples/npm_deps/package.json`. "ms@2.1.3": ["examples/npm_deps"], }, + replace_packages = { + "chalk@5.0.1": "@chalk_501//:pkg", + }, update_pnpm_lock = True, verify_node_modules_ignored = "//:.bazelignore", verify_patches = "//examples/npm_deps/patches:patches", diff --git a/WORKSPACE b/WORKSPACE index dd9a9be9a..9a4cf5d0c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -160,6 +160,9 @@ npm_translate_lock( # other direct dependencies in the `examples/npm_deps/package.json`. "ms@2.1.3": ["examples/npm_deps"], }, + replace_packages = { + "chalk@5.0.1": "@chalk_501//:pkg", + }, update_pnpm_lock = True, verify_node_modules_ignored = "//:.bazelignore", verify_patches = "//examples/npm_deps/patches:patches", diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod index bfe7c27f4..03e4a2764 100644 --- a/WORKSPACE.bzlmod +++ b/WORKSPACE.bzlmod @@ -8,3 +8,13 @@ load( fetch_shfmt() fetch_terraform() + +# dev dependency +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "chalk_501", + build_file = "//npm/private/test:vendored/chalk-5.0.1.BUILD", + integrity = "sha256-/nD5GSp77HDNFDwIL68S5PbS+8gefWkube2iIr80/x4=", + url = "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", +) diff --git a/docs/npm_import.md b/docs/npm_import.md index 60f736694..8c4e2a27f 100644 --- a/docs/npm_import.md +++ b/docs/npm_import.md @@ -26,10 +26,11 @@ for a given lockfile.
 npm_import(name, package, version, deps, extra_build_content, transitive_closure, root_package,
            link_workspace, link_packages, lifecycle_hooks, lifecycle_hooks_execution_requirements,
-           lifecycle_hooks_env, integrity, url, commit, package_visibility, patch_args, patches,
-           custom_postinstall, npm_auth, npm_auth_basic, npm_auth_username, npm_auth_password, bins,
-           dev, register_copy_directory_toolchains, register_copy_to_directory_toolchains,
-           run_lifecycle_hooks, lifecycle_hooks_no_sandbox, kwargs)
+           lifecycle_hooks_env, integrity, url, commit, replace_package, package_visibility,
+           patch_args, patches, custom_postinstall, npm_auth, npm_auth_basic, npm_auth_username,
+           npm_auth_password, bins, dev, register_copy_directory_toolchains,
+           register_copy_to_directory_toolchains, run_lifecycle_hooks, lifecycle_hooks_no_sandbox,
+           kwargs)
 
Import a single npm package into Bazel. @@ -149,6 +150,7 @@ Read more about the downloader config: <https://blog.aspect.dev/configuring-b | integrity | Expected checksum of the file downloaded, in Subresource Integrity format. This must match the checksum of the file downloaded.

This is the same as appears in the pnpm-lock.yaml, yarn.lock or package-lock.json file.

It is a security risk to omit the checksum as remote files can change.

At best omitting this field will make your build non-hermetic.

It is optional to make development easier but should be set before shipping. | "" | | url | Optional url for this package. If unset, a default npm registry url is generated from the package name and version.

May start with git+ssh:// or git+https:// to indicate a git repository. For example,

 git+ssh://git@github.com/org/repo.git 


If url is configured as a git repository, the commit attribute must be set to the desired commit. | "" | | commit | Specific commit to be checked out if url is a git repository. | "" | +| replace_package | Use the specified npm_package target when linking instead of the fetched sources for this npm package.

The injected npm_package target may optionally contribute transitive npm package dependencies on top of the transitive dependencies specified in the pnpm lock file for the same package, however, these transitive dependencies must not collide with pnpm lock specified transitive dependencies.

Any patches specified for this package will be not applied to the injected npm_package target. They will be applied, however, to the fetches sources so they can still be useful for patching the fetched package.json file, which is used to determine the generated bin entries for the package.

NB: lifecycle hooks and custom_postinstall scripts, if implicitly or explicitly enabled, will be run on the injected npm_package. These may be disabled explicitly using the lifecycle_hooks attribute. | None | | package_visibility | Visibility of generated node_module link targets. | ["//visibility:public"] | | patch_args | Arguments to pass to the patch tool.

-p1 will usually be needed for patches generated by git. | ["-p0"] | | patches | Patch files to apply onto the downloaded npm package. | [] | diff --git a/docs/npm_translate_lock.md b/docs/npm_translate_lock.md index 559c7313e..e861f3cb7 100644 --- a/docs/npm_translate_lock.md +++ b/docs/npm_translate_lock.md @@ -63,8 +63,8 @@ npm_translate_lock(name, patches, patch_args, custom_postinstalls, package_visibility, prod, public_hoist_packages, dev, no_optional, run_lifecycle_hooks, lifecycle_hooks, lifecycle_hooks_envs, lifecycle_hooks_exclude, - lifecycle_hooks_execution_requirements, lifecycle_hooks_no_sandbox, bins, - verify_node_modules_ignored, verify_patches, quiet, + lifecycle_hooks_execution_requirements, lifecycle_hooks_no_sandbox, + replace_packages, bins, verify_node_modules_ignored, verify_patches, quiet, external_repository_action_cache, link_workspace, pnpm_version, register_copy_directory_toolchains, register_copy_to_directory_toolchains, register_yq_toolchains, npm_package_target_name, use_starlark_yaml_parser, @@ -122,6 +122,7 @@ For more about how to use npm_translate_lock, read [pnpm and rules_js](/docs/pnp | lifecycle_hooks_exclude | A list of package names or package names with their version (e.g., "my-package" or "my-package@v1.2.3") to not run any lifecycle hooks on.

Equivalent to adding <value>: [] to lifecycle_hooks.

Read more: [lifecycles](/docs/pnpm.md#lifecycles) | [] | | lifecycle_hooks_execution_requirements | Execution requirements applied to the preinstall, install and postinstall lifecycle hooks on npm packages.

The execution requirements can be defined per package by package name or globally using "*".

Execution requirements are not additive. The most specific match wins.

Read more: [lifecycles](/docs/pnpm.md#lifecycles) | {} | | lifecycle_hooks_no_sandbox | If True, a "no-sandbox" execution requirement is added to all lifecycle hooks unless overridden by lifecycle_hooks_execution_requirements.

Equivalent to adding "*": ["no-sandbox"] to lifecycle_hooks_execution_requirements.

This defaults to True to limit the overhead of sandbox creation and copying the output TreeArtifacts out of the sandbox.

Read more: [lifecycles](/docs/pnpm.md#lifecycles) | True | +| replace_packages | A dict of package names to npm_package targets to link instead of the sources specified in the pnpm lock file for the corresponding packages.

The injected npm_package targets may optionally contribute transitive npm package dependencies on top of the transitive dependencies specified in the pnpm lock file for their respective packages, however, these transitive dependencies must not collide with pnpm lock specified transitive dependencies.

Any patches specified for the packages will be not applied to the injected npm_package targets. They will be applied, however, to the fetches sources for their respecitve packages so they can still be useful for patching the fetched package.json files, which are used to determine the generated bin entries for packages.

NB: lifecycle hooks and custom_postinstall scripts, if implicitly or explicitly enabled, will be run on the injected npm_package targets. These may be disabled explicitly using the lifecycle_hooks attribute. | {} | | bins | Binary files to create in node_modules/.bin for packages in this lock file.

For a given package, this is typically derived from the "bin" attribute in the package.json file of that package.

For example:

 bins = {     "@foo/bar": {         "foo": "./foo.js",         "bar": "./bar.js"     }, } 


Dicts of bins not additive. The most specific match wins.

In the future, this field may be automatically populated from information in the pnpm lock file. That feature is currently blocked on https://github.com/pnpm/pnpm/issues/5131.

Note: Bzlmod users must use an alternative syntax due to module extensions not supporting dict-of-dict attributes:

 bins = {     "@foo/bar": [         "foo=./foo.js",         "bar=./bar.js"     ], } 
| {} | | verify_node_modules_ignored | node_modules folders in the source tree should be ignored by Bazel.

This points to a .bazelignore file to verify that all nested node_modules directories pnpm will create are listed.

See https://github.com/bazelbuild/bazel/issues/8106 | None | | verify_patches | Label to a patch list file.

Use this in together with the list_patches macro to guarantee that all patches in a patch folder are included in the patches attribute.

For example:

 verify_patches = "//patches:patches.list", 


In your patches folder add a BUILD.bazel file containing.
 load("@aspect_rules_js//npm:repositories.bzl", "list_patches")

list_patches( name = "patches", out = "patches.list", )


Once you have created this file, you need to create an empty patches.list file before generating the first list. You can do this by running
 touch patches/patches.list 


Finally, write the patches file at least once to make sure all patches are listed. This can be done by running bazel run //patches:patches_update.

See the list_patches documentation for further info. NOTE: if you would like to customize the patches directory location, you can set a flag in the .npmrc. Here is an example of what this might look like
 # Set the directory for pnpm when patching # https://github.com/pnpm/pnpm/issues/6508#issuecomment-1537242124 patches-dir=bazel/js/patches 
If you do this, you will have to update the verify_patches path to be this path instead of //patches like above. | None | diff --git a/js/dev_repositories.bzl b/js/dev_repositories.bzl index f5e6e60b5..ea4a647ff 100644 --- a/js/dev_repositories.bzl +++ b/js/dev_repositories.bzl @@ -52,3 +52,10 @@ def rules_js_dev_dependencies(): strip_prefix = "rules_lint-0.7.0", url = "https://github.com/aspect-build/rules_lint/releases/download/v0.7.0/rules_lint-v0.7.0.tar.gz", ) + + http_archive( + name = "chalk_501", + integrity = "sha256-/nD5GSp77HDNFDwIL68S5PbS+8gefWkube2iIr80/x4=", + url = "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + build_file = "//npm/private/test:vendored/chalk-5.0.1.BUILD", + ) diff --git a/npm/extensions.bzl b/npm/extensions.bzl index 8a8d98b2d..9eca2c114 100644 --- a/npm/extensions.bzl +++ b/npm/extensions.bzl @@ -47,6 +47,7 @@ def _extension_impl(module_ctx): register_copy_directory_toolchains = False, # this registration is handled elsewhere with bzlmod register_copy_to_directory_toolchains = False, # this registration is handled elsewhere with bzlmod register_yq_toolchains = False, # this registration is handled elsewhere with bzlmod + replace_packages = attr.replace_packages, root_package = attr.root_package, run_lifecycle_hooks = attr.run_lifecycle_hooks, update_pnpm_lock = attr.update_pnpm_lock, @@ -160,6 +161,7 @@ WARNING: Cannot determine home directory in order to load home `.npmrc` file in package = i.package, patch_args = i.patch_args, patches = i.patches, + replace_package = i.replace_package, root_package = i.root_package, transitive_closure = i.transitive_closure, url = i.url, @@ -191,6 +193,7 @@ WARNING: Cannot determine home directory in order to load home `.npmrc` file in package = i.package, patch_args = i.patch_args, patches = i.patches, + replace_package = i.replace_package, root_package = i.root_package, run_lifecycle_hooks = i.run_lifecycle_hooks, transitive_closure = i.transitive_closure, diff --git a/npm/private/npm_import.bzl b/npm/private/npm_import.bzl index f9e81f000..2a66d54fa 100644 --- a/npm/private/npm_import.bzl +++ b/npm/private/npm_import.bzl @@ -718,7 +718,9 @@ def _impl_links(rctx): if npm_import_sources_repo_name.startswith("aspect_rules_js.npm."): npm_import_sources_repo_name = npm_import_sources_repo_name[len("aspect_rules_js.npm."):] - if rctx.attr.npm_translate_lock_repo: + if rctx.attr.replace_package: + npm_package_target = rctx.attr.replace_package + elif rctx.attr.npm_translate_lock_repo: npm_package_target = "@{}//:{}_source_directory".format( rctx.attr.npm_translate_lock_repo, npm_import_sources_repo_name, @@ -828,6 +830,7 @@ _ATTRS_LINKS = dicts.add(_COMMON_ATTRS, { "npm_translate_lock_repo": attr.string(), "transitive_closure": attr.string_list_dict(), "package_visibility": attr.string_list(), + "replace_package": attr.string(), }) _ATTRS = dicts.add(_COMMON_ATTRS, { @@ -899,6 +902,7 @@ def npm_import( integrity = "", url = "", commit = "", + replace_package = None, package_visibility = ["//visibility:public"], patch_args = ["-p0"], patches = [], @@ -1090,6 +1094,19 @@ def npm_import( commit: Specific commit to be checked out if url is a git repository. + replace_package: Use the specified npm_package target when linking instead of the fetched sources for this npm package. + + The injected npm_package target may optionally contribute transitive npm package dependencies on top + of the transitive dependencies specified in the pnpm lock file for the same package, however, these + transitive dependencies must not collide with pnpm lock specified transitive dependencies. + + Any patches specified for this package will be not applied to the injected npm_package target. They + will be applied, however, to the fetches sources so they can still be useful for patching the fetched + `package.json` file, which is used to determine the generated bin entries for the package. + + NB: lifecycle hooks and custom_postinstall scripts, if implicitly or explicitly enabled, will be run on + the injected npm_package. These may be disabled explicitly using the `lifecycle_hooks` attribute. + package_visibility: Visibility of generated node_module link targets. patch_args: Arguments to pass to the patch tool. @@ -1218,4 +1235,5 @@ def npm_import( bins = bins, npm_translate_lock_repo = npm_translate_lock_repo, package_visibility = package_visibility, + replace_package = replace_package, ) diff --git a/npm/private/npm_translate_lock.bzl b/npm/private/npm_translate_lock.bzl index 52b5bc7cb..5a00bd70d 100644 --- a/npm/private/npm_translate_lock.bzl +++ b/npm/private/npm_translate_lock.bzl @@ -53,6 +53,7 @@ _ATTRS = { "dev": attr.bool(), "external_repository_action_cache": attr.string(default = utils.default_external_repository_action_cache()), "generate_bzl_library_targets": attr.bool(), + "replace_packages": attr.string_dict(), "lifecycle_hooks_envs": attr.string_list_dict(), "lifecycle_hooks_execution_requirements": attr.string_list_dict(), "lifecycle_hooks": attr.string_list_dict(), @@ -173,6 +174,7 @@ def npm_translate_lock( lifecycle_hooks_exclude = [], lifecycle_hooks_execution_requirements = {}, lifecycle_hooks_no_sandbox = True, + replace_packages = {}, bins = {}, verify_node_modules_ignored = None, verify_patches = None, @@ -382,6 +384,19 @@ def npm_translate_lock( Read more: [lifecycles](/docs/pnpm.md#lifecycles) + replace_packages: A dict of package names to npm_package targets to link instead of the sources specified in the pnpm lock file for the corresponding packages. + + The injected npm_package targets may optionally contribute transitive npm package dependencies on top + of the transitive dependencies specified in the pnpm lock file for their respective packages, however, these + transitive dependencies must not collide with pnpm lock specified transitive dependencies. + + Any patches specified for the packages will be not applied to the injected npm_package targets. They + will be applied, however, to the fetches sources for their respecitve packages so they can still be useful + for patching the fetched `package.json` files, which are used to determine the generated bin entries for packages. + + NB: lifecycle hooks and custom_postinstall scripts, if implicitly or explicitly enabled, will be run on + the injected npm_package targets. These may be disabled explicitly using the `lifecycle_hooks` attribute. + bins: Binary files to create in `node_modules/.bin` for packages in this lock file. For a given package, this is typically derived from the "bin" attribute in @@ -616,6 +631,7 @@ WARNING: `package_json` attribute in `npm_translate_lock(name = "{name}")` is de lifecycle_hooks = lifecycle_hooks, lifecycle_hooks_envs = lifecycle_hooks_envs, lifecycle_hooks_execution_requirements = lifecycle_hooks_execution_requirements, + replace_packages = replace_packages, bins = bins_string_list_dict, verify_node_modules_ignored = verify_node_modules_ignored, verify_patches = verify_patches, diff --git a/npm/private/npm_translate_lock_generate.bzl b/npm/private/npm_translate_lock_generate.bzl index 688b50b37..41925f126 100644 --- a/npm/private/npm_translate_lock_generate.bzl +++ b/npm/private/npm_translate_lock_generate.bzl @@ -17,7 +17,7 @@ _NPM_IMPORT_TMPL = \ version = "{version}", url = "{url}", package_visibility = {package_visibility}, - npm_translate_lock_repo = "{npm_translate_lock_repo}",{maybe_dev}{maybe_commit}{maybe_generate_bzl_library_targets}{maybe_integrity}{maybe_deps}{maybe_transitive_closure}{maybe_patches}{maybe_patch_args}{maybe_lifecycle_hooks}{maybe_custom_postinstall}{maybe_lifecycle_hooks_env}{maybe_lifecycle_hooks_execution_requirements}{maybe_bins}{maybe_npm_auth}{maybe_npm_auth_basic}{maybe_npm_auth_username}{maybe_npm_auth_password} + npm_translate_lock_repo = "{npm_translate_lock_repo}",{maybe_dev}{maybe_commit}{maybe_generate_bzl_library_targets}{maybe_integrity}{maybe_deps}{maybe_transitive_closure}{maybe_patches}{maybe_patch_args}{maybe_lifecycle_hooks}{maybe_custom_postinstall}{maybe_lifecycle_hooks_env}{maybe_lifecycle_hooks_execution_requirements}{maybe_bins}{maybe_npm_auth}{maybe_npm_auth_basic}{maybe_npm_auth_username}{maybe_npm_auth_password}{maybe_replace_package} ) """ @@ -287,8 +287,8 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): links_bzl = {} links_targets_bzl = {} for (i, _import) in enumerate(npm_imports): - maybe_integrity = """ - integrity = "%s",""" % _import.integrity if _import.integrity else "" + maybe_integrity = (""" + integrity = "%s",""" % _import.integrity) if _import.integrity else "" maybe_deps = (""" deps = %s,""" % starlark_codegen_utils.to_dict_attr(_import.deps, 2)) if len(_import.deps) > 0 else "" maybe_transitive_closure = (""" @@ -309,8 +309,8 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): bins = %s,""" % starlark_codegen_utils.to_dict_attr(_import.bins, 2)) if len(_import.bins) > 0 else "" maybe_generate_bzl_library_targets = (""" generate_bzl_library_targets = True,""") if rctx.attr.generate_bzl_library_targets else "" - maybe_commit = """ - commit = "%s",""" % _import.commit if _import.commit else "" + maybe_commit = (""" + commit = "%s",""" % _import.commit) if _import.commit else "" maybe_npm_auth = (""" npm_auth = "%s",""" % _import.npm_auth) if _import.npm_auth else "" maybe_npm_auth_basic = (""" @@ -321,6 +321,8 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): npm_auth_password = "%s",""" % _import.npm_auth_password) if _import.npm_auth_password else "" maybe_dev = (""" dev = True,""") if _import.dev else "" + maybe_replace_package = (""" + replace_package = "%s",""" % _import.replace_package) if _import.replace_package else "" repositories_bzl.append(_NPM_IMPORT_TMPL.format( link_packages = starlark_codegen_utils.to_dict_attr(_import.link_packages, 2, quote_value = False), @@ -341,6 +343,7 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): maybe_npm_auth_username = maybe_npm_auth_username, maybe_patch_args = maybe_patch_args, maybe_patches = maybe_patches, + maybe_replace_package = maybe_replace_package, maybe_transitive_closure = maybe_transitive_closure, name = helpers.to_apparent_repo_name(_import.name), npm_translate_lock_repo = helpers.to_apparent_repo_name(rctx.name), diff --git a/npm/private/npm_translate_lock_helpers.bzl b/npm/private/npm_translate_lock_helpers.bzl index 94244d777..20c054fdb 100644 --- a/npm/private/npm_translate_lock_helpers.bzl +++ b/npm/private/npm_translate_lock_helpers.bzl @@ -360,6 +360,13 @@ ERROR: patch_args for package {package} contains a strip prefix that is incompat patches_used.extend(patches_keys) + # gather replace packages + replace_packages, _ = _gather_values_from_matching_names(True, attr.replace_packages, name, friendly_name, unfriendly_name) + if len(replace_packages) > 1: + msg = "Multiple package replacements found for package {}".format(name) + fail(msg) + replace_package = replace_packages[0] if replace_packages else None + # gather custom postinstalls custom_postinstalls, _ = _gather_values_from_matching_names(True, attr.custom_postinstalls, name, friendly_name, unfriendly_name) custom_postinstall = " && ".join([c for c in custom_postinstalls if c]) @@ -453,6 +460,7 @@ ERROR: patch_args for package {package} contains a strip prefix that is incompat bins = bins, package_info = package_info, dev = dev, + replace_package = replace_package, )) # Check that all patches files specified were used; this is a defense-in-depth since it is too diff --git a/npm/private/test/BUILD.bazel b/npm/private/test/BUILD.bazel index 498816071..287c2fcff 100644 --- a/npm/private/test/BUILD.bazel +++ b/npm/private/test/BUILD.bazel @@ -39,6 +39,7 @@ write_source_files( files = { "npm_defs_checked.bzl": "@npm//:defs.bzl", "repositories_checked.bzl": "@npm//:repositories.bzl", + "chalk_links_defs_checked.bzl": "@npm__chalk__5.0.1__links//:defs.bzl", "unused_links_defs_checked.bzl": "@npm__unused__0.2.2__links//:defs.bzl", "fsevents_links_defs_checked.bzl": "@npm__fsevents__2.3.2__links//:defs.bzl", "rollup_links_defs_checked.bzl": "@npm__rollup__2.70.2__links//:defs.bzl", diff --git a/npm/private/test/chalk_links_defs_checked.bzl b/npm/private/test/chalk_links_defs_checked.bzl new file mode 100644 index 000000000..aa228d57b --- /dev/null +++ b/npm/private/test/chalk_links_defs_checked.bzl @@ -0,0 +1,176 @@ +"@generated by @aspect_rules_js//npm/private:npm_import.bzl for npm package chalk@5.0.1" + +load("@aspect_rules_js//npm/private:npm_package_store_internal.bzl", _npm_package_store = "npm_package_store_internal") +load("@aspect_rules_js//npm/private:npm_link_package_store.bzl", _npm_link_package_store = "npm_link_package_store") +load("@aspect_rules_js//npm/private:utils.bzl", _utils = "utils") + +# Generated npm_package_store targets for npm package chalk@5.0.1 +# buildifier: disable=function-docstring +def npm_imported_package_store(name): + root_package = "" + is_root = native.package_name() == root_package + if not is_root: + msg = "No store links in bazel package '%s' for npm package npm package chalk@5.0.1. This is neither the root package nor a link package of this package." % native.package_name() + fail(msg) + if not name.endswith("/chalk"): + msg = "name must end with one of '/chalk' when linking the store in package 'chalk'; recommended value is 'node_modules/chalk'" + fail(msg) + link_root_name = name[:-len("/chalk")] + + deps = { + ":.aspect_rules_js/{}/chalk@5.0.1/pkg".format(link_root_name): "chalk", + } + ref_deps = {} + + store_target_name = ".aspect_rules_js/{}/chalk@5.0.1".format(link_root_name) + + # reference target used to avoid circular deps + _npm_package_store( + name = "{}/ref".format(store_target_name), + package = "chalk", + version = "5.0.1", + dev = False, + tags = ["manual"], + use_declare_symlink = select({ + "@aspect_rules_js//js:allow_unresolved_symlinks": True, + "//conditions:default": False, + }), + ) + + # post-lifecycle target with reference deps for use in terminal target with transitive closure + _npm_package_store( + name = "{}/pkg".format(store_target_name), + src = "{}/pkg_lc".format(store_target_name) if False else "@chalk_501//:pkg", + package = "chalk", + version = "5.0.1", + dev = False, + deps = ref_deps, + tags = ["manual"], + use_declare_symlink = select({ + "@aspect_rules_js//js:allow_unresolved_symlinks": True, + "//conditions:default": False, + }), + ) + + # virtual store target with transitive closure of all npm package dependencies + _npm_package_store( + name = store_target_name, + src = None if True else "@chalk_501//:pkg", + package = "chalk", + version = "5.0.1", + dev = False, + deps = deps, + visibility = ["//visibility:public"], + tags = ["manual"], + use_declare_symlink = select({ + "@aspect_rules_js//js:allow_unresolved_symlinks": True, + "//conditions:default": False, + }), + ) + + # filegroup target that provides a single file which is + # package directory for use in $(execpath) and $(rootpath) + native.filegroup( + name = "{}/dir".format(store_target_name), + srcs = [":{}".format(store_target_name)], + output_group = _utils.package_directory_output_group, + visibility = ["//visibility:public"], + tags = ["manual"], + ) + +# Generated npm_package_store and npm_link_package_store targets for npm package chalk@5.0.1 +# buildifier: disable=function-docstring +def npm_link_imported_package_store(name): + link_packages = { + "examples/npm_package/libs/lib_a": ["chalk"], + "npm/private/test/npm_package": ["chalk"], + } + if native.package_name() in link_packages: + link_aliases = link_packages[native.package_name()] + else: + link_aliases = ["chalk"] + + link_alias = None + for _link_alias in link_aliases: + if name.endswith("/{}".format(_link_alias)): + # longest match wins + if not link_alias or len(_link_alias) > len(link_alias): + link_alias = _link_alias + if not link_alias: + msg = "name must end with one of '/{{ {link_aliases_comma_separated} }}' when called from package 'chalk'; recommended value(s) are 'node_modules/{{ {link_aliases_comma_separated} }}'".format(link_aliases_comma_separated = ", ".join(link_aliases)) + fail(msg) + + link_root_name = name[:-len("/{}".format(link_alias))] + store_target_name = ".aspect_rules_js/{}/chalk@5.0.1".format(link_root_name) + + # terminal package store target to link + _npm_link_package_store( + name = name, + package = link_alias, + src = "//:{}".format(store_target_name), + visibility = ["//visibility:public"], + tags = ["manual"], + use_declare_symlink = select({ + "@aspect_rules_js//js:allow_unresolved_symlinks": True, + "//conditions:default": False, + }), + ) + + # filegroup target that provides a single file which is + # package directory for use in $(execpath) and $(rootpath) + native.filegroup( + name = "{}/dir".format(name), + srcs = [":{}".format(name)], + output_group = _utils.package_directory_output_group, + visibility = ["//visibility:public"], + tags = ["manual"], + ) + + return [":{}".format(name)] if True else [] + +# Generated npm_package_store and npm_link_package_store targets for npm package chalk@5.0.1 +# buildifier: disable=function-docstring +def npm_link_imported_package( + name = "node_modules", + link = None, + fail_if_no_link = True): + root_package = "" + link_packages = { + "examples/npm_package/libs/lib_a": ["chalk"], + "npm/private/test/npm_package": ["chalk"], + } + + if link_packages and link != None: + fail("link attribute cannot be specified when link_packages are set") + + is_link = (link == True) or (link == None and native.package_name() in link_packages) + is_root = native.package_name() == root_package + + if fail_if_no_link and not is_root and not link: + msg = "Nothing to link in bazel package '%s' for npm package npm package chalk@5.0.1. This is neither the root package nor a link package of this package." % native.package_name() + fail(msg) + + link_targets = [] + scoped_targets = {} + + if is_link: + link_aliases = [] + if native.package_name() in link_packages: + link_aliases = link_packages[native.package_name()] + if not link_aliases: + link_aliases = ["chalk"] + for link_alias in link_aliases: + link_target_name = "{}/{}".format(name, link_alias) + npm_link_imported_package_store(name = link_target_name) + if True: + link_targets.append(":{}".format(link_target_name)) + if len(link_alias.split("/", 1)) > 1: + link_scope = link_alias.split("/", 1)[0] + if link_scope not in scoped_targets: + scoped_targets[link_scope] = [] + scoped_targets[link_scope].append(link_target_name) + + if is_root: + npm_imported_package_store("{}/chalk".format(name)) + + return (link_targets, scoped_targets) diff --git a/npm/private/test/repositories_checked.bzl b/npm/private/test/repositories_checked.bzl index 43b83a9a0..db437feb1 100644 --- a/npm/private/test/repositories_checked.bzl +++ b/npm/private/test/repositories_checked.bzl @@ -6606,6 +6606,7 @@ def npm_repositories(): transitive_closure = { "chalk": ["5.0.1"], }, + replace_package = "@chalk_501//:pkg", ) npm_import( diff --git a/npm/private/test/vendored/chalk-5.0.1.BUILD b/npm/private/test/vendored/chalk-5.0.1.BUILD new file mode 100644 index 000000000..80faa377e --- /dev/null +++ b/npm/private/test/vendored/chalk-5.0.1.BUILD @@ -0,0 +1,11 @@ +load("@aspect_rules_js//npm:defs.bzl", "npm_package") + +npm_package( + name = "pkg", + srcs = glob( + include = ["package/**/*"], + ), + include_runfiles = False, + root_paths = ["package"], + visibility = ["//visibility:public"], +)