This commit replaces the `IncrementalDriver` abstraction which powered incremental compilation in the compiler with a new `IncrementalCompilation` design. Principally, it separates two concerns which were tied together in the previous implementation: 1. Tracking the reusable state of a compilation at any given point that could be reused in a subsequent future compilation. 2. Making use of a prior compilation's state to accelerate the current one. The new abstraction adds explicit tracking and types to deal with both of these concerns separately, which greatly reduces the complexity of the state tracking that `IncrementalDriver` used to perform. PR Close #41475
71 lines
3.0 KiB
TypeScript
71 lines
3.0 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {CompilationTicket, freshCompilationTicket, incrementalFromCompilerTicket, NgCompiler, resourceChangeTicket} from '@angular/compiler-cli/src/ngtsc/core';
|
|
import {NgCompilerOptions} from '@angular/compiler-cli/src/ngtsc/core/api';
|
|
import {AbsoluteFsPath, resolve} from '@angular/compiler-cli/src/ngtsc/file_system';
|
|
import {TrackedIncrementalBuildStrategy} from '@angular/compiler-cli/src/ngtsc/incremental';
|
|
import {ProgramDriver} from '@angular/compiler-cli/src/ngtsc/program_driver';
|
|
|
|
import {LanguageServiceAdapter} from './adapters';
|
|
|
|
/**
|
|
* Manages the `NgCompiler` instance which backs the language service, updating or replacing it as
|
|
* needed to produce an up-to-date understanding of the current program.
|
|
*
|
|
* TODO(alxhub): currently the options used for the compiler are specified at `CompilerFactory`
|
|
* construction, and are not changable. In a real project, users can update `tsconfig.json`. We need
|
|
* to properly handle a change in the compiler options, either by having an API to update the
|
|
* `CompilerFactory` to use new options, or by replacing it entirely.
|
|
*/
|
|
export class CompilerFactory {
|
|
private readonly incrementalStrategy = new TrackedIncrementalBuildStrategy();
|
|
private compiler: NgCompiler|null = null;
|
|
|
|
constructor(
|
|
private readonly adapter: LanguageServiceAdapter,
|
|
private readonly programStrategy: ProgramDriver,
|
|
private readonly options: NgCompilerOptions,
|
|
) {}
|
|
|
|
getOrCreate(): NgCompiler {
|
|
const program = this.programStrategy.getProgram();
|
|
const modifiedResourceFiles = new Set<AbsoluteFsPath>();
|
|
for (const fileName of this.adapter.getModifiedResourceFiles() ?? []) {
|
|
modifiedResourceFiles.add(resolve(fileName));
|
|
}
|
|
|
|
if (this.compiler !== null && program === this.compiler.getCurrentProgram()) {
|
|
if (modifiedResourceFiles.size > 0) {
|
|
// Only resource files have changed since the last NgCompiler was created.
|
|
const ticket = resourceChangeTicket(this.compiler, modifiedResourceFiles);
|
|
this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
|
|
} else {
|
|
// The previous NgCompiler is being reused, but we still want to reset its performance
|
|
// tracker to capture only the operations that are needed to service the current request.
|
|
this.compiler.perfRecorder.reset();
|
|
}
|
|
|
|
return this.compiler;
|
|
}
|
|
|
|
let ticket: CompilationTicket;
|
|
if (this.compiler === null) {
|
|
ticket = freshCompilationTicket(
|
|
program, this.options, this.incrementalStrategy, this.programStrategy,
|
|
/* perfRecorder */ null, true, true);
|
|
} else {
|
|
ticket = incrementalFromCompilerTicket(
|
|
this.compiler, program, this.incrementalStrategy, this.programStrategy,
|
|
modifiedResourceFiles, /* perfRecorder */ null);
|
|
}
|
|
this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
|
|
return this.compiler;
|
|
}
|
|
}
|