From 7c143bbd984ff40070e6578ad3ac1a511d807f5a Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Wed, 4 Sep 2019 15:39:15 -0700 Subject: [PATCH] fix(language-service): use tsLSHost to do module resolution This is a patch PR for https://github.com/angular/angular/pull/32479 This PR fixes a critical performance issue where the language service makes a MASSIVE number of filesystem calls when performing module resolution. This is because there is no caching. To make matters worse, module resolution is performed for every program change (which means every few keystrokes trigger a massive number of fs calls). There are two solutions here: Provide a cache to ts.resolveModuleName() Use ts.LanguageServiceHost.resolveModuleNames() Since every Project (and by extension ConfiguredProject) implements ts.LanguageServiceHost interface, (2) is the preferred solution here. i.e. the TypeScript LanguageServiceHost always has the resolveModuleNames() defined even though it's optional in the interface. --- packages/language-service/src/reflector_host.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/language-service/src/reflector_host.ts b/packages/language-service/src/reflector_host.ts index 5fcbb57286baf8..b00a986fbd8587 100644 --- a/packages/language-service/src/reflector_host.ts +++ b/packages/language-service/src/reflector_host.ts @@ -52,9 +52,9 @@ export class ReflectorHost implements StaticSymbolResolverHost { private metadataReaderCache = createMetadataReaderCache(); constructor( - getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost, + getProgram: () => ts.Program, private readonly tsLSHost: ts.LanguageServiceHost, private options: CompilerOptions) { - this.hostAdapter = new ReflectorModuleModuleResolutionHost(serviceHost, getProgram); + this.hostAdapter = new ReflectorModuleModuleResolutionHost(tsLSHost, getProgram); } getMetadataFor(modulePath: string): {[key: string]: any}[]|undefined { @@ -69,6 +69,11 @@ export class ReflectorHost implements StaticSymbolResolverHost { // Any containing file gives the same result for absolute imports containingFile = path.join(this.options.basePath !, 'index.ts').replace(/\\/g, '/'); } + if (this.tsLSHost.resolveModuleNames) { + // tsLSHost contains module resolution cache that improves performance. + const result = this.tsLSHost.resolveModuleNames([moduleName], containingFile)[0]; + return result ? result.resolvedFileName : null; + } const resolved = ts.resolveModuleName(moduleName, containingFile !, this.options, this.hostAdapter) .resolvedModule;