Skip to content

Commit 07126ae

Browse files
kormidejosephperrott
authored andcommitted
build(bazel): fix race conditions in windows aio build
Disables the rules_nodejs linker and adds a custom esm module resolver
1 parent 7c8ca11 commit 07126ae

20 files changed

Lines changed: 461 additions & 19 deletions

File tree

WORKSPACE

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ http_archive(
2424

2525
http_archive(
2626
name = "build_bazel_rules_nodejs",
27-
sha256 = "c78216f5be5d451a42275b0b7dc809fb9347e2b04a68f68bad620a2b01f5c774",
28-
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/5.5.2/rules_nodejs-5.5.2.tar.gz"],
27+
patch_args = ["-p1"],
28+
patches = [
29+
"//:tools/bazel-repo-patches/rules_nodejs__esm_no_linker_fix.patch",
30+
],
31+
sha256 = "ee3280a7f58aa5c1caa45cb9e08cbb8f4d74300848c508374daf37314d5390d6",
32+
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/5.5.1/rules_nodejs-5.5.1.tar.gz"],
2933
)
3034

3135
load("@build_bazel_rules_nodejs//:repositories.bzl", "build_bazel_rules_nodejs_dependencies")

aio/BUILD.bazel

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
load("@aio_npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
2-
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary", "npm_package_bin")
2+
load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin")
3+
load("//aio/tools:defaults.bzl", "nodejs_binary")
34
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
45

56
# The write_source_files macro is used to write bazel outputs to the source tree and test that they are up to date.

aio/scripts/BUILD.bazel

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "nodejs_binary")
1+
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
2+
load("//aio/tools:defaults.bzl", "nodejs_binary")
23

34
package(default_visibility = ["//visibility:public"])
45

aio/tools/BUILD.bazel

Whitespace-only changes.

aio/tools/defaults.bzl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
load("@build_bazel_rules_nodejs//:index.bzl", _nodejs_binary = "nodejs_binary")
2+
3+
def nodejs_binary(data = [], env = {}, templated_args = [], **kwargs):
4+
data = data + [
5+
"//aio/tools/esm-loader",
6+
"//aio/tools/esm-loader:esm-loader.mjs",
7+
]
8+
9+
env = dict(env, **{"NODE_MODULES_WORKSPACE_NAME": "aio_npm"})
10+
11+
templated_args = templated_args + [
12+
# Disable the linker and rely on patched resolution which works better on Windows
13+
# and is less prone to race conditions when targets build concurrently.
14+
"--nobazel_run_linker",
15+
# Provide a custom esm loader to resolve third-party depenencies. Unlike for cjs
16+
# modules, rules_nodejs doesn't patch imports when the linker is disabled.
17+
"--node_options=--loader=./$(rootpath //aio/tools/esm-loader:esm-loader.mjs)",
18+
]
19+
20+
_nodejs_binary(
21+
data = data,
22+
env = env,
23+
templated_args = templated_args,
24+
**kwargs
25+
)

aio/tools/esm-loader/BUILD.bazel

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package(default_visibility = ["//aio:__subpackages__"])
2+
3+
exports_files([
4+
"esm-loader.mjs",
5+
])
6+
7+
filegroup(
8+
name = "esm-loader",
9+
srcs = [
10+
"esm-loader.mjs",
11+
"//third_party/github.com/lukeed/resolve.exports",
12+
],
13+
)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import {pathToFileURL} from 'url';
4+
import {resolve as resolveExports} from '../../../third_party/github.com/lukeed/resolve.exports/index.mjs';
5+
6+
/*
7+
Custom module loader (see https://nodejs.org/api/cli.html#--experimental-loadermodule) to support
8+
loading third-party packages in esm modules when the rules_nodejs linker is disabled. Resolves
9+
third-party imports from the node_modules folder in the bazel workspace defined by
10+
process.env.NODE_MODULES_WORKSPACE_NAME, and uses default resolution for all other imports.
11+
12+
This is required because rules_nodejs only patches requires in cjs modules when the linker
13+
is disabled, not imports in mjs modules.
14+
*/
15+
export async function resolve(specifier, context, defaultResolve) {
16+
if (!isNodeOrNpmPackageImport(specifier)) {
17+
return defaultResolve(specifier, context, defaultResolve);
18+
}
19+
20+
const nodeModules = path.resolve('external', process.env.NODE_MODULES_WORKSPACE_NAME, 'node_modules');
21+
22+
const packageImport = parsePackageImport(specifier);
23+
const pathToNodeModule = path.join(nodeModules, packageImport.packageName);
24+
25+
const isInternalNodePackage = !fs.existsSync(pathToNodeModule);
26+
if (isInternalNodePackage) {
27+
return defaultResolve(specifier, context, defaultResolve);
28+
}
29+
30+
const packageJson = JSON.parse(fs.readFileSync(path.join(pathToNodeModule, 'package.json'), 'utf-8'));
31+
32+
const localPackagePath = resolvePackageLocalFilepath(packageImport, packageJson);
33+
const resolvedFilePath = path.join(pathToNodeModule, localPackagePath);
34+
35+
return {url: pathToFileURL(resolvedFilePath).href};
36+
}
37+
38+
function isNodeOrNpmPackageImport(specifier) {
39+
return !specifier.startsWith('./') && !specifier.startsWith('../') && !specifier.startsWith('node:') && !specifier.startsWith('file:');
40+
}
41+
42+
function parsePackageImport(specifier) {
43+
const [, packageName, pathInPackage = ''] = /^((?:@[^/]+\/)?[^/]+)(?:\/(.+))?$/.exec(specifier) ?? [];
44+
if (!packageName) {
45+
throw new Error(`Could not parse package name import statement '${specifier}'`);
46+
}
47+
return {packageName, pathInPackage, specifier};
48+
}
49+
50+
function resolvePackageLocalFilepath(packageImport, packageJson) {
51+
if (packageJson.exports) {
52+
return resolveExports(packageJson, packageImport.specifier);
53+
}
54+
55+
return packageImport.pathInPackage || packageJson.module || packageJson.main || 'index.js';
56+
}
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
1+
load("//aio/tools:defaults.bzl", "nodejs_binary")
22

33
package(default_visibility = ["//visibility:public"])
44

@@ -7,13 +7,11 @@ nodejs_binary(
77
data = [
88
"exampleZipper.mjs",
99
"//aio/tools/transforms/examples-package",
10+
"//aio/tools/transforms/helpers",
1011
"@aio_npm//archiver",
1112
"@aio_npm//canonical-path",
1213
"@aio_npm//fs-extra",
1314
"@aio_npm//globby",
1415
],
1516
entry_point = "generateZip.mjs",
16-
# --preserve-symlinks-main is not enabled by default (see https://github.com/bazelbuild/rules_nodejs/pull/2176)
17-
# However it seems to be required for mjs entry points to resolve node_modules within the sandbox.
18-
templated_args = ["--node_options=--preserve-symlinks-main"],
1917
)

aio/tools/example-zipper/exampleZipper.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import fs from 'fs-extra';
55
import {globbySync} from 'globby';
66
import {fileURLToPath} from 'url';
77

8-
import regionExtractor from 'aio/tools/examples-package/services/region-parser.js';
8+
import regionExtractor from '../transforms/examples-package/services/region-parser.js';
99

1010
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1111
const EXAMPLE_CONFIG_NAME = 'example-config.json';

aio/tools/examples/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
1+
load("//aio/tools:defaults.bzl", "nodejs_binary")
22
load("@aio_npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
33

44
package(default_visibility = ["//visibility:public"])

0 commit comments

Comments
 (0)