From 18b96f02273b9b3700a9d7257aca0e99273e795f Mon Sep 17 00:00:00 2001 From: Frank Portman Date: Mon, 27 Jul 2026 15:33:13 -0400 Subject: [PATCH] fix(@angular/build): rewrite paths from sandboxed execroots when running under Bazel rewriteForBazel only maps paths that fall inside BAZEL_BINDIR relative to JS_BINARY__EXECROOT. Locally sandboxed actions (darwin-sandbox, linux-sandbox) stage inputs as symlinks into a differently rooted copy of the execroot, so realpath resolves entry points to the unsandboxed execroot while the TypeScript program is keyed by sandbox paths. The compiler plugin then fails with "File 'main.ts' is missing from the TypeScript compilation". Derive the effective execroot from the working directory and re-anchor paths that resolve outside of it. --- .../esbuild/angular/rewrite-bazel-paths.ts | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/angular/build/src/tools/esbuild/angular/rewrite-bazel-paths.ts b/packages/angular/build/src/tools/esbuild/angular/rewrite-bazel-paths.ts index 8a6fb6aa82a4..b7cd376d560e 100644 --- a/packages/angular/build/src/tools/esbuild/angular/rewrite-bazel-paths.ts +++ b/packages/angular/build/src/tools/esbuild/angular/rewrite-bazel-paths.ts @@ -6,25 +6,55 @@ * found in the LICENSE file at https://angular.dev/license */ -import { join, relative } from 'node:path'; +import { join, relative, sep } from 'node:path'; const bazelBinDirectory = process.env['BAZEL_BINDIR']; const bazelExecRoot = process.env['JS_BINARY__EXECROOT']; +const execRootMarker = `${sep}execroot${sep}`; + +/** + * Sandboxed actions run in a copy of the execroot whose absolute path differs from + * `JS_BINARY__EXECROOT`, so the effective execroot is derived from the working directory. + */ +function effectiveExecRoot(): string | undefined { + const cwd = process.cwd(); + const markerIndex = cwd.indexOf(execRootMarker); + if (markerIndex === -1) { + return bazelExecRoot; + } + + const workspaceEnd = cwd.indexOf(sep, markerIndex + execRootMarker.length); + + return workspaceEnd === -1 ? cwd : cwd.slice(0, workspaceEnd); +} export function rewriteForBazel(path: string): string { if (!bazelBinDirectory || !bazelExecRoot) { return path; } - const fromExecRoot = relative(bazelExecRoot, path); + const execRoot = effectiveExecRoot() ?? bazelExecRoot; + + const fromExecRoot = relative(execRoot, path); if (!fromExecRoot.startsWith('..')) { return path; } const fromBinDirectory = relative(bazelBinDirectory, path); - if (fromBinDirectory.startsWith('..')) { - return path; + if (!fromBinDirectory.startsWith('..')) { + return join(execRoot, fromBinDirectory); + } + + // A path that realpath resolved out of a symlink-staged sandbox into the + // unsandboxed execroot: re-anchor its execroot-relative tail. + const markerIndex = path.indexOf(execRootMarker); + if (markerIndex !== -1) { + const tail = path.slice(markerIndex + execRootMarker.length); + const workspaceEnd = tail.indexOf(sep); + if (workspaceEnd !== -1) { + return join(execRoot, tail.slice(workspaceEnd + 1)); + } } - return join(bazelExecRoot, fromBinDirectory); + return path; }