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

View File

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