Skip to content

fix(host): invalidate cache less to speed up compilation for large projects #476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions __tests__/host.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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
});
21 changes: 17 additions & 4 deletions src/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class LanguageServiceHost implements tsTypes.LanguageServiceHost
private service?: tsTypes.LanguageService;
private fileNames: Set<string>;

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);
}
Expand All @@ -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;
}

Expand All @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ const typescript: PluginImpl<RPT2Options> = (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);

Expand Down Expand Up @@ -245,7 +245,8 @@ const typescript: PluginImpl<RPT2Options> = (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, () =>
Expand Down