build(bazel): fix race conditions in windows aio build

Disables the rules_nodejs linker and adds a custom esm module resolver
This commit is contained in:
Derek Cormier
2022-06-27 14:41:05 -07:00
committed by Joey Perrott
parent 7c8ca113c1
commit 07126ae40f
20 changed files with 461 additions and 19 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
load("@aio_npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary", "npm_package_bin")
load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin")
load("//aio/tools:defaults.bzl", "nodejs_binary")
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
# The write_source_files macro is used to write bazel outputs to the source tree and test that they are up to date.
+2 -1
View File
@@ -1,4 +1,5 @@
load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "nodejs_binary")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//aio/tools:defaults.bzl", "nodejs_binary")
package(default_visibility = ["//visibility:public"])
View File
+25
View File
@@ -0,0 +1,25 @@
load("@build_bazel_rules_nodejs//:index.bzl", _nodejs_binary = "nodejs_binary")
def nodejs_binary(data = [], env = {}, templated_args = [], **kwargs):
data = data + [
"//aio/tools/esm-loader",
"//aio/tools/esm-loader:esm-loader.mjs",
]
env = dict(env, **{"NODE_MODULES_WORKSPACE_NAME": "aio_npm"})
templated_args = templated_args + [
# Disable the linker and rely on patched resolution which works better on Windows
# and is less prone to race conditions when targets build concurrently.
"--nobazel_run_linker",
# Provide a custom esm loader to resolve third-party depenencies. Unlike for cjs
# modules, rules_nodejs doesn't patch imports when the linker is disabled.
"--node_options=--loader=./$(rootpath //aio/tools/esm-loader:esm-loader.mjs)",
]
_nodejs_binary(
data = data,
env = env,
templated_args = templated_args,
**kwargs
)
+13
View File
@@ -0,0 +1,13 @@
package(default_visibility = ["//aio:__subpackages__"])
exports_files([
"esm-loader.mjs",
])
filegroup(
name = "esm-loader",
srcs = [
"esm-loader.mjs",
"//third_party/github.com/lukeed/resolve.exports",
],
)
+56
View File
@@ -0,0 +1,56 @@
import fs from 'fs';
import path from 'path';
import {pathToFileURL} from 'url';
import {resolve as resolveExports} from '../../../third_party/github.com/lukeed/resolve.exports/index.mjs';
/*
Custom module loader (see https://nodejs.org/api/cli.html#--experimental-loadermodule) to support
loading third-party packages in esm modules when the rules_nodejs linker is disabled. Resolves
third-party imports from the node_modules folder in the bazel workspace defined by
process.env.NODE_MODULES_WORKSPACE_NAME, and uses default resolution for all other imports.
This is required because rules_nodejs only patches requires in cjs modules when the linker
is disabled, not imports in mjs modules.
*/
export async function resolve(specifier, context, defaultResolve) {
if (!isNodeOrNpmPackageImport(specifier)) {
return defaultResolve(specifier, context, defaultResolve);
}
const nodeModules = path.resolve('external', process.env.NODE_MODULES_WORKSPACE_NAME, 'node_modules');
const packageImport = parsePackageImport(specifier);
const pathToNodeModule = path.join(nodeModules, packageImport.packageName);
const isInternalNodePackage = !fs.existsSync(pathToNodeModule);
if (isInternalNodePackage) {
return defaultResolve(specifier, context, defaultResolve);
}
const packageJson = JSON.parse(fs.readFileSync(path.join(pathToNodeModule, 'package.json'), 'utf-8'));
const localPackagePath = resolvePackageLocalFilepath(packageImport, packageJson);
const resolvedFilePath = path.join(pathToNodeModule, localPackagePath);
return {url: pathToFileURL(resolvedFilePath).href};
}
function isNodeOrNpmPackageImport(specifier) {
return !specifier.startsWith('./') && !specifier.startsWith('../') && !specifier.startsWith('node:') && !specifier.startsWith('file:');
}
function parsePackageImport(specifier) {
const [, packageName, pathInPackage = ''] = /^((?:@[^/]+\/)?[^/]+)(?:\/(.+))?$/.exec(specifier) ?? [];
if (!packageName) {
throw new Error(`Could not parse package name import statement '${specifier}'`);
}
return {packageName, pathInPackage, specifier};
}
function resolvePackageLocalFilepath(packageImport, packageJson) {
if (packageJson.exports) {
return resolveExports(packageJson, packageImport.specifier);
}
return packageImport.pathInPackage || packageJson.module || packageJson.main || 'index.js';
}
+2 -4
View File
@@ -1,4 +1,4 @@
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
load("//aio/tools:defaults.bzl", "nodejs_binary")
package(default_visibility = ["//visibility:public"])
@@ -7,13 +7,11 @@ nodejs_binary(
data = [
"exampleZipper.mjs",
"//aio/tools/transforms/examples-package",
"//aio/tools/transforms/helpers",
"@aio_npm//archiver",
"@aio_npm//canonical-path",
"@aio_npm//fs-extra",
"@aio_npm//globby",
],
entry_point = "generateZip.mjs",
# --preserve-symlinks-main is not enabled by default (see https://github.com/bazelbuild/rules_nodejs/pull/2176)
# However it seems to be required for mjs entry points to resolve node_modules within the sandbox.
templated_args = ["--node_options=--preserve-symlinks-main"],
)
+1 -1
View File
@@ -5,7 +5,7 @@ import fs from 'fs-extra';
import {globbySync} from 'globby';
import {fileURLToPath} from 'url';
import regionExtractor from 'aio/tools/examples-package/services/region-parser.js';
import regionExtractor from '../transforms/examples-package/services/region-parser.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const EXAMPLE_CONFIG_NAME = 'example-config.json';
+1 -1
View File
@@ -1,4 +1,4 @@
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
load("//aio/tools:defaults.bzl", "nodejs_binary")
load("@aio_npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
package(default_visibility = ["//visibility:public"])
+2 -4
View File
@@ -1,4 +1,4 @@
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
load("//aio/tools:defaults.bzl", "nodejs_binary")
package(default_visibility = ["//visibility:public"])
@@ -7,13 +7,11 @@ nodejs_binary(
data = [
"builder.mjs",
"//aio/tools/transforms/examples-package",
"//aio/tools/transforms/helpers",
"@aio_npm//canonical-path",
"@aio_npm//fs-extra",
"@aio_npm//globby",
"@aio_npm//jsdom",
],
entry_point = "generateStackblitz.mjs",
# --preserve-symlinks-main is not enabled by default (see https://github.com/bazelbuild/rules_nodejs/pull/2176)
# However it seems to be required for mjs entry points to resolve node_modules within the sandbox.
templated_args = ["--node_options=--preserve-symlinks-main"],
)
+1 -1
View File
@@ -4,7 +4,7 @@ import fs from 'fs-extra';
import {globbySync} from 'globby';
import jsdom from 'jsdom';
import regionExtractor from 'aio/tools/examples-package/services/region-parser.js';
import regionExtractor from '../transforms/examples-package/services/region-parser.js';
export class StackblitzBuilder {
constructor(examplePath, destPath) {
@@ -3,7 +3,6 @@ load("@aio_npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
js_library(
name = "examples-package",
package_name = "aio/tools/examples-package",
srcs = glob(
["**/*.js"],
exclude = ["**/*.spec.js"],
@@ -4,7 +4,7 @@ const inlineC = require('./region-matchers/inline-c');
const inlineCOnly = require('./region-matchers/inline-c-only');
const inlineHash = require('./region-matchers/inline-hash');
const DEFAULT_PLASTER = '. . .';
const {mapObject} = require('helpers/utils');
const {mapObject} = require('../../helpers/utils');
const removeEslintComments = require('./removeEslintComments');
module.exports = function regionParser() {
+1 -2
View File
@@ -3,12 +3,11 @@ load("@aio_npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
js_library(
name = "helpers",
package_name = "helpers",
srcs = [
"test-package.js",
"utils.js",
],
visibility = ["//aio/tools/transforms:__subpackages__"],
visibility = ["//aio/tools:__subpackages__"],
deps = [
"//:package_json",
"@aio_npm//canonical-path",