Skip to content

Commit 94abf68

Browse files
authored
fix(builtin): support for scoped modules in linker (#1199)
1 parent 051b592 commit 94abf68

File tree

9 files changed

+85
-19
lines changed

9 files changed

+85
-19
lines changed

internal/linker/index.js

Lines changed: 23 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/linker/link_node_modules.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ Include as much of the build output as you can without disclosing anything confi
2424
`);
2525
}
2626

27+
/**
28+
* Create a new directory and any necessary subdirectories
29+
* if they do not exist.
30+
*/
31+
function mkdirp(p: string) {
32+
if (!fs.existsSync(p)) {
33+
mkdirp(path.dirname(p));
34+
fs.mkdirSync(p);
35+
}
36+
}
37+
2738
async function symlink(target: string, path: string) {
2839
log_verbose(`symlink( ${path} -> ${target} )`);
2940
// Use junction on Windows since symlinks require elevated permissions.
@@ -46,7 +57,7 @@ async function symlink(target: string, path: string) {
4657
if (!fs.existsSync(path)) {
4758
log_verbose(
4859
'ERROR\n***\nLooks like we created a bad symlink:' +
49-
`\n pwd ${process.cwd()}\n target ${target}\n***`);
60+
`\n pwd ${process.cwd()}\n target ${target}\n path ${path}\n***`);
5061
}
5162
}
5263
}
@@ -179,8 +190,6 @@ export class Runfiles {
179190
if (this.manifest) {
180191
return this.lookupDirectory(modulePath);
181192
}
182-
// how can we avoid this FS lookup every time? we don't know when process.cwd changed...
183-
// const runfilesRelative = runfiles.dir ? path.relative('.', runfiles.dir) : undefined;
184193
if (runfiles.dir) {
185194
return path.join(runfiles.dir, modulePath);
186195
}
@@ -262,7 +271,7 @@ export async function main(args: string[], runfiles: Runfiles) {
262271
process.chdir(rootDir);
263272

264273
// Symlinks to packages need to reach back to the workspace/runfiles directory
265-
const workspaceRelative = path.relative('.', workspaceDir);
274+
const workspaceAbs = path.resolve(workspaceDir);
266275

267276
// Now add symlinks to each of our first-party packages so they appear under the node_modules tree
268277
const links = [];
@@ -273,7 +282,7 @@ export async function main(args: string[], runfiles: Runfiles) {
273282
switch (root) {
274283
case 'bin':
275284
// FIXME(#1196)
276-
target = path.join(workspaceRelative, bin, toWorkspaceDir(modulePath));
285+
target = path.join(workspaceAbs, bin, toWorkspaceDir(modulePath));
277286
// Spend an extra FS lookup to give better error in this case
278287
if (!await exists(target)) {
279288
// TODO: there should be some docs explaining how users are
@@ -287,7 +296,7 @@ export async function main(args: string[], runfiles: Runfiles) {
287296
}
288297
break;
289298
case 'src':
290-
target = path.join(workspaceRelative, toWorkspaceDir(modulePath));
299+
target = path.join(workspaceAbs, toWorkspaceDir(modulePath));
291300
break;
292301
case 'runfiles':
293302
target = runfiles.resolve(modulePath) || '<runfiles resolution failed>';
@@ -298,6 +307,14 @@ export async function main(args: string[], runfiles: Runfiles) {
298307
}
299308

300309
for (const m of Object.keys(modules)) {
310+
const segments = m.split('/');
311+
if (segments.length > 2) {
312+
throw new Error(`module ${m} has more than 2 segments which is not a valid node module name`);
313+
}
314+
if (segments.length == 2) {
315+
// ensure the scope exists
316+
mkdirp(segments[0]);
317+
}
301318
const [kind, modulePath] = modules[m];
302319
links.push(linkModule(m, kind, modulePath));
303320
}

internal/linker/test/integration/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ sh_binary(
2121
":program.js",
2222
"//internal/linker:index.js",
2323
"//internal/linker/test/integration/static_linked_pkg",
24+
"//internal/linker/test/integration/static_linked_scoped_pkg",
2425
"@bazel_tools//tools/bash/runfiles",
2526
"@build_bazel_rules_nodejs//toolchains/node:node_bin",
2627
],
@@ -35,6 +36,7 @@ linked(
3536
"//%s/absolute_import:index.js" % package_name(),
3637
":run_program",
3738
"//internal/linker/test/integration/dynamic_linked_pkg",
39+
"//internal/linker/test/integration/dynamic_linked_scoped_pkg",
3840
"@npm//semver",
3941
],
4042
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
load("//internal/js_library:js_library.bzl", "js_library")
2+
3+
package(default_visibility = ["//internal/linker/test:__subpackages__"])
4+
5+
js_library(
6+
name = "dynamic_linked_scoped_pkg",
7+
srcs = ["index.js"],
8+
module_from_src = True,
9+
module_name = "@linker_scoped/dynamic_linked",
10+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function addD(str) {
2+
return `${str}_d`;
3+
}
4+
5+
exports.addD = addD;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.2.3_c_b_a
1+
1.2.3_a_b_c_d_e
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
// First-party package from ./static_linked_pkg
2-
// it should get resolved through runfiles
1+
// First-party "static linked" packages
2+
// they should get resolved through runfiles
33
const a = require('static_linked');
4-
// First-party package from ./dynamic_linked_pkg
5-
// it should get resolved from the execroot
4+
const e = require('@linker_scoped/static_linked');
5+
// First-party "dynamic linked" packages
6+
// they should get resolved from the execroot
67
const b = require('dynamic_linked');
8+
const d = require('@linker_scoped/dynamic_linked');
79
// We've always supported `require('my_workspace')` for absolute imports like Google does it
810
const c = require('build_bazel_rules_nodejs/internal/linker/test/integration/absolute_import');
911

1012
// Third-party package installed in the root node_modules
1113
const semver = require('semver');
1214

1315
// This output should match what's in the golden.txt file
14-
console.log(a.addA(b.addB(c.addC(semver.clean(' =v1.2.3 ')))));
16+
console.log(e.addE(d.addD(c.addC(b.addB(a.addA(semver.clean(' =v1.2.3 ')))))));
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
load("//internal/js_library:js_library.bzl", "js_library")
2+
3+
package(default_visibility = ["//internal/linker/test:__subpackages__"])
4+
5+
js_library(
6+
name = "static_linked_scoped_pkg",
7+
srcs = ["index.js"],
8+
module_name = "@linker_scoped/static_linked",
9+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function addE(str) {
2+
return `${str}_e`;
3+
}
4+
5+
exports.addE = addE;

0 commit comments

Comments
 (0)