Skip to content

Commit 007a8f6

Browse files
committed
feat(builtin): use linker for all generated :bin targets
rather than loader.js module-resolver hacks for generated targets, we now expect that tools will use the linker to set up their node_modules tree, or will explicitly use a runfiles helper library to resolve files in the runfiles, like other languages in Bazel. BREAKING CHANGE: If you use the generated nodejs_binary or nodejs_test rules in the npm workspace, for example @npm//typescript/bin:tsc, your custom rule must now link the node_modules directory into that process. A typical way to do this is with the run_node helper. See updates to examples in this commit.
1 parent 55d132f commit 007a8f6

16 files changed

Lines changed: 86 additions & 32 deletions

File tree

examples/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ example_integration_test(
9191
npm_packages = {
9292
"//packages/jasmine:npm_package": "@bazel/jasmine",
9393
},
94+
# Parcel spawns a subprocess which requires our node-patches
95+
# but we don't yet have a mechanism on Windows for spawned processes
96+
# to inherit the --require script needed to install the patches
97+
tags = ["no-bazelci-windows"],
9498
)
9599

96100
example_integration_test(

examples/parcel/parcel.bzl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
This is not a full-featured Parcel bazel rule, just enough to demonstrate how to write one.
1818
"""
1919

20+
load("@build_bazel_rules_nodejs//:providers.bzl", "run_node")
21+
2022
def _parcel_impl(ctx):
2123
"""The "implementation function" for our rule.
2224
@@ -31,9 +33,12 @@ def _parcel_impl(ctx):
3133
args += ["--out-dir", ctx.outputs.bundle.dirname]
3234
args += ["--out-file", ctx.outputs.bundle.basename]
3335

34-
ctx.actions.run(
36+
# We use the run_node helper rather than ctx.actions.run so that the npm package
37+
# gets automatically included in the action inputs
38+
run_node(
39+
ctx = ctx,
3540
inputs = ctx.files.srcs + [ctx.file.entry_point],
36-
executable = ctx.executable.parcel,
41+
executable = "parcel",
3742
outputs = [ctx.outputs.bundle, ctx.outputs.sourcemap],
3843
arguments = args,
3944
env = {"COMPILATION_MODE": ctx.var["COMPILATION_MODE"]},

internal/linker/index.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,16 @@ class Runfiles {
143143
}
144144
resolve(modulePath) {
145145
if (this.manifest) {
146-
return this.lookupDirectory(modulePath);
146+
const result = this.lookupDirectory(modulePath);
147+
if (result)
148+
return result;
147149
}
148-
if (exports.runfiles.dir) {
150+
if (exports.runfiles.dir && fs.existsSync(path.join(exports.runfiles.dir, modulePath))) {
149151
return path.join(exports.runfiles.dir, modulePath);
150152
}
151-
throw new Error(`could not resolve modulePath ${modulePath}`);
153+
const e = new Error(`could not resolve modulePath ${modulePath}`);
154+
e.code = 'MODULE_NOT_FOUND';
155+
throw e;
152156
}
153157
resolveWorkspaceRelative(modulePath) {
154158
if (!this.workspace) {
@@ -345,7 +349,12 @@ function main(args, runfiles) {
345349
else {
346350
runfilesPath = `${workspace}/${runfilesPath}`;
347351
}
348-
target = runfiles.resolve(runfilesPath) || '<runfiles resolution failed>';
352+
try {
353+
target = runfiles.resolve(runfilesPath);
354+
}
355+
catch (_a) {
356+
target = '<runfiles resolution failed>';
357+
}
349358
break;
350359
}
351360
yield symlink(target, m.name);

internal/linker/link_node_modules.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,15 @@ export class Runfiles {
245245
resolve(modulePath: string) {
246246
// Look in the runfiles first
247247
if (this.manifest) {
248-
return this.lookupDirectory(modulePath);
248+
const result = this.lookupDirectory(modulePath);
249+
if (result) return result;
249250
}
250-
if (runfiles.dir) {
251+
if (runfiles.dir && fs.existsSync(path.join(runfiles.dir, modulePath))) {
251252
return path.join(runfiles.dir, modulePath);
252253
}
253-
throw new Error(`could not resolve modulePath ${modulePath}`);
254+
const e = new Error(`could not resolve modulePath ${modulePath}`);
255+
(e as any).code = 'MODULE_NOT_FOUND';
256+
throw e;
254257
}
255258

256259
resolveWorkspaceRelative(modulePath: string) {
@@ -569,7 +572,11 @@ export async function main(args: string[], runfiles: Runfiles) {
569572
} else {
570573
runfilesPath = `${workspace}/${runfilesPath}`;
571574
}
572-
target = runfiles.resolve(runfilesPath) || '<runfiles resolution failed>';
575+
try {
576+
target = runfiles.resolve(runfilesPath);
577+
} catch {
578+
target = '<runfiles resolution failed>';
579+
}
573580
break;
574581
}
575582

internal/node/node_patches.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,10 @@ fi
593593
process.env.PATH = nodeDir + path.delimiter + process.env.PATH;
594594
}
595595
// fix execPath so folks use the proxy node
596-
process.argv[0] = process.execPath = path.join(nodeDir, 'node');
596+
if (process.platform == 'win32') ;
597+
else {
598+
process.argv[0] = process.execPath = path.join(nodeDir, 'node');
599+
}
597600
// replace any instances of require script in execArgv with the absolute path to the script.
598601
// example: bazel-require-script.js
599602
process.execArgv.map(v => {

internal/npm_install/generate_build_file.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1074,7 +1074,8 @@ nodejs_binary(
10741074
name = "${name}",
10751075
entry_point = "//:node_modules/${pkg._dir}/${path}",
10761076
install_source_map_support = False,
1077-
data = [${data.map(p => `"${p}"`).join(', ')}],${additionalAttributes(pkg, name)}
1077+
data = [${data.map(p => `"${p}"`).join(', ')}],
1078+
templated_args = ["--nobazel_patch_module_resolver"],${additionalAttributes(pkg, name)}
10781079
)
10791080
`;
10801081
}

internal/npm_install/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,8 @@ nodejs_binary(
645645
name = "${name}",
646646
entry_point = "//:node_modules/${pkg._dir}/${path}",
647647
install_source_map_support = False,
648-
data = [${data.map(p => `"${p}"`).join(', ')}],${additionalAttributes(pkg, name)}
648+
data = [${data.map(p => `"${p}"`).join(', ')}],
649+
templated_args = ["--nobazel_patch_module_resolver"],${additionalAttributes(pkg, name)}
649650
)
650651
`;
651652
}

internal/npm_install/test/golden/@gregmagolan/test-a/bin/BUILD.bazel.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ nodejs_binary(
66
entry_point = "//:node_modules/@gregmagolan/test-a/@bin/test.js",
77
install_source_map_support = False,
88
data = ["//@gregmagolan/test-a:test-a"],
9+
templated_args = ["--nobazel_patch_module_resolver"],
910
)

internal/npm_install/test/golden/jasmine/bin/BUILD.bazel.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ nodejs_binary(
66
entry_point = "//:node_modules/jasmine/bin/jasmine.js",
77
install_source_map_support = False,
88
data = ["//jasmine:jasmine"],
9+
templated_args = ["--nobazel_patch_module_resolver"],
910
)

internal/providers/node_runtime_deps_info.bzl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ def run_node(ctx, inputs, arguments, executable, **kwargs):
7474

7575
modules_manifest = write_node_modules_manifest(ctx, link_data)
7676
add_arg(arguments, "--bazel_node_modules_manifest=%s" % modules_manifest.path)
77-
inputs.append(modules_manifest)
7877

7978
# By using the run_node helper, you suggest that your program
8079
# doesn't implicitly use runfiles to require() things
@@ -98,7 +97,7 @@ def run_node(ctx, inputs, arguments, executable, **kwargs):
9897
env["BAZEL_NODE_MODULES_ROOT"] = _compute_node_modules_root(ctx)
9998

10099
ctx.actions.run(
101-
inputs = inputs + extra_inputs,
100+
inputs = inputs + extra_inputs + [modules_manifest],
102101
arguments = arguments,
103102
executable = exec_exec,
104103
env = env,

0 commit comments

Comments
 (0)