diff --git a/packages/language-service/src/language_service.ts b/packages/language-service/src/language_service.ts index fc86774081..0857d23ab6 100644 --- a/packages/language-service/src/language_service.ts +++ b/packages/language-service/src/language_service.ts @@ -29,20 +29,22 @@ export function createLanguageService(host: TypeScriptServiceHost): LanguageServ class LanguageServiceImpl implements LanguageService { constructor(private readonly host: TypeScriptServiceHost) {} - getTemplateReferences(): string[] { return this.host.getTemplateReferences(); } + getTemplateReferences(): string[] { + this.host.getAnalyzedModules(); // same role as 'synchronizeHostData' + return this.host.getTemplateReferences(); + } getDiagnostics(fileName: string): tss.Diagnostic[] { + const analyzedModules = this.host.getAnalyzedModules(); // same role as 'synchronizeHostData' const results: Diagnostic[] = []; const templates = this.host.getTemplates(fileName); for (const template of templates) { const ast = this.host.getTemplateAst(template, fileName); results.push(...getTemplateDiagnostics(template, ast)); } - const declarations = this.host.getDeclarations(fileName); if (declarations && declarations.length) { - const summary = this.host.getAnalyzedModules(); - results.push(...getDeclarationDiagnostics(declarations, summary)); + results.push(...getDeclarationDiagnostics(declarations, analyzedModules)); } if (!results.length) { return []; @@ -52,7 +54,8 @@ class LanguageServiceImpl implements LanguageService { } getPipesAt(fileName: string, position: number): CompilePipeSummary[] { - let templateInfo = this.host.getTemplateAstAtPosition(fileName, position); + this.host.getAnalyzedModules(); // same role as 'synchronizeHostData' + const templateInfo = this.host.getTemplateAstAtPosition(fileName, position); if (templateInfo) { return templateInfo.pipes; } @@ -60,21 +63,24 @@ class LanguageServiceImpl implements LanguageService { } getCompletionsAt(fileName: string, position: number): Completion[]|undefined { - let templateInfo = this.host.getTemplateAstAtPosition(fileName, position); + this.host.getAnalyzedModules(); // same role as 'synchronizeHostData' + const templateInfo = this.host.getTemplateAstAtPosition(fileName, position); if (templateInfo) { return getTemplateCompletions(templateInfo); } } getDefinitionAt(fileName: string, position: number): tss.DefinitionInfoAndBoundSpan|undefined { - let templateInfo = this.host.getTemplateAstAtPosition(fileName, position); + this.host.getAnalyzedModules(); // same role as 'synchronizeHostData' + const templateInfo = this.host.getTemplateAstAtPosition(fileName, position); if (templateInfo) { return getDefinitionAndBoundSpan(templateInfo); } } getHoverAt(fileName: string, position: number): tss.QuickInfo|undefined { - let templateInfo = this.host.getTemplateAstAtPosition(fileName, position); + this.host.getAnalyzedModules(); // same role as 'synchronizeHostData' + const templateInfo = this.host.getTemplateAstAtPosition(fileName, position); if (templateInfo) { return getHover(templateInfo); } diff --git a/packages/language-service/src/ts_plugin.ts b/packages/language-service/src/ts_plugin.ts index f828508bf7..26d562a673 100644 --- a/packages/language-service/src/ts_plugin.ts +++ b/packages/language-service/src/ts_plugin.ts @@ -18,6 +18,7 @@ const projectHostMap = new WeakMap(); export function getExternalFiles(project: tss.server.Project): string[]|undefined { const host = projectHostMap.get(project); if (host) { + host.getAnalyzedModules(); const externalFiles = host.getTemplateReferences(); return externalFiles; } diff --git a/packages/language-service/src/types.ts b/packages/language-service/src/types.ts index a0dcb3f330..1fc3cde624 100644 --- a/packages/language-service/src/types.ts +++ b/packages/language-service/src/types.ts @@ -174,11 +174,6 @@ export type Declarations = Declaration[]; * @publicApi */ export interface LanguageServiceHost { - /** - * The resolver to use to find compiler metadata. - */ - readonly resolver: CompileMetadataResolver; - /** * Returns the template information for templates in `fileName` at the given location. If * `fileName` refers to a template file then the `position` should be ignored. If the `position` diff --git a/packages/language-service/src/typescript_host.ts b/packages/language-service/src/typescript_host.ts index f444a06798..869cf9a017 100644 --- a/packages/language-service/src/typescript_host.ts +++ b/packages/language-service/src/typescript_host.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotSummaryResolver, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver, isFormattedError} from '@angular/compiler'; +import {AotSummaryResolver, CompileMetadataResolver, CompileNgModuleMetadata, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver, isFormattedError} from '@angular/compiler'; import {getClassMembersFromDeclaration, getPipesTable, getSymbolQuery} from '@angular/compiler-cli/src/language_services'; import {ViewEncapsulation, ɵConsole as Console} from '@angular/core'; import * as ts from 'typescript'; @@ -14,9 +14,7 @@ import * as ts from 'typescript'; import {AstResult, TemplateInfo} from './common'; import {createLanguageService} from './language_service'; import {ReflectorHost} from './reflector_host'; -import {Declaration, DeclarationError, Declarations, Diagnostic, DiagnosticKind, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, Symbol, SymbolQuery, TemplateSource, TemplateSources} from './types'; - - +import {Declaration, DeclarationError, Declarations, Diagnostic, DiagnosticKind, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, SymbolQuery, TemplateSource} from './types'; /** * Create a `LanguageServiceHost` @@ -54,38 +52,46 @@ export class DummyResourceLoader extends ResourceLoader { * @publicApi */ export class TypeScriptServiceHost implements LanguageServiceHost { - // TODO(issue/24571): remove '!'. - private _resolver !: CompileMetadataResolver | null; - private _staticSymbolCache = new StaticSymbolCache(); - // TODO(issue/24571): remove '!'. - private _summaryResolver !: AotSummaryResolver; - // TODO(issue/24571): remove '!'. - private _staticSymbolResolver !: StaticSymbolResolver; - // TODO(issue/24571): remove '!'. - private _reflector !: StaticReflector | null; - // TODO(issue/24571): remove '!'. - private _reflectorHost !: ReflectorHost; - // TODO(issue/24571): remove '!'. - private _checker !: ts.TypeChecker | null; - private lastProgram: ts.Program|undefined; - private modulesOutOfDate: boolean = true; - // TODO(issue/24571): remove '!'. - private analyzedModules !: NgAnalyzedModules | null; - private fileToComponent = new Map(); - // TODO(issue/24571): remove '!'. - private templateReferences !: string[] | null; - private collectedErrors = new Map(); - private fileVersions = new Map(); + private readonly summaryResolver: AotSummaryResolver; + private readonly reflectorHost: ReflectorHost; + private readonly staticSymbolResolver: StaticSymbolResolver; - constructor(private host: ts.LanguageServiceHost, private tsService: ts.LanguageService) {} + private readonly staticSymbolCache = new StaticSymbolCache(); + private readonly fileToComponent = new Map(); + private readonly collectedErrors = new Map(); + private readonly fileVersions = new Map(); - /** - * Angular LanguageServiceHost implementation - */ - get resolver(): CompileMetadataResolver { - this.validate(); - let result = this._resolver; - if (!result) { + private lastProgram: ts.Program|undefined = undefined; + private templateReferences: string[] = []; + private analyzedModules: NgAnalyzedModules = { + files: [], + ngModuleByPipeOrDirective: new Map(), + ngModules: [], + }; + + // Data members below are prefixed with '_' because they have corresponding + // getters. These properties get invalidated when caches are cleared. + private _resolver: CompileMetadataResolver|null = null; + private _reflector: StaticReflector|null = null; + + constructor( + private readonly host: ts.LanguageServiceHost, private readonly tsLS: ts.LanguageService) { + this.summaryResolver = new AotSummaryResolver( + { + loadSummary(filePath: string) { return null; }, + isSourceFile(sourceFilePath: string) { return true; }, + toSummaryFileName(sourceFilePath: string) { return sourceFilePath; }, + fromSummaryFileName(filePath: string): string{return filePath;}, + }, + this.staticSymbolCache); + this.reflectorHost = new ReflectorHost(() => tsLS.getProgram() !, host); + this.staticSymbolResolver = new StaticSymbolResolver( + this.reflectorHost, this.staticSymbolCache, this.summaryResolver, + (e, filePath) => this.collectError(e, filePath !)); + } + + private get resolver(): CompileMetadataResolver { + if (!this._resolver) { const moduleResolver = new NgModuleResolver(this.reflector); const directiveResolver = new DirectiveResolver(this.reflector); const pipeResolver = new PipeResolver(this.reflector); @@ -95,24 +101,23 @@ export class TypeScriptServiceHost implements LanguageServiceHost { const htmlParser = new DummyHtmlParser(); // This tracks the CompileConfig in codegen.ts. Currently these options // are hard-coded. - const config = - new CompilerConfig({defaultEncapsulation: ViewEncapsulation.Emulated, useJit: false}); + const config = new CompilerConfig({ + defaultEncapsulation: ViewEncapsulation.Emulated, + useJit: false, + }); const directiveNormalizer = new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config); - result = this._resolver = new CompileMetadataResolver( + this._resolver = new CompileMetadataResolver( config, htmlParser, moduleResolver, directiveResolver, pipeResolver, new JitSummaryResolver(), elementSchemaRegistry, directiveNormalizer, new Console(), - this._staticSymbolCache, this.reflector, + this.staticSymbolCache, this.reflector, (error, type) => this.collectError(error, type && type.filePath)); } - return result; + return this._resolver; } - getTemplateReferences(): string[] { - this.ensureTemplateMap(); - return this.templateReferences || []; - } + getTemplateReferences(): string[] { return [...this.templateReferences]; } /** * Get the Angular template in the file, if any. If TS file is provided then @@ -126,54 +131,64 @@ export class TypeScriptServiceHost implements LanguageServiceHost { if (sourceFile) { const node = this.findNode(sourceFile, position); if (node) { - return this.getSourceFromNode( - fileName, this.host.getScriptVersion(sourceFile.fileName), node); + return this.getSourceFromNode(fileName, node); } } } else { - this.ensureTemplateMap(); const componentSymbol = this.fileToComponent.get(fileName); if (componentSymbol) { - return this.getSourceFromType( - fileName, this.host.getScriptVersion(fileName), componentSymbol); + return this.getSourceFromType(fileName, componentSymbol); } } return undefined; } + /** + * Checks whether the program has changed and returns all analyzed modules. + * If program has changed, invalidate all caches and update fileToComponent + * and templateReferences. + * In addition to returning information about NgModules, this method plays the + * same role as 'synchronizeHostData' in tsserver. + */ getAnalyzedModules(): NgAnalyzedModules { - this.updateAnalyzedModules(); - return this.ensureAnalyzedModules(); - } - - private ensureAnalyzedModules(): NgAnalyzedModules { - let analyzedModules = this.analyzedModules; - if (!analyzedModules) { - if (this.host.getScriptFileNames().length === 0) { - analyzedModules = { - files: [], - ngModuleByPipeOrDirective: new Map(), - ngModules: [], - }; - } else { - const analyzeHost = {isSourceFile(filePath: string) { return true; }}; - const programFiles = this.program !.getSourceFiles().map(sf => sf.fileName); - analyzedModules = - analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver); - } - this.analyzedModules = analyzedModules; + if (this.upToDate()) { + return this.analyzedModules; } - return analyzedModules; + + // Invalidate caches + this.templateReferences = []; + this.fileToComponent.clear(); + this.collectedErrors.clear(); + + const analyzeHost = {isSourceFile(filePath: string) { return true; }}; + const programFiles = this.program.getSourceFiles().map(sf => sf.fileName); + this.analyzedModules = + analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver); + + // update template references and fileToComponent + const urlResolver = createOfflineCompileUrlResolver(); + for (const ngModule of this.analyzedModules.ngModules) { + for (const directive of ngModule.declaredDirectives) { + const {metadata} = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference) !; + if (metadata.isComponent && metadata.template && metadata.template.templateUrl) { + const templateName = urlResolver.resolve( + this.reflector.componentModuleUrl(directive.reference), + metadata.template.templateUrl); + this.fileToComponent.set(templateName, directive.reference); + this.templateReferences.push(templateName); + } + } + } + + return this.analyzedModules; } getTemplates(fileName: string): TemplateSource[] { const results: TemplateSource[] = []; if (fileName.endsWith('.ts')) { - let version = this.host.getScriptVersion(fileName); - - // Find each template string in the file - let visit = (child: ts.Node) => { - let templateSource = this.getSourceFromNode(fileName, version, child); + // Find every template string in the file + const visit = (child: ts.Node) => { + const templateSource = this.getSourceFromNode(fileName, child); if (templateSource) { results.push(templateSource); } else { @@ -181,12 +196,11 @@ export class TypeScriptServiceHost implements LanguageServiceHost { } }; - let sourceFile = this.getSourceFile(fileName); + const sourceFile = this.getSourceFile(fileName); if (sourceFile) { ts.forEachChild(sourceFile, visit); } } else { - this.ensureTemplateMap(); const componentSymbol = this.fileToComponent.get(fileName); if (componentSymbol) { const templateSource = this.getTemplateAt(fileName, 0); @@ -222,116 +236,91 @@ export class TypeScriptServiceHost implements LanguageServiceHost { if (!fileName.endsWith('.ts')) { throw new Error(`Non-TS source file requested: ${fileName}`); } - return this.tsService.getProgram() !.getSourceFile(fileName); + return this.program.getSourceFile(fileName); } - updateAnalyzedModules() { - this.validate(); - if (this.modulesOutOfDate) { - this.analyzedModules = null; - this._reflector = null; - this.templateReferences = null; - this.fileToComponent.clear(); - this.ensureAnalyzedModules(); - this.modulesOutOfDate = false; + private get program() { + const program = this.tsLS.getProgram(); + if (!program) { + // Program is very very unlikely to be undefined. + throw new Error('No program in language service!'); } + return program; } - private get program() { return this.tsService.getProgram(); } - - private get checker() { - let checker = this._checker; - if (!checker) { - checker = this._checker = this.program !.getTypeChecker(); - } - return checker; - } - - private validate() { + /** + * Checks whether the program has changed, and invalidate caches if it has. + * Returns true if modules are up-to-date, false otherwise. + * This should only be called by getAnalyzedModules(). + */ + private upToDate() { const program = this.program; - if (this.lastProgram !== program) { - // Invalidate file that have changed in the static symbol resolver - const invalidateFile = (fileName: string) => - this._staticSymbolResolver.invalidateFile(fileName); - this.clearCaches(); - const seen = new Set(); - for (let sourceFile of this.program !.getSourceFiles()) { - const fileName = sourceFile.fileName; - seen.add(fileName); - const version = this.host.getScriptVersion(fileName); - const lastVersion = this.fileVersions.get(fileName); - if (version != lastVersion) { - this.fileVersions.set(fileName, version); - if (this._staticSymbolResolver) { - invalidateFile(fileName); - } - } - } - - // Remove file versions that are no longer in the file and invalidate them. - const missing = Array.from(this.fileVersions.keys()).filter(f => !seen.has(f)); - missing.forEach(f => this.fileVersions.delete(f)); - if (this._staticSymbolResolver) { - missing.forEach(invalidateFile); - } - - this.lastProgram = program; + if (this.lastProgram === program) { + return true; } - } - private clearCaches() { - this._checker = null; this._resolver = null; - this.collectedErrors.clear(); - this.modulesOutOfDate = true; - } + this._reflector = null; - private ensureTemplateMap() { - if (!this.templateReferences) { - const templateReference: string[] = []; - const ngModuleSummary = this.getAnalyzedModules(); - const urlResolver = createOfflineCompileUrlResolver(); - for (const module of ngModuleSummary.ngModules) { - for (const directive of module.declaredDirectives) { - const {metadata} = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference) !; - if (metadata.isComponent && metadata.template && metadata.template.templateUrl) { - const templateName = urlResolver.resolve( - this.reflector.componentModuleUrl(directive.reference), - metadata.template.templateUrl); - this.fileToComponent.set(templateName, directive.reference); - templateReference.push(templateName); - } - } + // Invalidate file that have changed in the static symbol resolver + const seen = new Set(); + for (const sourceFile of program.getSourceFiles()) { + const fileName = sourceFile.fileName; + seen.add(fileName); + const version = this.host.getScriptVersion(fileName); + const lastVersion = this.fileVersions.get(fileName); + if (version !== lastVersion) { + this.fileVersions.set(fileName, version); + this.staticSymbolResolver.invalidateFile(fileName); } - this.templateReferences = templateReference; } + + // Remove file versions that are no longer in the file and invalidate them. + const missing = Array.from(this.fileVersions.keys()).filter(f => !seen.has(f)); + missing.forEach(f => { + this.fileVersions.delete(f); + this.staticSymbolResolver.invalidateFile(f); + }); + + this.lastProgram = program; + + return false; } + /** + * Return the template source given the Class declaration node for the template. + * @param fileName Name of the file that contains the template. Could be TS or HTML. + * @param source Source text of the template. + * @param span Source span of the template. + * @param classSymbol Angular symbol for the class declaration. + * @param declaration TypeScript symbol for the class declaration. + * @param node If file is TS this is the template node, otherwise it's the class declaration node. + * @param sourceFile Source file of the class declaration. + */ private getSourceFromDeclaration( - fileName: string, version: string, source: string, span: Span, type: StaticSymbol, + fileName: string, source: string, span: Span, classSymbol: StaticSymbol, declaration: ts.ClassDeclaration, node: ts.Node, sourceFile: ts.SourceFile): TemplateSource |undefined { let queryCache: SymbolQuery|undefined = undefined; - const t = this; + const self = this; + const program = this.program; + const typeChecker = program.getTypeChecker(); if (declaration) { return { - version, + version: this.host.getScriptVersion(fileName), source, span, - type, + type: classSymbol, get members() { - return getClassMembersFromDeclaration(t.program !, t.checker, sourceFile, declaration); + return getClassMembersFromDeclaration(program, typeChecker, sourceFile, declaration); }, get query() { if (!queryCache) { - let pipes: CompilePipeSummary[] = []; - const templateInfo = t.getTemplateAstAtPosition(fileName, node.getStart()); - if (templateInfo) { - pipes = templateInfo.pipes; - } + const templateInfo = self.getTemplateAst(this, fileName); + const pipes = templateInfo && templateInfo.pipes || []; queryCache = getSymbolQuery( - t.program !, t.checker, sourceFile, - () => getPipesTable(sourceFile, t.program !, t.checker, pipes)); + program, typeChecker, sourceFile, + () => getPipesTable(sourceFile, program, typeChecker, pipes)); } return queryCache; } @@ -339,49 +328,47 @@ export class TypeScriptServiceHost implements LanguageServiceHost { } } - private getSourceFromNode(fileName: string, version: string, node: ts.Node): TemplateSource - |undefined { - let result: TemplateSource|undefined = undefined; - const t = this; + /** + * Return the TemplateSource for the inline template. + * @param fileName TS file that contains the template + * @param node Potential template node + */ + private getSourceFromNode(fileName: string, node: ts.Node): TemplateSource|undefined { switch (node.kind) { case ts.SyntaxKind.NoSubstitutionTemplateLiteral: case ts.SyntaxKind.StringLiteral: - let [declaration, decorator] = this.getTemplateClassDeclFromNode(node); + const [declaration] = this.getTemplateClassDeclFromNode(node); if (declaration && declaration.name) { const sourceFile = this.getSourceFile(fileName); if (sourceFile) { return this.getSourceFromDeclaration( - fileName, version, this.stringOf(node) || '', shrink(spanOf(node)), + fileName, this.stringOf(node) || '', shrink(spanOf(node)), this.reflector.getStaticSymbol(sourceFile.fileName, declaration.name.text), declaration, node, sourceFile); } } break; } - return result; + return; } - private getSourceFromType(fileName: string, version: string, type: StaticSymbol): TemplateSource - |undefined { - let result: TemplateSource|undefined = undefined; - const declaration = this.getTemplateClassFromStaticSymbol(type); + /** + * Return the TemplateSource for the template associated with the classSymbol. + * @param fileName Template file (HTML) + * @param classSymbol + */ + private getSourceFromType(fileName: string, classSymbol: StaticSymbol): TemplateSource|undefined { + const declaration = this.getTemplateClassFromStaticSymbol(classSymbol); if (declaration) { const snapshot = this.host.getScriptSnapshot(fileName); if (snapshot) { const source = snapshot.getText(0, snapshot.getLength()); - result = this.getSourceFromDeclaration( - fileName, version, source, {start: 0, end: source.length}, type, declaration, - declaration, declaration.getSourceFile()); + return this.getSourceFromDeclaration( + fileName, source, {start: 0, end: source.length}, classSymbol, declaration, declaration, + declaration.getSourceFile()); } } - return result; - } - - private get reflectorHost(): ReflectorHost { - if (!this._reflectorHost) { - this._reflectorHost = new ReflectorHost(() => this.tsService.getProgram() !, this.host); - } - return this._reflectorHost; + return; } private collectError(error: any, filePath: string|null) { @@ -395,49 +382,30 @@ export class TypeScriptServiceHost implements LanguageServiceHost { } } - private get staticSymbolResolver(): StaticSymbolResolver { - let result = this._staticSymbolResolver; - if (!result) { - this._summaryResolver = new AotSummaryResolver( - { - loadSummary(filePath: string) { return null; }, - isSourceFile(sourceFilePath: string) { return true; }, - toSummaryFileName(sourceFilePath: string) { return sourceFilePath; }, - fromSummaryFileName(filePath: string): string{return filePath;}, - }, - this._staticSymbolCache); - result = this._staticSymbolResolver = new StaticSymbolResolver( - this.reflectorHost as any, this._staticSymbolCache, this._summaryResolver, - (e, filePath) => this.collectError(e, filePath !)); - } - return result; - } - private get reflector(): StaticReflector { let result = this._reflector; if (!result) { const ssr = this.staticSymbolResolver; result = this._reflector = new StaticReflector( - this._summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath !)); + this.summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath !)); } return result; } private getTemplateClassFromStaticSymbol(type: StaticSymbol): ts.ClassDeclaration|undefined { const source = this.getSourceFile(type.filePath); - if (source) { - const declarationNode = ts.forEachChild(source, child => { - if (child.kind === ts.SyntaxKind.ClassDeclaration) { - const classDeclaration = child as ts.ClassDeclaration; - if (classDeclaration.name != null && classDeclaration.name.text === type.name) { - return classDeclaration; - } - } - }); - return declarationNode as ts.ClassDeclaration; + if (!source) { + return; } - - return undefined; + const declarationNode = ts.forEachChild(source, child => { + if (child.kind === ts.SyntaxKind.ClassDeclaration) { + const classDeclaration = child as ts.ClassDeclaration; + if (classDeclaration.name && classDeclaration.name.text === type.name) { + return classDeclaration; + } + } + }); + return declarationNode as ts.ClassDeclaration; } private static missingTemplate: [ts.ClassDeclaration | undefined, ts.Expression|undefined] = @@ -509,7 +477,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost { if (classDeclaration.name) { const call = decorator.expression as ts.CallExpression; const target = call.expression; - const type = this.checker.getTypeAtLocation(target); + const type = this.program.getTypeChecker().getTypeAtLocation(target); if (type) { const staticSymbol = this.reflector.getStaticSymbol(sourceFile.fileName, classDeclaration.name.text); @@ -598,12 +566,11 @@ export class TypeScriptServiceHost implements LanguageServiceHost { config, this.resolver.getReflector(), expressionParser, new DomElementSchemaRegistry(), htmlParser, null !, []); const htmlResult = htmlParser.parse(template.source, '', {tokenizeExpansionForms: true}); - const analyzedModules = this.getAnalyzedModules(); let errors: Diagnostic[]|undefined = undefined; - let ngModule = analyzedModules.ngModuleByPipeOrDirective.get(template.type); + let ngModule = this.analyzedModules.ngModuleByPipeOrDirective.get(template.type); if (!ngModule) { // Reported by the the declaration diagnostics. - ngModule = findSuitableDefaultModule(analyzedModules); + ngModule = findSuitableDefaultModule(this.analyzedModules); } if (ngModule) { const directives = diff --git a/packages/language-service/test/completions_spec.ts b/packages/language-service/test/completions_spec.ts index 518c7acae6..0f5813d26a 100644 --- a/packages/language-service/test/completions_spec.ts +++ b/packages/language-service/test/completions_spec.ts @@ -151,7 +151,7 @@ export class MyComponent { it('should hot crash with an incomplete class', () => { expect(() => { - addCode('\nexport class', fileName => { ngHost.updateAnalyzedModules(); }); + addCode('\nexport class', fileName => { ngHost.getAnalyzedModules(); }); }).not.toThrow(); }); @@ -180,7 +180,7 @@ export class MyComponent { tree: Node; } `); - ngHost.updateAnalyzedModules(); + ngHost.getAnalyzedModules(); contains('/app/my.component.ts', 'tree', 'children'); }); @@ -210,7 +210,7 @@ export class MyComponent { const originalContent = mockHost.getFileContent(fileName); const newContent = originalContent + code; mockHost.override(fileName, originalContent + code); - ngHost.updateAnalyzedModules(); + ngHost.getAnalyzedModules(); try { cb(fileName, newContent); } finally { diff --git a/packages/language-service/test/diagnostics_spec.ts b/packages/language-service/test/diagnostics_spec.ts index f1360cd890..823bcad12b 100644 --- a/packages/language-service/test/diagnostics_spec.ts +++ b/packages/language-service/test/diagnostics_spec.ts @@ -371,7 +371,7 @@ describe('diagnostics', () => { const originalContent = mockHost.getFileContent(fileName); const newContent = originalContent + code; mockHost.override(fileName, originalContent + code); - ngHost.updateAnalyzedModules(); + ngHost.getAnalyzedModules(); try { cb(fileName, newContent); } finally {