Skip to content

Commit 7d1cff0

Browse files
committed
refactor(@angular-devkit/build-angular): allow internal Angular compilation control of diagnostic modes
To support generate diagnostics in varying ways, the internal compilation classes now support a `modes` argument when diagnosing files during a build using the `application` builder. This is not exposed as an option at this time but can be experimented with a build using the `NG_BUILD_TYPE_CHECK` environment variable. This environment variable is not considered part of the public API and may be removed or altered in the future. Its current purpose is to allow profiling of the type checking diagnostics functionality of the build system.
1 parent 898b750 commit 7d1cff0

File tree

8 files changed

+89
-44
lines changed

8 files changed

+89
-44
lines changed

packages/angular_devkit/build_angular/src/tools/esbuild/angular/compilation/angular-compilation.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ export interface EmitFileResult {
2020
dependencies?: readonly string[];
2121
}
2222

23+
export enum DiagnosticModes {
24+
None = 0,
25+
Option = 1 << 0,
26+
Syntactic = 1 << 1,
27+
Semantic = 1 << 2,
28+
All = Option | Syntactic | Semantic,
29+
}
30+
2331
export abstract class AngularCompilation {
2432
static #angularCompilerCliModule?: typeof ng;
2533
static #typescriptModule?: typeof ts;
@@ -71,19 +79,21 @@ export abstract class AngularCompilation {
7179

7280
abstract emitAffectedFiles(): Iterable<EmitFileResult> | Promise<Iterable<EmitFileResult>>;
7381

74-
protected abstract collectDiagnostics():
75-
| Iterable<ts.Diagnostic>
76-
| Promise<Iterable<ts.Diagnostic>>;
82+
protected abstract collectDiagnostics(
83+
modes: DiagnosticModes,
84+
): Iterable<ts.Diagnostic> | Promise<Iterable<ts.Diagnostic>>;
7785

78-
async diagnoseFiles(): Promise<{ errors?: PartialMessage[]; warnings?: PartialMessage[] }> {
86+
async diagnoseFiles(
87+
modes = DiagnosticModes.All,
88+
): Promise<{ errors?: PartialMessage[]; warnings?: PartialMessage[] }> {
7989
const result: { errors?: PartialMessage[]; warnings?: PartialMessage[] } = {};
8090

8191
// Avoid loading typescript until actually needed.
8292
// This allows for avoiding the load of typescript in the main thread when using the parallel compilation.
8393
const typescript = await AngularCompilation.loadTypescript();
8494

8595
await profileAsync('NG_DIAGNOSTICS_TOTAL', async () => {
86-
for (const diagnostic of await this.collectDiagnostics()) {
96+
for (const diagnostic of await this.collectDiagnostics(modes)) {
8797
const message = convertTypeScriptDiagnostic(typescript, diagnostic);
8898
if (diagnostic.category === typescript.DiagnosticCategory.Error) {
8999
(result.errors ??= []).push(message);

packages/angular_devkit/build_angular/src/tools/esbuild/angular/compilation/aot-compilation.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
ensureSourceFileVersions,
1717
} from '../angular-host';
1818
import { createWorkerTransformer } from '../web-worker-transformer';
19-
import { AngularCompilation, EmitFileResult } from './angular-compilation';
19+
import { AngularCompilation, DiagnosticModes, EmitFileResult } from './angular-compilation';
2020

2121
// Temporary deep import for transformer support
2222
// TODO: Move these to a private exports location or move the implementation into this package.
@@ -127,7 +127,7 @@ export class AotCompilation extends AngularCompilation {
127127
return { affectedFiles, compilerOptions, referencedFiles };
128128
}
129129

130-
*collectDiagnostics(): Iterable<ts.Diagnostic> {
130+
*collectDiagnostics(modes: DiagnosticModes): Iterable<ts.Diagnostic> {
131131
assert(this.#state, 'Angular compilation must be initialized prior to collecting diagnostics.');
132132
const {
133133
affectedFiles,
@@ -137,25 +137,39 @@ export class AotCompilation extends AngularCompilation {
137137
typeScriptProgram,
138138
} = this.#state;
139139

140+
const syntactic = modes & DiagnosticModes.Syntactic;
141+
const semantic = modes & DiagnosticModes.Semantic;
142+
140143
// Collect program level diagnostics
141-
yield* typeScriptProgram.getConfigFileParsingDiagnostics();
142-
yield* angularCompiler.getOptionDiagnostics();
143-
yield* typeScriptProgram.getOptionsDiagnostics();
144-
yield* typeScriptProgram.getGlobalDiagnostics();
144+
if (modes & DiagnosticModes.Option) {
145+
yield* typeScriptProgram.getConfigFileParsingDiagnostics();
146+
yield* angularCompiler.getOptionDiagnostics();
147+
yield* typeScriptProgram.getOptionsDiagnostics();
148+
}
149+
if (syntactic) {
150+
yield* typeScriptProgram.getGlobalDiagnostics();
151+
}
145152

146153
// Collect source file specific diagnostics
147154
for (const sourceFile of typeScriptProgram.getSourceFiles()) {
148155
if (angularCompiler.ignoreForDiagnostics.has(sourceFile)) {
149156
continue;
150157
}
151158

152-
// TypeScript will use cached diagnostics for files that have not been
153-
// changed or affected for this build when using incremental building.
154-
yield* profileSync(
155-
'NG_DIAGNOSTICS_SYNTACTIC',
156-
() => typeScriptProgram.getSyntacticDiagnostics(sourceFile),
157-
true,
158-
);
159+
if (syntactic) {
160+
// TypeScript will use cached diagnostics for files that have not been
161+
// changed or affected for this build when using incremental building.
162+
yield* profileSync(
163+
'NG_DIAGNOSTICS_SYNTACTIC',
164+
() => typeScriptProgram.getSyntacticDiagnostics(sourceFile),
165+
true,
166+
);
167+
}
168+
169+
if (!semantic) {
170+
continue;
171+
}
172+
159173
yield* profileSync(
160174
'NG_DIAGNOSTICS_SEMANTIC',
161175
() => typeScriptProgram.getSemanticDiagnostics(sourceFile),

packages/angular_devkit/build_angular/src/tools/esbuild/angular/compilation/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
export { AngularCompilation } from './angular-compilation';
9+
export { AngularCompilation, DiagnosticModes } from './angular-compilation';
1010
export { createAngularCompilation } from './factory';
1111
export { NoopCompilation } from './noop-compilation';

packages/angular_devkit/build_angular/src/tools/esbuild/angular/compilation/jit-compilation.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { profileSync } from '../../profiling';
1313
import { AngularHostOptions, createAngularCompilerHost } from '../angular-host';
1414
import { createJitResourceTransformer } from '../jit-resource-transformer';
1515
import { createWorkerTransformer } from '../web-worker-transformer';
16-
import { AngularCompilation, EmitFileResult } from './angular-compilation';
16+
import { AngularCompilation, DiagnosticModes, EmitFileResult } from './angular-compilation';
1717

1818
class JitCompilationState {
1919
constructor(
@@ -82,18 +82,26 @@ export class JitCompilation extends AngularCompilation {
8282
return { affectedFiles, compilerOptions, referencedFiles };
8383
}
8484

85-
*collectDiagnostics(): Iterable<ts.Diagnostic> {
85+
*collectDiagnostics(modes: DiagnosticModes): Iterable<ts.Diagnostic> {
8686
assert(this.#state, 'Compilation must be initialized prior to collecting diagnostics.');
8787
const { typeScriptProgram } = this.#state;
8888

8989
// Collect program level diagnostics
90-
yield* typeScriptProgram.getConfigFileParsingDiagnostics();
91-
yield* typeScriptProgram.getOptionsDiagnostics();
92-
yield* typeScriptProgram.getGlobalDiagnostics();
93-
yield* profileSync('NG_DIAGNOSTICS_SYNTACTIC', () =>
94-
typeScriptProgram.getSyntacticDiagnostics(),
95-
);
96-
yield* profileSync('NG_DIAGNOSTICS_SEMANTIC', () => typeScriptProgram.getSemanticDiagnostics());
90+
if (modes & DiagnosticModes.Option) {
91+
yield* typeScriptProgram.getConfigFileParsingDiagnostics();
92+
yield* typeScriptProgram.getOptionsDiagnostics();
93+
}
94+
if (modes & DiagnosticModes.Syntactic) {
95+
yield* typeScriptProgram.getGlobalDiagnostics();
96+
yield* profileSync('NG_DIAGNOSTICS_SYNTACTIC', () =>
97+
typeScriptProgram.getSyntacticDiagnostics(),
98+
);
99+
}
100+
if (modes & DiagnosticModes.Semantic) {
101+
yield* profileSync('NG_DIAGNOSTICS_SEMANTIC', () =>
102+
typeScriptProgram.getSemanticDiagnostics(),
103+
);
104+
}
97105
}
98106

99107
emitAffectedFiles(): Iterable<EmitFileResult> {

packages/angular_devkit/build_angular/src/tools/esbuild/angular/compilation/parallel-compilation.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { MessageChannel } from 'node:worker_threads';
1313
import Piscina from 'piscina';
1414
import type { SourceFile } from 'typescript';
1515
import type { AngularHostOptions } from '../angular-host';
16-
import { AngularCompilation, EmitFileResult } from './angular-compilation';
16+
import { AngularCompilation, DiagnosticModes, EmitFileResult } from './angular-compilation';
1717

1818
/**
1919
* An Angular compilation which uses a Node.js Worker thread to load and execute
@@ -122,8 +122,10 @@ export class ParallelCompilation extends AngularCompilation {
122122
throw new Error('Not implemented in ParallelCompilation.');
123123
}
124124

125-
override diagnoseFiles(): Promise<{ errors?: PartialMessage[]; warnings?: PartialMessage[] }> {
126-
return this.#worker.run(undefined, { name: 'diagnose' });
125+
override diagnoseFiles(
126+
modes = DiagnosticModes.All,
127+
): Promise<{ errors?: PartialMessage[]; warnings?: PartialMessage[] }> {
128+
return this.#worker.run(modes, { name: 'diagnose' });
127129
}
128130

129131
override emitAffectedFiles(): Promise<Iterable<EmitFileResult>> {

packages/angular_devkit/build_angular/src/tools/esbuild/angular/compilation/parallel-worker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import assert from 'node:assert';
1111
import { randomUUID } from 'node:crypto';
1212
import { type MessagePort, receiveMessageOnPort } from 'node:worker_threads';
1313
import { SourceFileCache } from '../source-file-cache';
14-
import type { AngularCompilation } from './angular-compilation';
14+
import type { AngularCompilation, DiagnosticModes } from './angular-compilation';
1515
import { AotCompilation } from './aot-compilation';
1616
import { JitCompilation } from './jit-compilation';
1717

@@ -99,13 +99,13 @@ export async function initialize(request: InitRequest) {
9999
};
100100
}
101101

102-
export async function diagnose(): Promise<{
102+
export async function diagnose(modes: DiagnosticModes): Promise<{
103103
errors?: PartialMessage[];
104104
warnings?: PartialMessage[];
105105
}> {
106106
assert(compilation);
107107

108-
const diagnostics = await compilation.diagnoseFiles();
108+
const diagnostics = await compilation.diagnoseFiles(modes);
109109

110110
return diagnostics;
111111
}

packages/angular_devkit/build_angular/src/tools/esbuild/angular/compiler-plugin.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@ import type {
1717
} from 'esbuild';
1818
import assert from 'node:assert';
1919
import * as path from 'node:path';
20-
import { maxWorkers } from '../../../utils/environment-options';
20+
import { maxWorkers, useTypeChecking } from '../../../utils/environment-options';
2121
import { JavaScriptTransformer } from '../javascript-transformer';
2222
import { LoadResultCache, createCachedLoad } from '../load-result-cache';
2323
import { logCumulativeDurations, profileAsync, resetCumulativeDurations } from '../profiling';
2424
import { BundleStylesheetOptions } from '../stylesheets/bundle-options';
2525
import { AngularHostOptions } from './angular-host';
26-
import { AngularCompilation, NoopCompilation, createAngularCompilation } from './compilation';
26+
import {
27+
AngularCompilation,
28+
DiagnosticModes,
29+
NoopCompilation,
30+
createAngularCompilation,
31+
} from './compilation';
2732
import { SharedTSCompilationState, getSharedCompilationState } from './compilation-state';
2833
import { ComponentStylesheetBundler } from './component-stylesheets';
2934
import { FileReferenceTracker } from './file-reference-tracker';
@@ -258,14 +263,6 @@ export function createCompilerPlugin(
258263
return result;
259264
}
260265

261-
const diagnostics = await compilation.diagnoseFiles();
262-
if (diagnostics.errors?.length) {
263-
(result.errors ??= []).push(...diagnostics.errors);
264-
}
265-
if (diagnostics.warnings?.length) {
266-
(result.warnings ??= []).push(...diagnostics.warnings);
267-
}
268-
269266
// Update TypeScript file output cache for all affected files
270267
try {
271268
await profileAsync('NG_EMIT_TS', async () => {
@@ -286,6 +283,16 @@ export function createCompilerPlugin(
286283
});
287284
}
288285

286+
const diagnostics = await compilation.diagnoseFiles(
287+
useTypeChecking ? DiagnosticModes.All : DiagnosticModes.All & ~DiagnosticModes.Semantic,
288+
);
289+
if (diagnostics.errors?.length) {
290+
(result.errors ??= []).push(...diagnostics.errors);
291+
}
292+
if (diagnostics.warnings?.length) {
293+
(result.warnings ??= []).push(...diagnostics.warnings);
294+
}
295+
289296
// Add errors from failed additional results.
290297
// This must be done after emit to capture latest web worker results.
291298
for (const { errors } of additionalResults.values()) {

packages/angular_devkit/build_angular/src/utils/environment-options.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,7 @@ export const debugPerformance = isPresent(debugPerfVariable) && isEnabled(debugP
102102

103103
const watchRootVariable = process.env['NG_BUILD_WATCH_ROOT'];
104104
export const shouldWatchRoot = isPresent(watchRootVariable) && isEnabled(watchRootVariable);
105+
106+
const typeCheckingVariable = process.env['NG_BUILD_TYPE_CHECK'];
107+
export const useTypeChecking =
108+
!isPresent(typeCheckingVariable) || !isDisabled(typeCheckingVariable);

0 commit comments

Comments
 (0)