fix(language-service): Instantiate MetadataResolver once (#32145)
Instead of destroying and recreating MetadataResolver every time the program changes, create one instance and reuse it throughout the lifetime of the language service. Since Angular StaticSymbols are invalidated when program gets out-of-date, this should be safe. This should make the language service more more performant. PR Close #32145
This commit is contained in:
parent
373d9660d0
commit
6a0b1d58ba
|
@ -56,6 +56,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
private readonly summaryResolver: AotSummaryResolver;
|
||||
private readonly reflectorHost: ReflectorHost;
|
||||
private readonly staticSymbolResolver: StaticSymbolResolver;
|
||||
private readonly reflector: StaticReflector;
|
||||
private readonly resolver: CompileMetadataResolver;
|
||||
|
||||
private readonly staticSymbolCache = new StaticSymbolCache();
|
||||
private readonly fileToComponent = new Map<string, StaticSymbol>();
|
||||
|
@ -70,11 +72,6 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
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(
|
||||
|
@ -88,11 +85,22 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
this.reflectorHost = new ReflectorHost(() => tsLS.getProgram() !, host);
|
||||
this.staticSymbolResolver = new StaticSymbolResolver(
|
||||
this.reflectorHost, this.staticSymbolCache, this.summaryResolver,
|
||||
(e, filePath) => this.collectError(e, filePath !));
|
||||
(e, filePath) => this.collectError(e, filePath));
|
||||
this.reflector = new StaticReflector(
|
||||
this.summaryResolver, this.staticSymbolResolver,
|
||||
[], // knownMetadataClasses
|
||||
[], // knownMetadataFunctions
|
||||
(e, filePath) => this.collectError(e, filePath));
|
||||
this.resolver = this.createMetadataResolver();
|
||||
}
|
||||
|
||||
private get resolver(): CompileMetadataResolver {
|
||||
if (!this._resolver) {
|
||||
/**
|
||||
* Creates a new metadata resolver. This should only be called once.
|
||||
*/
|
||||
private createMetadataResolver(): CompileMetadataResolver {
|
||||
if (this.resolver) {
|
||||
return this.resolver; // There should only be a single instance
|
||||
}
|
||||
const moduleResolver = new NgModuleResolver(this.reflector);
|
||||
const directiveResolver = new DirectiveResolver(this.reflector);
|
||||
const pipeResolver = new PipeResolver(this.reflector);
|
||||
|
@ -108,15 +116,12 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
});
|
||||
const directiveNormalizer =
|
||||
new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
||||
|
||||
this._resolver = new CompileMetadataResolver(
|
||||
return new CompileMetadataResolver(
|
||||
config, htmlParser, moduleResolver, directiveResolver, pipeResolver,
|
||||
new JitSummaryResolver(), elementSchemaRegistry, directiveNormalizer, new Console(),
|
||||
this.staticSymbolCache, this.reflector,
|
||||
(error, type) => this.collectError(error, type && type.filePath));
|
||||
}
|
||||
return this._resolver;
|
||||
}
|
||||
|
||||
getTemplateReferences(): string[] { return [...this.templateReferences]; }
|
||||
|
||||
|
@ -236,9 +241,6 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
return true;
|
||||
}
|
||||
|
||||
this._resolver = null;
|
||||
this._reflector = null;
|
||||
|
||||
// Invalidate file that have changed in the static symbol resolver
|
||||
const seen = new Set<string>();
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
|
@ -325,7 +327,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
return new ExternalTemplate(source, fileName, classDecl, classSymbol, this);
|
||||
}
|
||||
|
||||
private collectError(error: any, filePath: string|null) {
|
||||
private collectError(error: any, filePath?: string) {
|
||||
if (filePath) {
|
||||
let errors = this.collectedErrors.get(filePath);
|
||||
if (!errors) {
|
||||
|
@ -336,20 +338,13 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
}
|
||||
}
|
||||
|
||||
private get reflector(): StaticReflector {
|
||||
if (!this._reflector) {
|
||||
this._reflector = new StaticReflector(
|
||||
this.summaryResolver, this.staticSymbolResolver,
|
||||
[], // knownMetadataClasses
|
||||
[], // knownMetadataFunctions
|
||||
(e, filePath) => this.collectError(e, filePath !));
|
||||
}
|
||||
return this._reflector;
|
||||
}
|
||||
|
||||
private getCollectedErrors(defaultSpan: Span, sourceFile: ts.SourceFile): DeclarationError[] {
|
||||
const errors = this.collectedErrors.get(sourceFile.fileName);
|
||||
return (errors && errors.map((e: any) => {
|
||||
if (!errors) {
|
||||
return [];
|
||||
}
|
||||
// TODO: Add better typings for the errors
|
||||
return errors.map((e: any) => {
|
||||
const line = e.line || (e.position && e.position.line);
|
||||
const column = e.column || (e.position && e.position.column);
|
||||
const span = spanAt(sourceFile, line, column) || defaultSpan;
|
||||
|
@ -357,8 +352,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
return errorToDiagnosticWithChain(e, span);
|
||||
}
|
||||
return {message: e.message, span};
|
||||
})) ||
|
||||
[];
|
||||
});
|
||||
}
|
||||
|
||||
private getDeclarationFromNode(sourceFile: ts.SourceFile, node: ts.Node): Declaration|undefined {
|
||||
|
@ -457,8 +451,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
|||
const expressionParser = new Parser(new Lexer());
|
||||
const config = new CompilerConfig();
|
||||
const parser = new TemplateParser(
|
||||
config, this.resolver.getReflector(), expressionParser, new DomElementSchemaRegistry(),
|
||||
htmlParser, null !, []);
|
||||
config, this.reflector, expressionParser, new DomElementSchemaRegistry(), htmlParser,
|
||||
null !, []);
|
||||
const htmlResult = htmlParser.parse(template.source, '', {tokenizeExpansionForms: true});
|
||||
const errors: Diagnostic[]|undefined = undefined;
|
||||
const ngModule = this.analyzedModules.ngModuleByPipeOrDirective.get(template.type) ||
|
||||
|
|
Loading…
Reference in New Issue