Skip to content

Commit 79b5e18

Browse files
alxhubSplaktar
authored andcommitted
refactor(compiler-cli): introduce the TemplateTypeChecker abstraction (angular#38105)
This commit significantly refactors the 'typecheck' package to introduce a new abstraction, the `TemplateTypeChecker`. To achieve this: * a 'typecheck:api' package is introduced, containing common interfaces that consumers of the template type-checking infrastructure can depend on without incurring a dependency on the template type-checking machinery as a whole. * interfaces for `TemplateTypeChecker` and `TypeCheckContext` are introduced which contain the abstract operations supported by the implementation classes `TemplateTypeCheckerImpl` and `TypeCheckContextImpl` respectively. * the `TemplateTypeChecker` interface supports diagnostics on a whole program basis to start with, but the implementation is purposefully designed to support incremental diagnostics at a per-file or per-component level. * `TemplateTypeChecker` supports direct access to the type check block of a component. * the testing utility is refactored to be a lot more useful, and new tests are added for the new abstraction. PR Close angular#38105
1 parent b4b453d commit 79b5e18

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+694
-313
lines changed

packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.ts

+2
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,6 @@ class NoIncrementalBuild implements IncrementalBuild<any, any> {
9090
priorTypeCheckingResultsFor(): null {
9191
return null;
9292
}
93+
94+
recordSuccessfulTypeCheck(): void {}
9395
}

packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ ts_library(
2222
"//packages/compiler-cli/src/ngtsc/scope",
2323
"//packages/compiler-cli/src/ngtsc/shims:api",
2424
"//packages/compiler-cli/src/ngtsc/transform",
25-
"//packages/compiler-cli/src/ngtsc/typecheck",
25+
"//packages/compiler-cli/src/ngtsc/typecheck/api",
2626
"//packages/compiler-cli/src/ngtsc/util",
2727
"@npm//@types/node",
2828
"@npm//typescript",

packages/compiler-cli/src/ngtsc/annotations/src/component.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {EnumValue, PartialEvaluator} from '../../partial_evaluator';
2121
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
2222
import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope';
2323
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
24-
import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck';
24+
import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api';
2525
import {tsSourceMapBug29300Fixed} from '../../util/src/ts_source_map_bug_29300';
2626
import {SubsetOfKeys} from '../../util/src/typescript';
2727

@@ -426,10 +426,10 @@ export class ComponentDecoratorHandler implements
426426
schemas = scope.schemas;
427427
}
428428

429-
const bound = new R3TargetBinder(matcher).bind({template: meta.template.diagNodes});
429+
const binder = new R3TargetBinder(matcher);
430430
ctx.addTemplate(
431-
new Reference(node), bound, pipes, schemas, meta.template.sourceMapping,
432-
meta.template.file);
431+
new Reference(node), binder, meta.template.diagNodes, pipes, schemas,
432+
meta.template.sourceMapping, meta.template.file);
433433
}
434434

435435
resolve(node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>):

packages/compiler-cli/src/ngtsc/core/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ ts_library(
3333
"//packages/compiler-cli/src/ngtsc/switch",
3434
"//packages/compiler-cli/src/ngtsc/transform",
3535
"//packages/compiler-cli/src/ngtsc/typecheck",
36+
"//packages/compiler-cli/src/ngtsc/typecheck/api",
3637
"//packages/compiler-cli/src/ngtsc/util",
3738
"@npm//typescript",
3839
],

packages/compiler-cli/src/ngtsc/core/src/compiler.ts

+8-9
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecorato
1313
import {CycleAnalyzer, ImportGraph} from '../../cycles';
1414
import {ErrorCode, ngErrorCode} from '../../diagnostics';
1515
import {checkForPrivateExports, ReferenceGraph} from '../../entry_point';
16-
import {getSourceFileOrError, LogicalFileSystem} from '../../file_system';
16+
import {LogicalFileSystem} from '../../file_system';
1717
import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports';
1818
import {IncrementalBuildStrategy, IncrementalDriver} from '../../incremental';
1919
import {generateAnalysis, IndexedComponent, IndexingContext} from '../../indexer';
@@ -28,7 +28,8 @@ import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeRe
2828
import {generatedFactoryTransform} from '../../shims';
2929
import {ivySwitchTransform} from '../../switch';
3030
import {aliasTransformFactory, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform';
31-
import {isTemplateDiagnostic, TemplateTypeChecker, TypeCheckContext, TypeCheckingConfig, TypeCheckingProgramStrategy} from '../../typecheck';
31+
import {isTemplateDiagnostic, TemplateTypeCheckerImpl} from '../../typecheck';
32+
import {TemplateTypeChecker, TypeCheckingConfig, TypeCheckingProgramStrategy} from '../../typecheck/api';
3233
import {getSourceFileOrNull, isDtsPath, resolveModuleName} from '../../util/src/typescript';
3334
import {LazyRoute, NgCompilerAdapter, NgCompilerOptions} from '../api';
3435

@@ -209,6 +210,10 @@ export class NgCompiler {
209210
return this.nextProgram;
210211
}
211212

213+
getTemplateTypeChecker(): TemplateTypeChecker {
214+
return this.ensureAnalyzed().templateTypeChecker;
215+
}
216+
212217
/**
213218
* Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`)
214219
* asynchronously.
@@ -494,12 +499,6 @@ export class NgCompiler {
494499

495500
const compilation = this.ensureAnalyzed();
496501

497-
// Execute the typeCheck phase of each decorator in the program.
498-
const prepSpan = this.perfRecorder.start('typeCheckPrep');
499-
const results = compilation.templateTypeChecker.refresh();
500-
this.incrementalDriver.recordSuccessfulTypeCheck(results.perFileData);
501-
this.perfRecorder.stop(prepSpan);
502-
503502
// Get the diagnostics.
504503
const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
505504
const diagnostics: ts.Diagnostic[] = [];
@@ -734,7 +733,7 @@ export class NgCompiler {
734733
handlers, reflector, this.perfRecorder, this.incrementalDriver,
735734
this.options.compileNonExportedClasses !== false, dtsTransforms);
736735

737-
const templateTypeChecker = new TemplateTypeChecker(
736+
const templateTypeChecker = new TemplateTypeCheckerImpl(
738737
this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler,
739738
this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver);
740739

packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import * as ts from 'typescript';
1111
import {absoluteFrom as _, FileSystem, getFileSystem, getSourceFileOrError, NgtscCompilerHost, setFileSystem} from '../../file_system';
1212
import {runInEachFileSystem} from '../../file_system/testing';
1313
import {NoopIncrementalBuildStrategy} from '../../incremental';
14-
import {ReusedProgramStrategy} from '../../typecheck/src/augmented_program';
14+
import {ReusedProgramStrategy} from '../../typecheck';
1515
import {NgCompilerOptions} from '../api';
1616
import {NgCompiler} from '../src/compiler';
1717
import {NgCompilerHost} from '../src/host';

packages/compiler-cli/src/ngtsc/incremental/api.ts

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ export interface IncrementalBuild<AnalysisT, FileTypeCheckDataT> {
2727
* Retrieve the prior type-checking work, if any, that's been done for the given source file.
2828
*/
2929
priorTypeCheckingResultsFor(fileSf: ts.SourceFile): FileTypeCheckDataT|null;
30+
31+
/**
32+
* Reports that template type-checking has completed successfully, with a map of type-checking
33+
* data for each user file which can be reused in a future incremental iteration.
34+
*/
35+
recordSuccessfulTypeCheck(results: Map<AbsoluteFsPath, FileTypeCheckDataT>): void;
3036
}
3137

3238
/**

packages/compiler-cli/src/ngtsc/incremental/src/noop.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ import {IncrementalBuild} from '../api';
1111
export const NOOP_INCREMENTAL_BUILD: IncrementalBuild<any, any> = {
1212
priorWorkFor: () => null,
1313
priorTypeCheckingResultsFor: () => null,
14+
recordSuccessfulTypeCheck: () => {},
1415
};

packages/compiler-cli/src/ngtsc/incremental/src/state.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as ts from 'typescript';
1010

1111
import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath} from '../../file_system';
1212
import {ClassRecord, TraitCompiler} from '../../transform';
13-
import {FileTypeCheckingData} from '../../typecheck/src/context';
13+
import {FileTypeCheckingData} from '../../typecheck/src/checker';
1414
import {IncrementalBuild} from '../api';
1515

1616
import {FileDependencyGraph} from './dependency_tracking';

packages/compiler-cli/src/ngtsc/program.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {ReusedProgramStrategy} from './typecheck';
2828
* command-line main() function or the Angular CLI.
2929
*/
3030
export class NgtscProgram implements api.Program {
31-
private compiler: NgCompiler;
31+
readonly compiler: NgCompiler;
3232

3333
/**
3434
* The primary TypeScript program, which is used for analysis and emit.

packages/compiler-cli/src/ngtsc/scope/BUILD.bazel

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ ts_library(
1313
"//packages/compiler-cli/src/ngtsc/imports",
1414
"//packages/compiler-cli/src/ngtsc/metadata",
1515
"//packages/compiler-cli/src/ngtsc/reflection",
16-
"//packages/compiler-cli/src/ngtsc/typecheck",
1716
"//packages/compiler-cli/src/ngtsc/util",
1817
"@npm//typescript",
1918
],

packages/compiler-cli/src/ngtsc/transform/BUILD.bazel

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ ts_library(
1818
"//packages/compiler-cli/src/ngtsc/perf",
1919
"//packages/compiler-cli/src/ngtsc/reflection",
2020
"//packages/compiler-cli/src/ngtsc/translator",
21-
"//packages/compiler-cli/src/ngtsc/typecheck",
21+
"//packages/compiler-cli/src/ngtsc/typecheck/api",
2222
"//packages/compiler-cli/src/ngtsc/util",
2323
"@npm//typescript",
2424
],

packages/compiler-cli/src/ngtsc/transform/src/api.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {Reexport} from '../../imports';
1313
import {IndexingContext} from '../../indexer';
1414
import {ClassDeclaration, Decorator} from '../../reflection';
1515
import {ImportManager} from '../../translator';
16-
import {TypeCheckContext} from '../../typecheck';
16+
import {TypeCheckContext} from '../../typecheck/api';
1717

1818
export enum HandlerPrecedence {
1919
/**

packages/compiler-cli/src/ngtsc/transform/src/compilation.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {IncrementalBuild} from '../../incremental/api';
1414
import {IndexingContext} from '../../indexer';
1515
import {PerfRecorder} from '../../perf';
1616
import {ClassDeclaration, Decorator, ReflectionHost} from '../../reflection';
17-
import {ProgramTypeCheckAdapter, TypeCheckContext} from '../../typecheck';
17+
import {ProgramTypeCheckAdapter, TypeCheckContext} from '../../typecheck/api';
1818
import {getSourceFile, isExported} from '../../util/src/typescript';
1919

2020
import {AnalysisOutput, CompileResult, DecoratorHandler, HandlerFlags, HandlerPrecedence, ResolveResult} from './api';

packages/compiler-cli/src/ngtsc/typecheck/BUILD.bazel

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ package(default_visibility = ["//visibility:public"])
44

55
ts_library(
66
name = "typecheck",
7-
srcs = glob(["**/*.ts"]),
7+
srcs = glob(
8+
["**/*.ts"],
9+
),
810
deps = [
911
"//packages:types",
1012
"//packages/compiler",
@@ -17,6 +19,7 @@ ts_library(
1719
"//packages/compiler-cli/src/ngtsc/shims",
1820
"//packages/compiler-cli/src/ngtsc/shims:api",
1921
"//packages/compiler-cli/src/ngtsc/translator",
22+
"//packages/compiler-cli/src/ngtsc/typecheck/api",
2023
"//packages/compiler-cli/src/ngtsc/util",
2124
"@npm//@types/node",
2225
"@npm//typescript",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
load("//tools:defaults.bzl", "ts_library")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_library(
6+
name = "api",
7+
srcs = glob(["**/*.ts"]),
8+
module_name = "@angular/compiler-cli/src/ngtsc/typecheck/api",
9+
deps = [
10+
"//packages:types",
11+
"//packages/compiler",
12+
"//packages/compiler-cli/src/ngtsc/file_system",
13+
"//packages/compiler-cli/src/ngtsc/imports",
14+
"//packages/compiler-cli/src/ngtsc/metadata",
15+
"//packages/compiler-cli/src/ngtsc/reflection",
16+
"@npm//typescript",
17+
],
18+
)

packages/compiler-cli/src/ngtsc/typecheck/src/api.ts renamed to packages/compiler-cli/src/ngtsc/typecheck/api/api.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {AbsoluteFsPath} from '../../file_system';
1313
import {Reference} from '../../imports';
1414
import {TemplateGuardMeta} from '../../metadata';
1515
import {ClassDeclaration} from '../../reflection';
16-
import {ComponentToShimMappingStrategy} from './context';
1716

1817

1918
/**
@@ -278,6 +277,25 @@ export interface ExternalTemplateSourceMapping {
278277
templateUrl: string;
279278
}
280279

280+
/**
281+
* Abstracts the operation of determining which shim file will host a particular component's
282+
* template type-checking code.
283+
*
284+
* Different consumers of the type checking infrastructure may choose different approaches to
285+
* optimize for their specific use case (for example, the command-line compiler optimizes for
286+
* efficient `ts.Program` reuse in watch mode).
287+
*/
288+
export interface ComponentToShimMappingStrategy {
289+
/**
290+
* Given a component, determine a path to the shim file into which that component's type checking
291+
* code will be generated.
292+
*
293+
* A major constraint is that components in different input files must not share the same shim
294+
* file. The behavior of the template type-checking system is undefined if this is violated.
295+
*/
296+
shimPathForComponent(node: ts.ClassDeclaration): AbsoluteFsPath;
297+
}
298+
281299
/**
282300
* Strategy used to manage a `ts.Program` which contains template type-checking code and update it
283301
* over time.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {TmplAstNode} from '@angular/compiler';
10+
11+
import * as ts from 'typescript';
12+
13+
/**
14+
* Interface to the Angular Template Type Checker to extract diagnostics and intelligence from the
15+
* compiler's understanding of component templates.
16+
*
17+
* This interface is analogous to TypeScript's own `ts.TypeChecker` API.
18+
*
19+
* In general, this interface supports two kinds of operations:
20+
* - updating Type Check Blocks (TCB)s that capture the template in the form of TypeScript code
21+
* - querying information about available TCBs, including diagnostics
22+
*
23+
* Once a TCB is available, information about it can be queried. If no TCB is available to answer a
24+
* query, depending on the method either `null` will be returned or an error will be thrown.
25+
*/
26+
export interface TemplateTypeChecker {
27+
/**
28+
* Get all `ts.Diagnostic`s currently available for the given `ts.SourceFile`.
29+
*
30+
* This method will fail (throw) if there are components within the `ts.SourceFile` that do not
31+
* have TCBs available.
32+
*/
33+
getDiagnosticsForFile(sf: ts.SourceFile): ts.Diagnostic[];
34+
35+
/**
36+
* Retrieve the top-level node representing the TCB for the given component.
37+
*
38+
* This can return `null` if there is no TCB available for the component.
39+
*
40+
* This method always runs in `OptimizeFor.SingleFile` mode.
41+
*/
42+
getTypeCheckBlock(component: ts.ClassDeclaration): ts.Node|null;
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {ParseSourceFile, R3TargetBinder, SchemaMetadata, TmplAstNode} from '@angular/compiler';
10+
import * as ts from 'typescript';
11+
12+
import {Reference} from '../../imports';
13+
import {ClassDeclaration} from '../../reflection';
14+
15+
import {TemplateSourceMapping, TypeCheckableDirectiveMeta} from './api';
16+
17+
/**
18+
* A currently pending type checking operation, into which templates for type-checking can be
19+
* registered.
20+
*/
21+
export interface TypeCheckContext {
22+
/**
23+
* Register a template to potentially be type-checked.
24+
*
25+
* Templates registered via `addTemplate` are available for checking, but might be skipped if
26+
* checking of that component is not required. This can happen for a few reasons, including if
27+
* the component was previously checked and the prior results are still valid.
28+
*
29+
* @param ref a `Reference` to the component class which yielded this template.
30+
* @param binder an `R3TargetBinder` which encapsulates the scope of this template, including all
31+
* available directives.
32+
* @param template the original template AST of this component.
33+
* @param pipes a `Map` of pipes available within the scope of this template.
34+
* @param schemas any schemas which apply to this template.
35+
* @param sourceMapping a `TemplateSourceMapping` instance which describes the origin of the
36+
* template text described by the AST.
37+
* @param file the `ParseSourceFile` associated with the template.
38+
*/
39+
addTemplate(
40+
ref: Reference<ClassDeclaration<ts.ClassDeclaration>>,
41+
binder: R3TargetBinder<TypeCheckableDirectiveMeta>, template: TmplAstNode[],
42+
pipes: Map<string, Reference<ClassDeclaration<ts.ClassDeclaration>>>,
43+
schemas: SchemaMetadata[], sourceMapping: TemplateSourceMapping, file: ParseSourceFile): void;
44+
}
45+
46+
/**
47+
* Interface to trigger generation of type-checking code for a program given a new
48+
* `TypeCheckContext`.
49+
*/
50+
export interface ProgramTypeCheckAdapter {
51+
typeCheck(sf: ts.SourceFile, ctx: TypeCheckContext): void;
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export * from './api';
10+
export * from './checker';
11+
export * from './context';

packages/compiler-cli/src/ngtsc/typecheck/index.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
export * from './src/api';
109
export {ReusedProgramStrategy} from './src/augmented_program';
11-
export {TemplateTypeChecker, ProgramTypeCheckAdapter} from './src/checker';
12-
export {TypeCheckContext} from './src/context';
13-
export {TemplateDiagnostic, isTemplateDiagnostic} from './src/diagnostics';
14-
export {TypeCheckShimGenerator} from './src/shim';
10+
export {FileTypeCheckingData, TemplateTypeCheckerImpl} from './src/checker';
11+
export {TypeCheckContextImpl} from './src/context';
12+
export {isTemplateDiagnostic, TemplateDiagnostic} from './src/diagnostics';
1513
export {TypeCheckProgramHost} from './src/host';
14+
export {TypeCheckShimGenerator} from './src/shim';
1615
export {typeCheckFilePath} from './src/type_check_file';

packages/compiler-cli/src/ngtsc/typecheck/src/augmented_program.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import * as ts from 'typescript';
1010

1111
import {absoluteFromSourceFile, AbsoluteFsPath} from '../../file_system';
1212
import {retagAllTsFiles, untagAllTsFiles} from '../../shims';
13+
import {TypeCheckingProgramStrategy, UpdateMode} from '../api';
1314

14-
import {TypeCheckingProgramStrategy, UpdateMode} from './api';
1515
import {TypeCheckProgramHost} from './host';
1616
import {TypeCheckShimGenerator} from './shim';
1717

0 commit comments

Comments
 (0)