diff --git a/__tests__/host.spec.ts b/__tests__/host.spec.ts index 8d16ef6f..14240dd9 100644 --- a/__tests__/host.spec.ts +++ b/__tests__/host.spec.ts @@ -15,10 +15,14 @@ setTypescriptModule(ts); }; const defaultConfig = { fileNames: [], errors: [], options: {} }; +const defaultFilter = () => true; const unaryFunc = "const unary = (x: string): string => x.reverse()"; const unaryFuncSnap = { text: unaryFunc }; +const binaryFunc = "const binary = (a: number, b: number): number => a - b"; +const binaryFuncSnap = { text: binaryFunc }; + // host.ts uses `/` normalized path, as does TS itself (https://github.com/microsoft/TypeScript/blob/7f022c58fb8b7253f23c49f0d9eee6fde82b477b/src/compiler/path.ts#L4) const local = (x: string) => normalize(path.resolve(__dirname, x)); const testDir = local("__temp/host"); @@ -37,14 +41,18 @@ test("LanguageServiceHost", async () => { const testOpts = { test: "this is a test" }; const config = { ...defaultConfig, options: testOpts }; const transformers = [() => ({})]; - const host = new LanguageServiceHost(config, transformers, testDir); + const host = new LanguageServiceHost(config, transformers, testDir, defaultFilter); // test core snapshot functionality expect(host.getScriptSnapshot(testFile)).toEqual(unaryFuncSnap); expect(host.getScriptVersion(testFile)).toEqual("1"); - expect(host.setSnapshot(testFile, unaryFunc)).toEqual(unaryFuncSnap); // version 2 + expect(host.setSnapshot(testFile, unaryFunc)).toEqual(unaryFuncSnap); // unchanged expect(host.getScriptSnapshot(testFile)).toEqual(unaryFuncSnap); // get from dict + expect(host.getScriptVersion(testFile)).toEqual("1"); + + expect(host.setSnapshot(testFile, binaryFunc)).toEqual(binaryFuncSnap); // version 2 + expect(host.getScriptSnapshot(testFile)).toEqual(binaryFuncSnap); expect(host.getScriptVersion(testFile)).toEqual("2"); expect(host.getScriptSnapshot(nonExistent)).toBeFalsy(); @@ -89,7 +97,7 @@ test("LanguageServiceHost - getCustomTransformers", () => { after: () => "testAfter", afterDeclarations: () => "testAfterDeclarations", })]; - const host = new LanguageServiceHost(config, transformers as any, testDir); + const host = new LanguageServiceHost(config, transformers as any, testDir, defaultFilter); host.setLanguageService(true as any); const customTransformers = host.getCustomTransformers(); @@ -107,13 +115,13 @@ test("LanguageServiceHost - getCustomTransformers -- undefined cases", () => { const config = { ...defaultConfig }; // no LS and no transformers cases - let host = new LanguageServiceHost(config, undefined as any, testDir); + let host = new LanguageServiceHost(config, undefined as any, testDir, defaultFilter); expect(host.getCustomTransformers()).toBeFalsy(); // no LS host.setLanguageService(true as any); expect(host.getCustomTransformers()).toBeFalsy(); // no transformers // empty transformers case - host = new LanguageServiceHost(config, [], testDir); + host = new LanguageServiceHost(config, [], testDir, defaultFilter); host.setLanguageService(true as any); expect(host.getCustomTransformers()).toBeFalsy(); // empty transformers }); diff --git a/src/host.ts b/src/host.ts index b0a2b615..d5b0d0b8 100644 --- a/src/host.ts +++ b/src/host.ts @@ -11,7 +11,7 @@ export class LanguageServiceHost implements tsTypes.LanguageServiceHost private service?: tsTypes.LanguageService; private fileNames: Set; - constructor(private parsedConfig: tsTypes.ParsedCommandLine, private transformers: TransformerFactoryCreator[], private cwd: string) + constructor(private parsedConfig: tsTypes.ParsedCommandLine, private transformers: TransformerFactoryCreator[], private cwd: string, private filter: (id: string | unknown) => boolean) { this.fileNames = new Set(parsedConfig.fileNames); } @@ -27,14 +27,27 @@ export class LanguageServiceHost implements tsTypes.LanguageServiceHost this.service = service; } - public setSnapshot(fileName: string, source: string): tsTypes.IScriptSnapshot + public setSnapshot(fileName: string, source: string, forcedUpdate = false): tsTypes.IScriptSnapshot { fileName = normalize(fileName); + // don't update the snapshot if there are no changes + const prevSnapshot = this.snapshots[fileName]; + if (prevSnapshot?.getText(0, prevSnapshot.getLength()) === source) { + if (forcedUpdate) { + this.versions[fileName] = (this.versions[fileName] || 0) + 1; + } + + return prevSnapshot; + } + const snapshot = tsModule.ScriptSnapshot.fromString(source); this.snapshots[fileName] = snapshot; this.versions[fileName] = (this.versions[fileName] || 0) + 1; - this.fileNames.add(fileName); + + if (this.filter(fileName)) + this.fileNames.add(fileName); + return snapshot; } @@ -46,7 +59,7 @@ export class LanguageServiceHost implements tsTypes.LanguageServiceHost return this.snapshots[fileName]; const source = tsModule.sys.readFile(fileName); - if (source) + if (source !== undefined) return this.setSnapshot(fileName, source); return undefined; diff --git a/src/index.ts b/src/index.ts index 3ccca119..bbdcc891 100644 --- a/src/index.ts +++ b/src/index.ts @@ -174,7 +174,7 @@ const typescript: PluginImpl = (options) => filter = createFilter(context, pluginOptions, parsedConfig); - servicesHost = new LanguageServiceHost(parsedConfig, pluginOptions.transformers, pluginOptions.cwd); + servicesHost = new LanguageServiceHost(parsedConfig, pluginOptions.transformers, pluginOptions.cwd, filter); service = tsModule.createLanguageService(servicesHost, documentRegistry); servicesHost.setLanguageService(service); @@ -245,7 +245,8 @@ const typescript: PluginImpl = (options) => if (!filter(id)) return undefined; - const snapshot = servicesHost.setSnapshot(id, code); + // force version update under watch mode to ensure recompile + const snapshot = servicesHost.setSnapshot(id, code, watchMode); // getting compiled file from cache or from ts const result = cache.getCompiled(id, snapshot, () =>