From e8896b9de3ed6a3370b1fb104f5fd0b01222ac88 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Fri, 17 Jul 2020 11:15:29 -0700 Subject: [PATCH] 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 --- .../language-service/ivy/compiler/compiler.ts | 37 ++++++++----------- .../language-service/ivy/language_service.ts | 7 ++-- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/packages/language-service/ivy/compiler/compiler.ts b/packages/language-service/ivy/compiler/compiler.ts index 37f73f0efe..9454666ffb 100644 --- a/packages/language-service/ivy/compiler/compiler.ts +++ b/packages/language-service/ivy/compiler/compiler.ts @@ -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, + }; } } diff --git a/packages/language-service/ivy/language_service.ts b/packages/language-service/ivy/language_service.ts index fe49c13a75..b0b6699784 100644 --- a/packages/language-service/ivy/language_service.ts +++ b/packages/language-service/ivy/language_service.ts @@ -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) {