From 26c701c3512ea2948b333ca211c8340591efaa64 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 23 Mar 2022 15:57:39 -0700 Subject: [PATCH] Fix document registry cache key calculation for `paths` compiler option (#48389) * Fix document registry cache key calculation for `paths` compiler option * PR feedback --- src/services/documentRegistry.ts | 18 ++++++- .../unittests/tsserver/languageService.ts | 51 ++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index 7446f895db2..88d7daf468b 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -357,7 +357,23 @@ namespace ts { }; } + function compilerOptionValueToString(value: unknown): string { + if (value === null || typeof value !== "object") { // eslint-disable-line no-null/no-null + return "" + value; + } + if (isArray(value)) { + return `[${map(value, e => compilerOptionValueToString(e))?.join(",")}]`; + } + let str = "{"; + for (const key in value) { + if (ts.hasOwnProperty.call(value, key)) { // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier + str += `${key}: ${compilerOptionValueToString((value as any)[key])}`; + } + } + return str + "}"; + } + function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey { - return sourceFileAffectingCompilerOptions.map(option => getCompilerOptionValue(settings, option)).join("|") as DocumentRegistryBucketKey; + return sourceFileAffectingCompilerOptions.map(option => compilerOptionValueToString(getCompilerOptionValue(settings, option))).join("|") + (settings.pathsBasePath ? `|${settings.pathsBasePath}` : undefined) as DocumentRegistryBucketKey; } } diff --git a/src/testRunner/unittests/tsserver/languageService.ts b/src/testRunner/unittests/tsserver/languageService.ts index 86d426664df..8e7bf0eedcd 100644 --- a/src/testRunner/unittests/tsserver/languageService.ts +++ b/src/testRunner/unittests/tsserver/languageService.ts @@ -1,5 +1,5 @@ namespace ts.projectSystem { - describe("unittests:: tsserver:: Language service", () => { + describe("unittests:: tsserver:: languageService", () => { it("should work correctly on case-sensitive file systems", () => { const lib = { path: "/a/Lib/lib.d.ts", @@ -15,5 +15,54 @@ namespace ts.projectSystem { projectService.checkNumberOfProjects({ inferredProjects: 1 }); projectService.inferredProjects[0].getLanguageService().getProgram(); }); + + it("should support multiple projects with the same file under differing `paths` settings", () => { + const files = [ + { + path: "/project/shared.ts", + content: Utils.dedent` + import {foo_a} from "foo"; + ` + }, + { + path: `/project/a/tsconfig.json`, + content: `{ "compilerOptions": { "paths": { "foo": ["./foo.d.ts"] } }, "files": ["./index.ts", "./foo.d.ts"] }` + }, + { + path: `/project/a/foo.d.ts`, + content: Utils.dedent` + export const foo_a = 1; + ` + }, + { + path: "/project/a/index.ts", + content: `import "../shared";` + }, + { + path: `/project/b/tsconfig.json`, + content: `{ "compilerOptions": { "paths": { "foo": ["./foo.d.ts"] } }, "files": ["./index.ts", "./foo.d.ts"] }` + }, + { + path: `/project/b/foo.d.ts`, + content: Utils.dedent` + export const foo_b = 1; + ` + }, + { + path: "/project/b/index.ts", + content: `import "../shared";` + } + ]; + + const host = createServerHost(files, { executingFilePath: "/project/tsc.js", useCaseSensitiveFileNames: true }); + const projectService = createProjectService(host); + projectService.openClientFile(files[3].path); + projectService.openClientFile(files[6].path); + projectService.checkNumberOfProjects({ configuredProjects: 2 }); + const proj1Diags = projectService.configuredProjects.get(files[1].path)!.getLanguageService().getProgram()!.getSemanticDiagnostics(); + Debug.assertEqual(proj1Diags.length, 0); + const proj2Diags = projectService.configuredProjects.get(files[4].path)!.getLanguageService().getProgram()!.getSemanticDiagnostics(); + Debug.assertEqual(proj2Diags.length, 1); + }); }); }