diff --git a/packages/compiler-cli/src/ngtsc/core/README.md b/packages/compiler-cli/src/ngtsc/core/README.md index 6f41d0a056..b21d4f44b9 100644 --- a/packages/compiler-cli/src/ngtsc/core/README.md +++ b/packages/compiler-cli/src/ngtsc/core/README.md @@ -22,11 +22,22 @@ A compiler which integrates Angular compilation into this process follows a very 1. A `ts.CompilerHost` is created. 2. That `ts.CompilerHost` is wrapped in an `NgCompilerHost`, which adds Angular specific files to the compilation. 3. A `ts.Program` is created from the `NgCompilerHost` and its augmented set of root files. -4. An `NgCompiler` is created using the `ts.Program`. +4. A `CompilationTicket` is created, optionally incorporating any state from a previous compilation run. +4. An `NgCompiler` is created using the `CompilationTicket`. 5. Diagnostics can be gathered from the `ts.Program` as normal, as well as from the `NgCompiler`. 6. Prior to `emit`, `NgCompiler.prepareEmit` is called to retrieve the Angular transformers which need to be fed to `ts.Program.emit`. 7. `emit` is called on the `ts.Program` with the Angular transformers from above, which produces JavaScript code with Angular extensions. +# `NgCompiler` and incremental compilation + +The Angular compiler is capable of incremental compilation, where information from a previous compilation is used to accelerate the next compilation. During compilation, the compiler produces two major kinds of information: local information (such as component and directive metadata) and global information (such as reified NgModule scopes). Incremental compilation is managed in two ways: + +1. For most changes, a new `NgCompiler` can selectively inherit local information from a previous instance, and only needs to recompute it where an underlying TypeScript file has change. Global information is always recomputed from scratch in this case. + +2. For specific changes, such as those in component resources, an `NgCompiler` can be reused in its entirety, and updated to incorporate the effects of such changes without needing to recompute any other information. + +Note that these two modes differ in terms of whether a new `NgCompiler` instance is needed or whether a previous one can reused. To prevent leaking this implementation complexity and shield consumers from having to manage the lifecycle of `NgCompiler` so specifically, this process is abstracted via `CompilationTicket`s. Consumers first obtain a `CompilationTicket` (depending on the nature of the incoming change), and then use this ticket to retrieve an `NgCompiler` instance. In creating the `CompilationTicket`, the compiler can decide whether to reuse an old `NgCompiler` instance or to create a new one. + ## Asynchronous compilation In some compilation environments (such as the Webpack-driven compilation inside the Angular CLI), various inputs to the compilation are only producible in an asynchronous fashion. For example, SASS compilation of `styleUrls` that link to SASS files requires spawning a child Webpack compilation. To support this, Angular has an asynchronous interface for loading such resources. diff --git a/packages/compiler-cli/src/ngtsc/core/index.ts b/packages/compiler-cli/src/ngtsc/core/index.ts index 475e18145a..a9eba8a032 100644 --- a/packages/compiler-cli/src/ngtsc/core/index.ts +++ b/packages/compiler-cli/src/ngtsc/core/index.ts @@ -6,5 +6,5 @@ * found in the LICENSE file at https://angular.io/license */ -export {NgCompiler} from './src/compiler'; -export {NgCompilerHost} from './src/host'; +export * from './src/compiler'; +export {NgCompilerHost} from './src/host'; \ No newline at end of file diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 840d52f500..687872955d 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -55,6 +55,132 @@ interface LazyCompilationState { resourceRegistry: ResourceRegistry; } + + +/** + * Discriminant type for a `CompilationTicket`. + */ +export enum CompilationTicketKind { + Fresh, + IncrementalTypeScript, +} + +/** + * Begin an Angular compilation operation from scratch. + */ +export interface FreshCompilationTicket { + kind: CompilationTicketKind.Fresh; + options: NgCompilerOptions; + incrementalBuildStrategy: IncrementalBuildStrategy; + typeCheckingProgramStrategy: TypeCheckingProgramStrategy; + enableTemplateTypeChecker: boolean; + usePoisonedData: boolean; + tsProgram: ts.Program; +} + +/** + * Begin an Angular compilation operation that incorporates changes to TypeScript code. + */ +export interface IncrementalTypeScriptCompilationTicket { + kind: CompilationTicketKind.IncrementalTypeScript; + options: NgCompilerOptions; + oldProgram: ts.Program; + newProgram: ts.Program; + incrementalBuildStrategy: IncrementalBuildStrategy; + typeCheckingProgramStrategy: TypeCheckingProgramStrategy; + newDriver: IncrementalDriver; + enableTemplateTypeChecker: boolean; + usePoisonedData: boolean; +} + +/** + * A request to begin Angular compilation, either starting from scratch or from a known prior state. + * + * `CompilationTicket`s are used to initialize (or update) an `NgCompiler` instance, the core of the + * Angular compiler. They abstract the starting state of compilation and allow `NgCompiler` to be + * managed independently of any incremental compilation lifecycle. + */ +export type CompilationTicket = FreshCompilationTicket|IncrementalTypeScriptCompilationTicket; + +/** + * Create a `CompilationTicket` for a brand new compilation, using no prior state. + */ +export function freshCompilationTicket( + tsProgram: ts.Program, options: NgCompilerOptions, + incrementalBuildStrategy: IncrementalBuildStrategy, + typeCheckingProgramStrategy: TypeCheckingProgramStrategy, enableTemplateTypeChecker: boolean, + usePoisonedData: boolean): CompilationTicket { + return { + kind: CompilationTicketKind.Fresh, + tsProgram, + options, + incrementalBuildStrategy, + typeCheckingProgramStrategy, + enableTemplateTypeChecker, + usePoisonedData, + }; +} + +/** + * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler` + * instance and a new `ts.Program`. + */ +export function incrementalFromCompilerTicket( + oldCompiler: NgCompiler, newProgram: ts.Program, + incrementalBuildStrategy: IncrementalBuildStrategy, + typeCheckingProgramStrategy: TypeCheckingProgramStrategy, + modifiedResourceFiles: Set): CompilationTicket { + const oldProgram = oldCompiler.getNextProgram(); + const oldDriver = oldCompiler.incrementalStrategy.getIncrementalDriver(oldProgram); + if (oldDriver === null) { + // No incremental step is possible here, since no IncrementalDriver was found for the old + // program. + return freshCompilationTicket( + newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, + oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData); + } + + const newDriver = + IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles); + + return { + kind: CompilationTicketKind.IncrementalTypeScript, + enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker, + usePoisonedData: oldCompiler.usePoisonedData, + options: oldCompiler.options, + incrementalBuildStrategy, + typeCheckingProgramStrategy, + newDriver, + oldProgram, + newProgram, + }; +} + +/** + * Create a `CompilationTicket` directly from an old `ts.Program` and associated Angular compilation + * state, along with a new `ts.Program`. + */ +export function incrementalFromDriverTicket( + oldProgram: ts.Program, oldDriver: IncrementalDriver, newProgram: ts.Program, + options: NgCompilerOptions, incrementalBuildStrategy: IncrementalBuildStrategy, + typeCheckingProgramStrategy: TypeCheckingProgramStrategy, modifiedResourceFiles: Set, + enableTemplateTypeChecker: boolean, usePoisonedData: boolean): CompilationTicket { + const newDriver = + IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles); + return { + kind: CompilationTicketKind.IncrementalTypeScript, + oldProgram, + newProgram, + options, + incrementalBuildStrategy, + newDriver, + typeCheckingProgramStrategy, + enableTemplateTypeChecker, + usePoisonedData, + }; +} + + /** * The heart of the Angular Ivy compiler. * @@ -96,19 +222,56 @@ export class NgCompiler { private moduleResolver: ModuleResolver; private resourceManager: AdapterResourceLoader; private cycleAnalyzer: CycleAnalyzer; - readonly incrementalDriver: IncrementalDriver; readonly ignoreForDiagnostics: Set; readonly ignoreForEmit: Set; - constructor( + /** + * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation. + * + * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused + * from a previous compilation and updated with any changes, it may be a new instance which + * incrementally reuses state from a previous compilation, or it may represent a fresh compilation + * entirely. + */ + static fromTicket( + ticket: CompilationTicket, adapter: NgCompilerAdapter, perfRecorder?: PerfRecorder) { + switch (ticket.kind) { + case CompilationTicketKind.Fresh: + return new NgCompiler( + adapter, + ticket.options, + ticket.tsProgram, + ticket.typeCheckingProgramStrategy, + ticket.incrementalBuildStrategy, + IncrementalDriver.fresh(ticket.tsProgram), + ticket.enableTemplateTypeChecker, + ticket.usePoisonedData, + perfRecorder, + ); + case CompilationTicketKind.IncrementalTypeScript: + return new NgCompiler( + adapter, + ticket.options, + ticket.newProgram, + ticket.typeCheckingProgramStrategy, + ticket.incrementalBuildStrategy, + ticket.newDriver, + ticket.enableTemplateTypeChecker, + ticket.usePoisonedData, + perfRecorder, + ); + } + } + + private constructor( private adapter: NgCompilerAdapter, - private options: NgCompilerOptions, + readonly options: NgCompilerOptions, private tsProgram: ts.Program, - private typeCheckingProgramStrategy: TypeCheckingProgramStrategy, - private incrementalStrategy: IncrementalBuildStrategy, - private enableTemplateTypeChecker: boolean, - private usePoisonedData: boolean, - oldProgram: ts.Program|null = null, + readonly typeCheckingProgramStrategy: TypeCheckingProgramStrategy, + readonly incrementalStrategy: IncrementalBuildStrategy, + readonly incrementalDriver: IncrementalDriver, + readonly enableTemplateTypeChecker: boolean, + readonly usePoisonedData: boolean, private perfRecorder: PerfRecorder = NOOP_PERF_RECORDER, ) { this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics); @@ -136,31 +299,10 @@ export class NgCompiler { new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache); this.resourceManager = new AdapterResourceLoader(adapter, this.options); this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver)); - - let modifiedResourceFiles: Set|null = null; - if (this.adapter.getModifiedResourceFiles !== undefined) { - modifiedResourceFiles = this.adapter.getModifiedResourceFiles() || null; - } - - if (oldProgram === null) { - this.incrementalDriver = IncrementalDriver.fresh(tsProgram); - } else { - const oldDriver = this.incrementalStrategy.getIncrementalDriver(oldProgram); - if (oldDriver !== null) { - this.incrementalDriver = - IncrementalDriver.reconcile(oldProgram, oldDriver, tsProgram, modifiedResourceFiles); - } else { - // A previous ts.Program was used to create the current one, but it wasn't from an - // `NgCompiler`. That doesn't hurt anything, but the Angular analysis will have to start - // from a fresh state. - this.incrementalDriver = IncrementalDriver.fresh(tsProgram); - } - } this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram); this.ignoreForDiagnostics = new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf))); - this.ignoreForEmit = this.adapter.ignoreForEmit; } diff --git a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts index 6ba8d8c7dd..f71982ed2f 100644 --- a/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts +++ b/packages/compiler-cli/src/ngtsc/core/test/compiler_test.ts @@ -10,16 +10,26 @@ import * as ts from 'typescript'; import {absoluteFrom as _, FileSystem, getFileSystem, getSourceFileOrError, NgtscCompilerHost, setFileSystem} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; -import {NoopIncrementalBuildStrategy} from '../../incremental'; +import {IncrementalBuildStrategy, NoopIncrementalBuildStrategy} from '../../incremental'; import {ClassDeclaration, isNamedClassDeclaration} from '../../reflection'; import {ReusedProgramStrategy} from '../../typecheck'; -import {OptimizeFor} from '../../typecheck/api'; +import {OptimizeFor, TypeCheckingProgramStrategy} from '../../typecheck/api'; import {NgCompilerOptions} from '../api'; -import {NgCompiler} from '../src/compiler'; +import {freshCompilationTicket, NgCompiler} from '../src/compiler'; import {NgCompilerHost} from '../src/host'; +function makeFreshCompiler( + host: NgCompilerHost, options: NgCompilerOptions, program: ts.Program, + programStrategy: TypeCheckingProgramStrategy, incrementalStrategy: IncrementalBuildStrategy, + enableTemplateTypeChecker: boolean, usePoisonedData: boolean): NgCompiler { + const ticket = freshCompilationTicket( + program, options, incrementalStrategy, programStrategy, enableTemplateTypeChecker, + usePoisonedData); + return NgCompiler.fromTicket(ticket, host); +} + runInEachFileSystem(() => { describe('NgCompiler', () => { let fs: FileSystem; @@ -50,7 +60,7 @@ runInEachFileSystem(() => { const baseHost = new NgtscCompilerHost(getFileSystem(), options); const host = NgCompilerHost.wrap(baseHost, [COMPONENT], options, /* oldProgram */ null); const program = ts.createProgram({host, options, rootNames: host.inputFiles}); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, /* usePoisonedData */ false); @@ -101,7 +111,7 @@ runInEachFileSystem(() => { const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); const CmpC = getClass(getSourceFileOrError(program, cmpCFile), 'CmpC'); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, /* usePoisonedData */ false); @@ -153,7 +163,7 @@ runInEachFileSystem(() => { const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); const CmpC = getClass(getSourceFileOrError(program, cmpCFile), 'CmpC'); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, /* usePoisonedData */ false); @@ -187,7 +197,7 @@ runInEachFileSystem(() => { const host = NgCompilerHost.wrap(baseHost, [cmpAFile], options, /* oldProgram */ null); const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, /* usePoisonedData */ false); @@ -223,7 +233,7 @@ runInEachFileSystem(() => { const host = NgCompilerHost.wrap(baseHost, [cmpAFile], options, /* oldProgram */ null); const program = ts.createProgram({host, options, rootNames: host.inputFiles}); const CmpA = getClass(getSourceFileOrError(program, cmpAFile), 'CmpA'); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, /* usePoisonedData */ false); @@ -255,7 +265,7 @@ runInEachFileSystem(() => { const baseHost = new NgtscCompilerHost(getFileSystem(), options); const host = NgCompilerHost.wrap(baseHost, [COMPONENT], options, /* oldProgram */ null); const program = ts.createProgram({host, options, rootNames: host.inputFiles}); - const compiler = new NgCompiler( + const compiler = makeFreshCompiler( host, options, program, new ReusedProgramStrategy(program, host, options, []), new NoopIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, /* usePoisonedData */ false); diff --git a/packages/compiler-cli/src/ngtsc/incremental/src/strategy.ts b/packages/compiler-cli/src/ngtsc/incremental/src/strategy.ts index b7d60b9856..9eacb262c7 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/src/strategy.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/src/strategy.ts @@ -24,6 +24,12 @@ export interface IncrementalBuildStrategy { * future compilations. */ setIncrementalDriver(driver: IncrementalDriver, program: ts.Program): void; + + /** + * Convert this `IncrementalBuildStrategy` into a possibly new instance to be used in the next + * incremental compilation (may be a no-op if the strategy is not stateful). + */ + toNextBuildStrategy(): IncrementalBuildStrategy; } /** @@ -36,6 +42,10 @@ export class NoopIncrementalBuildStrategy implements IncrementalBuildStrategy { } setIncrementalDriver(): void {} + + toNextBuildStrategy(): IncrementalBuildStrategy { + return this; + } } /** @@ -78,6 +88,10 @@ export class PatchedProgramIncrementalBuildStrategy implements IncrementalBuildS setIncrementalDriver(driver: IncrementalDriver, program: ts.Program): void { (program as any)[SYM_INCREMENTAL_DRIVER] = driver; } + + toNextBuildStrategy(): IncrementalBuildStrategy { + return this; + } } diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index da7c2493bf..28345f5810 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -12,8 +12,9 @@ import * as ts from 'typescript'; import * as api from '../transformers/api'; import {verifySupportedTypeScriptVersion} from '../typescript_support'; -import {NgCompiler, NgCompilerHost} from './core'; +import {CompilationTicket, freshCompilationTicket, incrementalFromCompilerTicket, NgCompiler, NgCompilerHost} from './core'; import {NgCompilerOptions} from './core/api'; +import {absoluteFrom, AbsoluteFsPath} from './file_system'; import {TrackedIncrementalBuildStrategy} from './incremental'; import {IndexedComponent} from './indexer'; import {NOOP_PERF_RECORDER, PerfRecorder, PerfTracker} from './perf'; @@ -97,12 +98,34 @@ export class NgtscProgram implements api.Program { this.incrementalStrategy = oldProgram !== undefined ? oldProgram.incrementalStrategy.toNextBuildStrategy() : new TrackedIncrementalBuildStrategy(); + const modifiedResourceFiles = new Set(); + if (this.host.getModifiedResourceFiles !== undefined) { + const strings = this.host.getModifiedResourceFiles(); + if (strings !== undefined) { + for (const fileString of strings) { + modifiedResourceFiles.add(absoluteFrom(fileString)); + } + } + } + + let ticket: CompilationTicket; + if (oldProgram === undefined) { + ticket = freshCompilationTicket( + this.tsProgram, options, this.incrementalStrategy, reusedProgramStrategy, + /* enableTemplateTypeChecker */ false, /* usePoisonedData */ false); + } else { + ticket = incrementalFromCompilerTicket( + oldProgram.compiler, + this.tsProgram, + this.incrementalStrategy, + reusedProgramStrategy, + modifiedResourceFiles, + ); + } + // Create the NgCompiler which will drive the rest of the compilation. - this.compiler = new NgCompiler( - this.host, options, this.tsProgram, reusedProgramStrategy, this.incrementalStrategy, - /** enableTemplateTypeChecker */ false, /* usePoisonedData */ false, reuseProgram, - this.perfRecorder); + this.compiler = NgCompiler.fromTicket(ticket, this.host, this.perfRecorder); } getTsProgram(): ts.Program { diff --git a/packages/compiler-cli/src/ngtsc/tsc_plugin.ts b/packages/compiler-cli/src/ngtsc/tsc_plugin.ts index b1deb80358..e6ae2920ca 100644 --- a/packages/compiler-cli/src/ngtsc/tsc_plugin.ts +++ b/packages/compiler-cli/src/ngtsc/tsc_plugin.ts @@ -8,7 +8,7 @@ import * as ts from 'typescript'; -import {NgCompiler, NgCompilerHost} from './core'; +import {CompilationTicket, freshCompilationTicket, incrementalFromDriverTicket, NgCompiler, NgCompilerHost} from './core'; import {NgCompilerOptions, UnifiedModulesHost} from './core/api'; import {NodeJSFileSystem, setFileSystem} from './file_system'; import {PatchedProgramIncrementalBuildStrategy} from './incremental'; @@ -101,10 +101,29 @@ export class NgTscPlugin implements TscPlugin { untagAllTsFiles(program); const typeCheckStrategy = new ReusedProgramStrategy( program, this.host, this.options, this.host.shimExtensionPrefixes); - this._compiler = new NgCompiler( - this.host, this.options, program, typeCheckStrategy, - new PatchedProgramIncrementalBuildStrategy(), /** enableTemplateTypeChecker */ false, - /* usePoisonedData */ false, oldProgram, NOOP_PERF_RECORDER); + const strategy = new PatchedProgramIncrementalBuildStrategy(); + const oldDriver = oldProgram !== undefined ? strategy.getIncrementalDriver(oldProgram) : null; + let ticket: CompilationTicket; + + let modifiedResourceFiles: Set|undefined = undefined; + if (this.host.getModifiedResourceFiles !== undefined) { + modifiedResourceFiles = this.host.getModifiedResourceFiles(); + } + if (modifiedResourceFiles === undefined) { + modifiedResourceFiles = new Set(); + } + + if (oldProgram === undefined || oldDriver === null) { + ticket = freshCompilationTicket( + program, this.options, strategy, typeCheckStrategy, + /* enableTemplateTypeChecker */ false, /* usePoisonedData */ false); + } else { + strategy.toNextBuildStrategy().getIncrementalDriver(oldProgram); + ticket = incrementalFromDriverTicket( + oldProgram, oldDriver, program, this.options, strategy, typeCheckStrategy, + modifiedResourceFiles, false, false); + } + this._compiler = NgCompiler.fromTicket(ticket, this.host, NOOP_PERF_RECORDER); return { ignoreForDiagnostics: this._compiler.ignoreForDiagnostics, ignoreForEmit: this._compiler.ignoreForEmit, diff --git a/packages/language-service/ivy/compiler_factory.ts b/packages/language-service/ivy/compiler_factory.ts index b42b1e660e..fed921d857 100644 --- a/packages/language-service/ivy/compiler_factory.ts +++ b/packages/language-service/ivy/compiler_factory.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; +import {CompilationTicket, freshCompilationTicket, incrementalFromCompilerTicket, NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; import {NgCompilerOptions} from '@angular/compiler-cli/src/ngtsc/core/api'; import {TrackedIncrementalBuildStrategy} from '@angular/compiler-cli/src/ngtsc/incremental'; import {TypeCheckingProgramStrategy} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; @@ -38,17 +38,15 @@ export class CompilerFactory { getOrCreate(): NgCompiler { const program = this.programStrategy.getProgram(); if (this.compiler === null || program !== this.lastKnownProgram) { - this.compiler = new NgCompiler( - this.adapter, // like compiler host - this.options, // angular compiler options - program, - this.programStrategy, - this.incrementalStrategy, - true, // enableTemplateTypeChecker - true, // usePoisonedData - this.lastKnownProgram, - undefined, // perfRecorder (use default) - ); + let ticket: CompilationTicket; + if (this.compiler === null || this.lastKnownProgram === null) { + ticket = freshCompilationTicket( + program, this.options, this.incrementalStrategy, this.programStrategy, true, true); + } else { + ticket = incrementalFromCompilerTicket( + this.compiler, program, this.incrementalStrategy, this.programStrategy, new Set()); + } + this.compiler = NgCompiler.fromTicket(ticket, this.adapter); this.lastKnownProgram = program; } return this.compiler;