perf(ivy): basic incremental compilation for ngtsc (#29380)
This commit introduces a mechanism for incremental compilation to the ngtsc compiler. Previously, incremental information was used in the construction of the ts.Program for subsequent compilations, but was not used in ngtsc itself. This commit adds an IncrementalState class, which tracks state between ngtsc compilations. Currently, this supports skipping the TypeScript emit step when the compiler can prove the contents of emit have not changed. This is implemented for @Injectables as well as for files which don't contain any Angular decorated types. These are the only files which can be proven to be safe today. See ngtsc/incremental/README.md for more details. PR Close #29380
This commit is contained in:

committed by
Jason Aden

parent
7316212c1e
commit
7041e61562
@ -11,6 +11,7 @@ ts_library(
|
||||
"//packages/compiler",
|
||||
"//packages/compiler-cli/src/ngtsc/diagnostics",
|
||||
"//packages/compiler-cli/src/ngtsc/imports",
|
||||
"//packages/compiler-cli/src/ngtsc/incremental",
|
||||
"//packages/compiler-cli/src/ngtsc/perf",
|
||||
"//packages/compiler-cli/src/ngtsc/reflection",
|
||||
"//packages/compiler-cli/src/ngtsc/translator",
|
||||
|
@ -110,6 +110,7 @@ export interface AnalysisOutput<A> {
|
||||
diagnostics?: ts.Diagnostic[];
|
||||
factorySymbolName?: string;
|
||||
typeCheck?: boolean;
|
||||
allowSkipAnalysisAndEmit?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,6 +11,7 @@ import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||
import {ImportRewriter} from '../../imports';
|
||||
import {IncrementalState} from '../../incremental';
|
||||
import {PerfRecorder} from '../../perf';
|
||||
import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration, reflectNameOfDeclaration} from '../../reflection';
|
||||
import {TypeCheckContext} from '../../typecheck';
|
||||
@ -76,7 +77,8 @@ export class IvyCompilation {
|
||||
constructor(
|
||||
private handlers: DecoratorHandler<any, any>[], private checker: ts.TypeChecker,
|
||||
private reflector: ReflectionHost, private importRewriter: ImportRewriter,
|
||||
private perf: PerfRecorder, private sourceToFactorySymbols: Map<string, Set<string>>|null) {}
|
||||
private incrementalState: IncrementalState, private perf: PerfRecorder,
|
||||
private sourceToFactorySymbols: Map<string, Set<string>>|null) {}
|
||||
|
||||
|
||||
get exportStatements(): Map<string, Map<string, [string, string]>> { return this.reexportMap; }
|
||||
@ -170,6 +172,10 @@ export class IvyCompilation {
|
||||
private analyze(sf: ts.SourceFile, preanalyze: boolean): Promise<void>|undefined {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
// This flag begins as true for the file. If even one handler is matched and does not explicitly
|
||||
// state that analysis/emit can be skipped, then the flag will be set to false.
|
||||
let allowSkipAnalysisAndEmit = true;
|
||||
|
||||
const analyzeClass = (node: ClassDeclaration): void => {
|
||||
const ivyClass = this.detectHandlersForClass(node);
|
||||
|
||||
@ -197,6 +203,11 @@ export class IvyCompilation {
|
||||
this.sourceToFactorySymbols.get(sf.fileName) !.add(match.analyzed.factorySymbolName);
|
||||
}
|
||||
|
||||
// Update the allowSkipAnalysisAndEmit flag - it will only remain true if match.analyzed
|
||||
// also explicitly specifies a value of true for the flag.
|
||||
allowSkipAnalysisAndEmit =
|
||||
allowSkipAnalysisAndEmit && (!!match.analyzed.allowSkipAnalysisAndEmit);
|
||||
|
||||
} catch (err) {
|
||||
if (err instanceof FatalDiagnosticError) {
|
||||
this._diagnostics.push(err.toDiagnostic());
|
||||
@ -239,9 +250,19 @@ export class IvyCompilation {
|
||||
|
||||
visit(sf);
|
||||
|
||||
const updateIncrementalState = () => {
|
||||
if (allowSkipAnalysisAndEmit) {
|
||||
this.incrementalState.markFileAsSafeToSkipEmitIfUnchanged(sf);
|
||||
}
|
||||
};
|
||||
|
||||
if (preanalyze && promises.length > 0) {
|
||||
return Promise.all(promises).then(() => undefined);
|
||||
return Promise.all(promises).then(() => {
|
||||
updateIncrementalState();
|
||||
return undefined;
|
||||
});
|
||||
} else {
|
||||
updateIncrementalState();
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user