From a124eecb9dc8fd4aba0af2148b89b63d112efb2c Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Tue, 21 Nov 2023 19:02:08 -0500 Subject: [PATCH 1/2] fix(@angular-devkit/build-angular): avoid native realpath in application builder When the `preserveSymlinks` option is false (the default), the tsconfig path was passed through realpath to ensure that the TypeScript source files were resolved correctly. However, the promise version of the Node.js API was previously used. This version used `realpath.native` internally but the native version has numerous behavior problems on Windows systems. This includes potential case conversion which can result in differing cases for files within the build system and in turn failed path comparison checks. The synchronous version is now used which has a JavaScript implementation that does not suffer from these problems. --- .../src/tools/esbuild/angular/compiler-plugin.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/angular/compiler-plugin.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/angular/compiler-plugin.ts index 9e6546c38275..8568a30c771f 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/angular/compiler-plugin.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/angular/compiler-plugin.ts @@ -16,7 +16,7 @@ import type { PluginBuild, } from 'esbuild'; import assert from 'node:assert'; -import { realpath } from 'node:fs/promises'; +import { realpathSync } from 'node:fs'; import * as path from 'node:path'; import { maxWorkers } from '../../../utils/environment-options'; import { JavaScriptTransformer } from '../javascript-transformer'; @@ -61,8 +61,11 @@ export function createCompilerPlugin( if (!preserveSymlinks) { // Use the real path of the tsconfig if not preserving symlinks. // This ensures the TS source file paths are based on the real path of the configuration. + // NOTE: promises.realpath should not be used here since it uses realpath.native which + // can cause case conversion and other undesirable behavior on Windows systems. + // ref: https://github.com/nodejs/node/issues/7726 try { - tsconfigPath = await realpath(tsconfigPath); + tsconfigPath = realpathSync(tsconfigPath); } catch {} } From 23b89fb1de5edda16055c5fc65d2d9256ca0d27d Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:55:19 -0500 Subject: [PATCH 2/2] fix(@angular-devkit/build-angular): use workspace real path when not preserving symlinks When not preserving symlinks (the default), the workspace root path should be converted to the real path on the filesystem to ensure that resolution of all other path-based build options reflects the actual location of the files. This ensures that typescript and esbuild resolve files in a consistent manner and avoids hard to diagnose errors involving missing files at build time. --- .../src/builders/application/options.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/angular_devkit/build_angular/src/builders/application/options.ts b/packages/angular_devkit/build_angular/src/builders/application/options.ts index 5abae5922272..94d55e10b81a 100644 --- a/packages/angular_devkit/build_angular/src/builders/application/options.ts +++ b/packages/angular_devkit/build_angular/src/builders/application/options.ts @@ -8,6 +8,7 @@ import { BuilderContext } from '@angular-devkit/architect'; import type { Plugin } from 'esbuild'; +import { realpathSync } from 'node:fs'; import { access, constants } from 'node:fs/promises'; import { createRequire } from 'node:module'; import path from 'node:path'; @@ -83,7 +84,17 @@ export async function normalizeOptions( options: ApplicationBuilderInternalOptions, plugins?: Plugin[], ) { - const workspaceRoot = context.workspaceRoot; + // If not explicitly set, default to the Node.js process argument + const preserveSymlinks = + options.preserveSymlinks ?? process.execArgv.includes('--preserve-symlinks'); + + // Setup base paths based on workspace root and project information + const workspaceRoot = preserveSymlinks + ? context.workspaceRoot + : // NOTE: promises.realpath should not be used here since it uses realpath.native which + // can cause case conversion and other undesirable behavior on Windows systems. + // ref: https://github.com/nodejs/node/issues/7726 + realpathSync(context.workspaceRoot); const projectMetadata = await context.getProjectMetadata(projectName); const projectRoot = normalizeDirectoryPath( path.join(workspaceRoot, (projectMetadata.root as string | undefined) ?? ''), @@ -258,7 +269,6 @@ export async function normalizeOptions( serviceWorker, poll, polyfills, - preserveSymlinks, statsJson, stylePreprocessorOptions, subresourceIntegrity, @@ -289,8 +299,7 @@ export async function normalizeOptions( poll, progress, externalPackages, - // If not explicitly set, default to the Node.js process argument - preserveSymlinks: preserveSymlinks ?? process.execArgv.includes('--preserve-symlinks'), + preserveSymlinks, stylePreprocessorOptions, subresourceIntegrity, serverEntryPoint,