fix(language-service): [ivy] do not retrieve ts.Program at startup (#38120)

This commit removes compiler instantiation at startup.
This is because the constructor is invoked during the plugin loading phase,
in which the project has not been completely loaded.

Retrieving `ts.Program` at startup will trigger an `updateGraph` operation,
which could only be called after the Project has loaded completely.
Without this change, the Ivy LS cannot be loaded as a tsserver plugin.

Note that the whole `Compiler` class is temporary, so changes made there are
only for development. Once we have proper integration with ngtsc the
`Compiler` class would be removed.

PR Close #38120
This commit is contained in:
Keen Yee Liau 2020-07-17 11:15:29 -07:00 committed by Alex Rickabaugh
parent 6f6102d8ad
commit e8896b9de3
2 changed files with 20 additions and 24 deletions

View File

@ -17,44 +17,40 @@ import * as ts from 'typescript/lib/tsserverlibrary';
import {makeCompilerHostFromProject} from './compiler_host';
interface AnalysisResult {
compiler: NgCompiler;
program: ts.Program;
}
export class Compiler {
private tsCompilerHost: ts.CompilerHost;
private lastKnownProgram: ts.Program;
private compiler: NgCompiler;
private lastKnownProgram: ts.Program|null = null;
private readonly strategy: TypeCheckingProgramStrategy;
constructor(private readonly project: ts.server.Project, private options: CompilerOptions) {
this.tsCompilerHost = makeCompilerHostFromProject(project);
const ngCompilerHost = NgCompilerHost.wrap(
this.tsCompilerHost,
project.getRootFiles(), // input files
options,
null, // old program
);
this.strategy = createTypeCheckingProgramStrategy(project);
this.lastKnownProgram = this.strategy.getProgram();
this.compiler = new NgCompiler(
ngCompilerHost, options, this.lastKnownProgram, this.strategy,
new PatchedProgramIncrementalBuildStrategy());
// Do not retrieve the program in constructor because project is still in
// the process of loading, and not all data members have been initialized.
}
setCompilerOptions(options: CompilerOptions) {
this.options = options;
}
analyze(): ts.Program|undefined {
analyze(): AnalysisResult|undefined {
const inputFiles = this.project.getRootFiles();
const ngCompilerHost =
NgCompilerHost.wrap(this.tsCompilerHost, inputFiles, this.options, this.lastKnownProgram);
const program = this.strategy.getProgram();
this.compiler = new NgCompiler(
const compiler = new NgCompiler(
ngCompilerHost, this.options, program, this.strategy,
new PatchedProgramIncrementalBuildStrategy(), this.lastKnownProgram);
try {
// This is the only way to force the compiler to update the typecheck file
// in the program. We have to do try-catch because the compiler immediately
// throws if it fails to parse any template in the entire program!
const d = this.compiler.getDiagnostics();
const d = compiler.getDiagnostics();
if (d.length) {
// There could be global compilation errors. It's useful to print them
// out in development.
@ -64,12 +60,11 @@ export class Compiler {
console.error('Failed to analyze program', e.message);
return;
}
this.lastKnownProgram = this.compiler.getNextProgram();
return this.lastKnownProgram;
}
getDiagnostics(sourceFile: ts.SourceFile): ts.Diagnostic[] {
return this.compiler.getDiagnostics(sourceFile);
this.lastKnownProgram = compiler.getNextProgram();
return {
compiler,
program: this.lastKnownProgram,
};
}
}

View File

@ -21,15 +21,16 @@ export class LanguageService {
}
getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {
const program = this.compiler.analyze();
if (!program) {
const result = this.compiler.analyze();
if (!result) {
return [];
}
const {compiler, program} = result;
const sourceFile = program.getSourceFile(fileName);
if (!sourceFile) {
return [];
}
return this.compiler.getDiagnostics(sourceFile);
return compiler.getDiagnostics(sourceFile);
}
private watchConfigFile(project: ts.server.Project) {