From 29b7eb4287a82e0620e8b949c242d3112b5e6eb1 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:12:47 -0400 Subject: [PATCH] refactor(@angular/build): unify configuration loading and option transformations in TypeScriptCompilation Both AotCompilation and JitCompilation previously executed identical logic during initialization to read tsconfig.json using readConfiguration and immediately pass the options to transformCompilerOptions. The loadConfiguration method on TypeScriptCompilation now accepts optional compilerOptionOverrides and applies transformCompilerOptions directly. This unifies configuration reading and option transformations into a single cohesive step, eliminates duplicate setup boilerplate across AotCompilation and JitCompilation, and reduces unnecessary direct imports of transformCompilerOptions. --- .../compilation/angular-compilation_spec.ts | 32 +++++++++++++++++- .../angular/compilation/aot-compilation.ts | 15 +++------ .../angular/compilation/jit-compilation.ts | 15 +++------ .../compilation/typescript-compilation.ts | 33 +++++++++++++++++-- 4 files changed, 70 insertions(+), 25 deletions(-) diff --git a/packages/angular/build/src/tools/angular/compilation/angular-compilation_spec.ts b/packages/angular/build/src/tools/angular/compilation/angular-compilation_spec.ts index a867f666c349..71eacd562259 100644 --- a/packages/angular/build/src/tools/angular/compilation/angular-compilation_spec.ts +++ b/packages/angular/build/src/tools/angular/compilation/angular-compilation_spec.ts @@ -8,7 +8,7 @@ import ts from 'typescript'; import type { AngularHostOptions } from '../angular-host'; -import { transformCompilerOptions } from './compiler-options'; +import { CompilerOptionOverrides, transformCompilerOptions } from './compiler-options'; import { TypeScriptCompilation } from './typescript-compilation'; import { AngularCompilation, @@ -90,6 +90,10 @@ describe('AngularCompilation', () => { public getCachedSourceFiles(): Map { return this.sourceFiles; } + + public async testLoadConfiguration(tsconfig: string, overrides?: CompilerOptionOverrides) { + return this.loadConfiguration(tsconfig, overrides); + } } it('collects and converts diagnostics categorized by error and warning', async () => { @@ -134,6 +138,32 @@ describe('AngularCompilation', () => { expect(compilerCli).toBeDefined(); expect(typeof compilerCli.readConfiguration).toBe('function'); }); + + it('loads configuration and applies compiler option transformations', async () => { + const compilation = new MockTypeScriptCompilation(); + const mockReadConfig = jasmine.createSpy('readConfiguration').and.returnValue({ + options: { target: ts.ScriptTarget.ES2020 }, + rootNames: ['/src/main.ts'], + errors: [], + }); + spyOn(TypeScriptCompilation, 'loadCompilerCli').and.resolveTo({ + readConfiguration: mockReadConfig, + } as unknown as typeof import('@angular/compiler-cli')); + + const result = await compilation.testLoadConfiguration('tsconfig.json', { sourcemap: true }); + + expect(mockReadConfig).toHaveBeenCalledWith( + 'tsconfig.json', + jasmine.objectContaining({ + suppressOutputPathCheck: true, + outDir: undefined, + }), + ); + expect(result.rootNames).toEqual(['/src/main.ts']); + expect(result.compilerOptions.target).toBe(ts.ScriptTarget.ES2022); + expect(result.compilerOptions.inlineSources).toBe(true); + expect(result.warnings.length).toBeGreaterThan(0); + }); }); describe('createAngularCompilation', () => { 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 9fd160774cf7..ca74e721ba7d 100644 --- a/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts @@ -7,7 +7,6 @@ */ import type * as ng from '@angular/compiler-cli'; -import type { PartialMessage } from 'esbuild'; import assert from 'node:assert'; import { relative } from 'node:path'; import ts from 'typescript'; @@ -26,7 +25,7 @@ import { DiagnosticModes, type EmitFileResult, } from './angular-compilation'; -import { CompilerOptionOverrides, transformCompilerOptions } from './compiler-options'; +import type { CompilerOptionOverrides } from './compiler-options'; import { collectHmrCandidates } from './hmr-candidates'; import { TypeScriptCompilation } from './typescript-compilation'; import { printSourceFileWithMap } from './typescript-printer'; @@ -72,17 +71,11 @@ export class AotCompilation extends TypeScriptCompilation { // Load the compiler configuration and transform as needed const { - options: originalCompilerOptions, + compilerOptions, rootNames, errors: configurationDiagnostics, - } = await this.loadConfiguration(tsconfig); - - const { compilerOptions, warnings } = transformCompilerOptions( - ts, - originalCompilerOptions, - compilerOptionOverrides, - tsconfig, - ); + warnings, + } = await this.loadConfiguration(tsconfig, compilerOptionOverrides); const useTypeScriptTranspilation = (compilerOptions['_useTypeScriptTranspilation'] as boolean | undefined) ?? diff --git a/packages/angular/build/src/tools/angular/compilation/jit-compilation.ts b/packages/angular/build/src/tools/angular/compilation/jit-compilation.ts index fee636219be0..233f5977e5a6 100644 --- a/packages/angular/build/src/tools/angular/compilation/jit-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/jit-compilation.ts @@ -7,7 +7,6 @@ */ import type * as ng from '@angular/compiler-cli'; -import type { PartialMessage } from 'esbuild'; import assert from 'node:assert'; import ts from 'typescript'; import { profileSync } from '../../esbuild/profiling'; @@ -20,7 +19,7 @@ import { DiagnosticModes, type EmitFileResult, } from './angular-compilation'; -import { CompilerOptionOverrides, transformCompilerOptions } from './compiler-options'; +import type { CompilerOptionOverrides } from './compiler-options'; import { TypeScriptCompilation } from './typescript-compilation'; class JitCompilationState { @@ -51,17 +50,11 @@ export class JitCompilation extends TypeScriptCompilation { // Load the compiler configuration and transform as needed const { - options: originalCompilerOptions, + compilerOptions, rootNames, errors: configurationDiagnostics, - } = await this.loadConfiguration(tsconfig); - - const { compilerOptions, warnings } = transformCompilerOptions( - ts, - originalCompilerOptions, - compilerOptionOverrides, - tsconfig, - ); + warnings, + } = await this.loadConfiguration(tsconfig, compilerOptionOverrides); if (hostOptions.modifiedFiles) { this.invalidateFiles(hostOptions.modifiedFiles); diff --git a/packages/angular/build/src/tools/angular/compilation/typescript-compilation.ts b/packages/angular/build/src/tools/angular/compilation/typescript-compilation.ts index 970041f916fc..71061031f3a4 100644 --- a/packages/angular/build/src/tools/angular/compilation/typescript-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/typescript-compilation.ts @@ -12,8 +12,16 @@ import ts from 'typescript'; import { toPosixPath } from '../../../utils/path'; import { profileAsync, profileSync } from '../../esbuild/profiling'; import { AngularCompilation, DiagnosticModes } from './angular-compilation'; +import { type CompilerOptionOverrides, transformCompilerOptions } from './compiler-options'; import { convertTypeScriptDiagnostic } from './diagnostics'; +export interface TransformedConfiguration { + compilerOptions: ng.CompilerOptions; + rootNames: string[]; + errors: ts.Diagnostic[]; + warnings: PartialMessage[]; +} + export abstract class TypeScriptCompilation extends AngularCompilation { static #angularCompilerCliModule?: typeof ng; @@ -23,10 +31,17 @@ export abstract class TypeScriptCompilation extends AngularCompilation { return TypeScriptCompilation.#angularCompilerCliModule; } - protected async loadConfiguration(tsconfig: string): Promise { + protected async loadConfiguration( + tsconfig: string, + compilerOptionOverrides?: CompilerOptionOverrides, + ): Promise { const { readConfiguration } = await TypeScriptCompilation.loadCompilerCli(); - return profileSync('NG_READ_CONFIG', () => + const { + options: originalCompilerOptions, + rootNames, + errors, + } = profileSync('NG_READ_CONFIG', () => readConfiguration(tsconfig, { // Angular specific configuration defaults and overrides to ensure a functioning compilation. suppressOutputPathCheck: true, @@ -44,6 +59,20 @@ export abstract class TypeScriptCompilation extends AngularCompilation { removeComments: false, }), ); + + const { compilerOptions, warnings } = transformCompilerOptions( + ts, + originalCompilerOptions, + compilerOptionOverrides, + tsconfig, + ); + + return { + compilerOptions, + rootNames, + errors, + warnings, + }; } protected readonly sourceFiles = new Map();