mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Include actual generated module specifiers in module specifier cache (#44176)
* Add cache invalidation for node_modules, config, and preferences changes * Share watches with closed script info * Update API baseline * Small adjustments for fewer object allocations * Document overloaded return value * Update baselines * Store isAutoImportable separately from modulePaths * Add back missing return * Return wrapped watcher instead of underlying one * Make user preferences part of the cache key instead of implicitly clearing in editor services * Fix missed merge conflict
This commit is contained in:
@@ -4,23 +4,27 @@ namespace ts.projectSystem {
|
||||
content: `{ "dependencies": { "mobx": "*" } }`
|
||||
};
|
||||
const aTs: File = {
|
||||
path: "/a.ts",
|
||||
path: "/src/a.ts",
|
||||
content: "export const foo = 0;",
|
||||
};
|
||||
const bTs: File = {
|
||||
path: "/b.ts",
|
||||
path: "/src/b.ts",
|
||||
content: "foo",
|
||||
};
|
||||
const cTs: File = {
|
||||
path: "/src/c.ts",
|
||||
content: "import ",
|
||||
};
|
||||
const bSymlink: SymLink = {
|
||||
path: "/b-link.ts",
|
||||
path: "/src/b-link.ts",
|
||||
symLink: "./b.ts",
|
||||
};
|
||||
const tsconfig: File = {
|
||||
path: "/tsconfig.json",
|
||||
content: "{}",
|
||||
content: `{ "include": ["src"] }`,
|
||||
};
|
||||
const ambientDeclaration: File = {
|
||||
path: "/ambient.d.ts",
|
||||
path: "/src/ambient.d.ts",
|
||||
content: "declare module 'ambient' {}"
|
||||
};
|
||||
const mobxDts: File = {
|
||||
@@ -31,50 +35,111 @@ namespace ts.projectSystem {
|
||||
describe("unittests:: tsserver:: moduleSpecifierCache", () => {
|
||||
it("caches importability within a file", () => {
|
||||
const { moduleSpecifierCache } = setup();
|
||||
assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path));
|
||||
assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {})?.isAutoImportable);
|
||||
});
|
||||
|
||||
it("caches module specifiers within a file", () => {
|
||||
const { moduleSpecifierCache, triggerCompletions } = setup();
|
||||
// Completion at an import statement will calculate and cache module specifiers
|
||||
triggerCompletions({ file: cTs.path, line: 1, offset: cTs.content.length + 1 });
|
||||
const mobxCache = moduleSpecifierCache.get(cTs.path as Path, mobxDts.path as Path, {});
|
||||
assert.deepEqual(mobxCache, {
|
||||
modulePaths: [{
|
||||
path: mobxDts.path,
|
||||
isInNodeModules: true,
|
||||
isRedirect: false
|
||||
}],
|
||||
moduleSpecifiers: ["mobx"],
|
||||
isAutoImportable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("invalidates module specifiers when changes happen in contained node_modules directories", () => {
|
||||
const { host, moduleSpecifierCache, triggerCompletions } = setup();
|
||||
// Completion at an import statement will calculate and cache module specifiers
|
||||
triggerCompletions({ file: cTs.path, line: 1, offset: cTs.content.length + 1 });
|
||||
checkWatchedDirectories(host, ["/src", "/node_modules"], /*recursive*/ true);
|
||||
host.writeFile("/node_modules/.staging/mobx-12345678/package.json", "{}");
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.equal(moduleSpecifierCache.count(), 0);
|
||||
});
|
||||
|
||||
it("does not invalidate the cache when new files are added", () => {
|
||||
const { host, moduleSpecifierCache } = setup();
|
||||
host.writeFile("/src/a2.ts", aTs.content);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path));
|
||||
assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {})?.isAutoImportable);
|
||||
});
|
||||
|
||||
it("invalidates the cache when symlinks are added or removed", () => {
|
||||
const { host, moduleSpecifierCache } = setup();
|
||||
host.renameFile(bSymlink.path, "/b-link2.ts");
|
||||
host.renameFile(bSymlink.path, "/src/b-link2.ts");
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.equal(moduleSpecifierCache.count(), 0);
|
||||
});
|
||||
|
||||
it("invalidates the cache when package.json changes", () => {
|
||||
it("invalidates the cache when local package.json changes", () => {
|
||||
const { host, moduleSpecifierCache } = setup();
|
||||
host.writeFile("/package.json", `{}`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.isUndefined(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path));
|
||||
assert.equal(moduleSpecifierCache.count(), 0);
|
||||
});
|
||||
|
||||
it("invalidates the cache when module resolution settings change", () => {
|
||||
const { host, moduleSpecifierCache } = setup();
|
||||
host.writeFile(tsconfig.path, `{ "compilerOptions": { "moduleResolution": "classic" }, "include": ["src"] }`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
assert.equal(moduleSpecifierCache.count(), 0);
|
||||
});
|
||||
|
||||
it("invalidates the cache when user preferences change", () => {
|
||||
const { moduleSpecifierCache, session, triggerCompletions } = setup();
|
||||
const preferences: UserPreferences = { importModuleSpecifierPreference: "project-relative" };
|
||||
|
||||
assert.ok(getWithPreferences({}));
|
||||
executeSessionRequest<protocol.ConfigureRequest, protocol.ConfigureResponse>(session, protocol.CommandTypes.Configure, { preferences });
|
||||
// Nothing changes yet
|
||||
assert.ok(getWithPreferences({}));
|
||||
assert.isUndefined(getWithPreferences(preferences));
|
||||
// Completions will request (getting nothing) and set the cache with new preferences
|
||||
triggerCompletions({ file: bTs.path, line: 1, offset: 3 });
|
||||
assert.isUndefined(getWithPreferences({}));
|
||||
assert.ok(getWithPreferences(preferences));
|
||||
|
||||
// Test other affecting preference
|
||||
executeSessionRequest<protocol.ConfigureRequest, protocol.ConfigureResponse>(session, protocol.CommandTypes.Configure, {
|
||||
preferences: { importModuleSpecifierEnding: "js" },
|
||||
});
|
||||
triggerCompletions({ file: bTs.path, line: 1, offset: 3 });
|
||||
assert.isUndefined(getWithPreferences(preferences));
|
||||
|
||||
function getWithPreferences(preferences: UserPreferences) {
|
||||
return moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, preferences);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function setup() {
|
||||
const host = createServerHost([aTs, bTs, bSymlink, ambientDeclaration, tsconfig, packageJson, mobxDts]);
|
||||
const host = createServerHost([aTs, bTs, cTs, bSymlink, ambientDeclaration, tsconfig, packageJson, mobxDts]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([aTs, bTs], session);
|
||||
openFilesForSession([aTs, bTs, cTs], session);
|
||||
const projectService = session.getProjectService();
|
||||
const project = configuredProjectAt(projectService, 0);
|
||||
triggerCompletions();
|
||||
return { host, project, projectService, moduleSpecifierCache: project.getModuleSpecifierCache(), triggerCompletions };
|
||||
executeSessionRequest<protocol.ConfigureRequest, protocol.ConfigureResponse>(session, protocol.CommandTypes.Configure, {
|
||||
preferences: {
|
||||
includeCompletionsForImportStatements: true,
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsWithInsertText: true,
|
||||
includeCompletionsWithSnippetText: true,
|
||||
},
|
||||
});
|
||||
triggerCompletions({ file: bTs.path, line: 1, offset: 3 });
|
||||
|
||||
function triggerCompletions() {
|
||||
const requestLocation: protocol.FileLocationRequestArgs = {
|
||||
file: bTs.path,
|
||||
line: 1,
|
||||
offset: 3,
|
||||
};
|
||||
return { host, project, projectService, session, moduleSpecifierCache: project.getModuleSpecifierCache(), triggerCompletions };
|
||||
|
||||
function triggerCompletions(requestLocation: protocol.FileLocationRequestArgs) {
|
||||
executeSessionRequest<protocol.CompletionsRequest, protocol.CompletionInfoResponse>(session, protocol.CommandTypes.CompletionInfo, {
|
||||
...requestLocation,
|
||||
includeExternalModuleExports: true,
|
||||
prefix: "foo",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user