diff --git a/internal/node/node.bzl b/internal/node/node.bzl index 4f328e5d35..812b9ab6b4 100644 --- a/internal/node/node.bzl +++ b/internal/node/node.bzl @@ -260,6 +260,8 @@ fi node_tool_files = [] node_tool_files.extend(node_toolchain.nodeinfo.tool_files) + if ctx.attr.include_all_node_files: + node_tool_files.extend(node_toolchain.nodeinfo.all_node_files) node_tool_files.append(ctx.file._link_modules_script) node_tool_files.append(ctx.file._runfile_helpers_bundle) node_tool_files.append(ctx.file._runfile_helpers_main) @@ -506,6 +508,12 @@ and make variable expansion. """, default = {}, ), + "include_all_node_files": attr.bool( + doc = """Include all files shipped with node as inputs as specified by the toolchain. + +By default, only the node executable is specified as an input as the remaining node distribution files +are not needed by most programs.""", + ), "link_workspace_root": attr.bool( doc = """Link the workspace root to the bin_dir to support absolute requires like 'my_wksp/path/to/file'. If source files need to be required then they can be copied to the bin_dir with copy_to_bin.""", diff --git a/internal/node/node_patches.cjs b/internal/node/node_patches.cjs index 239bb5f581..b5018fd03a 100644 --- a/internal/node/node_patches.cjs +++ b/internal/node/node_patches.cjs @@ -573,6 +573,18 @@ set Path=${nodeDir};%Path% "${process.execPath}" --require "${requireScriptName}" %* `); } + ['npm', 'npx'].forEach(cmd => { + const cmdBat = `${cmd}.bat`; + const cmdEntry = path__default['default'].join(nodeDir, cmdBat); + const cmdActual = path__default['default'].join(path__default['default'].dirname(process.execPath), cmdBat); + if (!fs__default['default'].existsSync(cmdEntry)) { + fs__default['default'].writeFileSync(cmdEntry, `@if not defined DEBUG_HELPER @ECHO OFF +set NP_SUBPROCESS_NODE_DIR=${nodeDir} +set Path=${nodeDir};%Path% +exec ${cmdActual} %* + `, { mode: 0o777 }); + } + }); } else { const nodeEntry = path__default['default'].join(nodeDir, 'node'); @@ -587,6 +599,17 @@ else fi `, { mode: 0o777 }); } + ['npm', 'npx'].forEach(cmd => { + const cmdEntry = path__default['default'].join(nodeDir, cmd); + const cmdActual = path__default['default'].join(path__default['default'].dirname(process.execPath), `../lib/node_modules/npm/bin/${cmd}-cli.js`); + if (!fs__default['default'].existsSync(cmdEntry)) { + fs__default['default'].writeFileSync(cmdEntry, `#!/bin/bash +export NP_SUBPROCESS_NODE_DIR="${nodeDir}" +export PATH="${nodeDir}":\$PATH +exec ${cmdActual} "$@" + `, { mode: 0o777 }); + } + }); } if (!process.env.PATH) { process.env.PATH = nodeDir; diff --git a/internal/node/test/BUILD.bazel b/internal/node/test/BUILD.bazel index b4e1a324cb..1e70b042a2 100644 --- a/internal/node/test/BUILD.bazel +++ b/internal/node/test/BUILD.bazel @@ -702,3 +702,15 @@ nodejs_test( ], ) ] + +nodejs_test( + name = "spawn_npm_test", + entry_point = "spawn-npm.js", + include_all_node_files = True, +) + +nodejs_test( + name = "spawn_npx_test", + entry_point = "spawn-npx.js", + include_all_node_files = True, +) diff --git a/internal/node/test/spawn-npm.js b/internal/node/test/spawn-npm.js new file mode 100644 index 0000000000..b0b9d852d6 --- /dev/null +++ b/internal/node/test/spawn-npm.js @@ -0,0 +1,8 @@ +const spawnSync = require('child_process').spawnSync + +const res = spawnSync('npm', ['--version']); +if (res.status) { + process.stderr.write(res.stderr); + throw new Error('failed. to execute child process. code ' + res.status); +} +console.log(JSON.stringify({version: res.stdout + ''})); \ No newline at end of file diff --git a/internal/node/test/spawn-npx.js b/internal/node/test/spawn-npx.js new file mode 100644 index 0000000000..a6d46364ee --- /dev/null +++ b/internal/node/test/spawn-npx.js @@ -0,0 +1,8 @@ +const spawnSync = require('child_process').spawnSync + +const res = spawnSync('npx', ['--version']); +if (res.status) { + process.stderr.write(res.stderr); + throw new Error('failed. to execute child process. code ' + res.status); +} +console.log(JSON.stringify({version: res.stdout + ''})); \ No newline at end of file diff --git a/nodejs/private/nodejs_repo_host_os_alias.bzl b/nodejs/private/nodejs_repo_host_os_alias.bzl index c551ac69ac..7b7aaaa269 100644 --- a/nodejs/private/nodejs_repo_host_os_alias.bzl +++ b/nodejs/private/nodejs_repo_host_os_alias.bzl @@ -20,6 +20,7 @@ alias(name = "node", actual = "@{node_repository}_{os_name}// alias(name = "npm", actual = "@{node_repository}_{os_name}//:npm") alias(name = "node_files", actual = "@{node_repository}_{os_name}//:node_files") alias(name = "npm_files", actual = "@{node_repository}_{os_name}//:npm_files") +alias(name = "all_node_files", actual = "@{node_repository}_{os_name}//:all_node_files") exports_files([ "index.bzl", "bin/node{ext}", diff --git a/nodejs/repositories.bzl b/nodejs/repositories.bzl index 35bc2d6706..2e1de0a476 100644 --- a/nodejs/repositories.bzl +++ b/nodejs/repositories.bzl @@ -322,11 +322,16 @@ filegroup( name = "npm_files", srcs = {npm_files_glob}[":node_files"], ) +filegroup( + name = "all_node_files", + srcs = {all_node_files_glob}[":node_files"], +) """.format( node_bin_export = "\n \"%s\"," % node_bin, npm_bin_export = "\n \"%s\"," % npm_bin, npx_bin_export = "\n \"%s\"," % npx_bin, npm_files_glob = "glob([\"bin/nodejs/**\"]) + ", + all_node_files_glob = "glob([\"bin/nodejs/**\"]) + ", node_bin_label = node_bin_label, npm_bin_label = npm_bin_label, npx_bin_label = npx_bin_label, @@ -342,6 +347,7 @@ node_toolchain( name = "node_toolchain", target_tool = ":node_bin", run_npm = ":run_npm.template", + all_node_files = ":all_node_files", ) """ repository_ctx.file("BUILD.bazel", content = build_content) diff --git a/nodejs/toolchain.bzl b/nodejs/toolchain.bzl index 854c5971f6..0d872746cf 100644 --- a/nodejs/toolchain.bzl +++ b/nodejs/toolchain.bzl @@ -26,6 +26,9 @@ NodeInfo = provider( May be empty if the target_tool_path points to a locally installed node binary.""", "run_npm": """A template for a script that wraps npm. On Windows, this is a Batch script, otherwise it uses Bash.""", + "all_node_files": """All files in the node distrubution. + +These are generally not needed for most programs since the node executable is self-contained.""", }, ) @@ -44,11 +47,15 @@ def _node_toolchain_impl(ctx): tool_files = [] target_tool_path = ctx.attr.target_tool_path + all_node_files = [] if ctx.attr.target_tool: - tool_files = ctx.attr.target_tool.files.to_list() + tool_files.extend(ctx.attr.target_tool.files.to_list()) target_tool_path = _to_manifest_path(ctx, tool_files[0]) + if ctx.attr.all_node_files: + all_node_files.extend(ctx.attr.all_node_files.files.to_list()) + # Make the $(NODE_PATH) variable available in places like genrules. # See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables template_variables = platform_common.TemplateVariableInfo({ @@ -62,6 +69,7 @@ def _node_toolchain_impl(ctx): target_tool_path = target_tool_path, tool_files = tool_files, run_npm = ctx.file.run_npm, + all_node_files = all_node_files, ) # Export all the providers inside our ToolchainInfo @@ -93,6 +101,10 @@ node_toolchain = rule( doc = "A template file that allows us to execute npm", allow_single_file = True, ), + "all_node_files": attr.label( + doc = "All files in the node distribution", + allow_files = True, + ), }, doc = """Defines a node toolchain. diff --git a/packages/node-patches/src/subprocess.ts b/packages/node-patches/src/subprocess.ts index 71f7afa6a5..021a87d819 100644 --- a/packages/node-patches/src/subprocess.ts +++ b/packages/node-patches/src/subprocess.ts @@ -25,6 +25,21 @@ set Path=${nodeDir};%Path% "${process.execPath}" --require "${requireScriptName}" %* `); } + + ['npm', 'npx'].forEach(cmd => { + const cmdBat = `${cmd}.bat`; + const cmdEntry = path.join(nodeDir, cmdBat); + const cmdActual = path.join(path.dirname(process.execPath), cmdBat); + if (!fs.existsSync(cmdEntry)) { + fs.writeFileSync( + cmdEntry, `@if not defined DEBUG_HELPER @ECHO OFF +set NP_SUBPROCESS_NODE_DIR=${nodeDir} +set Path=${nodeDir};%Path% +exec ${cmdActual} %* + `, + {mode: 0o777}); + } + }); } else { const nodeEntry = path.join(nodeDir, 'node'); if (!fs.existsSync(nodeEntry)) { @@ -40,6 +55,20 @@ fi `, {mode: 0o777}); } + + ['npm', 'npx'].forEach(cmd => { + const cmdEntry = path.join(nodeDir, cmd); + const cmdActual = path.join(path.dirname(process.execPath), `../lib/node_modules/npm/bin/${cmd}-cli.js`); + if (!fs.existsSync(cmdEntry)) { + fs.writeFileSync( + cmdEntry, `#!/bin/bash +export NP_SUBPROCESS_NODE_DIR="${nodeDir}" +export PATH="${nodeDir}":\$PATH +exec ${cmdActual} "$@" + `, + {mode: 0o777}); + } + }); } if (!process.env.PATH) {