diff --git a/.prettierignore b/.prettierignore index 5dc79acb2..14ef4c357 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,7 +4,6 @@ e2e/**/*-docs.md examples/**/*-docs.md js/private/coverage/coverage.js js/private/node-patches/fs.js -js/private/node-patches_legacy/fs.js min/ npm/private/test/vendored/ js/private/worker/worker.js diff --git a/docs/js_binary.md b/docs/js_binary.md index 4d8b9be9d..25f469659 100644 --- a/docs/js_binary.md +++ b/docs/js_binary.md @@ -25,8 +25,7 @@ js_binary( js_binary(name, chdir, copy_data_to_bin, data, enable_runfiles, entry_point, env, expected_exit_code, fixed_args, include_declarations, include_npm, include_npm_linked_packages, include_transitive_sources, log_level, no_copy_to_bin, - node_options, node_toolchain, patch_node_fs, preserve_symlinks_main, - unresolved_symlinks_enabled) + node_options, node_toolchain, patch_node_fs, preserve_symlinks_main) Execute a program in the Node.js runtime. @@ -87,7 +86,6 @@ The following environment variables are made available to the Node.js runtime ba | node_toolchain | The Node.js toolchain to use for this target.

See https://bazelbuild.github.io/rules_nodejs/Toolchains.html

Typically this is left unset so that Bazel automatically selects the right Node.js toolchain for the target platform. See https://bazel.build/extending/toolchains#toolchain-resolution for more information. | Label | optional | None | | patch_node_fs | Patch the to Node.js fs API (https://nodejs.org/api/fs.html) for this node program to prevent the program from following symlinks out of the execroot, runfiles and the sandbox.

When enabled, js_binary patches the Node.js sync and async fs API functions lstat, readlink, realpath, readdir and opendir so that the node program being run cannot resolve symlinks out of the execroot and the runfiles tree. When in the sandbox, these patches prevent the program being run from resolving symlinks out of the sandbox.

When disabled, node programs can leave the execroot, runfiles and sandbox by following symlinks which can lead to non-hermetic behavior. | Boolean | optional | True | | preserve_symlinks_main | When True, the --preserve-symlinks-main flag is passed to node.

This prevents node from following an ESM entry script out of runfiles and the sandbox. This can happen for .mjs ESM entry points where the fs node patches, which guard the runfiles and sandbox, are not applied. See https://github.com/aspect-build/rules_js/issues/362 for more information. Once #362 is resolved, the default for this attribute can be set to False.

This flag was added in Node.js v10.2.0 (released 2018-05-23). If your node toolchain is configured to use a Node.js version older than this you'll need to set this attribute to False.

See https://nodejs.org/api/cli.html#--preserve-symlinks-main for more information. | Boolean | optional | True | -| unresolved_symlinks_enabled | Whether unresolved symlinks are enabled in the current build configuration.

These are enabled with the --allow_unresolved_symlinks flag (named --experimental_allow_unresolved_symlinks in Bazel versions prior to 7.0).

Typical usage of this rule is via a macro which automatically sets this attribute based on a
config_setting rule. See /js/private/BUILD.bazel in rules_js for an example. | Boolean | required | | @@ -98,7 +96,7 @@ The following environment variables are made available to the Node.js runtime ba js_test(name, chdir, copy_data_to_bin, data, enable_runfiles, entry_point, env, expected_exit_code, fixed_args, include_declarations, include_npm, include_npm_linked_packages, include_transitive_sources, log_level, no_copy_to_bin, node_options, node_toolchain, - patch_node_fs, preserve_symlinks_main, unresolved_symlinks_enabled) + patch_node_fs, preserve_symlinks_main) Identical to js_binary, but usable under `bazel test`. @@ -147,7 +145,6 @@ the contract between Bazel and a test runner. | node_toolchain | The Node.js toolchain to use for this target.

See https://bazelbuild.github.io/rules_nodejs/Toolchains.html

Typically this is left unset so that Bazel automatically selects the right Node.js toolchain for the target platform. See https://bazel.build/extending/toolchains#toolchain-resolution for more information. | Label | optional | None | | patch_node_fs | Patch the to Node.js fs API (https://nodejs.org/api/fs.html) for this node program to prevent the program from following symlinks out of the execroot, runfiles and the sandbox.

When enabled, js_binary patches the Node.js sync and async fs API functions lstat, readlink, realpath, readdir and opendir so that the node program being run cannot resolve symlinks out of the execroot and the runfiles tree. When in the sandbox, these patches prevent the program being run from resolving symlinks out of the sandbox.

When disabled, node programs can leave the execroot, runfiles and sandbox by following symlinks which can lead to non-hermetic behavior. | Boolean | optional | True | | preserve_symlinks_main | When True, the --preserve-symlinks-main flag is passed to node.

This prevents node from following an ESM entry script out of runfiles and the sandbox. This can happen for .mjs ESM entry points where the fs node patches, which guard the runfiles and sandbox, are not applied. See https://github.com/aspect-build/rules_js/issues/362 for more information. Once #362 is resolved, the default for this attribute can be set to False.

This flag was added in Node.js v10.2.0 (released 2018-05-23). If your node toolchain is configured to use a Node.js version older than this you'll need to set this attribute to False.

See https://nodejs.org/api/cli.html#--preserve-symlinks-main for more information. | Boolean | optional | True | -| unresolved_symlinks_enabled | Whether unresolved symlinks are enabled in the current build configuration.

These are enabled with the --allow_unresolved_symlinks flag (named --experimental_allow_unresolved_symlinks in Bazel versions prior to 7.0).

Typical usage of this rule is via a macro which automatically sets this attribute based on a
config_setting rule. See /js/private/BUILD.bazel in rules_js for an example. | Boolean | required | | diff --git a/e2e/gyp_no_install_script/snapshots/bzlmod/repositories.bzl b/e2e/gyp_no_install_script/snapshots/bzlmod/repositories.bzl index 6b0cc7783..a4347a026 100644 --- a/e2e/gyp_no_install_script/snapshots/bzlmod/repositories.bzl +++ b/e2e/gyp_no_install_script/snapshots/bzlmod/repositories.bzl @@ -1,8 +1,8 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@@//:pnpm-lock.yaml")""" load("@aspect_rules_js//npm:repositories.bzl", "npm_import") -# Generated npm_import repository rules corresponding to npm packages in //:pnpm-lock.yaml +# Generated npm_import repository rules corresponding to npm packages in @@//:pnpm-lock.yaml # buildifier: disable=function-docstring def npm_repositories(): npm_import( diff --git a/e2e/gyp_no_install_script/snapshots/bzlmod/segfault-handler_defs.bzl b/e2e/gyp_no_install_script/snapshots/bzlmod/segfault-handler_defs.bzl index 5426dceb2..a17329194 100644 --- a/e2e/gyp_no_install_script/snapshots/bzlmod/segfault-handler_defs.bzl +++ b/e2e/gyp_no_install_script/snapshots/bzlmod/segfault-handler_defs.bzl @@ -140,10 +140,6 @@ def npm_imported_package_store(name): version = "1.3.0", dev = False, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -155,10 +151,6 @@ def npm_imported_package_store(name): dev = False, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -171,17 +163,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@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 = "package_directory", visibility = ["//visibility:public"], tags = ["manual"], @@ -298,10 +279,6 @@ def npm_imported_package_store(name): dev = False, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # terminal pre-lifecycle target for use in lifecycle build target below @@ -312,17 +289,6 @@ def npm_imported_package_store(name): dev = False, deps = lc_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), - ) - - # lifecycle build action - _js_run_binary( - name = "{}/lc".format(store_target_name), - srcs = [ - "@@aspect_rules_js~~npm~npm__segfault-handler__1.3.0//:pkg", ":{}/pkg_pre_lc".format(store_target_name), ], # js_run_binary runs in the output dir; must add "../../../" because paths are relative to the exec root @@ -400,10 +366,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/e2e/npm_translate_lock/snapshots/bzlmod/repositories.bzl b/e2e/npm_translate_lock/snapshots/bzlmod/repositories.bzl index 15722a8aa..7699a55be 100644 --- a/e2e/npm_translate_lock/snapshots/bzlmod/repositories.bzl +++ b/e2e/npm_translate_lock/snapshots/bzlmod/repositories.bzl @@ -1,8 +1,8 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@@//:pnpm-lock.yaml")""" load("@aspect_rules_js//npm:repositories.bzl", "npm_import") -# Generated npm_import repository rules corresponding to npm packages in //:pnpm-lock.yaml +# Generated npm_import repository rules corresponding to npm packages in @@//:pnpm-lock.yaml # buildifier: disable=function-docstring def npm_repositories(): npm_import( diff --git a/e2e/npm_translate_lock/snapshots/wksp/repositories.bzl b/e2e/npm_translate_lock/snapshots/wksp/repositories.bzl index 15722a8aa..43d65a43f 100644 --- a/e2e/npm_translate_lock/snapshots/wksp/repositories.bzl +++ b/e2e/npm_translate_lock/snapshots/wksp/repositories.bzl @@ -1,8 +1,8 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@//:pnpm-lock.yaml")""" load("@aspect_rules_js//npm:repositories.bzl", "npm_import") -# Generated npm_import repository rules corresponding to npm packages in //:pnpm-lock.yaml +# Generated npm_import repository rules corresponding to npm packages in @//:pnpm-lock.yaml # buildifier: disable=function-docstring def npm_repositories(): npm_import( diff --git a/e2e/npm_translate_lock_empty/snapshots/bzlmod/repositories.bzl b/e2e/npm_translate_lock_empty/snapshots/bzlmod/repositories.bzl index 2c5e8a358..731030c12 100644 --- a/e2e/npm_translate_lock_empty/snapshots/bzlmod/repositories.bzl +++ b/e2e/npm_translate_lock_empty/snapshots/bzlmod/repositories.bzl @@ -1,6 +1,6 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@@//:pnpm-lock.yaml")""" -# Generated npm_import repository rules corresponding to npm packages in //:pnpm-lock.yaml +# Generated npm_import repository rules corresponding to npm packages in @@//:pnpm-lock.yaml # buildifier: disable=function-docstring def npm_repositories(): pass diff --git a/e2e/npm_translate_lock_empty/snapshots/wksp/repositories.bzl b/e2e/npm_translate_lock_empty/snapshots/wksp/repositories.bzl index 2c5e8a358..60608e83e 100644 --- a/e2e/npm_translate_lock_empty/snapshots/wksp/repositories.bzl +++ b/e2e/npm_translate_lock_empty/snapshots/wksp/repositories.bzl @@ -1,6 +1,6 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@//:pnpm-lock.yaml")""" -# Generated npm_import repository rules corresponding to npm packages in //:pnpm-lock.yaml +# Generated npm_import repository rules corresponding to npm packages in @//:pnpm-lock.yaml # buildifier: disable=function-docstring def npm_repositories(): pass diff --git a/js/BUILD.bazel b/js/BUILD.bazel index 2bd38d15d..75ded89be 100644 --- a/js/BUILD.bazel +++ b/js/BUILD.bazel @@ -1,7 +1,6 @@ "Public API" load("@aspect_bazel_lib//lib:utils.bzl", "is_bazel_7_or_greater") -load("@bazel_features//:features.bzl", "bazel_features") load("@bazel_skylib//:bzl_library.bzl", "bzl_library") exports_files( @@ -9,12 +8,6 @@ exports_files( visibility = ["//docs:__pkg__"], ) -config_setting( - name = "allow_unresolved_symlinks", - values = {bazel_features.flags.allow_unresolved_symlinks: "true"}, - visibility = ["//visibility:public"], -) - config_setting( name = "enable_runfiles", values = {"enable_runfiles": "true"}, diff --git a/js/defs.bzl b/js/defs.bzl index 80b28edc1..9f86b687e 100644 --- a/js/defs.bzl +++ b/js/defs.bzl @@ -32,10 +32,6 @@ def js_binary(**kwargs): Label("@aspect_rules_js//js:enable_runfiles"): True, "//conditions:default": False, }), - unresolved_symlinks_enabled = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), **kwargs ) @@ -45,10 +41,6 @@ def js_test(**kwargs): Label("@aspect_rules_js//js:enable_runfiles"): True, "//conditions:default": False, }), - unresolved_symlinks_enabled = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), **kwargs ) diff --git a/js/private/js_binary.bzl b/js/private/js_binary.bzl index 790c44339..d0e1ed20f 100644 --- a/js/private/js_binary.bzl +++ b/js/private/js_binary.bzl @@ -18,7 +18,6 @@ js_binary( load("@aspect_bazel_lib//lib:windows_utils.bzl", "create_windows_native_launcher_script") load("@aspect_bazel_lib//lib:expand_make_vars.bzl", "expand_locations", "expand_variables") load("@aspect_bazel_lib//lib:directory_path.bzl", "DirectoryPathInfo") -load("@aspect_bazel_lib//lib:utils.bzl", "consistent_label_str", "is_bazel_6_or_greater") load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "COPY_FILE_TO_BIN_TOOLCHAINS") load("@bazel_skylib//lib:dicts.bzl", "dicts") load(":js_helpers.bzl", "LOG_LEVELS", "envs_for_log_level", "gather_runfiles") @@ -257,18 +256,6 @@ _ATTRS = { to use npm. """, ), - "unresolved_symlinks_enabled": attr.bool( - doc = """Whether unresolved symlinks are enabled in the current build configuration. - - These are enabled with the `--allow_unresolved_symlinks` flag - (named `--experimental_allow_unresolved_symlinks in Bazel versions prior to 7.0). - - Typical usage of this rule is via a macro which automatically sets this - attribute based on a `config_setting` rule. - See /js/private/BUILD.bazel in rules_js for an example. - """, - mandatory = True, - ), "node_toolchain": attr.label( doc = """The Node.js toolchain to use for this target. @@ -300,14 +287,6 @@ _ATTRS = { allow_single_file = True, ), "_windows_constraint": attr.label(default = "@platforms//os:windows"), - "_node_patches_legacy_files": attr.label_list( - allow_files = True, - default = [Label("@aspect_rules_js//js/private/node-patches_legacy:fs.js")], - ), - "_node_patches_legacy": attr.label( - allow_single_file = True, - default = Label("@aspect_rules_js//js/private/node-patches_legacy:register.js"), - ), "_node_patches_files": attr.label_list( allow_files = True, default = [Label("@aspect_rules_js//js/private/node-patches:fs.js")], @@ -330,7 +309,7 @@ _NODE_OPTION = """JS_BINARY__NODE_OPTIONS+=(\"{value}\")""" def _deprecated_target_tool_path_to_short_path(tool_path): return ("../" + tool_path[len("external/"):]) if tool_path.startswith("external/") else tool_path -def _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_prefix_rule, fixed_args, fixed_env, is_windows, use_legacy_node_patches): +def _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_prefix_rule, fixed_args, fixed_env, is_windows): # Explicitly disable node fs patches on Windows: # https://github.com/aspect-build/rules_js/issues/1137 if is_windows: @@ -458,9 +437,9 @@ def _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_pre node_path = _deprecated_target_tool_path_to_short_path(nodeinfo.target_tool_path) launcher_subst = { - "{{target_label}}": consistent_label_str(ctx, ctx.label), - "{{template_label}}": consistent_label_str(ctx, ctx.attr._launcher_template.label), - "{{entry_point_label}}": consistent_label_str(ctx, ctx.attr.entry_point.label), + "{{target_label}}": str(ctx.label), + "{{template_label}}": str(ctx.attr._launcher_template.label), + "{{entry_point_label}}": str(ctx.attr.entry_point.label), "{{entry_point_path}}": entry_point_path, "{{envs}}": "\n".join(envs), "{{fixed_args}}": " ".join(fixed_args_expanded), @@ -468,7 +447,7 @@ def _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_pre "{{log_prefix_rule_set}}": log_prefix_rule_set, "{{log_prefix_rule}}": log_prefix_rule, "{{node_options}}": "\n".join(node_options), - "{{node_patches}}": ctx.file._node_patches_legacy.short_path if use_legacy_node_patches else ctx.file._node_patches.short_path, + "{{node_patches}}": ctx.file._node_patches.short_path, "{{node_wrapper}}": node_wrapper.short_path, "{{node}}": node_path, "{{npm}}": npm_path, @@ -487,11 +466,6 @@ def _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_pre def _create_launcher(ctx, log_prefix_rule_set, log_prefix_rule, fixed_args = [], fixed_env = {}): is_windows = ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo]) - is_bazel_6 = is_bazel_6_or_greater() - unresolved_symlinks_enabled = False - if hasattr(ctx.attr, "unresolved_symlinks_enabled"): - unresolved_symlinks_enabled = ctx.attr.unresolved_symlinks_enabled - use_legacy_node_patches = not is_bazel_6 or not unresolved_symlinks_enabled if ctx.attr.node_toolchain: nodeinfo = ctx.attr.node_toolchain[platform_common.ToolchainInfo].nodeinfo @@ -510,7 +484,7 @@ def _create_launcher(ctx, log_prefix_rule_set, log_prefix_rule, fixed_args = [], entry_point = ctx.files.entry_point[0] entry_point_path = entry_point.short_path - bash_launcher, toolchain_files = _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_prefix_rule, fixed_args, fixed_env, is_windows, use_legacy_node_patches) + bash_launcher, toolchain_files = _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_prefix_rule, fixed_args, fixed_env, is_windows) launcher = create_windows_native_launcher_script(ctx, bash_launcher) if is_windows else bash_launcher launcher_files = [bash_launcher] @@ -522,10 +496,7 @@ def _create_launcher(ctx, log_prefix_rule_set, log_prefix_rule, fixed_args = [], # TODO(3.0): drop support for deprecated toolchain attributes launcher_files.extend(nodeinfo.tool_files) - if use_legacy_node_patches: - launcher_files.extend(ctx.files._node_patches_legacy_files + [ctx.file._node_patches_legacy]) - else: - launcher_files.extend(ctx.files._node_patches_files + [ctx.file._node_patches]) + launcher_files.extend(ctx.files._node_patches_files + [ctx.file._node_patches]) transitive_launcher_files = None if ctx.attr.include_npm: if hasattr(nodeinfo, "npm_sources"): diff --git a/js/private/js_run_devserver.bzl b/js/private/js_run_devserver.bzl index 433fd2579..cd4ffd5e8 100644 --- a/js/private/js_run_devserver.bzl +++ b/js/private/js_run_devserver.bzl @@ -266,10 +266,6 @@ def js_run_devserver( Label("@aspect_rules_js//js:enable_runfiles"): True, "//conditions:default": False, }), - unresolved_symlinks_enabled = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), entry_point = Label("@aspect_rules_js//js/private:js_devserver_entrypoint"), # This rule speaks the ibazel protocol tags = kwargs.pop("tags", []) + ["ibazel_notify_changes"], diff --git a/js/private/node-patches_legacy/BUILD.bazel b/js/private/node-patches_legacy/BUILD.bazel deleted file mode 100644 index 95f17b728..000000000 --- a/js/private/node-patches_legacy/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files") - -write_source_files( - name = "checked_in_compile", - files = { - "fs.js": "//js/private/node-patches_legacy/src:fs-generated.js", - }, -) - -exports_files([ - "fs.js", - "register.js", -]) diff --git a/js/private/node-patches_legacy/fs.js b/js/private/node-patches_legacy/fs.js deleted file mode 100644 index 57838f1f7..000000000 --- a/js/private/node-patches_legacy/fs.js +++ /dev/null @@ -1,889 +0,0 @@ -// Generated by //js/private/node-patches_legacy:compile -"use strict"; -/** - * @license - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var __asyncValues = (this && this.__asyncValues) || function (o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -}; -var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } -var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var g = generator.apply(thisArg, _arguments || []), i, q = []; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; - function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } - function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } - function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } - function fulfill(value) { resume("next", value); } - function reject(value) { resume("throw", value); } - function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.escapeFunction = exports.isSubPath = exports.patcher = void 0; -const path = require("path"); -const util = require("util"); -// using require here on purpose so we can override methods with any -// also even though imports are mutable in typescript the cognitive dissonance is too high because -// es modules -const _fs = require('node:fs'); -const url = require('node:url'); -const HOP_NON_LINK = Symbol.for('HOP NON LINK'); -const HOP_NOT_FOUND = Symbol.for('HOP NOT FOUND'); -const patcher = (fs = _fs, roots) => { - fs = fs || _fs; - // Make the original version of the library available for when access to the - // unguarded file system is necessary, such as the esbuild plugin that - // protects against sandbox escaping that occurs through module resolution - // in the Go binary. See - // https://github.com/aspect-build/rules_esbuild/issues/58. - fs._unpatched = Object.assign({}, fs); - roots = roots || []; - roots = roots.filter((root) => fs.existsSync(root)); - if (!roots.length) { - if (process.env.VERBOSE_LOGS) { - console.error('fs patcher called without any valid root paths ' + __filename); - } - return; - } - const origLstat = fs.lstat.bind(fs); - const origLstatSync = fs.lstatSync.bind(fs); - const origReaddir = fs.readdir.bind(fs); - const origReaddirSync = fs.readdirSync.bind(fs); - const origReadlink = fs.readlink.bind(fs); - const origReadlinkSync = fs.readlinkSync.bind(fs); - const origRealpath = fs.realpath.bind(fs); - const origRealpathNative = fs.realpath - .native; - const origRealpathSync = fs.realpathSync.bind(fs); - const origRealpathSyncNative = fs.realpathSync - .native; - const { canEscape, isEscape } = escapeFunction(roots); - // ========================================================================= - // fs.lstat - // ========================================================================= - fs.lstat = function lstat(...args) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origLstat(...args); - } - const cb = once(args[args.length - 1]); - // override the callback - args[args.length - 1] = (err, stats) => { - if (err) - return cb(err); - if (!stats.isSymbolicLink()) { - // the file is not a symbolic link so there is nothing more to do - return cb(null, stats); - } - args[0] = resolvePathLike(args[0]); - if (!canEscape(args[0])) { - // the file can not escaped the sandbox so there is nothing more to do - return cb(null, stats); - } - return guardedReadLink(args[0], (str) => { - if (str != args[0]) { - // there are one or more hops within the guards so there is nothing more to do - return cb(null, stats); - } - // there are no hops so lets report the stats of the real file; - // we can't use origRealPath here since that function calls lstat internally - // which can result in an infinite loop - return unguardedRealPath(args[0], (err, str) => { - if (err) { - if (err.code === 'ENOENT') { - // broken link so there is nothing more to do - return cb(null, stats); - } - return cb(err); - } - return origLstat(str, (err, str) => cb(err, str)); - }); - }); - }; - origLstat(...args); - }; - fs.lstatSync = function lstatSync(...args) { - const stats = origLstatSync(...args); - if (!(stats === null || stats === void 0 ? void 0 : stats.isSymbolicLink())) { - // the file is not a symbolic link so there is nothing more to do - return stats; - } - args[0] = resolvePathLike(args[0]); - if (!canEscape(args[0])) { - // the file can not escaped the sandbox so there is nothing more to do - return stats; - } - const guardedReadLink = guardedReadLinkSync(args[0]); - if (guardedReadLink != args[0]) { - // there are one or more hops within the guards so there is nothing more to do - return stats; - } - try { - args[0] = unguardedRealPathSync(args[0]); - // there are no hops so lets report the stats of the real file; - // we can't use origRealPathSync here since that function calls lstat internally - // which can result in an infinite loop - return origLstatSync(...args); - } - catch (err) { - if (err.code === 'ENOENT') { - // broken link so there is nothing more to do - return stats; - } - throw err; - } - }; - // ========================================================================= - // fs.realpath - // ========================================================================= - fs.realpath = function realpath(...args) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origRealpath(...args); - } - const cb = once(args[args.length - 1]); - args[args.length - 1] = (err, str) => { - if (err) - return cb(err); - const escapedRoot = isEscape(args[0], str); - if (escapedRoot) { - return guardedRealPath(args[0], (err, str) => cb(err, str), escapedRoot); - } - else { - return cb(null, str); - } - }; - origRealpath(...args); - }; - fs.realpath.native = function realpath_native(...args) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origRealpathNative(...args); - } - const cb = once(args[args.length - 1]); - args[args.length - 1] = (err, str) => { - if (err) - return cb(err); - const escapedRoot = isEscape(args[0], str); - if (escapedRoot) { - return guardedRealPath(args[0], (err, str) => cb(err, str), escapedRoot); - } - else { - return cb(null, str); - } - }; - origRealpathNative(...args); - }; - fs.realpathSync = function realpathSync(...args) { - const str = origRealpathSync(...args); - const escapedRoot = isEscape(args[0], str); - if (escapedRoot) { - return guardedRealPathSync(args[0], escapedRoot); - } - return str; - }; - fs.realpathSync.native = function native_realpathSync(...args) { - const str = origRealpathSyncNative(...args); - const escapedRoot = isEscape(args[0], str); - if (escapedRoot) { - return guardedRealPathSync(args[0], escapedRoot); - } - return str; - }; - // ========================================================================= - // fs.readlink - // ========================================================================= - fs.readlink = function readlink(...args) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origReadlink(...args); - } - const cb = once(args[args.length - 1]); - args[args.length - 1] = (err, str) => { - if (err) - return cb(err); - const resolved = resolvePathLike(args[0]); - str = path.resolve(path.dirname(resolved), str); - const escapedRoot = isEscape(resolved, str); - if (escapedRoot) { - return nextHop(str, (next) => { - if (!next) { - if (next == undefined) { - // The escape from the root is not mappable back into the root; throw EINVAL - return cb(enoent('readlink', args[0])); - } - else { - // The escape from the root is not mappable back into the root; throw EINVAL - return cb(einval('readlink', args[0])); - } - } - next = path.resolve(path.dirname(resolved), path.relative(path.dirname(str), next)); - if (next != resolved && - !isEscape(resolved, next, [escapedRoot])) { - return cb(null, next); - } - // The escape from the root is not mappable back into the root; we must make - // this look like a real file so we call readlink on the realpath which we - // expect to return an error - return origRealpath(resolved, (err, str) => { - if (err) - return cb(err); - return origReadlink(str, (err, str) => cb(err, str)); - }); - }); - } - else { - return cb(null, str); - } - }; - origReadlink(...args); - }; - fs.readlinkSync = function readlinkSync(...args) { - const resolved = resolvePathLike(args[0]); - const str = path.resolve(path.dirname(resolved), origReadlinkSync(...args)); - const escapedRoot = isEscape(resolved, str); - if (escapedRoot) { - let next = nextHopSync(str); - if (!next) { - if (next == undefined) { - // The escape from the root is not mappable back into the root; throw EINVAL - throw enoent('readlink', args[0]); - } - else { - // The escape from the root is not mappable back into the root; throw EINVAL - throw einval('readlink', args[0]); - } - } - next = path.resolve(path.dirname(resolved), path.relative(path.dirname(str), next)); - if (next != resolved && !isEscape(resolved, next, [escapedRoot])) { - return next; - } - // The escape from the root is not mappable back into the root; throw EINVAL - throw einval('readlink', args[0]); - } - return str; - }; - // ========================================================================= - // fs.readdir - // ========================================================================= - fs.readdir = function readdir(...args) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origReaddir(...args); - } - const cb = once(args[args.length - 1]); - const p = resolvePathLike(args[0]); - args[args.length - 1] = (err, result) => { - if (err) - return cb(err); - // user requested withFileTypes - if (result[0] && result[0].isSymbolicLink) { - Promise.all(result.map((v) => handleDirent(p, v))) - .then(() => { - cb(null, result); - }) - .catch((err) => { - cb(err); - }); - } - else { - // string array return for readdir. - cb(null, result); - } - }; - origReaddir(...args); - }; - fs.readdirSync = function readdirSync(...args) { - const res = origReaddirSync(...args); - const p = resolvePathLike(args[0]); - res.forEach((v) => { - handleDirentSync(p, v); - }); - return res; - }; - // ========================================================================= - // fs.opendir - // ========================================================================= - if (fs.opendir) { - const origOpendir = fs.opendir.bind(fs); - fs.opendir = function opendir(...args) { - // if this is not a function opendir should throw an error. - // we call it so we don't have to throw a mock - if (typeof args[args.length - 1] === 'function') { - const cb = once(args[args.length - 1]); - args[args.length - 1] = async (err, dir) => { - try { - cb(null, await handleDir(dir)); - } - catch (err) { - cb(err); - } - }; - origOpendir(...args); - } - else { - return origOpendir(...args).then((dir) => { - return handleDir(dir); - }); - } - }; - } - // ========================================================================= - // fs.promises - // ========================================================================= - /** - * patch fs.promises here. - * - * this requires a light touch because if we trigger the getter on older nodejs versions - * it will log an experimental warning to stderr - * - * `(node:62945) ExperimentalWarning: The fs.promises API is experimental` - * - * this api is available as experimental without a flag so users can access it at any time. - */ - const promisePropertyDescriptor = Object.getOwnPropertyDescriptor(fs, 'promises'); - if (promisePropertyDescriptor) { - const promises = {}; - promises.lstat = util.promisify(fs.lstat); - // NOTE: node core uses the newer realpath function fs.promises.native instead of fs.realPath - promises.realpath = util.promisify(fs.realpath.native); - promises.readlink = util.promisify(fs.readlink); - promises.readdir = util.promisify(fs.readdir); - if (fs.opendir) - promises.opendir = util.promisify(fs.opendir); - // handle experimental api warnings. - // only applies to version of node where promises is a getter property. - if (promisePropertyDescriptor.get) { - const oldGetter = promisePropertyDescriptor.get.bind(fs); - const cachedPromises = {}; - promisePropertyDescriptor.get = () => { - const _promises = oldGetter(); - Object.assign(cachedPromises, _promises, promises); - return cachedPromises; - }; - Object.defineProperty(fs, 'promises', promisePropertyDescriptor); - } - else { - // api can be patched directly - Object.assign(fs.promises, promises); - } - } - // ========================================================================= - // helper functions for dirs - // ========================================================================= - async function handleDir(dir) { - const p = path.resolve(dir.path); - const origIterator = dir[Symbol.asyncIterator].bind(dir); - const origRead = dir.read.bind(dir); - dir[Symbol.asyncIterator] = function () { - return __asyncGenerator(this, arguments, function* () { - var _a, e_1, _b, _c; - try { - for (var _d = true, _f = __asyncValues(origIterator()), _g; _g = yield __await(_f.next()), _a = _g.done, !_a;) { - _c = _g.value; - _d = false; - try { - const entry = _c; - yield __await(handleDirent(p, entry)); - yield yield __await(entry); - } - finally { - _d = true; - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (!_d && !_a && (_b = _f.return)) yield __await(_b.call(_f)); - } - finally { if (e_1) throw e_1.error; } - } - }); - }; - dir.read = async (...args) => { - if (typeof args[args.length - 1] === 'function') { - const cb = args[args.length - 1]; - args[args.length - 1] = async (err, entry) => { - cb(err, entry ? await handleDirent(p, entry) : null); - }; - origRead(...args); - } - else { - const entry = await origRead(...args); - if (entry) { - await handleDirent(p, entry); - } - return entry; - } - }; - const origReadSync = dir.readSync.bind(dir); - dir.readSync = () => { - return handleDirentSync(p, origReadSync()); // intentionally sync for simplicity - }; - return dir; - } - function handleDirent(p, v) { - return new Promise((resolve, reject) => { - if (!v.isSymbolicLink()) { - return resolve(v); - } - const f = path.resolve(p, v.name); - return guardedReadLink(f, (str) => { - if (f != str) { - return resolve(v); - } - // There are no hops so we should hide the fact that the file is a symlink - v.isSymbolicLink = () => false; - origRealpath(f, (err, str) => { - if (err) { - throw err; - } - fs.stat(str, (err, stat) => { - if (err) { - throw err; - } - patchDirent(v, stat); - resolve(v); - }); - }); - }); - }); - } - function handleDirentSync(p, v) { - if (v && v.isSymbolicLink) { - if (v.isSymbolicLink()) { - const f = path.resolve(p, v.name); - if (f == guardedReadLinkSync(f)) { - // There are no hops so we should hide the fact that the file is a symlink - v.isSymbolicLink = () => false; - const stat = fs.statSync(origRealpathSync(f)); - patchDirent(v, stat); - } - } - } - } - function nextHop(loc, cb) { - let nested = []; - let maybe = loc; - let escapedHop = false; - readHopLink(maybe, function readNextHop(link) { - if (link === HOP_NOT_FOUND) { - return cb(undefined); - } - if (link !== HOP_NON_LINK) { - link = path.join(link, ...nested.reverse()); - if (!isEscape(loc, link)) { - return cb(link); - } - if (!escapedHop) { - escapedHop = link; - } - } - const dirname = path.dirname(maybe); - if (!dirname || - dirname == maybe || - dirname == '.' || - dirname == '/') { - // not a link - return cb(escapedHop); - } - nested.push(path.basename(maybe)); - maybe = dirname; - readHopLink(maybe, readNextHop); - }); - } - const hopLinkCache = Object.create(null); - function readHopLinkSync(p) { - if (hopLinkCache[p]) { - return hopLinkCache[p]; - } - let link; - try { - if (origLstatSync(p).isSymbolicLink()) { - link = origReadlinkSync(p); - if (link) { - if (!path.isAbsolute(link)) { - link = path.resolve(path.dirname(p), link); - } - } - else { - link = HOP_NON_LINK; - } - } - else { - link = HOP_NON_LINK; - } - } - catch (err) { - if (err.code === 'ENOENT') { - // file does not exist - link = HOP_NOT_FOUND; - } - else { - link = HOP_NON_LINK; - } - } - hopLinkCache[p] = link; - return link; - } - function readHopLink(p, cb) { - if (hopLinkCache[p]) { - return cb(hopLinkCache[p]); - } - origReadlink(p, (err, link) => { - if (err) { - let result; - if (err.code === 'ENOENT') { - // file does not exist - result = HOP_NOT_FOUND; - } - else { - result = HOP_NON_LINK; - } - hopLinkCache[p] = result; - return cb(result); - } - if (link === undefined) { - hopLinkCache[p] = HOP_NON_LINK; - return cb(HOP_NON_LINK); - } - if (!path.isAbsolute(link)) { - link = path.resolve(path.dirname(p), link); - } - hopLinkCache[p] = link; - cb(link); - }); - } - function nextHopSync(loc) { - let nested = []; - let maybe = loc; - let escapedHop = false; - for (;;) { - let link = readHopLinkSync(maybe); - if (link === HOP_NOT_FOUND) { - return undefined; - } - if (link !== HOP_NON_LINK) { - link = path.join(link, ...nested.reverse()); - if (!isEscape(loc, link)) { - return link; - } - if (!escapedHop) { - escapedHop = link; - } - } - const dirname = path.dirname(maybe); - if (!dirname || - dirname == maybe || - dirname == '.' || - dirname == '/') { - // not a link - return escapedHop; - } - nested.push(path.basename(maybe)); - maybe = dirname; - } - } - function guardedReadLink(start, cb) { - let loc = start; - return nextHop(loc, (next) => { - if (!next) { - // we're no longer hopping but we haven't escaped; - // something funky happened in the filesystem - return cb(loc); - } - if (isEscape(loc, next)) { - // this hop takes us out of the guard - return nextHop(next, (next2) => { - if (!next2) { - // the chain is done - return cb(loc); - } - const maybe = path.resolve(path.dirname(loc), path.relative(path.dirname(next), next2)); - if (!isEscape(loc, maybe)) { - // outside of the guard is a symlink but it is a relative link path - // we can map within the guard so return that - return cb(maybe); - } - // outside of the guard is a symlink that is not mappable inside the guard - return cb(loc); - }); - } - return cb(next); - }); - } - function guardedReadLinkSync(start) { - let loc = start; - let next = nextHopSync(loc); - if (!next) { - // we're no longer hopping but we haven't escaped; - // something funky happened in the filesystem - return loc; - } - if (isEscape(loc, next)) { - // this hop takes us out of the guard - const next2 = nextHopSync(next); - if (!next2) { - // the chain is done - return loc; - } - const maybe = path.resolve(path.dirname(loc), path.relative(path.dirname(next), next2)); - if (!isEscape(loc, maybe)) { - // outside of the guard is a symlink but it is a relative link path - // we can map within the guard so return that - return maybe; - } - // outside of the guard is a symlink that is not mappable inside the guard - return loc; - } - return next; - } - function unguardedRealPath(start, cb) { - start = stringifyPathLike(start); // handle the "undefined" case (matches behavior as fs.realpath) - const oneHop = (loc, cb) => { - nextHop(loc, (next) => { - if (next == undefined) { - // file does not exist (broken link) - return cb(enoent('realpath', start)); - } - else if (!next) { - // we've hit a real file - return cb(null, loc); - } - oneHop(next, cb); - }); - }; - oneHop(start, cb); - } - function guardedRealPath(start, cb, escapedRoot = undefined) { - start = stringifyPathLike(start); // handle the "undefined" case (matches behavior as fs.realpath) - const oneHop = (loc, cb) => { - nextHop(loc, (next) => { - if (!next) { - // we're no longer hopping but we haven't escaped - return fs.exists(loc, (e) => { - if (e) { - // we hit a real file within the guard and can go no further - return cb(null, loc); - } - else { - // something funky happened in the filesystem - return cb(enoent('realpath', start)); - } - }); - } - if (escapedRoot - ? isEscape(loc, next, [escapedRoot]) - : isEscape(loc, next)) { - // this hop takes us out of the guard - return nextHop(next, (next2) => { - if (!next2) { - // the chain is done - return cb(null, loc); - } - const maybe = path.resolve(path.dirname(loc), path.relative(path.dirname(next), next2)); - if (isEscape(loc, maybe)) { - // outside of the guard is a symlink that is not mappable inside the guard; - // call the unguarded realpath which will throw if the link is dangling; - // if it doesn't throw then return the last path within the guard - return origRealpath(start, (err, _) => { - if (err) - return cb(err); - return cb(null, loc); - }); - } - return oneHop(maybe, cb); - }); - } - oneHop(next, cb); - }); - }; - oneHop(start, cb); - } - function unguardedRealPathSync(start) { - start = stringifyPathLike(start); // handle the "undefined" case (matches behavior as fs.realpathSync) - for (let loc = start, next;; loc = next) { - next = nextHopSync(loc); - if (next == undefined) { - // file does not exist (broken link) - throw enoent('realpath', start); - } - else if (!next) { - // we've hit a real file - return loc; - } - } - } - function guardedRealPathSync(start, escapedRoot = undefined) { - start = stringifyPathLike(start); // handle the "undefined" case (matches behavior as fs.realpathSync) - for (let loc = start, next;; loc = next) { - next = nextHopSync(loc); - if (!next) { - // we're no longer hopping but we haven't escaped - if (fs.existsSync(loc)) { - // we hit a real file within the guard and can go no further - return loc; - } - else { - // something funky happened in the filesystem; throw ENOENT - throw enoent('realpath', start); - } - } - if (escapedRoot - ? isEscape(loc, next, [escapedRoot]) - : isEscape(loc, next)) { - // this hop takes us out of the guard - const next2 = nextHopSync(next); - if (!next2) { - // the chain is done - return loc; - } - const maybe = path.resolve(path.dirname(loc), path.relative(path.dirname(next), next2)); - if (isEscape(loc, maybe)) { - // outside of the guard is a symlink that is not mappable inside the guard; - // call the unguarded realpath which will throw if the link is dangling; - // if it doesn't throw then return the last path within the guard - origRealpathSync(start); - return loc; - } - next = maybe; - // outside of the guard is a symlink but it is a relative link path - // we can map within the guard so lets iterate one more time - } - } - } -}; -exports.patcher = patcher; -// ========================================================================= -// generic helper functions -// ========================================================================= -function isSubPath(parent, child) { - return (parent === child || - (child[parent.length] === path.sep && child.startsWith(parent))); -} -exports.isSubPath = isSubPath; -function stringifyPathLike(p) { - if (p instanceof URL) { - return url.fileURLToPath(p); - } - else { - return String(p); - } -} -function resolvePathLike(p) { - return path.resolve(stringifyPathLike(p)); -} -function normalizePathLike(p) { - const s = stringifyPathLike(p); - // TODO: are URLs always absolute? - if (!path.isAbsolute(s)) { - return path.resolve(s); - } - else { - return path.normalize(s); - } -} -function escapeFunction(_roots) { - // Ensure roots are always absolute. - // Sort to ensure escaping multiple roots chooses the longest one. - const defaultRoots = _roots - .map((root) => path.resolve(root)) - .sort((a, b) => b.length - a.length); - function fs_isEscape(linkPath, linkTarget, roots = defaultRoots) { - // linkPath is the path of the symlink file itself - // linkTarget is a path that the symlink points to one or more hops away - // linkTarget must already be normalized - linkPath = normalizePathLike(linkPath); - for (const root of roots) { - // If the link is in the root check if the realPath has escaped - if (isSubPath(root, linkPath) && !isSubPath(root, linkTarget)) { - return root; - } - } - return false; - } - function fs_canEscape(maybeLinkPath, roots = defaultRoots) { - // maybeLinkPath is the path which may be a symlink - // maybeLinkPath must already be normalized - for (const root of roots) { - // If the link is in the root check if the realPath has escaped - if (isSubPath(root, maybeLinkPath)) { - return true; - } - } - return false; - } - return { - isEscape: fs_isEscape, - canEscape: fs_canEscape, - }; -} -exports.escapeFunction = escapeFunction; -function once(fn) { - let called = false; - return (...args) => { - if (called) - return; - called = true; - let err = false; - try { - fn(...args); - } - catch (_e) { - err = _e; - } - // blow the stack to make sure this doesn't fall into any unresolved promise contexts - if (err) { - setImmediate(() => { - throw err; - }); - } - }; -} -function patchDirent(dirent, stat) { - // add all stat is methods to Dirent instances with their result. - for (const i in stat) { - if (i.startsWith('is') && typeof stat[i] === 'function') { - // - const result = stat[i](); - if (result) - dirent[i] = () => true; - else - dirent[i] = () => false; - } - } -} -function enoent(s, p) { - let err = new Error(`ENOENT: no such file or directory, ${s} '${p}'`); - err.errno = -2; - err.syscall = s; - err.code = 'ENOENT'; - err.path = p; - return err; -} -function einval(s, p) { - let err = new Error(`EINVAL: invalid argument, ${s} '${p}'`); - err.errno = -22; - err.syscall = s; - err.code = 'EINVAL'; - err.path = p; - return err; -} diff --git a/js/private/node-patches_legacy/register.js b/js/private/node-patches_legacy/register.js deleted file mode 100644 index 607afeb37..000000000 --- a/js/private/node-patches_legacy/register.js +++ /dev/null @@ -1,51 +0,0 @@ -const patchfs = require('./fs').patcher -const { - JS_BINARY__FS_PATCH_ROOTS, - JS_BINARY__LOG_DEBUG, - JS_BINARY__LOG_PREFIX, - JS_BINARY__NODE_WRAPPER, - JS_BINARY__PATCH_NODE_FS, -} = process.env - -// Keep a count of how many times these patches are applied; this should reflect the depth -// of child processes in the default case where a child process inherits process.env since -// child processes need to re-apply the patches. This is here primarily for testing but it -// could also be useful for debugging. -if (!process.env.JS_BINARY__NODE_PATCHES_DEPTH) { - process.env.JS_BINARY__NODE_PATCHES_DEPTH = '.' -} else { - process.env.JS_BINARY__NODE_PATCHES_DEPTH += '.' -} - -// subprocess patch -if (process.platform == 'win32') { - // FIXME: need to make an exe, or run in a shell so we can use .bat -} else { - if (JS_BINARY__LOG_DEBUG) { - console.error( - `DEBUG: ${JS_BINARY__LOG_PREFIX}: overriding process.execPath to node wrapper path ${JS_BINARY__NODE_WRAPPER}` - ) - } - process.argv[0] = process.execPath = JS_BINARY__NODE_WRAPPER -} - -// fs patches -if ( - JS_BINARY__PATCH_NODE_FS && - JS_BINARY__PATCH_NODE_FS != '0' && - JS_BINARY__FS_PATCH_ROOTS -) { - const fs = require('node:fs') - const module = require('node:module') - const roots = JS_BINARY__FS_PATCH_ROOTS.split(':') - if (JS_BINARY__LOG_DEBUG) { - console.error( - `DEBUG: ${JS_BINARY__LOG_PREFIX}: node fs patches will be applied with roots: ${roots}` - ) - } - patchfs(fs, roots) - - // Sync the esm modules to use the now patched fs cjs module. - // See: https://nodejs.org/api/esm.html#builtin-modules - module.syncBuiltinESMExports() -} diff --git a/js/private/node-patches_legacy/src/BUILD.bazel b/js/private/node-patches_legacy/src/BUILD.bazel deleted file mode 100644 index 8668e5edf..000000000 --- a/js/private/node-patches_legacy/src/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@npm//:typescript/package_json.bzl", typescript_bin = "bin") - -typescript_bin.tsc( - name = "compile", - srcs = [ - "fs.ts", - "tsconfig.json", - "//:node_modules/@types/node", - ], - outs = [ - "fs.js", - ], - args = [ - "-p", - "tsconfig.json", - ], - chdir = package_name(), - visibility = ["//js/private/test/node-patches_legacy:__pkg__"], -) - -genrule( - name = "fs_generated", - srcs = ["fs.js"], - outs = ["fs-generated.js"], - cmd = "echo \"// Generated by //js/private/node-patches_legacy:compile\" > $@ && cat $(execpath :fs.js) >> $@", - visibility = ["//js/private/node-patches_legacy:__pkg__"], -) diff --git a/js/private/node-patches_legacy/src/fs.ts b/js/private/node-patches_legacy/src/fs.ts deleted file mode 100644 index 7936b7e99..000000000 --- a/js/private/node-patches_legacy/src/fs.ts +++ /dev/null @@ -1,1013 +0,0 @@ -/** - * @license - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { PathLike, Stats } from 'fs' -import type * as FsType from 'fs' -import type * as UrlType from 'url' -import * as path from 'path' -import * as util from 'util' - -// windows cant find the right types -type Dir = any -type Dirent = any - -// using require here on purpose so we can override methods with any -// also even though imports are mutable in typescript the cognitive dissonance is too high because -// es modules -const _fs = require('node:fs') as typeof FsType -const url = require('node:url') as typeof UrlType - -const HOP_NON_LINK = Symbol.for('HOP NON LINK') -const HOP_NOT_FOUND = Symbol.for('HOP NOT FOUND') - -type HopResults = string | typeof HOP_NON_LINK | typeof HOP_NOT_FOUND - -export const patcher = (fs: any = _fs, roots: string[]) => { - fs = fs || _fs - // Make the original version of the library available for when access to the - // unguarded file system is necessary, such as the esbuild plugin that - // protects against sandbox escaping that occurs through module resolution - // in the Go binary. See - // https://github.com/aspect-build/rules_esbuild/issues/58. - fs._unpatched = { ...fs } - roots = roots || [] - roots = roots.filter((root) => fs.existsSync(root)) - if (!roots.length) { - if (process.env.VERBOSE_LOGS) { - console.error( - 'fs patcher called without any valid root paths ' + __filename - ) - } - return - } - - const origLstat = fs.lstat.bind(fs) as typeof FsType.lstat - const origLstatSync = fs.lstatSync.bind(fs) as typeof FsType.lstatSync - - const origReaddir = fs.readdir.bind(fs) as typeof FsType.readdir - const origReaddirSync = fs.readdirSync.bind(fs) as typeof FsType.readdirSync - - const origReadlink = fs.readlink.bind(fs) as typeof FsType.readlink - const origReadlinkSync = fs.readlinkSync.bind( - fs - ) as typeof FsType.readlinkSync - - const origRealpath = fs.realpath.bind(fs) as typeof FsType.realpath - const origRealpathNative = fs.realpath - .native as typeof FsType.realpath.native - const origRealpathSync = fs.realpathSync.bind( - fs - ) as typeof FsType.realpathSync - const origRealpathSyncNative = fs.realpathSync - .native as typeof FsType.realpathSync.native - - const { canEscape, isEscape } = escapeFunction(roots) - - // ========================================================================= - // fs.lstat - // ========================================================================= - - fs.lstat = function lstat(...args: Parameters) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origLstat(...args) - } - - const cb = once(args[args.length - 1] as any) - - // override the callback - args[args.length - 1] = (err: Error, stats: Stats) => { - if (err) return cb(err) - - if (!stats.isSymbolicLink()) { - // the file is not a symbolic link so there is nothing more to do - return cb(null, stats) - } - - args[0] = resolvePathLike(args[0]) - - if (!canEscape(args[0])) { - // the file can not escaped the sandbox so there is nothing more to do - return cb(null, stats) - } - - return guardedReadLink(args[0], (str: string) => { - if (str != args[0]) { - // there are one or more hops within the guards so there is nothing more to do - return cb(null, stats) - } - - // there are no hops so lets report the stats of the real file; - // we can't use origRealPath here since that function calls lstat internally - // which can result in an infinite loop - return unguardedRealPath(args[0], (err: Error, str: string) => { - if (err) { - if ((err as any).code === 'ENOENT') { - // broken link so there is nothing more to do - return cb(null, stats) - } - return cb(err) - } - return origLstat(str, (err, str) => cb(err, str)) - }) - }) - } - - origLstat(...args) - } - - fs.lstatSync = function lstatSync( - ...args: Parameters - ) { - const stats = origLstatSync(...args) - - if (!stats?.isSymbolicLink()) { - // the file is not a symbolic link so there is nothing more to do - return stats - } - - args[0] = resolvePathLike(args[0]) - - if (!canEscape(args[0])) { - // the file can not escaped the sandbox so there is nothing more to do - return stats - } - - const guardedReadLink: string = guardedReadLinkSync(args[0]) - if (guardedReadLink != args[0]) { - // there are one or more hops within the guards so there is nothing more to do - return stats - } - - try { - args[0] = unguardedRealPathSync(args[0]) - - // there are no hops so lets report the stats of the real file; - // we can't use origRealPathSync here since that function calls lstat internally - // which can result in an infinite loop - return origLstatSync(...args) - } catch (err) { - if (err.code === 'ENOENT') { - // broken link so there is nothing more to do - return stats - } - throw err - } - } - - // ========================================================================= - // fs.realpath - // ========================================================================= - - fs.realpath = function realpath(...args: Parameters) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origRealpath(...args) - } - - const cb = once(args[args.length - 1] as any) - - args[args.length - 1] = (err: Error, str: string) => { - if (err) return cb(err) - const escapedRoot: string | false = isEscape(args[0], str) - if (escapedRoot) { - return guardedRealPath( - args[0], - (err, str) => cb(err, str), - escapedRoot - ) - } else { - return cb(null, str) - } - } - - origRealpath(...args) - } - - fs.realpath.native = function realpath_native( - ...args: Parameters - ) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origRealpathNative(...args) - } - - const cb = once(args[args.length - 1] as any) - - args[args.length - 1] = (err: Error, str: string) => { - if (err) return cb(err) - const escapedRoot: string | false = isEscape(args[0], str) - if (escapedRoot) { - return guardedRealPath( - args[0], - (err, str) => cb(err, str), - escapedRoot - ) - } else { - return cb(null, str) - } - } - - origRealpathNative(...args) - } - - fs.realpathSync = function realpathSync( - ...args: Parameters - ) { - const str = origRealpathSync(...args) - const escapedRoot: string | false = isEscape(args[0], str) - if (escapedRoot) { - return guardedRealPathSync(args[0], escapedRoot) - } - return str - } - - fs.realpathSync.native = function native_realpathSync( - ...args: Parameters - ) { - const str = origRealpathSyncNative(...args) - const escapedRoot: string | false = isEscape(args[0], str) - if (escapedRoot) { - return guardedRealPathSync(args[0], escapedRoot) - } - return str - } - - // ========================================================================= - // fs.readlink - // ========================================================================= - - fs.readlink = function readlink(...args: Parameters) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origReadlink(...args) - } - - const cb = once(args[args.length - 1] as any) - - args[args.length - 1] = (err: Error, str: string) => { - if (err) return cb(err) - const resolved = resolvePathLike(args[0]) - str = path.resolve(path.dirname(resolved), str) - const escapedRoot: string | false = isEscape(resolved, str) - if (escapedRoot) { - return nextHop(str, (next: string | false) => { - if (!next) { - if (next == undefined) { - // The escape from the root is not mappable back into the root; throw EINVAL - return cb(enoent('readlink', args[0])) - } else { - // The escape from the root is not mappable back into the root; throw EINVAL - return cb(einval('readlink', args[0])) - } - } - next = path.resolve( - path.dirname(resolved), - path.relative(path.dirname(str), next) - ) - if ( - next != resolved && - !isEscape(resolved, next, [escapedRoot]) - ) { - return cb(null, next) - } - // The escape from the root is not mappable back into the root; we must make - // this look like a real file so we call readlink on the realpath which we - // expect to return an error - return origRealpath(resolved, (err, str) => { - if (err) return cb(err) - return origReadlink(str, (err, str) => cb(err, str)) - }) - }) - } else { - return cb(null, str) - } - } - - origReadlink(...args) - } - - fs.readlinkSync = function readlinkSync( - ...args: Parameters - ) { - const resolved = resolvePathLike(args[0]) - - const str = path.resolve( - path.dirname(resolved), - origReadlinkSync(...args) - ) - - const escapedRoot: string | false = isEscape(resolved, str) - if (escapedRoot) { - let next: string | false = nextHopSync(str) - if (!next) { - if (next == undefined) { - // The escape from the root is not mappable back into the root; throw EINVAL - throw enoent('readlink', args[0]) - } else { - // The escape from the root is not mappable back into the root; throw EINVAL - throw einval('readlink', args[0]) - } - } - next = path.resolve( - path.dirname(resolved), - path.relative(path.dirname(str), next) - ) - if (next != resolved && !isEscape(resolved, next, [escapedRoot])) { - return next - } - // The escape from the root is not mappable back into the root; throw EINVAL - throw einval('readlink', args[0]) - } - return str - } - - // ========================================================================= - // fs.readdir - // ========================================================================= - - fs.readdir = function readdir(...args: Parameters) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origReaddir(...args) - } - - const cb = once(args[args.length - 1] as any) - const p = resolvePathLike(args[0]) - - args[args.length - 1] = (err: Error, result: Dirent[]) => { - if (err) return cb(err) - // user requested withFileTypes - if (result[0] && result[0].isSymbolicLink) { - Promise.all(result.map((v: Dirent) => handleDirent(p, v))) - .then(() => { - cb(null, result) - }) - .catch((err) => { - cb(err) - }) - } else { - // string array return for readdir. - cb(null, result) - } - } - - origReaddir(...args) - } - - fs.readdirSync = function readdirSync( - ...args: Parameters - ) { - const res = origReaddirSync(...args) - const p = resolvePathLike(args[0]) - res.forEach((v: Dirent | any) => { - handleDirentSync(p, v) - }) - return res - } - - // ========================================================================= - // fs.opendir - // ========================================================================= - - if (fs.opendir) { - const origOpendir = fs.opendir.bind(fs) - fs.opendir = function opendir(...args: Parameters) { - // if this is not a function opendir should throw an error. - // we call it so we don't have to throw a mock - if (typeof args[args.length - 1] === 'function') { - const cb = once(args[args.length - 1] as any) - args[args.length - 1] = async (err: Error, dir: Dir) => { - try { - cb(null, await handleDir(dir)) - } catch (err) { - cb(err) - } - } - origOpendir(...args) - } else { - return origOpendir(...args).then((dir: Dir) => { - return handleDir(dir) - }) - } - } - } - - // ========================================================================= - // fs.promises - // ========================================================================= - - /** - * patch fs.promises here. - * - * this requires a light touch because if we trigger the getter on older nodejs versions - * it will log an experimental warning to stderr - * - * `(node:62945) ExperimentalWarning: The fs.promises API is experimental` - * - * this api is available as experimental without a flag so users can access it at any time. - */ - const promisePropertyDescriptor = Object.getOwnPropertyDescriptor( - fs, - 'promises' - ) - if (promisePropertyDescriptor) { - const promises: any = {} - promises.lstat = util.promisify(fs.lstat) - // NOTE: node core uses the newer realpath function fs.promises.native instead of fs.realPath - promises.realpath = util.promisify(fs.realpath.native) - promises.readlink = util.promisify(fs.readlink) - promises.readdir = util.promisify(fs.readdir) - if (fs.opendir) promises.opendir = util.promisify(fs.opendir) - // handle experimental api warnings. - // only applies to version of node where promises is a getter property. - if (promisePropertyDescriptor.get) { - const oldGetter = promisePropertyDescriptor.get.bind(fs) - const cachedPromises = {} - - promisePropertyDescriptor.get = () => { - const _promises = oldGetter() - Object.assign(cachedPromises, _promises, promises) - return cachedPromises - } - Object.defineProperty(fs, 'promises', promisePropertyDescriptor) - } else { - // api can be patched directly - Object.assign(fs.promises, promises) - } - } - - // ========================================================================= - // helper functions for dirs - // ========================================================================= - - async function handleDir(dir: Dir) { - const p = path.resolve(dir.path) - const origIterator = dir[Symbol.asyncIterator].bind(dir) - const origRead: any = dir.read.bind(dir) - - dir[Symbol.asyncIterator] = async function* () { - for await (const entry of origIterator()) { - await handleDirent(p, entry) - yield entry - } - } - ;(dir.read as any) = async (...args: any[]) => { - if (typeof args[args.length - 1] === 'function') { - const cb = args[args.length - 1] - args[args.length - 1] = async (err: Error, entry: Dirent) => { - cb(err, entry ? await handleDirent(p, entry) : null) - } - origRead(...args) - } else { - const entry = await origRead(...args) - if (entry) { - await handleDirent(p, entry) - } - return entry - } - } - const origReadSync: any = dir.readSync.bind(dir) - ;(dir.readSync as any) = () => { - return handleDirentSync(p, origReadSync()) // intentionally sync for simplicity - } - - return dir - } - - function handleDirent(p: string, v: Dirent): Promise { - return new Promise((resolve, reject) => { - if (!v.isSymbolicLink()) { - return resolve(v) - } - const f = path.resolve(p, v.name) - return guardedReadLink(f, (str: string) => { - if (f != str) { - return resolve(v) - } - // There are no hops so we should hide the fact that the file is a symlink - v.isSymbolicLink = () => false - origRealpath(f, (err, str) => { - if (err) { - throw err - } - fs.stat(str, (err, stat) => { - if (err) { - throw err - } - patchDirent(v, stat) - resolve(v) - }) - }) - }) - }) - } - - function handleDirentSync(p: string, v: Dirent | null): void { - if (v && v.isSymbolicLink) { - if (v.isSymbolicLink()) { - const f = path.resolve(p, v.name) - if (f == guardedReadLinkSync(f)) { - // There are no hops so we should hide the fact that the file is a symlink - v.isSymbolicLink = () => false - const stat = fs.statSync(origRealpathSync(f)) - patchDirent(v, stat) - } - } - } - } - - function nextHop(loc: string, cb: (next: string | false) => void): void { - let nested: string[] = [] - let maybe = loc - let escapedHop: string | false = false - - readHopLink(maybe, function readNextHop(link) { - if (link === HOP_NOT_FOUND) { - return cb(undefined) - } - - if (link !== HOP_NON_LINK) { - link = path.join(link, ...nested.reverse()) - - if (!isEscape(loc, link)) { - return cb(link) - } - if (!escapedHop) { - escapedHop = link - } - } - - const dirname = path.dirname(maybe) - if ( - !dirname || - dirname == maybe || - dirname == '.' || - dirname == '/' - ) { - // not a link - return cb(escapedHop) - } - nested.push(path.basename(maybe)) - maybe = dirname - readHopLink(maybe, readNextHop) - }) - } - - const hopLinkCache = Object.create(null) as { [f: string]: HopResults } - function readHopLinkSync(p: string): HopResults { - if (hopLinkCache[p]) { - return hopLinkCache[p] - } - - let link: HopResults - - try { - if (origLstatSync(p).isSymbolicLink()) { - link = origReadlinkSync(p) as string - if (link) { - if (!path.isAbsolute(link)) { - link = path.resolve(path.dirname(p), link) - } - } else { - link = HOP_NON_LINK - } - } else { - link = HOP_NON_LINK - } - } catch (err) { - if (err.code === 'ENOENT') { - // file does not exist - link = HOP_NOT_FOUND - } else { - link = HOP_NON_LINK - } - } - - hopLinkCache[p] = link - return link - } - - function readHopLink(p: string, cb: (l: HopResults) => void) { - if (hopLinkCache[p]) { - return cb(hopLinkCache[p]) - } - - origReadlink(p, (err: Error, link: string) => { - if (err) { - let result: HopResults - - if ((err as any).code === 'ENOENT') { - // file does not exist - result = HOP_NOT_FOUND - } else { - result = HOP_NON_LINK - } - - hopLinkCache[p] = result - return cb(result) - } - - if (link === undefined) { - hopLinkCache[p] = HOP_NON_LINK - return cb(HOP_NON_LINK) - } - - if (!path.isAbsolute(link)) { - link = path.resolve(path.dirname(p), link) - } - - hopLinkCache[p] = link - cb(link) - }) - } - - function nextHopSync(loc: string): string | false { - let nested: string[] = [] - let maybe = loc - let escapedHop: string | false = false - - for (;;) { - let link = readHopLinkSync(maybe) - - if (link === HOP_NOT_FOUND) { - return undefined - } - - if (link !== HOP_NON_LINK) { - link = path.join(link, ...nested.reverse()) - - if (!isEscape(loc, link)) { - return link - } - if (!escapedHop) { - escapedHop = link - } - } - - const dirname = path.dirname(maybe) - if ( - !dirname || - dirname == maybe || - dirname == '.' || - dirname == '/' - ) { - // not a link - return escapedHop - } - - nested.push(path.basename(maybe)) - maybe = dirname - } - } - - function guardedReadLink(start: string, cb: (str: string) => void): void { - let loc = start - return nextHop(loc, (next: string | false) => { - if (!next) { - // we're no longer hopping but we haven't escaped; - // something funky happened in the filesystem - return cb(loc) - } - if (isEscape(loc, next)) { - // this hop takes us out of the guard - return nextHop(next, (next2: string | false) => { - if (!next2) { - // the chain is done - return cb(loc) - } - const maybe = path.resolve( - path.dirname(loc), - path.relative(path.dirname(next), next2) - ) - if (!isEscape(loc, maybe)) { - // outside of the guard is a symlink but it is a relative link path - // we can map within the guard so return that - return cb(maybe) - } - // outside of the guard is a symlink that is not mappable inside the guard - return cb(loc) - }) - } - return cb(next) - }) - } - - function guardedReadLinkSync(start: string): string { - let loc = start - let next: string | false = nextHopSync(loc) - if (!next) { - // we're no longer hopping but we haven't escaped; - // something funky happened in the filesystem - return loc - } - if (isEscape(loc, next)) { - // this hop takes us out of the guard - const next2: string | false = nextHopSync(next) - if (!next2) { - // the chain is done - return loc - } - const maybe = path.resolve( - path.dirname(loc), - path.relative(path.dirname(next), next2) - ) - if (!isEscape(loc, maybe)) { - // outside of the guard is a symlink but it is a relative link path - // we can map within the guard so return that - return maybe - } - // outside of the guard is a symlink that is not mappable inside the guard - return loc - } - return next - } - - function unguardedRealPath( - start: string, - cb: (err: Error, str?: string) => void - ): void { - start = stringifyPathLike(start) // handle the "undefined" case (matches behavior as fs.realpath) - const oneHop = (loc, cb) => { - nextHop(loc, (next) => { - if (next == undefined) { - // file does not exist (broken link) - return cb(enoent('realpath', start)) - } else if (!next) { - // we've hit a real file - return cb(null, loc) - } - oneHop(next, cb) - }) - } - oneHop(start, cb) - } - - function guardedRealPath( - start: PathLike, - cb: (err: Error, str?: string) => void, - escapedRoot: string = undefined - ): void { - start = stringifyPathLike(start) // handle the "undefined" case (matches behavior as fs.realpath) - const oneHop = ( - loc: string, - cb: (err: Error, str?: string) => void - ) => { - nextHop(loc, (next) => { - if (!next) { - // we're no longer hopping but we haven't escaped - return fs.exists(loc, (e) => { - if (e) { - // we hit a real file within the guard and can go no further - return cb(null, loc) - } else { - // something funky happened in the filesystem - return cb(enoent('realpath', start)) - } - }) - } - if ( - escapedRoot - ? isEscape(loc, next, [escapedRoot]) - : isEscape(loc, next) - ) { - // this hop takes us out of the guard - return nextHop(next, (next2) => { - if (!next2) { - // the chain is done - return cb(null, loc) - } - const maybe = path.resolve( - path.dirname(loc), - path.relative(path.dirname(next), next2) - ) - if (isEscape(loc, maybe)) { - // outside of the guard is a symlink that is not mappable inside the guard; - // call the unguarded realpath which will throw if the link is dangling; - // if it doesn't throw then return the last path within the guard - return origRealpath(start, (err, _) => { - if (err) return cb(err) - return cb(null, loc) - }) - } - return oneHop(maybe, cb) - }) - } - oneHop(next, cb) - }) - } - oneHop(start, cb) - } - - function unguardedRealPathSync(start: string): string { - start = stringifyPathLike(start) // handle the "undefined" case (matches behavior as fs.realpathSync) - for (let loc = start, next; ; loc = next) { - next = nextHopSync(loc) - if (next == undefined) { - // file does not exist (broken link) - throw enoent('realpath', start) - } else if (!next) { - // we've hit a real file - return loc - } - } - } - - function guardedRealPathSync( - start: PathLike, - escapedRoot: string = undefined - ): string { - start = stringifyPathLike(start) // handle the "undefined" case (matches behavior as fs.realpathSync) - for (let loc = start, next: string | false; ; loc = next as string) { - next = nextHopSync(loc) - if (!next) { - // we're no longer hopping but we haven't escaped - if (fs.existsSync(loc)) { - // we hit a real file within the guard and can go no further - return loc - } else { - // something funky happened in the filesystem; throw ENOENT - throw enoent('realpath', start) - } - } - if ( - escapedRoot - ? isEscape(loc, next, [escapedRoot]) - : isEscape(loc, next) - ) { - // this hop takes us out of the guard - const next2: string | false = nextHopSync(next) - if (!next2) { - // the chain is done - return loc - } - const maybe = path.resolve( - path.dirname(loc), - path.relative(path.dirname(next), next2) - ) - if (isEscape(loc, maybe)) { - // outside of the guard is a symlink that is not mappable inside the guard; - // call the unguarded realpath which will throw if the link is dangling; - // if it doesn't throw then return the last path within the guard - origRealpathSync(start) - return loc - } - next = maybe - // outside of the guard is a symlink but it is a relative link path - // we can map within the guard so lets iterate one more time - } - } - } -} - -// ========================================================================= -// generic helper functions -// ========================================================================= - -export function isSubPath(parent: string, child: string): boolean { - return ( - parent === child || - (child[parent.length] === path.sep && child.startsWith(parent)) - ) -} - -function stringifyPathLike(p: PathLike): string { - if (p instanceof URL) { - return url.fileURLToPath(p) - } else { - return String(p) - } -} - -function resolvePathLike(p: PathLike): string { - return path.resolve(stringifyPathLike(p)) -} - -function normalizePathLike(p: PathLike): string { - const s = stringifyPathLike(p) - - // TODO: are URLs always absolute? - if (!path.isAbsolute(s)) { - return path.resolve(s) - } else { - return path.normalize(s) - } -} - -export function escapeFunction(_roots: string[]) { - // Ensure roots are always absolute. - // Sort to ensure escaping multiple roots chooses the longest one. - const defaultRoots = _roots - .map((root) => path.resolve(root)) - .sort((a, b) => b.length - a.length) - - function fs_isEscape( - linkPath: PathLike, - linkTarget: string, - roots = defaultRoots - ): false | string { - // linkPath is the path of the symlink file itself - // linkTarget is a path that the symlink points to one or more hops away - // linkTarget must already be normalized - - linkPath = normalizePathLike(linkPath) - - for (const root of roots) { - // If the link is in the root check if the realPath has escaped - if (isSubPath(root, linkPath) && !isSubPath(root, linkTarget)) { - return root - } - } - - return false - } - - function fs_canEscape( - maybeLinkPath: string, - roots = defaultRoots - ): boolean { - // maybeLinkPath is the path which may be a symlink - // maybeLinkPath must already be normalized - - for (const root of roots) { - // If the link is in the root check if the realPath has escaped - if (isSubPath(root, maybeLinkPath)) { - return true - } - } - - return false - } - - return { - isEscape: fs_isEscape, - canEscape: fs_canEscape, - } -} - -function once(fn: (...args: unknown[]) => T) { - let called = false - - return (...args: unknown[]) => { - if (called) return - called = true - - let err: Error | false = false - try { - fn(...args) - } catch (_e) { - err = _e - } - - // blow the stack to make sure this doesn't fall into any unresolved promise contexts - if (err) { - setImmediate(() => { - throw err - }) - } - } -} - -function patchDirent(dirent: Dirent | any, stat: Stats | any): void { - // add all stat is methods to Dirent instances with their result. - for (const i in stat) { - if (i.startsWith('is') && typeof stat[i] === 'function') { - // - const result = stat[i]() - if (result) dirent[i] = () => true - else dirent[i] = () => false - } - } -} - -function enoent(s: string, p: PathLike): Error { - let err = new Error(`ENOENT: no such file or directory, ${s} '${p}'`) - ;(err as any).errno = -2 - ;(err as any).syscall = s - ;(err as any).code = 'ENOENT' - ;(err as any).path = p - return err -} - -function einval(s: string, p: PathLike): Error { - let err = new Error(`EINVAL: invalid argument, ${s} '${p}'`) - ;(err as any).errno = -22 - ;(err as any).syscall = s - ;(err as any).code = 'EINVAL' - ;(err as any).path = p - return err -} diff --git a/js/private/node-patches_legacy/src/tsconfig.json b/js/private/node-patches_legacy/src/tsconfig.json deleted file mode 100644 index a171f2e0d..000000000 --- a/js/private/node-patches_legacy/src/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es2017" - }, - "include": ["*.ts"] -} diff --git a/js/private/test/BUILD.bazel b/js/private/test/BUILD.bazel index 1d6c8ff80..b425c212a 100644 --- a/js/private/test/BUILD.bazel +++ b/js/private/test/BUILD.bazel @@ -30,9 +30,9 @@ genrule( ) write_source_files( - name = "write_shellcheck_launcher", + name = "write_launcher_wksp", files = { - "shellcheck_launcher.sh": ":shell_launcher_sed", + "snapshots/wksp/launcher.sh": ":shell_launcher_sed", }, target_compatible_with = select({ "@aspect_bazel_lib//lib:bzlmod": ["@platforms//:incompatible"], @@ -40,6 +40,17 @@ write_source_files( }), ) +write_source_files( + name = "write_launcher_bzlmod", + files = { + "snapshots/bzlmod/launcher.sh": ":shell_launcher_sed", + }, + target_compatible_with = select({ + "@aspect_bazel_lib//lib:bzlmod": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + js_library_test_suite(name = "js_library_test") # js_library(data) wrapper of the data diff --git a/js/private/test/coverage/BUILD.bazel b/js/private/test/coverage/BUILD.bazel index 7fd523aad..2c9c70abd 100644 --- a/js/private/test/coverage/BUILD.bazel +++ b/js/private/test/coverage/BUILD.bazel @@ -28,10 +28,6 @@ coverage_fail_test( "//conditions:default": False, }), entry_point = "lib.js", - unresolved_symlinks_enabled = select({ - "@aspect_rules_js//js:allow_unresolved_symlinks": True, - "//conditions:default": False, - }), ) PASS_CMD = """\ @@ -60,8 +56,4 @@ coverage_pass_test( "//conditions:default": False, }), entry_point = "lib.js", - unresolved_symlinks_enabled = select({ - "@aspect_rules_js//js:allow_unresolved_symlinks": True, - "//conditions:default": False, - }), ) diff --git a/js/private/test/create_launcher/custom_test.bzl b/js/private/test/create_launcher/custom_test.bzl index a625b34be..1b77da4ce 100644 --- a/js/private/test/create_launcher/custom_test.bzl +++ b/js/private/test/create_launcher/custom_test.bzl @@ -52,9 +52,5 @@ def custom_test(**kwargs): "//js:enable_runfiles": True, "//conditions:default": False, }), - unresolved_symlinks_enabled = select({ - "@aspect_rules_js//js:allow_unresolved_symlinks": True, - "//conditions:default": False, - }), **kwargs ) diff --git a/js/private/test/node-patches_legacy/BUILD.bazel b/js/private/test/node-patches_legacy/BUILD.bazel deleted file mode 100644 index 586331673..000000000 --- a/js/private/test/node-patches_legacy/BUILD.bazel +++ /dev/null @@ -1,90 +0,0 @@ -load("//js:defs.bzl", "js_test") -load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin") - -TESTS = [ - "escape.js", - "lstat.js", - "opendir.js", - "readdir.js", - "readlink.js", - "realpath.js", -] - -# Multiple node toolchains for testing across versions -TOOLCHAINS_NAMES = [ - "node16", - "node18", - "node20", -] - -TOOLCHAINS_VERSIONS = [ - select({ - "@bazel_tools//src/conditions:linux_x86_64": "@node16_linux_amd64//:node_toolchain", - "@bazel_tools//src/conditions:darwin": "@node16_darwin_amd64//:node_toolchain", - "@bazel_tools//src/conditions:windows": "@node16_windows_amd64//:node_toolchain", - }), - select({ - "@bazel_tools//src/conditions:linux_x86_64": "@node18_linux_amd64//:node_toolchain", - "@bazel_tools//src/conditions:darwin": "@node18_darwin_amd64//:node_toolchain", - "@bazel_tools//src/conditions:windows": "@node18_windows_amd64//:node_toolchain", - }), - select({ - "@bazel_tools//src/conditions:linux_x86_64": "@node20_linux_amd64//:node_toolchain", - "@bazel_tools//src/conditions:darwin": "@node20_darwin_amd64//:node_toolchain", - "@bazel_tools//src/conditions:windows": "@node20_windows_amd64//:node_toolchain", - }), -] - -# We need to copy the entry points to the bin so that the tests below don't follow the execroot -# symlink back to the source tree since the fs patches are not on for the tests as they are the code -# under test -[ - copy_to_bin( - name = "copy_entry_{}".format(t), - srcs = [t], - ) - for t in TESTS -] - -# Basic tests -[ - js_test( - name = "{}_test".format(t.replace(".js", "")), - data = [ - "//:node_modules/inline-fixtures", - "//js/private/node-patches_legacy/src:compile", - ], - entry_point = "copy_entry_{}".format(t), - patch_node_fs = False, - # Without node patches on for these tests, the program is going to escape the sandbox if it - # is on since the fs patches are not on for the tests as they are the code under test - tags = ["no-sandbox"], - ) - for t in TESTS -] - -# Node process spawning tests -[ - js_test( - name = "spawn_test_%s" % toolchain_name, - args = [ - "$(rootpath //js/private/test/node-patches_legacy:spawn_node_path.js)", - "$(rootpath //js/private/test/node-patches_legacy:spawn_node_path.sh)", - "$(rootpath //js/private/test/node-patches_legacy:spawn_patch_depth.js)", - "$(rootpath //js/private/test/node-patches_legacy:spawn_patch_depth.sh)", - ], - data = [ - "//js/private/test/node-patches_legacy:spawn_node_path.js", - "//js/private/test/node-patches_legacy:spawn_node_path.sh", - "//js/private/test/node-patches_legacy:spawn_patch_depth.js", - "//js/private/test/node-patches_legacy:spawn_patch_depth.sh", - ], - entry_point = "spawn.js", - node_toolchain = toolchain, - patch_node_fs = True, - ) - for toolchain_name, toolchain in zip( - TOOLCHAINS_NAMES, - TOOLCHAINS_VERSIONS, - ) -] diff --git a/js/private/test/node-patches_legacy/escape.js b/js/private/test/node-patches_legacy/escape.js deleted file mode 100644 index dd9d53248..000000000 --- a/js/private/test/node-patches_legacy/escape.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * @license - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const assert = require('assert') -const path = require('path') -const escapeFunction = - require('../../node-patches_legacy/src/fs').escapeFunction -const isSubPath = require('../../node-patches_legacy/src/fs').isSubPath - -// We don't want to bring jest into this repo so we just fake the describe and it functions here -async function describe(_, fn) { - await fn() -} -async function it(_, fn) { - await fn() -} - -describe('escape function', () => { - it('isSubPath is correct', () => { - assert.ok(!isSubPath('/a/b', '/a')) - assert.ok(!isSubPath('/a/b', '/a/c/b')) - assert.ok(isSubPath('/a/b', '/a/b')) - assert.ok(isSubPath('/a/b', '/a/b/c/d')) - }) - - it('isEscape is correct', () => { - const roots = ['/a/b', '/a/b/g/1', '/a/b/g/a/2', '/a/b/g/a/3'] - const { isEscape } = escapeFunction(roots) - - assert.ok(isEscape('/a/b/l', '/a/c/boop')) - assert.ok(isEscape('/a/b', '/a/c/boop')) - assert.ok(isEscape('/a/b', '/a')) - assert.ok(!isEscape('/a/c', '/a/c/boop')) - assert.ok(!isEscape('/a/b/l', '/a/b/f')) - - assert.ok(isEscape('/a/b/g/1', '/some/path')) - assert.ok(isEscape('/a/b/g/1/foo', '/some/path')) - assert.ok(isEscape('/a/b/g/h', '/some/path')) - assert.ok(isEscape('/a/b/g/h/i', '/some/path')) - assert.ok(isEscape('/a/b/g/a/2', '/some/path')) - assert.ok(isEscape('/a/b/g/a/2/foo', '/some/path')) - assert.ok(isEscape('/a/b/g/a/3', '/some/path')) - assert.ok(isEscape('/a/b/g/a/3/foo', '/some/path')) - assert.ok(isEscape('/a/b/g/a/h', '/some/path')) - assert.ok(isEscape('/a/b/g/a/h/i', '/some/path')) - - assert.ok(isEscape('/a/b/g/1', '/a/b')) - assert.ok(isEscape('/a/b/g/1/foo', '/a/b')) - assert.ok(!isEscape('/a/b/g/h', '/a/b')) - assert.ok(!isEscape('/a/b/g/h/i', '/a/b')) - assert.ok(isEscape('/a/b/g/a/2', '/a/b')) - assert.ok(isEscape('/a/b/g/a/2/foo', '/a/b')) - assert.ok(isEscape('/a/b/g/a/3', '/a/b')) - assert.ok(isEscape('/a/b/g/a/3/foo', '/a/b')) - assert.ok(!isEscape('/a/b/g/a/h', '/a/b')) - assert.ok(!isEscape('/a/b/g/a/h/i', '/a/b')) - - assert.ok(isEscape('/a/b/g/1', '/a/b/c')) - assert.ok(isEscape('/a/b/g/1/foo', '/a/b/c')) - assert.ok(!isEscape('/a/b/g/h', '/a/b/c')) - assert.ok(!isEscape('/a/b/g/h/i', '/a/b/c')) - assert.ok(isEscape('/a/b/g/a/2', '/a/b/c')) - assert.ok(isEscape('/a/b/g/a/2/foo', '/a/b/c')) - assert.ok(isEscape('/a/b/g/a/3', '/a/b/c')) - assert.ok(isEscape('/a/b/g/a/3/foo', '/a/b/c')) - assert.ok(!isEscape('/a/b/g/a/h', '/a/b/c')) - assert.ok(!isEscape('/a/b/g/a/h/i', '/a/b/c')) - }) - - it('isEscape handles relative paths', () => { - const roots = ['./a/b', './a/b/g/1', './a/b/g/a/2', './a/b/g/a/3'] - const { isEscape } = escapeFunction(roots) - - assert.ok(isEscape('./a/b/l', path.resolve('./a/c/boop'))) - assert.ok(isEscape('./a/b', path.resolve('./a/c/boop'))) - assert.ok(isEscape('./a/b', path.resolve('./a'))) - assert.ok(!isEscape('./a/c', path.resolve('./a/c/boop'))) - assert.ok(!isEscape('./a/b/l', path.resolve('./a/b/f'))) - - assert.ok(isEscape('./a/b/g/1', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/1/foo', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/h', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/h/i', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/a/2', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/a/2/foo', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/a/3', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/a/3/foo', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/a/h', path.resolve('./some/path'))) - assert.ok(isEscape('./a/b/g/a/h/i', path.resolve('./some/path'))) - - assert.ok(isEscape('./a/b/g/1', path.resolve('./a/b'))) - assert.ok(isEscape('./a/b/g/1/foo', path.resolve('./a/b'))) - assert.ok(!isEscape('./a/b/g/h', path.resolve('./a/b'))) - assert.ok(!isEscape('./a/b/g/h/i', path.resolve('./a/b'))) - assert.ok(isEscape('./a/b/g/a/2', path.resolve('./a/b'))) - assert.ok(isEscape('./a/b/g/a/2/foo', path.resolve('./a/b'))) - assert.ok(isEscape('./a/b/g/a/3', path.resolve('./a/b'))) - assert.ok(isEscape('./a/b/g/a/3/foo', path.resolve('./a/b'))) - assert.ok(!isEscape('./a/b/g/a/h', path.resolve('./a/b'))) - assert.ok(!isEscape('./a/b/g/a/h/i', path.resolve('./a/b'))) - - assert.ok(isEscape('./a/b/g/1', path.resolve('./a/b/c'))) - assert.ok(isEscape('./a/b/g/1/foo', path.resolve('./a/b/c'))) - assert.ok(!isEscape('./a/b/g/h', path.resolve('./a/b/c'))) - assert.ok(!isEscape('./a/b/g/h/i', path.resolve('./a/b/c'))) - assert.ok(isEscape('./a/b/g/a/2', path.resolve('./a/b/c'))) - assert.ok(isEscape('./a/b/g/a/2/foo', path.resolve('./a/b/c'))) - assert.ok(isEscape('./a/b/g/a/3', path.resolve('./a/b/c'))) - assert.ok(isEscape('./a/b/g/a/3/foo', path.resolve('./a/b/c'))) - assert.ok(!isEscape('./a/b/g/a/h', path.resolve('./a/b/c'))) - assert.ok(!isEscape('./a/b/g/a/h/i', path.resolve('./a/b/c'))) - }) -}) diff --git a/js/private/test/node-patches_legacy/lstat.js b/js/private/test/node-patches_legacy/lstat.js deleted file mode 100644 index 00bf9e346..000000000 --- a/js/private/test/node-patches_legacy/lstat.js +++ /dev/null @@ -1,273 +0,0 @@ -/** - * @license - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const assert = require('assert') -const fs = require('fs') -const withFixtures = require('inline-fixtures').withFixtures -const path = require('path') -const util = require('util') - -const patcher = require('../../node-patches_legacy/src/fs').patcher - -// We don't want to bring jest into this repo so we just fake the describe and it functions here -async function describe(_, fn) { - await fn() -} -async function it(_, fn) { - await fn() -} - -describe('testing lstat', async () => { - await it('can lstat symlink in root', async () => { - await withFixtures( - { - a: {}, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a/link to b/file - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [path.join(fixturesDir)]) - - const linkPath = path.join(fixturesDir, 'a', 'link') - assert.ok( - patchedFs.lstatSync(linkPath).isSymbolicLink(), - 'lstatSync should find symbolic link if link is the root' - ) - - assert.ok( - ( - await util.promisify(patchedFs.lstat)(linkPath) - ).isSymbolicLink(), - 'lstat should find symbolic link if link is the root' - ) - - assert.ok( - (await patchedFs.promises.lstat(linkPath)).isSymbolicLink(), - 'promises.lstat should find symbolic link if link is the root' - ) - } - ) - }) - - await it('can lstatSync throwIfNoEntry:false', async () => { - await withFixtures( - { - a: { g: {} }, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [ - path.join(fixturesDir), - path.join(fixturesDir, 'a', 'g'), - ]) - - assert.equal( - undefined, - patchedFs.lstatSync( - path.join(fixturesDir, 'doesnt-exist'), - { throwIfNoEntry: false } - ) - ) - } - ) - }) - - await it('can lstat symlink in guard is file', async () => { - await withFixtures( - { - a: { g: {} }, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a/g/link to b/file - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'g', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [ - path.join(fixturesDir), - path.join(fixturesDir, 'a', 'g'), - ]) - - const linkPath = path.join(fixturesDir, 'a', 'g', 'link') - assert.ok( - patchedFs.lstatSync(linkPath).isFile(), - 'lstatSync should find file if link is in guard' - ) - - assert.ok( - (await util.promisify(patchedFs.lstat)(linkPath)).isFile(), - 'lstat should find file if link is in guard' - ) - - assert.ok( - (await patchedFs.promises.lstat(linkPath)).isFile(), - 'promises.lstat should find file if link is in guard' - ) - } - ) - }) - - await it('lstat of symlink out of root is file.', async () => { - await withFixtures( - { - a: {}, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a/link to b/file - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [path.join(fixturesDir, 'a')]) - - const linkPath = path.join(fixturesDir, 'a', 'link') - - assert.ok( - patchedFs.lstatSync(linkPath).isFile(), - 'lstatSync should find file it file link is out of root' - ) - - assert.ok( - (await util.promisify(patchedFs.lstat)(linkPath)).isFile(), - 'lstat should find file it file link is out of root' - ) - - assert.ok( - (await patchedFs.promises.lstat(linkPath)).isFile(), - 'promises.lstat should find file it file link is out of root' - ) - - let brokenLinkPath = path.join(fixturesDir, 'a', 'broken-link') - fs.symlinkSync( - path.join(fixturesDir, 'doesnt-exist'), - brokenLinkPath - ) - - assert.ok( - patchedFs.lstatSync(brokenLinkPath).isSymbolicLink(), - 'if a symlink is broken but is escaping return it as a link.' - ) - assert.ok( - ( - await util.promisify(patchedFs.lstat)(brokenLinkPath) - ).isSymbolicLink(), - 'if a symlink is broken but is escaping return it as a link.' - ) - assert.ok( - ( - await patchedFs.promises.lstat(brokenLinkPath) - ).isSymbolicLink(), - 'if a symlink is broken but is escaping return it as a link.' - ) - - brokenLinkPath = path.join(fixturesDir, 'a', 'broken-link2') - fs.symlinkSync( - path.join(fixturesDir, 'a', 'doesnt-exist'), - brokenLinkPath - ) - - stat = await patchedFs.promises.lstat(brokenLinkPath) - assert.ok( - stat.isSymbolicLink(), - 'if a symlink is broken but not escaping return it as a link.' - ) - } - ) - }) - - await it('not in root, symlinks do what they would have before.', async () => { - await withFixtures( - { - a: {}, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a/link to b/file - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'b', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [path.join(fixturesDir, 'a')]) - - const linkPath = path.join(fixturesDir, 'b', 'link') - - assert.ok( - patchedFs.lstatSync(linkPath).isSymbolicLink(), - 'lstatSync should find symbolic link if link is out of the root' - ) - - assert.ok( - patchedFs - .lstatSync(new URL(`file://${linkPath}`)) - .isSymbolicLink(), - 'lstatSync should find symbolic link if link is out of the root' - ) - - const stat = await util.promisify(patchedFs.lstat)(linkPath) - assert.ok( - stat.isSymbolicLink(), - 'lstat should find symbolic link if link is outside' - ) - - assert.ok( - (await patchedFs.promises.lstat(linkPath)).isSymbolicLink(), - 'promises.lstat should find symbolic link if link is outside' - ) - } - ) - }) - - await it('includes parent calls in stack traces', async function lstatStackTest1() { - let err - try { - fs.lstatSync(null) - } catch (e) { - err = e - } finally { - if (!err) assert.fail('lstat should fail on invalid path') - if (!err.stack.includes('lstatStackTest1')) - assert.fail( - `lstat error stack should contain calling method: ${err.stack}` - ) - } - }) -}) diff --git a/js/private/test/node-patches_legacy/opendir.js b/js/private/test/node-patches_legacy/opendir.js deleted file mode 100644 index 7b3210b12..000000000 --- a/js/private/test/node-patches_legacy/opendir.js +++ /dev/null @@ -1,263 +0,0 @@ -/** - * @license - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const assert = require('assert') -const fs = require('fs') -const withFixtures = require('inline-fixtures').withFixtures -const path = require('path') -const util = require('util') - -const patcher = require('../../node-patches_legacy/src/fs').patcher - -// We don't want to bring jest into this repo so we just fake the describe and it functions here -async function describe(_, fn) { - await fn() -} -async function it(_, fn) { - await fn() -} - -describe('testing opendir', async () => { - await it('can opendir dirent in root', async () => { - await withFixtures( - { - a: { apples: 'contents' }, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [fixturesDir]) - - let dir - dir = await util.promisify(patchedFs.opendir)( - path.join(fixturesDir, 'a') - ) - const entry1 = await dir.read() - const entry2 = await util.promisify(dir.read.bind(dir))() - const empty = await dir.read() - - let names = [entry1.name, entry2.name] - names.sort() - assert.deepStrictEqual(names, ['apples', 'link']) - - let maybeLink = entry1.name === 'link' ? entry1 : entry2 - assert.ok(maybeLink.isSymbolicLink()) - - assert.ok(!empty, 'last read should be falsey') - - // Assert reading via URL produces the same (first) result. - dir = await util.promisify(patchedFs.opendir)( - new URL(`file://${path.join(fixturesDir, 'a')}`) - ) - assert.equal(entry1.name, (await dir.read()).name) - } - ) - }) - - await it('can opendir dirent link out of root', async () => { - await withFixtures( - { - a: { apples: 'contents' }, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [path.join(fixturesDir, 'a')]) - - let dir - dir = await util.promisify(patchedFs.opendir)( - path.join(fixturesDir, 'a') - ) - const entry1 = await dir.read() - const entry2 = await util.promisify(dir.read.bind(dir))() - const empty = await dir.read() - - let names = [entry1.name, entry2.name] - names.sort() - - assert.ok(!empty) - assert.deepStrictEqual(names, ['apples', 'link']) - - let maybeLink = entry1.name === 'link' ? entry1 : entry2 - - console.error(entry1, entry2) - assert.ok(!maybeLink.isSymbolicLink()) - } - ) - }) - - await it('can async iterate opendir', async () => { - await withFixtures( - { - a: { apples: 'contents' }, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [path.join(fixturesDir)]) - - const dir = await util.promisify(patchedFs.opendir)( - path.join(fixturesDir, 'a') - ) - const names = [] - for await (const entry of dir) { - names.push(entry.name) - if (entry.name === 'link') { - assert.ok(entry.isSymbolicLink()) - } else if (entry.name === 'apples') { - assert.ok(entry.isFile()) - } - } - names.sort() - assert.deepStrictEqual(names, ['apples', 'link']) - } - ) - }) - - await it('can async iterate opendir link out of root', async () => { - await withFixtures( - { - a: { apples: 'contents' }, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [path.join(fixturesDir, 'a')]) - - const dir = await util.promisify(patchedFs.opendir)( - path.join(fixturesDir, 'a') - ) - const names = [] - for await (const entry of dir) { - names.push(entry.name) - if (entry.name === 'link') { - assert.ok(!entry.isSymbolicLink()) - assert.ok(entry.isFile()) - } else if (entry.name === 'apples') { - assert.ok(entry.isFile()) - } - } - names.sort() - assert.deepStrictEqual(names, ['apples', 'link']) - } - ) - }) - - await it('can opendir dirent in a sandbox', async () => { - await withFixtures( - { - sandbox: {}, - execroot: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/link2 to execroot/file - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'file'), - path.join(fixturesDir, 'execroot', 'link2') - ) - // create symlink from execroot/link to execroot/link2 - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link2'), - path.join(fixturesDir, 'execroot', 'link') - ) - - // create sandbox - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'file'), - path.join(fixturesDir, 'sandbox', 'file') - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link'), - path.join(fixturesDir, 'sandbox', 'link') - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link2'), - path.join(fixturesDir, 'sandbox', 'link2') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - - let dir - dir = await util.promisify(patchedFs.opendir)( - path.join(fixturesDir, 'sandbox') - ) - const entry1 = await dir.read() - const entry2 = await util.promisify(dir.read.bind(dir))() - const entry3 = await util.promisify(dir.read.bind(dir))() - const empty = await dir.read() - - let names = [entry1.name, entry2.name, entry3.name] - names.sort() - assert.deepStrictEqual(names, ['file', 'link', 'link2']) - - assert.ok( - entry1.name === 'file' - ? !entry1.isSymbolicLink() - : entry1.isSymbolicLink() - ) - assert.ok( - entry2.name === 'file' - ? !entry2.isSymbolicLink() - : entry2.isSymbolicLink() - ) - assert.ok( - entry3.name === 'file' - ? !entry3.isSymbolicLink() - : entry3.isSymbolicLink() - ) - - assert.ok(!empty, 'last read should be falsey') - } - ) - }) -}) diff --git a/js/private/test/node-patches_legacy/readdir.js b/js/private/test/node-patches_legacy/readdir.js deleted file mode 100644 index 0adcb088a..000000000 --- a/js/private/test/node-patches_legacy/readdir.js +++ /dev/null @@ -1,246 +0,0 @@ -/** - * @license - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const assert = require('assert') -const fs = require('fs') -const withFixtures = require('inline-fixtures').withFixtures -const path = require('path') -const util = require('util') - -const patcher = require('../../node-patches_legacy/src/fs').patcher - -// We don't want to bring jest into this repo so we just fake the describe and it functions here -async function describe(_, fn) { - await fn() -} -async function it(_, fn) { - await fn() -} - -describe('testing readdir', async () => { - await it('can readdir dirent in root', async () => { - await withFixtures( - { - a: { apples: 'contents' }, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [fixturesDir]) - - let dirents = patchedFs.readdirSync( - path.join(fixturesDir, 'a'), - { - withFileTypes: true, - } - ) - assert.deepStrictEqual(dirents[0].name, 'apples') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.ok(dirents[0].isFile()) - assert.ok(dirents[1].isSymbolicLink()) - - dirents = await util.promisify(patchedFs.readdir)( - path.join(fixturesDir, 'a'), - { withFileTypes: true } - ) - assert.deepStrictEqual(dirents[0].name, 'apples') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.ok(dirents[0].isFile()) - assert.ok(dirents[1].isSymbolicLink()) - - dirents = await patchedFs.promises.readdir( - path.join(fixturesDir, 'a'), - { withFileTypes: true } - ) - assert.deepStrictEqual(dirents[0].name, 'apples') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.ok(dirents[0].isFile()) - assert.ok(dirents[1].isSymbolicLink()) - - // Assert the same with URL file references - dirents = patchedFs.readdirSync( - new URL(`file://${path.join(fixturesDir, 'a')}`), - { - withFileTypes: true, - } - ) - assert.deepStrictEqual(dirents[0].name, 'apples') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.ok(dirents[0].isFile()) - assert.ok(dirents[1].isSymbolicLink()) - } - ) - }) - - await it('can readdir link dirents as files out of root', async () => { - await withFixtures( - { - a: { apples: 'contents' }, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - patcher(patchedFs, [path.join(fixturesDir, 'a')]) - - console.error('FOO') - console.error(patchedFs.readdirSync) - let dirents = patchedFs.readdirSync( - path.join(fixturesDir, 'a'), - { - withFileTypes: true, - } - ) - console.error('BAR') - console.log(dirents) - assert.deepStrictEqual(dirents[0].name, 'apples') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.ok(dirents[0].isFile()) - assert.ok(!dirents[1].isSymbolicLink()) - assert.ok(dirents[1].isFile()) - - console.error('FOO') - dirents = await util.promisify(patchedFs.readdir)( - path.join(fixturesDir, 'a'), - { withFileTypes: true } - ) - console.error('BAR') - assert.deepStrictEqual(dirents[0].name, 'apples') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.ok(dirents[0].isFile()) - assert.ok(!dirents[1].isSymbolicLink()) - assert.ok(dirents[1].isFile()) - - dirents = await patchedFs.promises.readdir( - path.join(fixturesDir, 'a'), - { withFileTypes: true } - ) - assert.deepStrictEqual(dirents[0].name, 'apples') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.ok(dirents[0].isFile()) - assert.ok(!dirents[1].isSymbolicLink()) - assert.ok(dirents[1].isFile()) - } - ) - }) - - await it('can readdir dirent in a sandbox', async () => { - await withFixtures( - { - sandbox: {}, - execroot: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/link2 to execroot/file - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'file'), - path.join(fixturesDir, 'execroot', 'link2') - ) - // create symlink from execroot/link to execroot/link2 - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link2'), - path.join(fixturesDir, 'execroot', 'link') - ) - - // create sandbox - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'file'), - path.join(fixturesDir, 'sandbox', 'file') - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link'), - path.join(fixturesDir, 'sandbox', 'link') - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link2'), - path.join(fixturesDir, 'sandbox', 'link2') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - - let dirents = patchedFs.readdirSync( - path.join(fixturesDir, 'sandbox'), - { - withFileTypes: true, - } - ) - assert.deepStrictEqual(dirents[0].name, 'file') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.deepStrictEqual(dirents[2].name, 'link2') - assert.ok(dirents[0].isFile()) - assert.ok(dirents[1].isSymbolicLink()) - assert.ok(dirents[2].isSymbolicLink()) - - dirents = await util.promisify(patchedFs.readdir)( - path.join(fixturesDir, 'sandbox'), - { withFileTypes: true } - ) - assert.deepStrictEqual(dirents[0].name, 'file') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.deepStrictEqual(dirents[2].name, 'link2') - assert.ok(dirents[0].isFile()) - assert.ok(dirents[1].isSymbolicLink()) - assert.ok(dirents[2].isSymbolicLink()) - - dirents = await patchedFs.promises.readdir( - path.join(fixturesDir, 'sandbox'), - { withFileTypes: true } - ) - assert.deepStrictEqual(dirents[0].name, 'file') - assert.deepStrictEqual(dirents[1].name, 'link') - assert.deepStrictEqual(dirents[2].name, 'link2') - assert.ok(dirents[0].isFile()) - assert.ok(dirents[1].isSymbolicLink()) - assert.ok(dirents[2].isSymbolicLink()) - } - ) - }) - - await it('includes parent calls in stack traces', async function readdirStackTest1() { - let err - try { - fs.readdirSync('/foo/bar' + Date.now()) - } catch (e) { - err = e - } finally { - if (!err) assert.fail('readdirSync should fail on invalid path') - if (!err.stack.includes('readdirStackTest1')) - assert.fail( - `readdirSync error stack should contain calling method: ${err.stack}` - ) - } - }) -}) diff --git a/js/private/test/node-patches_legacy/readlink.js b/js/private/test/node-patches_legacy/readlink.js deleted file mode 100644 index 6639719c0..000000000 --- a/js/private/test/node-patches_legacy/readlink.js +++ /dev/null @@ -1,256 +0,0 @@ -/** - * @license - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const assert = require('assert') -const fs = require('fs') -const withFixtures = require('inline-fixtures').withFixtures -const path = require('path') -const util = require('util') - -const patcher = require('../../node-patches_legacy/src/fs').patcher - -// We don't want to bring jest into this repo so we just fake the describe and it functions here -async function describe(_, fn) { - await fn() -} -async function it(_, fn) { - await fn() -} - -describe('testing readlink', async () => { - await it('can resolve symlink in root', async () => { - await withFixtures( - { - a: {}, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir)]) - const linkPath = path.join(fixturesDir, 'a', 'link') - - assert.deepStrictEqual( - patchedFs.readlinkSync(linkPath), - path.join(fixturesDir, 'b', 'file'), - 'SYNC: should read the symlink because its within root' - ) - - assert.deepStrictEqual( - patchedFs.readlinkSync(new URL(`file://${linkPath}`)), - path.join(fixturesDir, 'b', 'file'), - 'SYNC: should read the symlink because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.readlink)(linkPath), - path.join(fixturesDir, 'b', 'file'), - 'CB: should read the symlink because its within root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.readlink(linkPath), - path.join(fixturesDir, 'b', 'file'), - 'Promise: should read the symlink because its within root' - ) - } - ) - }) - - await it("doesn't resolve as symlink outside of root", async () => { - await withFixtures( - { - a: {}, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'a')]) - const linkPath = path.join( - fs.realpathSync(fixturesDir), - 'a', - 'link' - ) - - assert.throws(() => { - patchedFs.readlinkSync(linkPath) - }, "should throw because it's not a link") - - let thrown - try { - await util.promisify(patchedFs.readlink)(linkPath) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('must throw einval error') - } - - thrown = undefined - try { - await patchedFs.promises.readlink(linkPath) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('must throw einval error') - } - } - ) - }) - - await it('can resolve symlink to a symlink in the sandbox if it has a corresponding location', async () => { - await withFixtures( - { - sandbox: {}, - execroot: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/link to execroot/file - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'file'), - path.join(fixturesDir, 'execroot', 'link') - ) - - // create sandbox - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'file'), - path.join(fixturesDir, 'sandbox', 'file') - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link'), - path.join(fixturesDir, 'sandbox', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - const linkPath = path.join(fixturesDir, 'sandbox', 'link') - const filePath = path.join(fixturesDir, 'sandbox', 'file') - - assert.deepStrictEqual( - patchedFs.readlinkSync(linkPath), - filePath, - 'SYNC: should read the symlink in the sandbox' - ) - - assert.deepStrictEqual( - patchedFs.readlinkSync(new URL(`file://${linkPath}`)), - filePath, - 'SYNC: should read the symlink in the sandbox' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.readlink)(linkPath), - filePath, - 'CB: should read the symlink in the sandbox' - ) - - assert.deepStrictEqual( - await patchedFs.promises.readlink(linkPath), - filePath, - 'Promise: should read the symlink in the sandbox' - ) - } - ) - }) - - await it('cant resolve symlink to a symlink in the sandbox if it has no corresponding location', async () => { - await withFixtures( - { - sandbox: {}, - execroot: {}, - otherroot: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create dangling symlink from execroot/link to execroot/file - fs.symlinkSync( - path.join(fixturesDir, 'otherroot', 'file'), - path.join(fixturesDir, 'execroot', 'link') - ) - // create sandbox - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link'), - path.join(fixturesDir, 'sandbox', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - const linkPath = path.join(fixturesDir, 'sandbox', 'link') - const filePath = path.join(fixturesDir, 'sandbox', 'file') - - assert.throws(() => { - patchedFs.readlinkSync(linkPath) - }, "should throw because it's not a resolvable link") - - let thrown - try { - await util.promisify(patchedFs.readlink)(linkPath) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('must throw einval error') - } - - thrown = undefined - try { - await patchedFs.promises.readlink(linkPath) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('must throw einval error') - } - } - ) - }) - - await it('includes parent calls in stack traces', async function readlinkStackTest1() { - let err - try { - fs.readlinkSync(null) - } catch (e) { - err = e - } finally { - if (!err) assert.fail('readlinkSync should fail on invalid path') - if (!err.stack.includes('readlinkStackTest1')) - assert.fail( - `readlinkSync error stack should contain calling method: ${err.stack}` - ) - } - }) -}) diff --git a/js/private/test/node-patches_legacy/realpath.js b/js/private/test/node-patches_legacy/realpath.js deleted file mode 100644 index 7fc65d090..000000000 --- a/js/private/test/node-patches_legacy/realpath.js +++ /dev/null @@ -1,954 +0,0 @@ -/** - * @license - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const assert = require('assert') -const fs = require('fs') -const withFixtures = require('inline-fixtures').withFixtures -const path = require('path') -const util = require('util') - -const patcher = require('../../node-patches_legacy/src/fs').patcher - -// We don't want to bring jest into this repo so we just fake the describe and it functions here -async function describe(_, fn) { - await fn() -} -async function it(_, fn) { - await fn() -} - -describe('testing realpath', async () => { - await it('can handle empty, dot, undefined & null values', async () => { - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [process.cwd()]) - - // --------------------------------------------------------------------- - // empty string - - assert.deepStrictEqual( - patchedFs.realpathSync(''), - process.cwd(), - 'should handle an empty string' - ) - - assert.throws(() => { - patchedFs.realpathSync.native('') - }, 'should throw if empty string is passed') - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(''), - process.cwd(), - 'should handle an empty string' - ) - - let thrown - try { - await util.promisify(patchedFs.realpath.native)('') - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('should throw if empty string is passed') - } - - // --------------------------------------------------------------------- - // '.' - - assert.deepStrictEqual( - patchedFs.realpathSync('.'), - process.cwd(), - "should handle '.'" - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native('.'), - process.cwd(), - "should handle '.'" - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)('.'), - process.cwd(), - "should handle '.'" - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)('.'), - process.cwd(), - "should handle '.'" - ) - - // --------------------------------------------------------------------- - // undefined - - assert.throws(() => { - patchedFs.realpathSync(undefined) - }, 'should throw if undefined is passed') - - assert.throws(() => { - patchedFs.realpathSync.native(undefined) - }, 'should throw if undefined is passed') - - thrown = undefined - try { - await util.promisify(patchedFs.realpath)(undefined) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('should throw if undefined is passed') - } - - thrown = undefined - try { - await util.promisify(patchedFs.realpath.native)(undefined) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('should throw if undefined is passed') - } - - // --------------------------------------------------------------------- - // null - - assert.throws(() => { - patchedFs.realpathSync(null) - }, 'should throw if null is passed') - - assert.throws(() => { - patchedFs.realpathSync.native(null) - }, 'should throw if null is passed') - - thrown = undefined - try { - await util.promisify(patchedFs.realpath)(null) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('should throw if null is passed') - } - - thrown = undefined - try { - await util.promisify(patchedFs.realpath.native)(null) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('should throw if null is passed') - } - }) - - await it('can resolve symlink in root', async () => { - await withFixtures( - { - a: {}, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - // on mac, inside of bazel, the fixtures dir returned here is not realpath-ed. - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir)]) - const linkPath = path.join( - fs.realpathSync(fixturesDir), - 'a', - 'link' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath), - path.join(fixturesDir, 'b', 'file'), - 'SYNC: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(linkPath), - path.join(fixturesDir, 'b', 'file'), - 'SYNC.native: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath), - path.join(fixturesDir, 'b', 'file'), - 'CB: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(linkPath), - path.join(fixturesDir, 'b', 'file'), - 'CB: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath), - path.join(fixturesDir, 'b', 'file'), - 'Promise: should resolve the symlink the same because its within root' - ) - } - ) - }) - - await it('can resolve a real file and directory in root', async () => { - await withFixtures( - { - a: { file: 'contents' }, - }, - async (fixturesDir) => { - // on mac, inside of bazel, the fixtures dir returned here is not realpath-ed. - fixturesDir = fs.realpathSync(fixturesDir) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir)]) - const filePath = path.join( - fs.realpathSync(fixturesDir), - 'a', - 'file' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(filePath), - filePath, - 'SYNC: should resolve the a real file within the root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(filePath), - filePath, - 'SYNC.native: should resolve the a real file within the root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(filePath), - filePath, - 'CB: should resolve the a real file within the root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(filePath), - filePath, - 'CB: should resolve the a real file within the root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(filePath), - filePath, - 'Promise: should resolve the a real file within the root' - ) - - const directoryPath = path.join( - fs.realpathSync(fixturesDir), - 'a' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(directoryPath), - directoryPath, - 'SYNC: should resolve the a real directory within the root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(directoryPath), - directoryPath, - 'SYNC.native: should resolve the a real directory within the root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(directoryPath), - directoryPath, - 'CB: should resolve the a real directory within the root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)( - directoryPath - ), - directoryPath, - 'CB: should resolve the a real directory within the root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(directoryPath), - directoryPath, - 'Promise: should resolve the a real directory within the root' - ) - } - ) - }) - - await it("doesn't resolve as symlink outside of root", async () => { - await withFixtures( - { - a: {}, - b: { file: 'contents' }, - }, - async (fixturesDir) => { - // ensure realpath. - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from a to b - fs.symlinkSync( - path.join(fixturesDir, 'b', 'file'), - path.join(fixturesDir, 'a', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'a')]) - const linkPath = path.join( - fs.realpathSync(fixturesDir), - 'a', - 'link' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath), - path.join(fixturesDir, 'a', 'link'), - 'should pretend symlink is in the root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(new URL(`file://${linkPath}`)), - path.join(fixturesDir, 'a', 'link'), - 'should pretend symlink is in the root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath), - path.join(fixturesDir, 'a', 'link'), - 'should pretend symlink is in the root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath), - path.join(fixturesDir, 'a', 'link'), - 'should pretend symlink is in the root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath( - new URL(`file://${linkPath}`) - ), - path.join(fixturesDir, 'a', 'link'), - 'should pretend symlink is in the root' - ) - } - ) - }) - - await it('can resolve symlink to a symlink in the sandbox if it has a corresponding location', async () => { - await withFixtures( - { - sandbox: {}, - execroot: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/link2 to execroot/file - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'file'), - path.join(fixturesDir, 'execroot', 'link2') - ) - // create symlink from execroot/link to execroot/link2 - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link2'), - path.join(fixturesDir, 'execroot', 'link') - ) - - // create sandbox - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'file'), - path.join(fixturesDir, 'sandbox', 'file') - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link'), - path.join(fixturesDir, 'sandbox', 'link') - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link2'), - path.join(fixturesDir, 'sandbox', 'link2') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - const linkPath = path.join(fixturesDir, 'sandbox', 'link') - const filePath = path.join(fixturesDir, 'sandbox', 'file') - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath), - filePath, - 'SYNC: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(linkPath), - filePath, - 'SYNC.native: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath), - filePath, - 'CB: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(linkPath), - filePath, - 'CB: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath), - filePath, - 'Promise: should resolve the symlink the same because its within root' - ) - } - ) - }) - - await it('can resolve symlink to a symlink in the sandbox if there is no corresponding location in the sandbox but is a realpath outside', async () => { - await withFixtures( - { - sandbox: {}, - execroot: {}, - otherroot: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/link to otherroot/file - fs.symlinkSync( - path.join(fixturesDir, 'otherroot', 'file'), - path.join(fixturesDir, 'execroot', 'link') - ) - - // create sandbox - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link'), - path.join(fixturesDir, 'sandbox', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - const linkPath = path.join(fixturesDir, 'sandbox', 'link') - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath), - linkPath, - 'SYNC: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(new URL(`file://${linkPath}`)), - linkPath, - 'SYNC: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(linkPath), - linkPath, - 'SYNC.native: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native( - new URL(`file://${linkPath}`) - ), - linkPath, - 'SYNC.native: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath), - linkPath, - 'CB: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(linkPath), - linkPath, - 'CB: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath), - linkPath, - 'Promise: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath( - new URL(`file://${linkPath}`) - ), - linkPath, - 'Promise: should resolve the symlink the same because its within root' - ) - } - ) - }) - - await it('realpath will stop resolving at the last hop with a corresponding path in the sandbox', async () => { - await withFixtures( - { - sandbox: {}, - execroot: {}, - otherroot: { file: 'contents' }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/link2 to otherroot/file - fs.symlinkSync( - path.join(fixturesDir, 'otherroot', 'file'), - path.join(fixturesDir, 'execroot', 'link2') - ) - // create symlink from execroot/link to execroot/link2 - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link2'), - path.join(fixturesDir, 'execroot', 'link') - ) - - // create sandbox - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link'), - path.join(fixturesDir, 'sandbox', 'link') - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link2'), - path.join(fixturesDir, 'sandbox', 'link2') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - const linkPath = path.join(fixturesDir, 'sandbox', 'link') - const link2Path = path.join(fixturesDir, 'sandbox', 'link2') - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath), - link2Path, - 'SYNC: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(linkPath), - link2Path, - 'SYNC.native: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath), - link2Path, - 'CB: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(linkPath), - link2Path, - 'CB: should resolve the symlink the same because its within root' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath), - link2Path, - 'Promise: should resolve the symlink the same because its within root' - ) - } - ) - }) - - await it('cant resolve symlink to a symlink in the sandbox if it is dangling outside of the sandbox', async () => { - await withFixtures( - { - sandbox: {}, - execroot: {}, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/link to otherroot/file - fs.symlinkSync( - path.join(fixturesDir, 'otherroot', 'file'), - path.join(fixturesDir, 'execroot', 'link') - ) - - // create sandbox - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'link'), - path.join(fixturesDir, 'sandbox', 'link') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - const linkPath = path.join(fixturesDir, 'sandbox', 'link') - - assert.throws(() => { - patchedFs.realpathSync(linkPath) - }, "should throw because it's not a resolvable link") - - assert.throws(() => { - patchedFs.realpathSync.native(linkPath) - }, "should throw because it's not a resolvable link") - - let thrown - try { - await util.promisify(patchedFs.realpath)(linkPath) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('must throw einval error') - } - - thrown = undefined - try { - await util.promisify(patchedFs.realpath.native)(linkPath) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('must throw einval error') - } - - thrown = undefined - try { - await patchedFs.promises.realpath(linkPath) - } catch (e) { - thrown = e - } finally { - if (!thrown) assert.fail('must throw einval error') - } - } - ) - }) - - await it('can resolve a nested escaping symlinking within a escaping parent directory symlink', async () => { - await withFixtures( - { - sandbox: { - node_modules: {}, - virtual_store: { pkg: {} }, - }, - execroot: { - node_modules: {}, - virtual_store: { - pkg: { - file: 'contents', - }, - }, - }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/node_modules/pkg to execroot/virtual_store/pkg - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'virtual_store', 'pkg'), - path.join(fixturesDir, 'execroot', 'node_modules', 'pkg') - ) - - // create sandbox (with relative symlinks in place) - fs.symlinkSync( - path.join( - fixturesDir, - 'execroot', - 'virtual_store', - 'pkg', - 'file' - ), - path.join( - fixturesDir, - 'sandbox', - 'virtual_store', - 'pkg', - 'file' - ) - ) - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'node_modules', 'pkg'), - path.join(fixturesDir, 'sandbox', 'node_modules', 'pkg') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - const linkPath = path.join( - fixturesDir, - 'sandbox', - 'node_modules', - 'pkg', - 'file' - ) - const filePath = path.join( - fixturesDir, - 'sandbox', - 'virtual_store', - 'pkg', - 'file' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath), - filePath, - 'SYNC: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(linkPath), - filePath, - 'SYNC.native: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath), - filePath, - 'CB: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(linkPath), - filePath, - 'CB: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath), - filePath, - 'Promise: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - const linkPath2 = path.join( - fixturesDir, - 'sandbox', - 'node_modules', - 'pkg' - ) - const filePath2 = path.join( - fixturesDir, - 'sandbox', - 'virtual_store', - 'pkg' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath2), - filePath2, - 'SYNC: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(linkPath2), - filePath2, - 'SYNC.native: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath2), - filePath2, - 'CB: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(linkPath2), - filePath2, - 'CB: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath2), - filePath2, - 'Promise: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - } - ) - }) - - await it('can resolve a nested escaping symlinking within a non-escaping parent directory symlink', async () => { - await withFixtures( - { - sandbox: { - node_modules: {}, - virtual_store: { pkg: {} }, - }, - execroot: { - node_modules: {}, - virtual_store: { - pkg: { - file: 'contents', - }, - }, - }, - }, - async (fixturesDir) => { - fixturesDir = fs.realpathSync(fixturesDir) - - // create symlink from execroot/node_modules/pkg to execroot/virtual_store/pkg - fs.symlinkSync( - path.join(fixturesDir, 'execroot', 'virtual_store', 'pkg'), - path.join(fixturesDir, 'execroot', 'node_modules', 'pkg') - ) - - // create sandbox (with relative symlinks in place) - fs.symlinkSync( - path.join( - fixturesDir, - 'execroot', - 'virtual_store', - 'pkg', - 'file' - ), - path.join( - fixturesDir, - 'sandbox', - 'virtual_store', - 'pkg', - 'file' - ) - ) - fs.symlinkSync( - path.join(fixturesDir, 'sandbox', 'virtual_store', 'pkg'), - path.join(fixturesDir, 'sandbox', 'node_modules', 'pkg') - ) - - const patchedFs = Object.assign({}, fs) - patchedFs.promises = Object.assign({}, fs.promises) - - patcher(patchedFs, [path.join(fixturesDir, 'sandbox')]) - const linkPath = path.join( - fixturesDir, - 'sandbox', - 'node_modules', - 'pkg', - 'file' - ) - const filePath = path.join( - fixturesDir, - 'sandbox', - 'virtual_store', - 'pkg', - 'file' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath), - filePath, - 'SYNC: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(linkPath), - filePath, - 'SYNC.native: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath), - filePath, - 'CB: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(linkPath), - filePath, - 'CB: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath), - filePath, - 'Promise: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - const linkPath2 = path.join( - fixturesDir, - 'sandbox', - 'node_modules', - 'pkg' - ) - const filePath2 = path.join( - fixturesDir, - 'sandbox', - 'virtual_store', - 'pkg' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync(linkPath2), - filePath2, - 'SYNC: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - patchedFs.realpathSync.native(linkPath2), - filePath2, - 'SYNC.native: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath)(linkPath2), - filePath2, - 'CB: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await util.promisify(patchedFs.realpath.native)(linkPath2), - filePath2, - 'CB: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - - assert.deepStrictEqual( - await patchedFs.promises.realpath(linkPath2), - filePath2, - 'Promise: should resolve the nested escaping symlinking within a non-escaping parent directory symlink' - ) - } - ) - }) - - await it('includes parent calls in stack traces', async function realpathStackTest1() { - let err - try { - fs.realpathSync(null) - } catch (e) { - err = e - } finally { - if (!err) assert.fail('realpathSync should fail on invalid path') - if (!err.stack.includes('realpathStackTest1')) - assert.fail( - `realpathSync error stack should contain calling method: ${err.stack}` - ) - } - }) -}) diff --git a/js/private/test/node-patches_legacy/spawn.js b/js/private/test/node-patches_legacy/spawn.js deleted file mode 100644 index 24a54d519..000000000 --- a/js/private/test/node-patches_legacy/spawn.js +++ /dev/null @@ -1,239 +0,0 @@ -const child_process = require('child_process') - -const printbinjs = process.argv[2] -const printbinsh = process.argv[3] -const printdepthjs = process.argv[4] -const printdepthsh = process.argv[5] - -// We don't want to bring jest into this repo so we just fake the describe and it functions here -async function describe(_, fn) { - await fn() -} -async function it(_, fn) { - await fn() -} - -describe('child_process node path', async () => { - function assertNodePath({ stdout, stderr, code, error }) { - // Process errors - if (stderr?.toString().trim()) { - throw new Error(`Received stderr: ${stderr.toString()}`) - } else if (code) { - throw new Error(`Exit code: ${code}`) - } else if (error) { - throw new Error(`Error: ${error}`) - } - - const childNodePath = stdout.toString().trim() - const expectedNodePath = process.execPath - - // NdoeJS path - if (childNodePath !== expectedNodePath) { - throw new Error( - `Expected: ${expectedNodePath}\n Actual: ${childNodePath}` - ) - } - } - - function testNodePathAsync(cp) { - return new Promise((resolve, reject) => { - let stdout = '', - stderr = '' - cp.stdout.on('data', (moreOut) => (stdout += moreOut)) - cp.stderr.on('data', (moreError) => (stderr += moreError)) - - cp.on('error', reject) - cp.on('close', (code) => { - try { - resolve(assertNodePath({ stdout, stderr, code })) - } catch (e) { - reject(e) - } - }) - }) - } - - function testNodePathSync(cp) { - return assertNodePath(cp) - } - - function createAssertNodePathCallback(resolve, reject) { - return (error, stdout, stderr) => { - try { - resolve(assertNodePath({ stdout, stderr, error })) - } catch (e) { - reject(e) - } - } - } - - await it('should launch patched node via child_process.execSync("node")', () => { - testNodePathSync({ - stdout: child_process.execSync(`node ${printbinjs}`), - }) - }) - - await it('should launch patched node via child_process.spawnSync("node")', () => { - testNodePathSync(child_process.spawnSync('node', [printbinjs])) - }) - - await it('should launch patched node via child_process.spawn("node")', async () => { - await testNodePathAsync(child_process.spawn('node', [printbinjs])) - }) - - await it('should launch patched node via child_process.spawn("node") with {shell: true}', async () => { - await testNodePathAsync( - child_process.spawn('node', [printbinjs], { shell: true }) - ) - }) - - await it('should launch patched node via child_process.fork()', async () => { - await testNodePathAsync( - child_process.fork(printbinjs, { stdio: 'pipe' }) - ) - }) - - await it('should launch patched node via child_process.exec("node")', async () => { - await new Promise((resolve, reject) => - child_process.exec( - `node ${printbinjs}`, - createAssertNodePathCallback(resolve, reject) - ) - ) - }) - - await it('should return patched node via exec(`which node`)', async () => { - await new Promise((resolve, reject) => - child_process.exec( - 'which node', - createAssertNodePathCallback(resolve, reject) - ) - ) - }) - - await it('should launch patched node via child_process.execFile()', async () => { - await new Promise((resolve, reject) => - child_process.execFile( - printbinsh, - createAssertNodePathCallback(resolve, reject) - ) - ) - }) - - await it('should launch patched node via child_process.execFileSync()', () => { - testNodePathSync({ stdout: child_process.execFileSync(printbinsh) }) - }) -}) - -describe('child_process patch depth', async () => { - function assertPatchDepth({ stdout, stderr, code, error }) { - // Process errors - if (stderr?.toString().trim()) { - throw new Error(`Received stderr: ${stderr.toString()}`) - } else if (code) { - throw new Error(`Exit code: ${code}`) - } else if (error) { - throw new Error(`Error: ${error}`) - } - - const expectedPatchDepth = - process.env.JS_BINARY__NODE_PATCHES_DEPTH + '.' - const childPatchDepth = stdout.toString().trim() - - // NdoeJS path - if (childPatchDepth !== expectedPatchDepth) { - throw new Error( - `Expected: ${expectedPatchDepth}\n Actual: ${childPatchDepth}` - ) - } - } - - function testPatchDepthAsync(cp) { - return new Promise((resolve, reject) => { - let stdout = '', - stderr = '' - cp.stdout.on('data', (moreOut) => (stdout += moreOut)) - cp.stderr.on('data', (moreError) => (stderr += moreError)) - - cp.on('error', reject) - cp.on('close', (code) => { - try { - resolve(assertPatchDepth({ stdout, stderr, code })) - } catch (e) { - reject(e) - } - }) - }) - } - - function testPatchDepthSync(cp) { - return assertPatchDepth(cp) - } - - function createAssertPatchDepthCallback(resolve, reject) { - return (error, stdout, stderr) => { - try { - resolve(assertPatchDepth({ stdout, stderr, error })) - } catch (e) { - reject(e) - } - } - } - - await it('should launch patched node via child_process.execSync("node")', () => { - testPatchDepthSync({ - stdout: child_process.execSync(`node ${printdepthjs}`), - }) - }) - - await it('should launch patched node via child_process.spawnSync("node")', () => { - testPatchDepthSync(child_process.spawnSync('node', [printdepthjs])) - }) - - await it('should launch patched node via child_process.spawn("node")', async () => { - await testPatchDepthAsync(child_process.spawn('node', [printdepthjs])) - }) - - await it('should launch patched node via child_process.spawn("node") with {shell: true}', async () => { - await testPatchDepthAsync( - child_process.spawn('node', [printdepthjs], { shell: true }) - ) - }) - - await it('should launch patched node via child_process.fork()', async () => { - await testPatchDepthAsync( - child_process.fork(printdepthjs, { stdio: 'pipe' }) - ) - }) - - await it('should launch patched node via child_process.exec("node")', async () => { - await new Promise((resolve, reject) => - child_process.exec( - `node ${printdepthjs}`, - createAssertPatchDepthCallback(resolve, reject) - ) - ) - }) - - await it('should return patched node via node -e ""', async () => { - await new Promise((resolve, reject) => - child_process.exec( - 'node -e "console.log(process.env.JS_BINARY__NODE_PATCHES_DEPTH)"', - createAssertPatchDepthCallback(resolve, reject) - ) - ) - }) - - await it('should launch patched node via child_process.execFile()', async () => { - await new Promise((resolve, reject) => - child_process.execFile( - printdepthsh, - createAssertPatchDepthCallback(resolve, reject) - ) - ) - }) - - await it('should launch patched node via child_process.execFileSync()', () => { - testPatchDepthSync({ stdout: child_process.execFileSync(printdepthsh) }) - }) -}) diff --git a/js/private/test/node-patches_legacy/spawn_node_path.js b/js/private/test/node-patches_legacy/spawn_node_path.js deleted file mode 100644 index 89fbdc5a7..000000000 --- a/js/private/test/node-patches_legacy/spawn_node_path.js +++ /dev/null @@ -1 +0,0 @@ -console.log(process.argv[0]) diff --git a/js/private/test/node-patches_legacy/spawn_node_path.sh b/js/private/test/node-patches_legacy/spawn_node_path.sh deleted file mode 100755 index 80dee5a44..000000000 --- a/js/private/test/node-patches_legacy/spawn_node_path.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -which node diff --git a/js/private/test/node-patches_legacy/spawn_patch_depth.js b/js/private/test/node-patches_legacy/spawn_patch_depth.js deleted file mode 100644 index 428b91e25..000000000 --- a/js/private/test/node-patches_legacy/spawn_patch_depth.js +++ /dev/null @@ -1 +0,0 @@ -console.log(process.env.JS_BINARY__NODE_PATCHES_DEPTH) diff --git a/js/private/test/node-patches_legacy/spawn_patch_depth.sh b/js/private/test/node-patches_legacy/spawn_patch_depth.sh deleted file mode 100755 index acb0bff38..000000000 --- a/js/private/test/node-patches_legacy/spawn_patch_depth.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -node -e "console.log(process.env.JS_BINARY__NODE_PATCHES_DEPTH)" diff --git a/js/private/test/snapshots/bzlmod/launcher.sh b/js/private/test/snapshots/bzlmod/launcher.sh new file mode 100644 index 000000000..4c83b07f6 --- /dev/null +++ b/js/private/test/snapshots/bzlmod/launcher.sh @@ -0,0 +1,617 @@ +#!/usr/bin/env bash + +# This bash script is a wrapper around the NodeJS JavaScript file +# entry point with the following bazel label: +# @@//js/private/test:shellcheck.js +# +# The script's was generated to execute the js_binary target +# @@//js/private/test:shellcheck_launcher +# +# The template used to generate this script is +# @@//js/private:js_binary.sh.tpl + +set -o pipefail -o errexit -o nounset + +export JS_BINARY__BINDIR="bazel-out/k8-fastbuild/bin" +export JS_BINARY__COMPILATION_MODE="fastbuild" +export JS_BINARY__TARGET_CPU="k8" +export JS_BINARY__BUILD_FILE_PATH="js/private/test/BUILD.bazel" +export JS_BINARY__PACKAGE="js/private/test" +export JS_BINARY__TARGET_NAME="shellcheck_launcher" +export JS_BINARY__TARGET="//js/private/test:shellcheck_launcher" +export JS_BINARY__WORKSPACE="_main" +if [[ -z "${JS_BINARY__PATCH_NODE_FS:-}" ]]; then export JS_BINARY__PATCH_NODE_FS="1"; fi +export JS_BINARY__COPY_DATA_TO_BIN="1" +if [[ -z "${JS_BINARY__LOG_FATAL:-}" ]]; then export JS_BINARY__LOG_FATAL="1"; fi +if [[ -z "${JS_BINARY__LOG_ERROR:-}" ]]; then export JS_BINARY__LOG_ERROR="1"; fi + +# ============================================================================== +# Prepare stdout capture, stderr capture && logging +# ============================================================================== + +if [ "${JS_BINARY__STDOUT_OUTPUT_FILE:-}" ] || [ "${JS_BINARY__SILENT_ON_SUCCESS:-}" ]; then + STDOUT_CAPTURE=$(mktemp) +fi + +if [ "${JS_BINARY__STDERR_OUTPUT_FILE:-}" ] || [ "${JS_BINARY__SILENT_ON_SUCCESS:-}" ]; then + STDERR_CAPTURE=$(mktemp) +fi + +export JS_BINARY__LOG_PREFIX="aspect_rules_js[js_binary]" + +function logf_stderr { + local format_string="$1\n" + shift + if [ "${STDERR_CAPTURE:-}" ]; then + # shellcheck disable=SC2059,SC2046 + echo -e $(printf "$format_string" "$@") >>"$STDERR_CAPTURE" + else + # shellcheck disable=SC2059,SC2046 + echo -e $(printf "$format_string" "$@") >&2 + fi +} + +function logf_fatal { + if [ "${JS_BINARY__LOG_FATAL:-}" ]; then + if [ "${STDERR_CAPTURE:-}" ]; then + printf "FATAL: %s: " "$JS_BINARY__LOG_PREFIX" >>"$STDERR_CAPTURE" + else + printf "FATAL: %s: " "$JS_BINARY__LOG_PREFIX" >&2 + fi + logf_stderr "$@" + fi +} + +function logf_error { + if [ "${JS_BINARY__LOG_ERROR:-}" ]; then + if [ "${STDERR_CAPTURE:-}" ]; then + printf "ERROR: %s: " "$JS_BINARY__LOG_PREFIX" >>"$STDERR_CAPTURE" + else + printf "ERROR: %s: " "$JS_BINARY__LOG_PREFIX" >&2 + fi + logf_stderr "$@" + fi +} + +function logf_warn { + if [ "${JS_BINARY__LOG_WARN:-}" ]; then + if [ "${STDERR_CAPTURE:-}" ]; then + printf "WARN: %s: " "$JS_BINARY__LOG_PREFIX" >>"$STDERR_CAPTURE" + else + printf "WARN: %s: " "$JS_BINARY__LOG_PREFIX" >&2 + fi + logf_stderr "$@" + fi +} + +function logf_info { + if [ "${JS_BINARY__LOG_INFO:-}" ]; then + if [ "${STDERR_CAPTURE:-}" ]; then + printf "INFO: %s: " "$JS_BINARY__LOG_PREFIX" >>"$STDERR_CAPTURE" + else + printf "INFO: %s: " "$JS_BINARY__LOG_PREFIX" >&2 + fi + logf_stderr "$@" + fi +} + +function logf_debug { + if [ "${JS_BINARY__LOG_DEBUG:-}" ]; then + if [ "${STDERR_CAPTURE:-}" ]; then + printf "DEBUG: %s: " "$JS_BINARY__LOG_PREFIX" >>"$STDERR_CAPTURE" + else + printf "DEBUG: %s: " "$JS_BINARY__LOG_PREFIX" >&2 + fi + logf_stderr "$@" + fi +} + +function resolve_execroot_bin_path { + local short_path="$1" + if [[ "$short_path" == ../* ]]; then + echo "$JS_BINARY__EXECROOT/${BAZEL_BINDIR:-$JS_BINARY__BINDIR}/external/${short_path:3}" + else + echo "$JS_BINARY__EXECROOT/${BAZEL_BINDIR:-$JS_BINARY__BINDIR}/$short_path" + fi +} + +function resolve_execroot_src_path { + local short_path="$1" + if [[ "$short_path" == ../* ]]; then + echo "$JS_BINARY__EXECROOT/external/${short_path:3}" + else + echo "$JS_BINARY__EXECROOT/$short_path" + fi +} + +_exit() { + EXIT_CODE=$? + + if [ "${STDERR_CAPTURE:-}" ]; then + if [ "${JS_BINARY__STDERR_OUTPUT_FILE:-}" ]; then + cp -f "$STDERR_CAPTURE" "$JS_BINARY__STDERR_OUTPUT_FILE" + fi + if [ "$EXIT_CODE" != 0 ] || [ -z "${JS_BINARY__SILENT_ON_SUCCESS:-}" ]; then + cat "$STDERR_CAPTURE" >&2 + fi + rm "$STDERR_CAPTURE" + fi + + if [ "${STDOUT_CAPTURE:-}" ]; then + if [ "${JS_BINARY__STDOUT_OUTPUT_FILE:-}" ]; then + cp -f "$STDOUT_CAPTURE" "$JS_BINARY__STDOUT_OUTPUT_FILE" + fi + if [ "$EXIT_CODE" != 0 ] || [ -z "${JS_BINARY__SILENT_ON_SUCCESS:-}" ]; then + cat "$STDOUT_CAPTURE" + fi + rm "$STDOUT_CAPTURE" + fi + + logf_debug "exit code: %s" "$EXIT_CODE" + + exit $EXIT_CODE +} + +trap _exit EXIT + +# ============================================================================== +# Initialize RUNFILES environment variable +# ============================================================================== + +# It helps to determine if we are running on a Windows environment (excludes WSL as it acts like Unix) +case "$(uname -s)" in +CYGWIN*) _IS_WINDOWS=1 ;; +MINGW*) _IS_WINDOWS=1 ;; +MSYS_NT*) _IS_WINDOWS=1 ;; +*) _IS_WINDOWS=0 ;; +esac + +# It helps to normalizes paths when running on Windows. +# +# Example: +# C:/Users/XUser/_bazel_XUser/7q7kkv32/execroot/A/b/C -> /c/Users/XUser/_bazel_XUser/7q7kkv32/execroot/A/b/C +function _normalize_path { + if [ "$_IS_WINDOWS" -eq "1" ]; then + # Apply the followings paths transformations to normalize paths on Windows + # -process driver letter + # -convert path separator + sed -e 's#^\(.\):#/\L\1#' -e 's#\\#/#g' <<<"$1" + else + echo "$1" + fi + return +} + +# Set a RUNFILES environment variable to the root of the runfiles tree +# since RUNFILES_DIR is not set by Bazel in all contexts. +# For example, `RUNFILES=/path/to/my_js_binary.sh.runfiles`. +# +# Call this program X. X was generated by a genrule and may be invoked +# in many ways: +# 1a) directly by a user, with $0 in the output tree +# 1b) via 'bazel run' (similar to case 1a) +# 2) directly by a user, with $0 in X's runfiles +# 3) by another program Y which has a data dependency on X, with $0 in Y's +# runfiles +# 4a) via 'bazel test' +# 4b) case 3 in the context of a test +# 5a) by a genrule cmd, with $0 in the output tree +# 6a) case 3 in the context of a genrule +# +# For case 1, $0 will be a regular file, and the runfiles will be +# at $0.runfiles. +# For case 2 or 3, $0 will be a symlink to the file seen in case 1. +# For case 4, $TEST_SRCDIR should already be set to the runfiles by +# blaze. +# Case 5a is handled like case 1. +# Case 6a is handled like case 3. +if [ "${TEST_SRCDIR:-}" ]; then + # Case 4, bazel has identified runfiles for us. + RUNFILES=$(_normalize_path "$TEST_SRCDIR") +elif [ "${RUNFILES_MANIFEST_FILE:-}" ]; then + RUNFILES=$(_normalize_path "$RUNFILES_MANIFEST_FILE") + if [[ "${RUNFILES}" == *.runfiles_manifest ]]; then + # Newer versions of Bazel put the manifest besides the runfiles with the suffix .runfiles_manifest. + # For example, the runfiles directory is named my_binary.runfiles then the manifest is beside the + # runfiles directory and named my_binary.runfiles_manifest + RUNFILES=${RUNFILES%_manifest} + elif [[ "${RUNFILES}" == */MANIFEST ]]; then + # Older versions of Bazel put the manifest file named MANIFEST in the runfiles directory + RUNFILES=${RUNFILES%/MANIFEST} + else + logf_fatal "Unexpected RUNFILES_MANIFEST_FILE value $RUNFILES_MANIFEST_FILE" + exit 1 + fi +else + case "$0" in + /*) self="$0" ;; + *) self="$PWD/$0" ;; + esac + while true; do + if [ -e "$self.runfiles" ]; then + RUNFILES="$self.runfiles" + break + fi + + if [[ "$self" == *.runfiles/* ]]; then + RUNFILES="${self%%.runfiles/*}.runfiles" + # don't break; this is a last resort for case 6b + fi + + if [ ! -L "$self" ]; then + break + fi + + readlink="$(readlink "$self")" + if [[ "$readlink" == /* ]]; then + self="$readlink" + else + # resolve relative symlink + self="${self%%/*}/$readlink" + fi + done + + if [ -z "${RUNFILES:-}" ]; then + logf_fatal "RUNFILES environment variable is not set" + exit 1 + fi + + RUNFILES=$(_normalize_path "$RUNFILES") +fi +if [ "${RUNFILES:0:1}" != "/" ]; then + # Ensure RUNFILES set above is an absolute path. It may be a path relative + # to the PWD in case where RUNFILES_MANIFEST_FILE is used above. + RUNFILES="$PWD/$RUNFILES" +fi + +# TODO(2.0): export only JS_BINARY__RUNFILES +export RUNFILES +JS_BINARY__RUNFILES="$RUNFILES" +export JS_BINARY__RUNFILES + +# ============================================================================== +# Prepare to run main program +# ============================================================================== + +# Convert stdout, stderr and exit_code capture outputs paths to absolute paths +if [ "${JS_BINARY__STDOUT_OUTPUT_FILE:-}" ]; then + JS_BINARY__STDOUT_OUTPUT_FILE="$PWD/$JS_BINARY__STDOUT_OUTPUT_FILE" +fi +if [ "${JS_BINARY__STDERR_OUTPUT_FILE:-}" ]; then + JS_BINARY__STDERR_OUTPUT_FILE="$PWD/$JS_BINARY__STDERR_OUTPUT_FILE" +fi +if [ "${JS_BINARY__EXIT_CODE_OUTPUT_FILE:-}" ]; then + JS_BINARY__EXIT_CODE_OUTPUT_FILE="$PWD/$JS_BINARY__EXIT_CODE_OUTPUT_FILE" +fi + +if [[ "$PWD" == *"/bazel-out/"* ]]; then + bazel_out_segment="/bazel-out/" +elif [[ "$PWD" == *"/BAZEL-~1/"* ]]; then + bazel_out_segment="/BAZEL-~1/" +elif [[ "$PWD" == *"/bazel-~1/"* ]]; then + bazel_out_segment="/bazel-~1/" +fi + +if [[ "${bazel_out_segment:-}" ]]; then + if [ "${JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT:-}" ] && [ "${JS_BINARY__EXECROOT:-}" ]; then + logf_debug "inheriting JS_BINARY__EXECROOT %s from parent js_binary process as JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT is set" "$JS_BINARY__EXECROOT" + else + # We in runfiles and we don't yet know the execroot + rest="${PWD#*"$bazel_out_segment"}" + index=$((${#PWD} - ${#rest} - ${#bazel_out_segment})) + if [ ${index} -lt 0 ]; then + printf "\nERROR: %s: No 'bazel-out' folder found in path '${PWD}'\n" "$JS_BINARY__LOG_PREFIX" >&2 + exit 1 + fi + JS_BINARY__EXECROOT="${PWD:0:$index}" + fi +else + if [ "${JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT:-}" ] && [ "${JS_BINARY__EXECROOT:-}" ]; then + logf_debug "inheriting JS_BINARY__EXECROOT %s from parent js_binary process as JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT is set" "$JS_BINARY__EXECROOT" + else + # We are in execroot or in some other context all together such as a nodejs_image or a manually run js_binary + JS_BINARY__EXECROOT="$PWD" + fi + + if [ -z "${JS_BINARY__NO_CD_BINDIR:-}" ]; then + if [ -z "${BAZEL_BINDIR:-}" ]; then + logf_fatal "BAZEL_BINDIR must be set in environment to the makevar \$(BINDIR) in js_binary build actions (which \ +run in the execroot) so that build actions can change directories to always run out of the root of the Bazel output \ +tree. See https://docs.bazel.build/versions/main/be/make-variables.html#predefined_variables. This is automatically set \ +by 'js_run_binary' (https://github.com/aspect-build/rules_js/blob/main/docs/js_run_binary.md) which is the recommended \ +rule to use for using a js_binary as the tool of a build action. If this is not a build action you can set the \ +BAZEL_BINDIR to '.' instead to supress this error. For more context on this design decision, please read the \ +aspect_rules_js README https://github.com/aspect-build/rules_js/tree/dbb5af0d2a9a2bb50e4cf4a96dbc582b27567155#running-nodejs-programs." + exit 1 + fi + + # Since the process was launched in the execroot, we automatically change directory into the root of the + # output tree (which we expect to be set in BAZEL_BIN). See + # https://github.com/aspect-build/rules_js/tree/dbb5af0d2a9a2bb50e4cf4a96dbc582b27567155#running-nodejs-programs + # for more context on why we do this. + logf_debug "changing directory to BAZEL_BINDIR (root of Bazel output tree) %s" "$BAZEL_BINDIR" + cd "$BAZEL_BINDIR" + fi +fi +export JS_BINARY__EXECROOT + +if [ "${JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT:-}" ]; then + if [ -z "${BAZEL_BINDIR:-}" ]; then + logf_fatal "Expected BAZEL_BINDIR to be set when JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT is set" + exit 1 + fi + if [ -z "${JS_BINARY__COPY_DATA_TO_BIN:-}" ] && [ -z "${JS_BINARY__ALLOW_EXECROOT_ENTRY_POINT_WITH_NO_COPY_DATA_TO_BIN:-}" ]; then + logf_fatal "Expected js_binary copy_data_to_bin to be True when js_run_binary use_execroot_entry_point is True. \ +To disable this validation you can set allow_execroot_entry_point_with_no_copy_data_to_bin to True in js_run_binary" + exit 1 + fi +fi + +if [ "${JS_BINARY__NO_RUNFILES:-}" ]; then + if [ -z "${JS_BINARY__COPY_DATA_TO_BIN:-}" ] && [ -z "${JS_BINARY__ALLOW_EXECROOT_ENTRY_POINT_WITH_NO_COPY_DATA_TO_BIN:-}" ]; then + logf_fatal "Expected js_binary copy_data_to_bin to be True when js_binary use_execroot_entry_point is True. \ +To disable this validation you can set allow_execroot_entry_point_with_no_copy_data_to_bin to True in js_run_binary" + exit 1 + fi +fi + +if [ "${JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT:-}" ] || [ "${JS_BINARY__NO_RUNFILES:-}" ]; then + entry_point=$(resolve_execroot_bin_path "js/private/test/shellcheck.js") +else + entry_point="$JS_BINARY__RUNFILES/_main/js/private/test/shellcheck.js" +fi +if [ ! -f "$entry_point" ]; then + logf_fatal "the entry_point '%s' not found" "$entry_point" + exit 1 +fi + +node="$(_normalize_path "../rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/bin/node")" +if [ "${node:0:1}" = "/" ]; then + # A user may specify an absolute path to node using target_tool_path in node_toolchain + export JS_BINARY__NODE_BINARY="$node" + if [ ! -f "$JS_BINARY__NODE_BINARY" ]; then + logf_fatal "node binary '%s' not found" "$JS_BINARY__NODE_BINARY" + exit 1 + fi +else + if [ "${JS_BINARY__NO_RUNFILES:-}" ]; then + export JS_BINARY__NODE_BINARY + JS_BINARY__NODE_BINARY=$(resolve_execroot_src_path "../rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/bin/node") + else + export JS_BINARY__NODE_BINARY="$JS_BINARY__RUNFILES/_main/../rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/bin/node" + fi + if [ ! -f "$JS_BINARY__NODE_BINARY" ]; then + logf_fatal "node binary '%s' not found" "$JS_BINARY__NODE_BINARY" + exit 1 + fi +fi +if [ "$_IS_WINDOWS" -ne "1" ] && [ ! -x "$JS_BINARY__NODE_BINARY" ]; then + logf_fatal "node binary '%s' is not executable" "$JS_BINARY__NODE_BINARY" + exit 1 +fi + +npm="" +if [ "$npm" ]; then + npm="$(_normalize_path "$npm")" + if [ "${npm:0:1}" = "/" ]; then + # A user may specify an absolute path to npm using npm_path in node_toolchain + export JS_BINARY__NPM_BINARY="$npm" + if [ ! -f "$JS_BINARY__NPM_BINARY" ]; then + logf_fatal "npm binary '%s' not found" "$JS_BINARY__NPM_BINARY" + exit 1 + fi + else + if [ "${JS_BINARY__NO_RUNFILES:-}" ]; then + export JS_BINARY__NPM_BINARY + JS_BINARY__NPM_BINARY=$(resolve_execroot_src_path "") + else + export JS_BINARY__NPM_BINARY="$JS_BINARY__RUNFILES/_main/" + fi + if [ ! -f "$JS_BINARY__NPM_BINARY" ]; then + logf_fatal "npm binary '%s' not found" "$JS_BINARY__NPM_BINARY" + exit 1 + fi + fi + if [ "$_IS_WINDOWS" -ne "1" ] && [ ! -x "$JS_BINARY__NPM_BINARY" ]; then + logf_fatal "npm binary '%s' is not executable" "$JS_BINARY__NPM_BINARY" + exit 1 + fi +fi + +if [ "${JS_BINARY__NO_RUNFILES:-}" ]; then + export JS_BINARY__NODE_WRAPPER + JS_BINARY__NODE_WRAPPER=$(resolve_execroot_bin_path "js/private/test/shellcheck_launcher_node_bin/node") +else + export JS_BINARY__NODE_WRAPPER="$JS_BINARY__RUNFILES/_main/js/private/test/shellcheck_launcher_node_bin/node" +fi +if [ ! -f "$JS_BINARY__NODE_WRAPPER" ]; then + logf_fatal "node wrapper '%s' not found" "$JS_BINARY__NODE_WRAPPER" + exit 1 +fi +if [ "$_IS_WINDOWS" -ne "1" ] && [ ! -x "$JS_BINARY__NODE_WRAPPER" ]; then + logf_fatal "node wrapper '%s' is not executable" "$JS_BINARY__NODE_WRAPPER" + exit 1 +fi + +if [ "${JS_BINARY__NO_RUNFILES:-}" ]; then + export JS_BINARY__NODE_PATCHES + JS_BINARY__NODE_PATCHES=$(resolve_execroot_src_path "js/private/node-patches/register.js") +else + export JS_BINARY__NODE_PATCHES="$JS_BINARY__RUNFILES/_main/js/private/node-patches/register.js" +fi +if [ ! -f "$JS_BINARY__NODE_PATCHES" ]; then + logf_fatal "node patches '%s' not found" "$JS_BINARY__NODE_PATCHES" + exit 1 +fi + +# Change directory to user specified package if set +if [ "${JS_BINARY__CHDIR:-}" ]; then + logf_debug "changing directory to user specified package %s" "$JS_BINARY__CHDIR" + cd "$JS_BINARY__CHDIR" +fi + +# Gather node options +JS_BINARY__NODE_OPTIONS=() +JS_BINARY__NODE_OPTIONS+=("--preserve-symlinks-main") + +ARGS=() +ALL_ARGS=(--my_arg "$@") +for ARG in ${ALL_ARGS[@]+"${ALL_ARGS[@]}"}; do + case "$ARG" in + # Let users pass through arguments to node itself + --node_options=*) JS_BINARY__NODE_OPTIONS+=("${ARG#--node_options=}") ;; + # Remaining argv is collected to pass to the program + *) ARGS+=("$ARG") ;; + esac +done + +# Configure JS_BINARY__FS_PATCH_ROOTS for node fs patches which are run via --require in the node wrapper. +# Don't override JS_BINARY__FS_PATCH_ROOTS if already set by an outer js_binary incase a js_binary such +# as js_run_deverser runs another js_binary tool. +if [ -z "${JS_BINARY__FS_PATCH_ROOTS:-}" ]; then + JS_BINARY__FS_PATCH_ROOTS="$JS_BINARY__EXECROOT:$JS_BINARY__RUNFILES" +fi +export JS_BINARY__FS_PATCH_ROOTS + +# Enable coverage if requested +if [ "${COVERAGE_DIR:-}" ]; then + logf_debug "enabling v8 coverage support ${COVERAGE_DIR}" + export NODE_V8_COVERAGE=${COVERAGE_DIR} +fi + +# Put the node wrapper directory on the path so that child processes find it first +PATH="$(dirname "$JS_BINARY__NODE_WRAPPER"):$PATH" +export PATH + +# Debug logs +if [ "${JS_BINARY__LOG_DEBUG:-}" ]; then + logf_debug "PATH %s" "$PATH" + if [ "${BAZEL_BINDIR:-}" ]; then + logf_debug "BAZEL_BINDIR %s" "$BAZEL_BINDIR" + fi + if [ "${BAZEL_BUILD_FILE_PATH:-}" ]; then + logf_debug "BAZEL_BUILD_FILE_PATH %s" "$BAZEL_BUILD_FILE_PATH" + fi + if [ "${BAZEL_COMPILATION_MODE:-}" ]; then + logf_debug "BAZEL_COMPILATION_MODE %s" "$BAZEL_COMPILATION_MODE" + fi + if [ "${BAZEL_INFO_FILE:-}" ]; then + logf_debug "BAZEL_INFO_FILE %s" "$BAZEL_INFO_FILE" + fi + if [ "${BAZEL_PACKAGE:-}" ]; then + logf_debug "BAZEL_PACKAGE %s" "$BAZEL_PACKAGE" + fi + if [ "${BAZEL_TARGET_CPU:-}" ]; then + logf_debug "BAZEL_TARGET_CPU %s" "$BAZEL_TARGET_CPU" + fi + if [ "${BAZEL_TARGET_NAME:-}" ]; then + logf_debug "BAZEL_TARGET_NAME %s" "$BAZEL_TARGET_NAME" + fi + if [ "${BAZEL_VERSION_FILE:-}" ]; then + logf_debug "BAZEL_VERSION_FILE %s" "$BAZEL_VERSION_FILE" + fi + if [ "${BAZEL_WORKSPACE:-}" ]; then + logf_debug "BAZEL_WORKSPACE %s" "$BAZEL_WORKSPACE" + fi + logf_debug "JS_BINARY__FS_PATCH_ROOTS %s" "${JS_BINARY__FS_PATCH_ROOTS:-}" + logf_debug "JS_BINARY__NODE_PATCHES %s" "${JS_BINARY__NODE_PATCHES:-}" + logf_debug "JS_BINARY__NODE_OPTIONS %s" "${JS_BINARY__NODE_OPTIONS:-}" + logf_debug "JS_BINARY__BINDIR %s" "${JS_BINARY__BINDIR:-}" + logf_debug "JS_BINARY__BUILD_FILE_PATH %s" "${JS_BINARY__BUILD_FILE_PATH:-}" + logf_debug "JS_BINARY__COMPILATION_MODE %s" "${JS_BINARY__COMPILATION_MODE:-}" + logf_debug "JS_BINARY__NODE_BINARY %s" "${JS_BINARY__NODE_BINARY:-}" + logf_debug "JS_BINARY__NODE_WRAPPER %s" "${JS_BINARY__NODE_WRAPPER:-}" + if [ "${JS_BINARY__NPM_BINARY:-}" ]; then + logf_debug "JS_BINARY__NPM_BINARY %s" "$JS_BINARY__NPM_BINARY" + fi + if [ "${JS_BINARY__NO_RUNFILES:-}" ]; then + logf_debug "JS_BINARY__NO_RUNFILES %s" "$JS_BINARY__NO_RUNFILES" + fi + logf_debug "JS_BINARY__PACKAGE %s" "${JS_BINARY__PACKAGE:-}" + logf_debug "JS_BINARY__TARGET_CPU %s" "${JS_BINARY__TARGET_CPU:-}" + logf_debug "JS_BINARY__TARGET_NAME %s" "${JS_BINARY__TARGET_NAME:-}" + logf_debug "JS_BINARY__WORKSPACE %s" "${JS_BINARY__WORKSPACE:-}" + logf_debug "js_binary entry point %s" "$entry_point" + if [ "${JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT:-}" ]; then + logf_debug "JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT %s" "$JS_RUN_BINARY__USE_EXECROOT_ENTRY_POINT" + fi +fi + +# Info logs +if [ "${JS_BINARY__LOG_INFO:-}" ]; then + if [ "${BAZEL_TARGET:-}" ]; then + logf_info "BAZEL_TARGET %s" "${BAZEL_TARGET:-}" + fi + logf_info "JS_BINARY__TARGET %s" "${JS_BINARY__TARGET:-}" + logf_info "JS_BINARY__RUNFILES %s" "${JS_BINARY__RUNFILES:-}" + logf_info "JS_BINARY__EXECROOT %s" "${JS_BINARY__EXECROOT:-}" + logf_info "PWD %s" "$PWD" +fi + +# ============================================================================== +# Run the main program +# ============================================================================== + +if [ "${JS_BINARY__LOG_INFO:-}" ]; then + logf_info "$(echo -n "running" "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"})" +fi + +set +e + +if [ "${STDOUT_CAPTURE:-}" ] && [ "${STDERR_CAPTURE:-}" ]; then + "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" 2>>"$STDERR_CAPTURE" & +elif [ "${STDOUT_CAPTURE:-}" ]; then + "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" & +elif [ "${STDERR_CAPTURE:-}" ]; then + "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 2>>"$STDERR_CAPTURE" & +else + "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 & +fi + +# ============================================================================== +# Wait for program to finish +# ============================================================================== + +readonly child=$! +# Bash does not forward termination signals to any child process when +# running in docker so need to manually trap and forward the signals +_term() { kill -TERM "${child}" 2>/dev/null; } +_int() { kill -INT "${child}" 2>/dev/null; } +trap _term SIGTERM +trap _int SIGINT +wait "$child" +# Remove trap after first signal has been receieved and wait for child to exit +# (first wait returns immediatel if SIGTERM is received while waiting). Second +# wait is a no-op if child has already terminated. +trap - SIGTERM SIGINT +wait "$child" + +RESULT="$?" +set -e + +# ============================================================================== +# Mop up after main program +# ============================================================================== + +if [ "${JS_BINARY__EXPECTED_EXIT_CODE:-}" ]; then + if [ "$RESULT" != "$JS_BINARY__EXPECTED_EXIT_CODE" ]; then + logf_error "expected exit code to be '%s', but got '%s'" "$JS_BINARY__EXPECTED_EXIT_CODE" "$RESULT" + if [ $RESULT -eq 0 ]; then + # This exit code is handled specially by Bazel: + # https://github.com/bazelbuild/bazel/blob/486206012a664ecb20bdb196a681efc9a9825049/src/main/java/com/google/devtools/build/lib/util/ExitCode.java#L44 + readonly BAZEL_EXIT_TESTS_FAILED=3 + exit $BAZEL_EXIT_TESTS_FAILED + fi + exit $RESULT + else + exit 0 + fi +fi + +if [ "${JS_BINARY__EXIT_CODE_OUTPUT_FILE:-}" ]; then + # Exit zero if the exit code was captured + echo -n "$RESULT" >"$JS_BINARY__EXIT_CODE_OUTPUT_FILE" + exit 0 +else + exit $RESULT +fi diff --git a/js/private/test/shellcheck_launcher.sh b/js/private/test/snapshots/wksp/launcher.sh similarity index 100% rename from js/private/test/shellcheck_launcher.sh rename to js/private/test/snapshots/wksp/launcher.sh diff --git a/npm/BUILD.bazel b/npm/BUILD.bazel index 61ea2e816..8e22606e0 100644 --- a/npm/BUILD.bazel +++ b/npm/BUILD.bazel @@ -57,8 +57,6 @@ bzl_library( name = "repositories", srcs = [ "repositories.bzl", - # bazel_features does not provide bzl_library targets. - "@bazel_features//:bzl_files", ], visibility = ["//visibility:public"], deps = [ diff --git a/npm/private/npm_import.bzl b/npm/private/npm_import.bzl index 1715aa82d..d53ac9c77 100644 --- a/npm/private/npm_import.bzl +++ b/npm/private/npm_import.bzl @@ -19,7 +19,6 @@ for a given lockfile. load("@aspect_bazel_lib//lib:repo_utils.bzl", "patch", "repo_utils") load("@aspect_bazel_lib//lib:repositories.bzl", _register_copy_directory_toolchains = "register_copy_directory_toolchains", _register_copy_to_directory_toolchains = "register_copy_to_directory_toolchains") -load("@aspect_bazel_lib//lib:utils.bzl", "is_bazel_6_or_greater") load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:paths.bzl", "paths") load( @@ -74,10 +73,6 @@ def npm_imported_package_store(name): version = "{version}", dev = {dev}, tags = ["manual"], - use_declare_symlink = select({{ - Label("@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 @@ -89,10 +84,6 @@ def npm_imported_package_store(name): dev = {dev}, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({{ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }}), ) # virtual store target with transitive closure of all npm package dependencies @@ -105,10 +96,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({{ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }}), ) # filegroup target that provides a single file which is @@ -133,10 +120,6 @@ _LINK_JS_PACKAGE_LIFECYCLE_TMPL = """\ dev = {dev}, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({{ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }}), ) # terminal pre-lifecycle target for use in lifecycle build target below @@ -147,10 +130,6 @@ _LINK_JS_PACKAGE_LIFECYCLE_TMPL = """\ dev = {dev}, deps = lc_deps, tags = ["manual"], - use_declare_symlink = select({{ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }}), ) # lifecycle build action @@ -232,11 +211,7 @@ def npm_link_imported_package_store(name): package = link_alias, src = "//{root_package}:{{}}".format(store_target_name), visibility = {link_visibility}, - tags = ["manual"], - use_declare_symlink = select({{ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }}),{maybe_bins} + tags = ["manual"],{maybe_bins} ) # filegroup target that provides a single file which is @@ -659,8 +634,6 @@ def _npm_import_links_rule_impl(rctx): lc_deps = {} deps = {} - bzlmod_supported = is_bazel_6_or_greater() - for (dep_name, dep_version) in rctx.attr.deps.items(): store_package, store_version = utils.parse_pnpm_package_key(dep_name, dep_version) if dep_version.startswith("link:") or dep_version.startswith("file:"): @@ -733,8 +706,7 @@ def _npm_import_links_rule_impl(rctx): if rctx.attr.replace_package: npm_package_target = rctx.attr.replace_package else: - npm_package_target = "{}{}//:pkg".format( - "@@" if bzlmod_supported else "@", + npm_package_target = "@@{}//:pkg".format( npm_import_sources_repo_name, ) diff --git a/npm/private/npm_link_package.bzl b/npm/private/npm_link_package.bzl index e4965012a..dfe4f31b8 100644 --- a/npm/private/npm_link_package.bzl +++ b/npm/private/npm_link_package.bzl @@ -78,10 +78,6 @@ def npm_link_package( deps = deps, visibility = ["//visibility:public"], tags = tags, - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), **kwargs ) @@ -96,10 +92,6 @@ def npm_link_package( ), tags = tags, visibility = visibility, - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) link_target = ":{}".format(name) diff --git a/npm/private/npm_link_package_store.bzl b/npm/private/npm_link_package_store.bzl index a5b7c73d8..adfdfaf93 100644 --- a/npm/private/npm_link_package_store.bzl +++ b/npm/private/npm_link_package_store.bzl @@ -53,16 +53,6 @@ If set, takes precendance over the package name in the src npm_package_store. https://github.com/pnpm/pnpm/issues/5131. """, ), - "use_declare_symlink": attr.bool( - mandatory = True, - doc = """Whether unresolved symlinks are enabled in the current build configuration. - - These are enabled with the --allow_unresolved_symlinks flag. - - Typical usage of this rule is via a macro which automatically sets this - attribute based on a `config_setting` rule. - """, - ), } _BIN_TMPL = """#!/bin/sh diff --git a/npm/private/npm_package_store.bzl b/npm/private/npm_package_store.bzl index be21e59bb..8d13c9680 100644 --- a/npm/private/npm_package_store.bzl +++ b/npm/private/npm_package_store.bzl @@ -123,18 +123,6 @@ If set, takes precendance over the package version in the NpmPackageInfo src. "dev": attr.bool( doc = """Whether this npm package is a dev dependency""", ), - "use_declare_symlink": attr.bool( - mandatory = True, - doc = """Whether to use ctx.actions.declare_symlink to create symlinks. - - These are enabled with the `--allow_unresolved_symlinks` flag - (named `--experimental_allow_unresolved_symlinks in Bazel versions prior to 7.0). - - Typical usage of this rule is via a macro which automatically sets this - attribute with `select` based on a `config_setting` rule. - See /js/private/BUILD.bazel in rules_js for an example. - """, - ), "hardlink": attr.string( values = ["auto", "off", "on"], default = "auto", diff --git a/npm/private/npm_translate_lock_generate.bzl b/npm/private/npm_translate_lock_generate.bzl index dab83c781..52e6150f0 100644 --- a/npm/private/npm_translate_lock_generate.bzl +++ b/npm/private/npm_translate_lock_generate.bzl @@ -37,10 +37,6 @@ _FP_STORE_TMPL = \ deps = {deps}, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({{ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }}), )""" _FP_DIRECT_TMPL = \ @@ -53,10 +49,6 @@ _FP_DIRECT_TMPL = \ src = "//{root_package}:{virtual_store_root}/{{}}/{virtual_store_name}".format(name), visibility = {link_visibility}, tags = ["manual"], - use_declare_symlink = select({{ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }}), ) # filegroup target that provides a single file which is @@ -90,7 +82,7 @@ _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): # 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), utils.consistent_label_str(pnpm_lock_label)) + 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) @@ -258,7 +250,7 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): repositories_bzl.append("""load("@aspect_rules_js//npm:repositories.bzl", "npm_import")""") repositories_bzl.append("") - repositories_bzl.append("# Generated npm_import repository rules corresponding to npm packages in {}".format(utils.consistent_label_str(pnpm_lock_label))) + repositories_bzl.append("# Generated npm_import repository rules corresponding to npm packages in {}".format(str(pnpm_lock_label))) repositories_bzl.append("# buildifier: disable=function-docstring") repositories_bzl.append("def npm_repositories():") if len(npm_imports) == 0: @@ -273,8 +265,7 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): if _import.link_packages: defs_bzl_header.append( - """load("{at}{repo_name}{links_repo_suffix}//:defs.bzl", link_{i} = "npm_link_imported_package_store", store_{i} = "npm_imported_package_store")""".format( - at = "@@" if utils.bzlmod_supported else "@", + """load("@@{repo_name}{links_repo_suffix}//:defs.bzl", link_{i} = "npm_link_imported_package_store", store_{i} = "npm_imported_package_store")""".format( i = i, links_repo_suffix = utils.links_repo_suffix, repo_name = _import.name, @@ -282,8 +273,7 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): ) else: defs_bzl_header.append( - """load("{at}{repo_name}{links_repo_suffix}//:defs.bzl", store_{i} = "npm_imported_package_store")""".format( - at = "@@" if utils.bzlmod_supported else "@", + """load("@@{repo_name}{links_repo_suffix}//:defs.bzl", store_{i} = "npm_imported_package_store")""".format( i = i, links_repo_suffix = utils.links_repo_suffix, repo_name = _import.name, @@ -338,8 +328,7 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): ), )) package_json_bzl_file_path = paths.normalize(paths.join(link_package, _import.package, _PACKAGE_JSON_BZL_FILENAME)) - repo_package_json_bzl = "{at}{repo_name}//{link_package}:{package_json_bzl}".format( - at = "@@" if utils.bzlmod_supported else "@", + repo_package_json_bzl = "@@{repo_name}//{link_package}:{package_json_bzl}".format( repo_name = _import.name, link_package = link_package, package_json_bzl = _PACKAGE_JSON_BZL_FILENAME, diff --git a/npm/private/npm_translate_lock_state.bzl b/npm/private/npm_translate_lock_state.bzl index d75b66fa8..c759ca4e7 100644 --- a/npm/private/npm_translate_lock_state.bzl +++ b/npm/private/npm_translate_lock_state.bzl @@ -161,9 +161,11 @@ def _init_pnpm_labels(label_store, rctx): def _init_update_labels(priv, rctx, label_store): attr = rctx.attr + pnpm_lock_label = label_store.label("pnpm_lock") + pnpm_lock_label_str = "//{}:{}".format(pnpm_lock_label.package, pnpm_lock_label.name) action_cache_path = paths.join( priv["external_repository_action_cache"], - PNPM_LOCK_ACTION_CACHE_PREFIX + base64.encode(utils.hash(helpers.to_apparent_repo_name(rctx.name) + utils.consistent_label_str(label_store.label("pnpm_lock")))), + PNPM_LOCK_ACTION_CACHE_PREFIX + base64.encode(utils.hash(helpers.to_apparent_repo_name(rctx.name) + pnpm_lock_label_str)), ) label_store.add_root("action_cache", action_cache_path) for i, d in enumerate(attr.preupdate): @@ -408,7 +410,7 @@ def _action_cache_miss(priv, rctx, label_store): def _write_action_cache(priv, rctx, label_store): contents = [ "# @generated", - "# Input hashes for repository rule npm_translate_lock(name = \"{}\", pnpm_lock = \"{}\").".format(helpers.to_apparent_repo_name(rctx.name), utils.consistent_label_str(label_store.label("pnpm_lock"))), + "# Input hashes for repository rule npm_translate_lock(name = \"{}\", pnpm_lock = \"{}\").".format(helpers.to_apparent_repo_name(rctx.name), str(label_store.label("pnpm_lock"))), "# This file should be checked into version control along with the pnpm-lock.yaml file.", ] for key, value in priv["input_hashes"].items(): diff --git a/npm/private/test/snapshots/bzlmod/chalk_links_defs.bzl b/npm/private/test/snapshots/bzlmod/chalk_links_defs.bzl index 7ccbfaaf9..44a70305a 100644 --- a/npm/private/test/snapshots/bzlmod/chalk_links_defs.bzl +++ b/npm/private/test/snapshots/bzlmod/chalk_links_defs.bzl @@ -33,10 +33,6 @@ def npm_imported_package_store(name): version = "5.0.1", dev = False, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -48,10 +44,6 @@ def npm_imported_package_store(name): dev = False, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -64,10 +56,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -112,10 +100,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/bzlmod/fsevents_links_defs.bzl b/npm/private/test/snapshots/bzlmod/fsevents_links_defs.bzl index 8259aaec4..98442a000 100644 --- a/npm/private/test/snapshots/bzlmod/fsevents_links_defs.bzl +++ b/npm/private/test/snapshots/bzlmod/fsevents_links_defs.bzl @@ -37,10 +37,6 @@ def npm_imported_package_store(name): version = "2.3.2", dev = True, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -52,10 +48,6 @@ def npm_imported_package_store(name): dev = True, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -68,10 +60,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -96,10 +84,6 @@ def npm_imported_package_store(name): dev = True, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # terminal pre-lifecycle target for use in lifecycle build target below @@ -110,10 +94,6 @@ def npm_imported_package_store(name): dev = True, deps = lc_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # lifecycle build action @@ -196,10 +176,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/bzlmod/npm_defs.bzl b/npm/private/test/snapshots/bzlmod/npm_defs.bzl index 733a60984..923b975cb 100644 --- a/npm/private/test/snapshots/bzlmod/npm_defs.bzl +++ b/npm/private/test/snapshots/bzlmod/npm_defs.bzl @@ -1,4 +1,4 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@@//:pnpm-lock.yaml")""" # buildifier: disable=bzl-visibility load("@aspect_rules_js//js:defs.bzl", _js_library = "js_library") @@ -2208,10 +2208,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): }, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) if is_root: @@ -2226,10 +2222,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): }, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) if is_root: @@ -2244,10 +2236,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): }, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) for link_package in ["examples/js_binary", "examples/npm_deps"]: @@ -2258,10 +2246,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): src = "//:.aspect_rules_js/{}/@mycorp+pkg-a@0.0.0".format(name), visibility = ["//examples:__subpackages__"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -2286,10 +2270,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): }, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) for link_package in ["npm/private/test"]: @@ -2300,10 +2280,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): src = "//:.aspect_rules_js/{}/test-npm_package@0.0.0".format(name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/bzlmod/repositories.bzl b/npm/private/test/snapshots/bzlmod/repositories.bzl index 1acb65c1b..f7beaaffa 100644 --- a/npm/private/test/snapshots/bzlmod/repositories.bzl +++ b/npm/private/test/snapshots/bzlmod/repositories.bzl @@ -1,8 +1,8 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@@//:pnpm-lock.yaml")""" load("@aspect_rules_js//npm:repositories.bzl", "npm_import") -# Generated npm_import repository rules corresponding to npm packages in //:pnpm-lock.yaml +# Generated npm_import repository rules corresponding to npm packages in @@//:pnpm-lock.yaml # buildifier: disable=function-docstring def npm_repositories(): npm_import( diff --git a/npm/private/test/snapshots/bzlmod/rollup_links_defs.bzl b/npm/private/test/snapshots/bzlmod/rollup_links_defs.bzl index 4d8b0d841..315e1c3bf 100644 --- a/npm/private/test/snapshots/bzlmod/rollup_links_defs.bzl +++ b/npm/private/test/snapshots/bzlmod/rollup_links_defs.bzl @@ -36,10 +36,6 @@ def npm_imported_package_store(name): version = "2.70.2", dev = True, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -51,10 +47,6 @@ def npm_imported_package_store(name): dev = True, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -67,10 +59,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -114,10 +102,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/bzlmod/unused_links_defs.bzl b/npm/private/test/snapshots/bzlmod/unused_links_defs.bzl index f36bfacd8..d370c633b 100644 --- a/npm/private/test/snapshots/bzlmod/unused_links_defs.bzl +++ b/npm/private/test/snapshots/bzlmod/unused_links_defs.bzl @@ -40,10 +40,6 @@ def npm_imported_package_store(name): version = "0.2.2", dev = True, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -55,10 +51,6 @@ def npm_imported_package_store(name): dev = True, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -71,10 +63,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -118,10 +106,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:private"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/wksp/chalk_links_defs.bzl b/npm/private/test/snapshots/wksp/chalk_links_defs.bzl index 7ccbfaaf9..44a70305a 100644 --- a/npm/private/test/snapshots/wksp/chalk_links_defs.bzl +++ b/npm/private/test/snapshots/wksp/chalk_links_defs.bzl @@ -33,10 +33,6 @@ def npm_imported_package_store(name): version = "5.0.1", dev = False, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -48,10 +44,6 @@ def npm_imported_package_store(name): dev = False, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -64,10 +56,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -112,10 +100,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/wksp/fsevents_links_defs.bzl b/npm/private/test/snapshots/wksp/fsevents_links_defs.bzl index ca53ea6db..af9c7f2ea 100644 --- a/npm/private/test/snapshots/wksp/fsevents_links_defs.bzl +++ b/npm/private/test/snapshots/wksp/fsevents_links_defs.bzl @@ -37,10 +37,6 @@ def npm_imported_package_store(name): version = "2.3.2", dev = True, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -52,10 +48,6 @@ def npm_imported_package_store(name): dev = True, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -68,10 +60,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -96,10 +84,6 @@ def npm_imported_package_store(name): dev = True, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # terminal pre-lifecycle target for use in lifecycle build target below @@ -110,10 +94,6 @@ def npm_imported_package_store(name): dev = True, deps = lc_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # lifecycle build action @@ -196,10 +176,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/wksp/npm_defs.bzl b/npm/private/test/snapshots/wksp/npm_defs.bzl index 669344334..624f5750f 100644 --- a/npm/private/test/snapshots/wksp/npm_defs.bzl +++ b/npm/private/test/snapshots/wksp/npm_defs.bzl @@ -1,4 +1,4 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@//:pnpm-lock.yaml")""" # buildifier: disable=bzl-visibility load("@aspect_rules_js//js:defs.bzl", _js_library = "js_library") @@ -2208,10 +2208,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): }, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) if is_root: @@ -2226,10 +2222,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): }, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) if is_root: @@ -2244,10 +2236,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): }, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) for link_package in ["examples/js_binary", "examples/npm_deps"]: @@ -2258,10 +2246,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): src = "//:.aspect_rules_js/{}/@mycorp+pkg-a@0.0.0".format(name), visibility = ["//examples:__subpackages__"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -2286,10 +2270,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): }, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) for link_package in ["npm/private/test"]: @@ -2300,10 +2280,6 @@ def npm_link_all_packages(name = "node_modules", imported_links = []): src = "//:.aspect_rules_js/{}/test-npm_package@0.0.0".format(name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/wksp/repositories.bzl b/npm/private/test/snapshots/wksp/repositories.bzl index 556014338..1e0ce1f78 100644 --- a/npm/private/test/snapshots/wksp/repositories.bzl +++ b/npm/private/test/snapshots/wksp/repositories.bzl @@ -1,8 +1,8 @@ -"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml")""" +"""@generated by npm_translate_lock(name = "npm", pnpm_lock = "@//:pnpm-lock.yaml")""" load("@aspect_rules_js//npm:repositories.bzl", "npm_import") -# Generated npm_import repository rules corresponding to npm packages in //:pnpm-lock.yaml +# Generated npm_import repository rules corresponding to npm packages in @//:pnpm-lock.yaml # buildifier: disable=function-docstring def npm_repositories(): npm_import( diff --git a/npm/private/test/snapshots/wksp/rollup_links_defs.bzl b/npm/private/test/snapshots/wksp/rollup_links_defs.bzl index 6743e6886..0cb9cccd0 100644 --- a/npm/private/test/snapshots/wksp/rollup_links_defs.bzl +++ b/npm/private/test/snapshots/wksp/rollup_links_defs.bzl @@ -36,10 +36,6 @@ def npm_imported_package_store(name): version = "2.70.2", dev = True, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -51,10 +47,6 @@ def npm_imported_package_store(name): dev = True, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -67,10 +59,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -114,10 +102,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/test/snapshots/wksp/unused_links_defs.bzl b/npm/private/test/snapshots/wksp/unused_links_defs.bzl index 593f21efd..17aa02574 100644 --- a/npm/private/test/snapshots/wksp/unused_links_defs.bzl +++ b/npm/private/test/snapshots/wksp/unused_links_defs.bzl @@ -40,10 +40,6 @@ def npm_imported_package_store(name): version = "0.2.2", dev = True, tags = ["manual"], - use_declare_symlink = select({ - Label("@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 @@ -55,10 +51,6 @@ def npm_imported_package_store(name): dev = True, deps = ref_deps, tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # virtual store target with transitive closure of all npm package dependencies @@ -71,10 +63,6 @@ def npm_imported_package_store(name): deps = deps, visibility = ["//visibility:public"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is @@ -118,10 +106,6 @@ def npm_link_imported_package_store(name): src = "//:{}".format(store_target_name), visibility = ["//visibility:private"], tags = ["manual"], - use_declare_symlink = select({ - Label("@aspect_rules_js//js:allow_unresolved_symlinks"): True, - "//conditions:default": False, - }), ) # filegroup target that provides a single file which is diff --git a/npm/private/utils.bzl b/npm/private/utils.bzl index 0ffbad303..fbc0da5b1 100644 --- a/npm/private/utils.bzl +++ b/npm/private/utils.bzl @@ -248,32 +248,18 @@ def _virtual_store_name(name, version): def _make_symlink(ctx, symlink_path, target_file): files = [] - if ctx.attr.use_declare_symlink: - symlink = ctx.actions.declare_symlink(symlink_path) - ctx.actions.symlink( - output = symlink, - target_path = relative_file(target_file.path, symlink.path), - ) - files.append(target_file) - else: - if _is_at_least_bazel_6() and target_file.is_directory: - # BREAKING CHANGE in Bazel 6 requires you to use declare_directory if your target_file - # in ctx.actions.symlink is a directory artifact - symlink = ctx.actions.declare_directory(symlink_path) - else: - symlink = ctx.actions.declare_file(symlink_path) - ctx.actions.symlink( - output = symlink, - target_file = target_file, - ) + if not is_bazel_6_or_greater(): + # ctx.actions.declare_symlink was added in Bazel 6 + fail("A minimum version of Bazel 6 required to use rules_js") + symlink = ctx.actions.declare_symlink(symlink_path) + ctx.actions.symlink( + output = symlink, + target_path = relative_file(target_file.path, symlink.path), + ) + files.append(target_file) files.append(symlink) return files -def _is_at_least_bazel_6(): - # Hacky way to check if the we're using at least Bazel 6. Would be nice if there was a ctx.bazel_version instead. - # native.bazel_version only works in repository rules. - return "apple_binary" not in dir(native) - def _parse_package_name(package): # Parse a @scope/name string and return a (scope, name) tuple segments = package.split("/", 1) @@ -325,15 +311,6 @@ def _dicts_match(a, b): return False return True -# Generate a consistent label string between Bazel versions. -def _consistent_label_str(label): - return "//{}:{}".format( - # Starting in Bazel 6, the workspace name is empty for the local workspace and there's no other way to determine it. - # This behavior differs from Bazel 5 where the local workspace name was fully qualified in str(label). - label.package, - label.name, - ) - # Copies a file from the external repository to the same relative location in the source tree def _reverse_force_copy(rctx, label, dst = None): if type(label) != "Label": @@ -481,8 +458,6 @@ utils = struct( default_registry = _default_registry, hash = _hash, dicts_match = _dicts_match, - consistent_label_str = _consistent_label_str, - bzlmod_supported = is_bazel_6_or_greater(), reverse_force_copy = _reverse_force_copy, exists = _exists, home_directory = _home_directory,