93 lines
3.5 KiB
TypeScript
93 lines
3.5 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 {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';
|
|
import * as ts from 'typescript/lib/tsserverlibrary';
|
|
|
|
import {LanguageServiceAdapter} from './adapters';
|
|
import {isExternalTemplate} from './utils';
|
|
|
|
/**
|
|
* 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;
|
|
private lastKnownProgram: ts.Program|null = null;
|
|
|
|
constructor(
|
|
private readonly adapter: LanguageServiceAdapter,
|
|
private readonly programStrategy: TypeCheckingProgramStrategy,
|
|
private readonly options: NgCompilerOptions,
|
|
) {}
|
|
|
|
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)
|
|
);
|
|
this.lastKnownProgram = program;
|
|
}
|
|
return this.compiler;
|
|
}
|
|
|
|
/**
|
|
* Create a new instance of the Ivy compiler if the program has changed since
|
|
* the last time the compiler was instantiated. If the program has not changed,
|
|
* return the existing instance.
|
|
* @param fileName override the template if this is an external template file
|
|
* @param options angular compiler options
|
|
*/
|
|
getOrCreateWithChangedFile(fileName: string): NgCompiler {
|
|
const compiler = this.getOrCreate();
|
|
if (isExternalTemplate(fileName)) {
|
|
this.overrideTemplate(fileName, compiler);
|
|
}
|
|
return compiler;
|
|
}
|
|
|
|
private overrideTemplate(fileName: string, compiler: NgCompiler) {
|
|
if (!this.adapter.isTemplateDirty(fileName)) {
|
|
return;
|
|
}
|
|
// 1. Get the latest snapshot
|
|
const latestTemplate = this.adapter.readResource(fileName);
|
|
// 2. Find all components that use the template
|
|
const ttc = compiler.getTemplateTypeChecker();
|
|
const components = compiler.getComponentsWithTemplateFile(fileName);
|
|
// 3. Update component template
|
|
for (const component of components) {
|
|
if (ts.isClassDeclaration(component)) {
|
|
ttc.overrideComponentTemplate(component, latestTemplate);
|
|
}
|
|
}
|
|
}
|
|
|
|
registerLastKnownProgram() {
|
|
this.lastKnownProgram = this.programStrategy.getProgram();
|
|
}
|
|
}
|