Skip to content

Commit 7cf7d73

Browse files
authored
fix(builtin): linker incorrectly resolves workspace node_modules for windows (#2659)
Depending on whether there are generated files being stored in `bazel-out/bin/external/npm` or not, the linker might resolve the workspace node modules incorrectly for Windows. e.g. consider the manifest being the followed: `` npm/karma/bin/_karma.module_mappings.json C:/users/paul/<..>/execroot/angular_material/bazel-out/<..>/bin/external/npm/<..>` npm/node_modules/@angular/core/index.js C:/users/paul/projects/material2/node_modules/@angular/core/index.js ``` The linker currently on Windows will resolve the NPM workspace to the `bazel-out` directory. This is incorrect as on Windows, the node modules are not symlinked as with linux/macOS. Hence the node modules are not symlinked to the execroot properly and tests/builds fail due to NodeJS imports being "faulty". The fix is to simply use the runfile helpers to resolve the workspace node modules. There is no need in resolving the workspace root if we can just directly narrow the lookup to the workspace node modules. This is needed as the runfile resolution with manifest naively looks for the first entry starting with `npm` in the manifest. This breaks the assumption that the resolved workspace path refers to a directory that also contains the `node_modules` folder (as seen in the example above).
1 parent ae67e63 commit 7cf7d73

File tree

2 files changed

+25
-25
lines changed

2 files changed

+25
-25
lines changed

internal/linker/index.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,25 @@ function symlink(target, p) {
111111
}
112112
});
113113
}
114-
function resolveExternalWorkspacePath(workspace, startCwd, isExecroot, execroot, runfiles) {
114+
function resolveWorkspaceNodeModules(workspace, startCwd, isExecroot, execroot, runfiles) {
115115
return __awaiter(this, void 0, void 0, function* () {
116+
const targetManifestPath = `${workspace}/node_modules`;
116117
if (isExecroot) {
117-
return `${execroot}/external/${workspace}`;
118+
return `${execroot}/external/${targetManifestPath}`;
118119
}
119120
if (!execroot) {
120-
return path.resolve(`${startCwd}/../${workspace}`);
121+
return path.resolve(`${startCwd}/../${targetManifestPath}`);
121122
}
122-
const fromManifest = runfiles.lookupDirectory(workspace);
123+
const fromManifest = runfiles.lookupDirectory(targetManifestPath);
123124
if (fromManifest) {
124125
return fromManifest;
125126
}
126127
else {
127-
const maybe = path.resolve(`${execroot}/external/${workspace}`);
128+
const maybe = path.resolve(`${execroot}/external/${targetManifestPath}`);
128129
if (yield exists(maybe)) {
129130
return maybe;
130131
}
131-
return path.resolve(`${startCwd}/../${workspace}`);
132+
return path.resolve(`${startCwd}/../${targetManifestPath}`);
132133
}
133134
});
134135
}
@@ -289,9 +290,8 @@ function main(args, runfiles) {
289290
for (const packagePath of Object.keys(roots)) {
290291
const workspace = roots[packagePath];
291292
if (workspace) {
292-
const workspacePath = yield resolveExternalWorkspacePath(workspace, startCwd, isExecroot, execroot, runfiles);
293-
log_verbose(`resolved ${workspace} workspace path to ${workspacePath}`);
294-
const workspaceNodeModules = `${workspacePath}/node_modules`;
293+
const workspaceNodeModules = yield resolveWorkspaceNodeModules(workspace, startCwd, isExecroot, execroot, runfiles);
294+
log_verbose(`resolved ${workspace} workspace node modules path to ${workspaceNodeModules}`);
295295
if (packagePath) {
296296
if (yield exists(workspaceNodeModules)) {
297297
yield mkdirp(packagePath);
@@ -309,7 +309,7 @@ function main(args, runfiles) {
309309
}
310310
else {
311311
log_verbose(`no npm workspace node_modules folder under ${packagePath} to link to; creating node_modules directories in ${process.cwd()} for ${packagePath} 1p deps`);
312-
yield mkdirp('${packagePath}/node_modules');
312+
yield mkdirp(`${packagePath}/node_modules`);
313313
if (!isExecroot) {
314314
const runfilesPackagePath = `${startCwd}/${packagePath}`;
315315
yield mkdirp(`${runfilesPackagePath}/node_modules`);

internal/linker/link_node_modules.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -131,24 +131,24 @@ async function symlink(target: string, p: string): Promise<boolean> {
131131
}
132132
}
133133

134-
/**
135-
* Resolve to the path to an external workspace
136-
*/
137-
async function resolveExternalWorkspacePath(
134+
/** Determines an absolute path to the given workspace if it contains node modules. */
135+
async function resolveWorkspaceNodeModules(
138136
workspace: string, startCwd: string, isExecroot: boolean, execroot: string|undefined,
139137
runfiles: Runfiles) {
138+
const targetManifestPath = `${workspace}/node_modules`;
139+
140140
if (isExecroot) {
141141
// Under execroot, the npm workspace will be under an external folder from the startCwd
142-
// `execroot/my_wksp`. For example, `execroot/my_wksp/external/npm`. If there is no
142+
// `execroot/my_wksp`. For example, `execroot/my_wksp/external/npm/node_modules`. If there is no
143143
// npm workspace, which will be the case if there are no third-party modules dependencies for
144144
// this target, npmWorkspace the root to `execroot/my_wksp/node_modules`.
145-
return `${execroot}/external/${workspace}`;
145+
return `${execroot}/external/${targetManifestPath}`;
146146
}
147147

148148
if (!execroot) {
149149
// This can happen if we are inside a nodejs_image or a nodejs_binary is run manually.
150150
// Resolve as if we are in runfiles in a sandbox.
151-
return path.resolve(`${startCwd}/../${workspace}`)
151+
return path.resolve(`${startCwd}/../${targetManifestPath}`)
152152
}
153153

154154
// Under runfiles, the linker should symlink node_modules at `execroot/my_wksp`
@@ -158,11 +158,11 @@ async function resolveExternalWorkspacePath(
158158
// If we got a runfilesManifest map, look through it for a resolution
159159
// This will happen if we are running a binary that had some npm packages
160160
// "statically linked" into its runfiles
161-
const fromManifest = runfiles.lookupDirectory(workspace);
161+
const fromManifest = runfiles.lookupDirectory(targetManifestPath);
162162
if (fromManifest) {
163163
return fromManifest;
164164
} else {
165-
const maybe = path.resolve(`${execroot}/external/${workspace}`);
165+
const maybe = path.resolve(`${execroot}/external/${targetManifestPath}`);
166166
if (await exists(maybe)) {
167167
// Under runfiles, when not in the sandbox we must symlink node_modules down at the execroot
168168
// `execroot/my_wksp/external/npm/node_modules` since `runfiles/npm/node_modules` will be a
@@ -173,7 +173,7 @@ async function resolveExternalWorkspacePath(
173173
// However, when in the sandbox, `execroot/my_wksp/external/npm/node_modules` does not exist,
174174
// so we must symlink into `runfiles/npm/node_modules`. This directory exists whether legacy
175175
// external runfiles are on or off.
176-
return path.resolve(`${startCwd}/../${workspace}`)
176+
return path.resolve(`${startCwd}/../${targetManifestPath}`)
177177
}
178178
}
179179

@@ -435,10 +435,10 @@ export async function main(args: string[], runfiles: Runfiles) {
435435
for (const packagePath of Object.keys(roots)) {
436436
const workspace = roots[packagePath];
437437
if (workspace) {
438-
const workspacePath =
439-
await resolveExternalWorkspacePath(workspace, startCwd, isExecroot, execroot, runfiles);
440-
log_verbose(`resolved ${workspace} workspace path to ${workspacePath}`);
441-
const workspaceNodeModules = `${workspacePath}/node_modules`;
438+
const workspaceNodeModules = await resolveWorkspaceNodeModules(
439+
workspace, startCwd, isExecroot, execroot, runfiles);
440+
log_verbose(`resolved ${workspace} workspace node modules path to ${workspaceNodeModules}`);
441+
442442
if (packagePath) {
443443
// sub-directory node_modules
444444
if (await exists(workspaceNodeModules)) {
@@ -474,7 +474,7 @@ export async function main(args: string[], runfiles: Runfiles) {
474474
log_verbose(`no npm workspace node_modules folder under ${
475475
packagePath} to link to; creating node_modules directories in ${process.cwd()} for ${
476476
packagePath} 1p deps`);
477-
await mkdirp('${packagePath}/node_modules');
477+
await mkdirp(`${packagePath}/node_modules`);
478478
if (!isExecroot) {
479479
// Under runfiles, we symlink into the package in runfiles as well.
480480
// When inside the sandbox, the execroot location will not exist to symlink to.

0 commit comments

Comments
 (0)