From 0042513513ca088eb607f28445b840021e197613 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:16:35 +0000 Subject: [PATCH] Revert "perf(@angular/build): skip semantic affected-file walk when type checking is disabled" This reverts commit 2462709d1b44ef58da0d2f0048041b4748f659a2. --- .../angular/compilation/aot-compilation.ts | 17 +--- .../tests/build/type-check-disabled-emit.ts | 96 ------------------- 2 files changed, 3 insertions(+), 110 deletions(-) delete mode 100644 tests/e2e/tests/build/type-check-disabled-emit.ts diff --git a/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts b/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts index bfe7fddec0e6..73462c2b3475 100644 --- a/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts @@ -10,7 +10,6 @@ import type * as ng from '@angular/compiler-cli'; import assert from 'node:assert'; import { relative } from 'node:path'; import ts from 'typescript'; -import { useTypeChecking } from '../../../utils/environment-options'; import { profileAsync, profileSync } from '../../esbuild/profiling'; import { AngularHostOptions, @@ -176,19 +175,9 @@ export class AotCompilation extends AngularCompilation { } } - // The affected files walk runs a full semantic type-check as a side effect - // (via `getSemanticDiagnosticsOfNextAffectedFile`). Its result is only consumed to - // scope Angular template diagnostics and to force re-emit of TS-affected files in the - // slow TypeScript emit path. When type-checking is disabled, template/semantic - // diagnostics are already suppressed at the consumption layer (see the compiler-plugin - // `diagnoseFiles` mask), and in the isolatedModules fast path emit is per-file, so the - // set is never read. Skip the walk in that case to avoid a discarded semantic pass. - const affectedFiles = - useTypeChecking || useTypeScriptTranspilation - ? profileSync('NG_FIND_AFFECTED', () => - findAffectedFiles(typeScriptProgram, angularCompiler, usingBuildInfo), - ) - : new Set(); + const affectedFiles = profileSync('NG_FIND_AFFECTED', () => + findAffectedFiles(typeScriptProgram, angularCompiler, usingBuildInfo), + ); const componentResourcesDependencies = new Map(); diff --git a/tests/e2e/tests/build/type-check-disabled-emit.ts b/tests/e2e/tests/build/type-check-disabled-emit.ts deleted file mode 100644 index e2b831e1360e..000000000000 --- a/tests/e2e/tests/build/type-check-disabled-emit.ts +++ /dev/null @@ -1,96 +0,0 @@ -import assert from 'node:assert/strict'; -import { readdir } from 'node:fs/promises'; -import { readFile, writeFile } from '../../utils/fs'; -import { execWithEnv, ng } from '../../utils/process'; -import { updateJsonFile } from '../../utils/project'; - -const OUTPUT_DIR = 'dist/test-project/browser'; - -async function readEmittedJs(): Promise> { - const contents = new Map(); - for (const file of (await readdir(OUTPUT_DIR)).sort()) { - if (file.endsWith('.js')) { - contents.set(file, await readFile(`${OUTPUT_DIR}/${file}`)); - } - } - - return contents; -} - -/** - * Verifies that disabling type checking (`NG_BUILD_TYPE_CHECK=0`) does not change the - * emitted JavaScript. When type checking is disabled, the AOT compilation skips the - * semantic "affected files" walk in the isolatedModules fast path (its only remaining - * consumers are diagnostics, which are already suppressed). The emitted output must remain - * byte-for-byte identical to a type-checked build. - */ -export default async function () { - // Disable the persistent disk cache so both builds are cold and independent, keeping the - // comparison free of incremental state carried over between the two runs. - await updateJsonFile('angular.json', (config) => { - config.cli ??= {}; - config.cli.cache = { enabled: false }; - }); - - // A type-only cross-file dependency. The interface is used solely as a type (and in the - // template via `user.name`), so it is fully erased from the emitted JS. This is exactly the - // kind of cross-file type relationship that the affected-file walk would type-check. - await writeFile( - 'src/app/user.model.ts', - 'export interface User {\n id: number;\n name: string;\n}\n', - ); - - // Root component with both a template and the type-only dependency above. - await writeFile( - 'src/app/app.ts', - [ - `import { Component, signal } from '@angular/core';`, - `import { RouterOutlet } from '@angular/router';`, - `import type { User } from './user.model';`, - ``, - `@Component({`, - ` selector: 'app-root',`, - ` imports: [RouterOutlet],`, - ` template: '

Hello, {{ user.name }}

',`, - `})`, - `export class App {`, - ` protected readonly title = signal('test-project');`, - ` protected readonly user: User = { id: 1, name: 'Angular' };`, - `}`, - ``, - ].join('\n'), - ); - - // Baseline: type checking enabled (default). The affected-file walk runs. - await ng('build', '--configuration=development', '--output-hashing=none'); - const withTypeChecking = await readEmittedJs(); - - // Sanity check that the component (and therefore real emit) is present in the baseline, - // so an all-empty comparison cannot pass trivially. - assert.ok( - [...withTypeChecking.values()].some((contents) => contents.includes('Angular')), - 'Expected the baseline build to emit the component.', - ); - - // Type checking disabled: the affected-file walk is skipped in the isolatedModules fast - // path. The emitted output must be byte-for-byte identical to the baseline. - await execWithEnv('ng', ['build', '--configuration=development', '--output-hashing=none'], { - ...process.env, - NG_BUILD_TYPE_CHECK: '0', - }); - const withoutTypeChecking = await readEmittedJs(); - - assert.deepStrictEqual( - [...withoutTypeChecking.keys()], - [...withTypeChecking.keys()], - 'Disabling type checking must not change the set of emitted JS files.', - ); - - for (const [file, baseline] of withTypeChecking) { - assert.strictEqual( - withoutTypeChecking.get(file), - baseline, - `Emitted output for "${file}" changed when type checking was disabled.`, - ); - } -}