From 020558c182e9961501196237d95789755caee721 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 9 May 2024 13:36:38 -0700 Subject: [PATCH 1/3] Test for dts file next to ts file --- .../unittests/tsserver/dynamicFiles.ts | 5 +- .../unittests/tsserver/projectReferences.ts | 53 +++ src/testRunner/unittests/tsserver/projects.ts | 2 +- ...eInferredProjectPerProjectRoot-is-false.js | 3 + .../with-dts-file-next-to-ts-file.js | 329 ++++++++++++++++++ .../projects/assert-when-removing-project.js | 12 +- 6 files changed, 398 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/tsserver/projectReferences/with-dts-file-next-to-ts-file.js diff --git a/src/testRunner/unittests/tsserver/dynamicFiles.ts b/src/testRunner/unittests/tsserver/dynamicFiles.ts index 7267a3e171b72..adbaff4bf957b 100644 --- a/src/testRunner/unittests/tsserver/dynamicFiles.ts +++ b/src/testRunner/unittests/tsserver/dynamicFiles.ts @@ -217,10 +217,7 @@ describe("unittests:: tsserver:: dynamicFiles:: ", () => { }], session); } catch (e) { - assert.strictEqual( - e.message.replace(/\r?\n/, "\n"), - `Debug Failure. False expression.\nVerbose Debug Information: {"fileName":"^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js","currentDirectory":"/user/username/projects/myproject","hostCurrentDirectory":"/","openKeys":[]}\nDynamic files must always be opened with service's current directory or service should support inferred project per projectRootPath.`, - ); + session.logger.info(e.message); } const file2Path = file.path.replace("#1", "#2"); openFilesForSession([{ file: file2Path, content: file.content }], session); diff --git a/src/testRunner/unittests/tsserver/projectReferences.ts b/src/testRunner/unittests/tsserver/projectReferences.ts index ee1a9fdbcf7d4..1a775d30f6e90 100644 --- a/src/testRunner/unittests/tsserver/projectReferences.ts +++ b/src/testRunner/unittests/tsserver/projectReferences.ts @@ -1,6 +1,7 @@ import * as ts from "../../_namespaces/ts.js"; import { dedent } from "../../_namespaces/Utils.js"; import { jsonToReadableText } from "../helpers.js"; +import { libContent } from "../helpers/contents.js"; import { solutionBuildWithBaseline } from "../helpers/solutionBuilder.js"; import { baselineTsserverLogs, @@ -1930,4 +1931,56 @@ const b: B = new B();`, }); } }); + + it("with dts file next to ts file", () => { + const indexDts: File = { + path: "/home/src/projects/project/src/index.d.ts", + content: dedent` + declare global { + interface Window { + electron: ElectronAPI + api: unknown + } + } + `, + }; + const host = createServerHost({ + [indexDts.path]: indexDts.content, + "/home/src/projects/project/src/index.ts": dedent` + const api = {} + `, + "/home/src/projects/project/tsconfig.json": jsonToReadableText({ + include: [ + "src/*.d.ts", + ], + references: [{ path: "./tsconfig.node.json" }], + }), + "/home/src/projects/project/tsconfig.node.json": jsonToReadableText({ + include: ["src/**/*"], + compilerOptions: { + composite: true, + }, + }), + [libFile.path]: libContent, + }); + const session = new TestSession(host); + openFilesForSession([{ file: indexDts, projectRootPath: "/home/src/projects/project" }], session); + session.executeCommandSeq({ + command: ts.server.protocol.CommandTypes.DocumentHighlights, + arguments: { + ...protocolFileLocationFromSubstring(indexDts, "global"), + filesToSearch: ["/home/src/projects/project/src/index.d.ts"], + }, + }); + session.executeCommandSeq({ + command: ts.server.protocol.CommandTypes.EncodedSemanticClassificationsFull, + arguments: { + file: indexDts.path, + start: 0, + length: indexDts.content.length, + format: "2020", + }, + }); + baselineTsserverLogs("projectReferences", "with dts file next to ts file", session); + }); }); diff --git a/src/testRunner/unittests/tsserver/projects.ts b/src/testRunner/unittests/tsserver/projects.ts index af6e99da25bdd..dfa401753906d 100644 --- a/src/testRunner/unittests/tsserver/projects.ts +++ b/src/testRunner/unittests/tsserver/projects.ts @@ -1416,7 +1416,7 @@ describe("unittests:: tsserver:: projects::", () => { }); } catch (e) { - assert.isTrue(e.message.indexOf("Debug Failure. False expression: Found script Info still attached to project") === 0); + session.logger.log(e.message); } baselineTsserverLogs("projects", "assert when removing project", session); }); diff --git a/tests/baselines/reference/tsserver/dynamicFiles/dynamic-file-with-projectRootPath-fails-when-useInferredProjectPerProjectRoot-is-false.js b/tests/baselines/reference/tsserver/dynamicFiles/dynamic-file-with-projectRootPath-fails-when-useInferredProjectPerProjectRoot-is-false.js index d3bac01b383ac..4b9e4a3236df5 100644 --- a/tests/baselines/reference/tsserver/dynamicFiles/dynamic-file-with-projectRootPath-fails-when-useInferredProjectPerProjectRoot-is-false.js +++ b/tests/baselines/reference/tsserver/dynamicFiles/dynamic-file-with-projectRootPath-fails-when-useInferredProjectPerProjectRoot-is-false.js @@ -32,6 +32,9 @@ Info seq [hh:mm:ss:mss] request: "seq": 1, "type": "request" } +Info seq [hh:mm:ss:mss] Debug Failure. False expression. +Verbose Debug Information: {"fileName":"^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js","currentDirectory":"/user/username/projects/myproject","hostCurrentDirectory":"/","openKeys":[]} +Dynamic files must always be opened with service's current directory or service should support inferred project per projectRootPath. Before request Info seq [hh:mm:ss:mss] request: diff --git a/tests/baselines/reference/tsserver/projectReferences/with-dts-file-next-to-ts-file.js b/tests/baselines/reference/tsserver/projectReferences/with-dts-file-next-to-ts-file.js new file mode 100644 index 0000000000000..590cf73593701 --- /dev/null +++ b/tests/baselines/reference/tsserver/projectReferences/with-dts-file-next-to-ts-file.js @@ -0,0 +1,329 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +Before request +//// [/home/src/projects/project/src/index.d.ts] +declare global { + interface Window { + electron: ElectronAPI + api: unknown + } +} + + +//// [/home/src/projects/project/src/index.ts] +const api = {} + + +//// [/home/src/projects/project/tsconfig.json] +{ + "include": [ + "src/*.d.ts" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} + +//// [/home/src/projects/project/tsconfig.node.json] +{ + "include": [ + "src/**/*" + ], + "compilerOptions": { + "composite": true + } +} + +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +declare const console: { log(msg: any): void; }; + + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/home/src/projects/project/src/index.d.ts", + "projectRootPath": "/home/src/projects/project" + }, + "seq": 1, + "type": "request" + } +Info seq [hh:mm:ss:mss] getConfigFileNameForFile:: File: /home/src/projects/project/src/index.d.ts ProjectRootPath: /home/src/projects/project:: Result: /home/src/projects/project/tsconfig.json +Info seq [hh:mm:ss:mss] Creating configuration project /home/src/projects/project/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/projects/project/tsconfig.json 2000 undefined Project: /home/src/projects/project/tsconfig.json WatchType: Config file +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "projectLoadingStart", + "body": { + "projectName": "/home/src/projects/project/tsconfig.json", + "reason": "Creating possible configured project for /home/src/projects/project/src/index.d.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /home/src/projects/project/tsconfig.json : { + "rootNames": [ + "/home/src/projects/project/src/index.d.ts" + ], + "options": { + "configFilePath": "/home/src/projects/project/tsconfig.json" + }, + "projectReferences": [ + { + "path": "/home/src/projects/project/tsconfig.node.json", + "originalPath": "./tsconfig.node.json" + } + ] +} +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src 0 undefined Config: /home/src/projects/project/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src 0 undefined Config: /home/src/projects/project/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /home/src/projects/project/tsconfig.json +Info seq [hh:mm:ss:mss] Config: /home/src/projects/project/tsconfig.node.json : { + "rootNames": [ + "/home/src/projects/project/src/index.ts" + ], + "options": { + "composite": true, + "configFilePath": "/home/src/projects/project/tsconfig.node.json" + } +} +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/projects/project/tsconfig.node.json 2000 undefined Project: /home/src/projects/project/tsconfig.json WatchType: Config file +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src 1 undefined Config: /home/src/projects/project/tsconfig.node.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src 1 undefined Config: /home/src/projects/project/tsconfig.node.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/projects/project/src/index.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/node_modules/@types 1 undefined Project: /home/src/projects/project/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/node_modules/@types 1 undefined Project: /home/src/projects/project/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/node_modules/@types 1 undefined Project: /home/src/projects/project/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/node_modules/@types 1 undefined Project: /home/src/projects/project/tsconfig.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /home/src/projects/project/tsconfig.json projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/home/src/projects/project/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (2) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };" + /home/src/projects/project/src/index.ts Text-1 "const api = {}\n" + + + ../../../../a/lib/lib.d.ts + Default library for target 'es5' + src/index.ts + Matched by include pattern 'src/*.d.ts' in 'tsconfig.json' + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "projectLoadingFinish", + "body": { + "projectName": "/home/src/projects/project/tsconfig.json" + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "telemetry", + "body": { + "telemetryEventName": "projectInfo", + "payload": { + "projectId": "1097a5f82e8323ba7aba7567ec06402f7ad4ea74abce44ec5efd223ac77ff169", + "fileStats": { + "js": 0, + "jsSize": 0, + "jsx": 0, + "jsxSize": 0, + "ts": 1, + "tsSize": 15, + "tsx": 0, + "tsxSize": 0, + "dts": 1, + "dtsSize": 413, + "deferred": 0, + "deferredSize": 0 + }, + "compilerOptions": {}, + "typeAcquisition": { + "enable": false, + "include": false, + "exclude": false + }, + "extends": false, + "files": false, + "include": true, + "exclude": false, + "compileOnSave": false, + "configFileName": "tsconfig.json", + "projectType": "configured", + "languageServiceEnabled": true, + "version": "FakeVersion" + } + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "configFileDiag", + "body": { + "triggerFile": "/home/src/projects/project/src/index.d.ts", + "configFile": "/home/src/projects/project/tsconfig.json", + "diagnostics": [] + } + } +Info seq [hh:mm:ss:mss] Project '/home/src/projects/project/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (2) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /home/src/projects/project/src/index.d.ts ProjectRootPath: /home/src/projects/project +Info seq [hh:mm:ss:mss] Projects: /home/src/projects/project/tsconfig.json +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/home/src/projects/node_modules/@types: *new* + {"pollingInterval":500} +/home/src/projects/project/node_modules/@types: *new* + {"pollingInterval":500} + +FsWatches:: +/a/lib/lib.d.ts: *new* + {} +/home/src/projects/project/src: *new* + {} +/home/src/projects/project/src/index.ts: *new* + {} +/home/src/projects/project/tsconfig.json: *new* + {} +/home/src/projects/project/tsconfig.node.json: *new* + {} + +FsWatchesRecursive:: +/home/src/projects/project/src: *new* + {} + +Projects:: +/home/src/projects/project/tsconfig.json (Configured) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + +ScriptInfos:: +/a/lib/lib.d.ts *new* + version: Text-1 + containingProjects: 1 + /home/src/projects/project/tsconfig.json +/home/src/projects/project/src/index.d.ts (Open) *new* + version: Text-0 + containingProjects: 1 + /home/src/projects/project/tsconfig.json *default* +/home/src/projects/project/src/index.ts *new* + version: Text-1 + containingProjects: 1 + /home/src/projects/project/tsconfig.json + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "documentHighlights", + "arguments": { + "file": "/home/src/projects/project/src/index.d.ts", + "line": 1, + "offset": 9, + "filesToSearch": [ + "/home/src/projects/project/src/index.d.ts" + ] + }, + "seq": 2, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "response": [ + { + "file": "/home/src/projects/project/src/index.ts", + "highlightSpans": [ + { + "start": { + "line": 1, + "offset": 7 + }, + "end": { + "line": 1, + "offset": 10 + }, + "contextStart": { + "line": 1, + "offset": 1 + }, + "contextEnd": { + "line": 1, + "offset": 15 + }, + "kind": "writtenReference" + } + ] + } + ], + "responseRequired": true + } +After request + +ScriptInfos:: +/a/lib/lib.d.ts + version: Text-1 + containingProjects: 1 + /home/src/projects/project/tsconfig.json +/home/src/projects/project/src/index.d.ts (Open) *changed* + version: SVC-1-0 *changed* + containingProjects: 1 + /home/src/projects/project/tsconfig.json *default* +/home/src/projects/project/src/index.ts + version: Text-1 + containingProjects: 1 + /home/src/projects/project/tsconfig.json + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "encodedSemanticClassifications-full", + "arguments": { + "file": "/home/src/projects/project/src/index.d.ts", + "start": 0, + "length": 99, + "format": "2020" + }, + "seq": 3, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "response": { + "spans": [ + 6, + 3, + 2057 + ], + "endOfLineState": 0 + }, + "responseRequired": true + } +After request diff --git a/tests/baselines/reference/tsserver/projects/assert-when-removing-project.js b/tests/baselines/reference/tsserver/projects/assert-when-removing-project.js index 19e2d200b1649..036d88cfb5d6f 100644 --- a/tests/baselines/reference/tsserver/projects/assert-when-removing-project.js +++ b/tests/baselines/reference/tsserver/projects/assert-when-removing-project.js @@ -148,4 +148,14 @@ Info seq [hh:mm:ss:mss] Files (2) commonFile1.ts Root file specified for compilation -Info seq [hh:mm:ss:mss] ----------------------------------------------- \ No newline at end of file +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Debug Failure. False expression: Found script Info still attached to project +Verbose Debug Information: /dev/null/inferredProject1*: ScriptInfos still attached: [ + { + "fileName": "/a/b/commonFile2.ts", + "projects": [ + "/dev/null/inferredProject1*" + ], + "hasMixedContent": false + } +] \ No newline at end of file From 35237e6a0e65c91c55d41559a196e3851ccfea21 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 16 May 2024 12:03:55 -0700 Subject: [PATCH 2/3] First update - remove rootFiles --- src/server/project.ts | 28 +++++++------------ tests/baselines/reference/api/typescript.d.ts | 3 +- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/server/project.ts b/src/server/project.ts index d993607fa68d6..0a4f143346f5d 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -88,7 +88,6 @@ import { noopFileWatcher, normalizePath, normalizeSlashes, - orderedRemoveItem, PackageJsonAutoImportPreference, PackageJsonInfo, ParsedCommandLine, @@ -309,8 +308,7 @@ const enum TypingWatcherType { type TypingWatchers = Map & { isInvoked?: boolean; }; export abstract class Project implements LanguageServiceHost, ModuleResolutionHost { - private rootFiles: ScriptInfo[] = []; - private rootFilesMap = new Map(); + private rootFilesMap = new Map(); private program: Program | undefined; private externalFiles: SortedReadonlyArray | undefined; private missingFilesMap: Map | undefined; @@ -641,7 +639,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo } getScriptFileNames() { - if (!this.rootFiles) { + if (!this.rootFilesMap.size) { return ts.emptyArray; } @@ -667,7 +665,6 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo const existingValue = this.rootFilesMap.get(scriptInfo.path); if (existingValue && existingValue.info !== scriptInfo) { // This was missing path earlier but now the file exists. Update the root - this.rootFiles.push(scriptInfo); existingValue.info = scriptInfo; } scriptInfo.attachToProject(this); @@ -1079,12 +1076,9 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo // Release external files forEach(this.externalFiles, externalFile => this.detachScriptInfoIfNotRoot(externalFile)); // Always remove root files from the project - for (const root of this.rootFiles) { - root.detachFromProject(this); - } + this.rootFilesMap.forEach(root => root.info?.detachFromProject(this)); this.projectService.pendingEnsureProjectForOpenFiles = true; - this.rootFiles = undefined!; this.rootFilesMap = undefined!; this.externalFiles = undefined; this.program = undefined; @@ -1135,11 +1129,11 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo } isClosed() { - return this.rootFiles === undefined; + return this.rootFilesMap === undefined; } hasRoots() { - return this.rootFiles && this.rootFiles.length > 0; + return !!this.rootFilesMap?.size; } /** @internal */ @@ -1147,8 +1141,8 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo return false; } - getRootFiles() { - return this.rootFiles && this.rootFiles.map(info => info.fileName); + getRootFiles(): NormalizedPath[] { + return this.rootFilesMap && arrayFrom(ts.mapDefinedIterator(this.rootFilesMap.values(), value => value.info?.fileName)); } /** @internal */ @@ -1157,13 +1151,13 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo } getRootScriptInfos() { - return this.rootFiles; + return arrayFrom(ts.mapDefinedIterator(this.rootFilesMap.values(), value => value.info)); } getScriptInfos(): ScriptInfo[] { if (!this.languageServiceEnabled) { // if language service is not enabled - return just root files - return this.rootFiles; + return this.getRootScriptInfos(); } return map(this.program!.getSourceFiles(), sourceFile => { const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.resolvedPath); @@ -1256,13 +1250,12 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo } isRoot(info: ScriptInfo) { - return this.rootFilesMap && this.rootFilesMap.get(info.path)?.info === info; + return this.rootFilesMap?.get(info.path)?.info === info; } // add a root file to project addRoot(info: ScriptInfo, fileName?: NormalizedPath) { Debug.assert(!this.isRoot(info)); - this.rootFiles.push(info); this.rootFilesMap.set(info.path, { fileName: fileName || info.fileName, info }); info.attachToProject(this); @@ -2006,7 +1999,6 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo // remove a root file from project protected removeRoot(info: ScriptInfo): void { - orderedRemoveItem(this.rootFiles, info); this.rootFilesMap.delete(info.path); } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 59692349afdc9..1a45222440d28 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2756,7 +2756,6 @@ declare namespace ts { private compilerOptions; compileOnSaveEnabled: boolean; protected watchOptions: WatchOptions | undefined; - private rootFiles; private rootFilesMap; private program; private externalFiles; @@ -2837,7 +2836,7 @@ declare namespace ts { private detachScriptInfoIfNotRoot; isClosed(): boolean; hasRoots(): boolean; - getRootFiles(): ts.server.NormalizedPath[]; + getRootFiles(): NormalizedPath[]; getRootScriptInfos(): ts.server.ScriptInfo[]; getScriptInfos(): ScriptInfo[]; getExcludedFiles(): readonly NormalizedPath[]; From c1fc93dcd02195352ddee5983967557683dd3a65 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 17 May 2024 10:34:18 -0700 Subject: [PATCH 3/3] Update roots script info on project update to have correct ones --- src/server/editorServices.ts | 2 +- src/server/project.ts | 10 + ...nced-project-and-using-declaration-maps.js | 21 +- .../with-dts-file-next-to-ts-file.js | 379 +++++++++++++++--- 4 files changed, 346 insertions(+), 66 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 7dc2c1cbd8eeb..ae10458e5ad79 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -2893,7 +2893,7 @@ export class ProjectService { path = normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName); const existingValue = projectRootFilesMap.get(path); if (existingValue) { - if (existingValue.info) { + if (existingValue.info?.path === path) { project.removeFile(existingValue.info, /*fileExists*/ false, /*detachFromProject*/ true); existingValue.info = undefined; } diff --git a/src/server/project.ts b/src/server/project.ts index 0a4f143346f5d..94c2537a95333 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1579,6 +1579,16 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo }); } + // Update roots + this.rootFilesMap.forEach((value, path) => { + const file = this.program!.getSourceFileByPath(path); + const info = value.info; + if (!file || value.info?.path === file.resolvedPath) return; + value.info = this.projectService.getScriptInfo(file.fileName)!; + Debug.assert(value.info.isAttached(this)); + info?.detachFromProject(this); + }); + // Update the missing file paths watcher updateMissingFilePathsWatch( this.program, diff --git a/tests/baselines/reference/tsserver/projectReferences/root-file-is-file-from-referenced-project-and-using-declaration-maps.js b/tests/baselines/reference/tsserver/projectReferences/root-file-is-file-from-referenced-project-and-using-declaration-maps.js index be60282465dd8..bc5ff13880e54 100644 --- a/tests/baselines/reference/tsserver/projectReferences/root-file-is-file-from-referenced-project-and-using-declaration-maps.js +++ b/tests/baselines/reference/tsserver/projectReferences/root-file-is-file-from-referenced-project-and-using-declaration-maps.js @@ -701,7 +701,7 @@ Info seq [hh:mm:ss:mss] Files (4) Info seq [hh:mm:ss:mss] ----------------------------------------------- Info seq [hh:mm:ss:mss] Open files: Info seq [hh:mm:ss:mss] FileName: /user/username/projects/project/src/common/input/keyboard.ts ProjectRootPath: undefined -Info seq [hh:mm:ss:mss] Projects: /user/username/projects/project/src/common/tsconfig.json,/user/username/projects/project/src/tsconfig.json +Info seq [hh:mm:ss:mss] Projects: /user/username/projects/project/src/common/tsconfig.json Info seq [hh:mm:ss:mss] FileName: /user/username/projects/project/src/terminal.ts ProjectRootPath: undefined Info seq [hh:mm:ss:mss] Projects: /user/username/projects/project/src/tsconfig.json Info seq [hh:mm:ss:mss] response: @@ -777,16 +777,14 @@ ScriptInfos:: version: Text-1 containingProjects: 1 /user/username/projects/project/src/tsconfig.json -/user/username/projects/project/src/common/input/keyboard.test.ts *changed* +/user/username/projects/project/src/common/input/keyboard.test.ts version: Text-1 - containingProjects: 2 *changed* + containingProjects: 1 /user/username/projects/project/src/common/tsconfig.json - /user/username/projects/project/src/tsconfig.json *new* -/user/username/projects/project/src/common/input/keyboard.ts (Open) *changed* +/user/username/projects/project/src/common/input/keyboard.ts (Open) version: SVC-1-0 - containingProjects: 2 *changed* + containingProjects: 1 /user/username/projects/project/src/common/tsconfig.json *default* - /user/username/projects/project/src/tsconfig.json *new* /user/username/projects/project/src/terminal.ts (Open) *new* version: SVC-1-0 containingProjects: 1 @@ -972,9 +970,8 @@ Projects:: projectProgramVersion: 1 documentPositionMappers: 1 *changed* /user/username/projects/project/out/input/keyboard.d.ts: DocumentPositionMapper1 *new* - originalConfiguredProjects: 2 *changed* + originalConfiguredProjects: 1 *changed* /user/username/projects/project/src/common/tsconfig.json *new* - /user/username/projects/project/src/tsconfig.json *new* ScriptInfos:: /a/lib/lib.d.ts @@ -1000,14 +997,12 @@ ScriptInfos:: /user/username/projects/project/src/tsconfig.json /user/username/projects/project/src/common/input/keyboard.test.ts version: Text-1 - containingProjects: 2 + containingProjects: 1 /user/username/projects/project/src/common/tsconfig.json - /user/username/projects/project/src/tsconfig.json /user/username/projects/project/src/common/input/keyboard.ts (Open) version: SVC-1-0 - containingProjects: 2 + containingProjects: 1 /user/username/projects/project/src/common/tsconfig.json *default* - /user/username/projects/project/src/tsconfig.json /user/username/projects/project/src/terminal.ts (Open) version: SVC-1-0 containingProjects: 1 diff --git a/tests/baselines/reference/tsserver/projectReferences/with-dts-file-next-to-ts-file.js b/tests/baselines/reference/tsserver/projectReferences/with-dts-file-next-to-ts-file.js index 590cf73593701..6f2e26b3eafc8 100644 --- a/tests/baselines/reference/tsserver/projectReferences/with-dts-file-next-to-ts-file.js +++ b/tests/baselines/reference/tsserver/projectReferences/with-dts-file-next-to-ts-file.js @@ -184,24 +184,136 @@ Info seq [hh:mm:ss:mss] event: "diagnostics": [] } } -Info seq [hh:mm:ss:mss] Project '/home/src/projects/project/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Creating configuration project /home/src/projects/project/tsconfig.node.json +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "projectLoadingStart", + "body": { + "projectName": "/home/src/projects/project/tsconfig.node.json", + "reason": "Creating project referenced in solution /home/src/projects/project/tsconfig.json to find possible configured project for /home/src/projects/project/src/index.d.ts to open" + } + } +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /home/src/projects/project/tsconfig.node.json +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/node_modules/@types 1 undefined Project: /home/src/projects/project/tsconfig.node.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/node_modules/@types 1 undefined Project: /home/src/projects/project/tsconfig.node.json WatchType: Type roots +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/node_modules/@types 1 undefined Project: /home/src/projects/project/tsconfig.node.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/node_modules/@types 1 undefined Project: /home/src/projects/project/tsconfig.node.json WatchType: Type roots +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /home/src/projects/project/tsconfig.node.json projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/home/src/projects/project/tsconfig.node.json' (Configured) Info seq [hh:mm:ss:mss] Files (2) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };" + /home/src/projects/project/src/index.ts Text-1 "const api = {}\n" + + + ../../../../a/lib/lib.d.ts + Default library for target 'es5' + src/index.ts + Matched by include pattern 'src/**/*' in 'tsconfig.node.json' Info seq [hh:mm:ss:mss] ----------------------------------------------- -Info seq [hh:mm:ss:mss] Open files: -Info seq [hh:mm:ss:mss] FileName: /home/src/projects/project/src/index.d.ts ProjectRootPath: /home/src/projects/project -Info seq [hh:mm:ss:mss] Projects: /home/src/projects/project/tsconfig.json -Info seq [hh:mm:ss:mss] response: +Info seq [hh:mm:ss:mss] event: { - "responseRequired": false + "seq": 0, + "type": "event", + "event": "projectLoadingFinish", + "body": { + "projectName": "/home/src/projects/project/tsconfig.node.json" + } } -After request +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "telemetry", + "body": { + "telemetryEventName": "projectInfo", + "payload": { + "projectId": "1e7c125feb7a2a7047f05d3ea96f5c07aebb6404fadc111f1dab518d4196edea", + "fileStats": { + "js": 0, + "jsSize": 0, + "jsx": 0, + "jsxSize": 0, + "ts": 1, + "tsSize": 15, + "tsx": 0, + "tsxSize": 0, + "dts": 1, + "dtsSize": 413, + "deferred": 0, + "deferredSize": 0 + }, + "compilerOptions": { + "composite": true + }, + "typeAcquisition": { + "enable": false, + "include": false, + "exclude": false + }, + "extends": false, + "files": false, + "include": true, + "exclude": false, + "compileOnSave": false, + "configFileName": "other", + "projectType": "configured", + "languageServiceEnabled": true, + "version": "FakeVersion" + } + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "configFileDiag", + "body": { + "triggerFile": "/home/src/projects/project/src/index.d.ts", + "configFile": "/home/src/projects/project/tsconfig.node.json", + "diagnostics": [] + } + } +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/projects/project/src/tsconfig.json 2000 undefined WatchType: Config file for the inferred project root +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/projects/project/src/jsconfig.json 2000 undefined WatchType: Config file for the inferred project root +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/projects/project/jsconfig.json 2000 undefined WatchType: Config file for the inferred project root +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1* +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred) +Info seq [hh:mm:ss:mss] Files (2) + /a/lib/lib.d.ts Text-1 "/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };" + /home/src/projects/project/src/index.d.ts SVC-1-0 "declare global {\n interface Window {\n electron: ElectronAPI\n api: unknown\n }\n}\n" + + + ../../../../../a/lib/lib.d.ts + Default library for target 'es5' + index.d.ts + Root file specified for compilation + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +TI:: Creating typing installer PolledWatches:: /home/src/projects/node_modules/@types: *new* {"pollingInterval":500} +/home/src/projects/project/jsconfig.json: *new* + {"pollingInterval":2000} /home/src/projects/project/node_modules/@types: *new* {"pollingInterval":500} +/home/src/projects/project/src/jsconfig.json: *new* + {"pollingInterval":2000} +/home/src/projects/project/src/node_modules/@types: *new* + {"pollingInterval":500} +/home/src/projects/project/src/tsconfig.json: *new* + {"pollingInterval":2000} FsWatches:: /a/lib/lib.d.ts: *new* @@ -220,23 +332,213 @@ FsWatchesRecursive:: {} Projects:: +/dev/null/inferredProject1* (Inferred) *new* + projectStateVersion: 1 + projectProgramVersion: 0 /home/src/projects/project/tsconfig.json (Configured) *new* projectStateVersion: 1 projectProgramVersion: 1 + noOpenRef: true +/home/src/projects/project/tsconfig.node.json (Configured) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + noOpenRef: true ScriptInfos:: /a/lib/lib.d.ts *new* version: Text-1 - containingProjects: 1 + containingProjects: 3 /home/src/projects/project/tsconfig.json + /home/src/projects/project/tsconfig.node.json + /dev/null/inferredProject1* /home/src/projects/project/src/index.d.ts (Open) *new* - version: Text-0 + version: SVC-1-0 containingProjects: 1 - /home/src/projects/project/tsconfig.json *default* + /dev/null/inferredProject1* *default* /home/src/projects/project/src/index.ts *new* version: Text-1 - containingProjects: 1 + containingProjects: 2 /home/src/projects/project/tsconfig.json + /home/src/projects/project/tsconfig.node.json + +TI:: [hh:mm:ss:mss] Global cache location '/a/data', safe file path '/safeList.json', types map path /typesMap.json +TI:: [hh:mm:ss:mss] Processing cache location '/a/data' +TI:: [hh:mm:ss:mss] Trying to find '/a/data/package.json'... +TI:: [hh:mm:ss:mss] Finished processing cache location '/a/data' +TI:: [hh:mm:ss:mss] Npm config file: /a/data/package.json +TI:: [hh:mm:ss:mss] Npm config file: '/a/data/package.json' is missing, creating new one... +TI:: [hh:mm:ss:mss] Updating types-registry npm package... +TI:: [hh:mm:ss:mss] npm install --ignore-scripts types-registry@latest +TI:: [hh:mm:ss:mss] Updated types-registry npm package +TI:: typing installer creation complete +//// [/a/data/package.json] +{ "private": true } + +//// [/a/data/node_modules/types-registry/index.json] +{ + "entries": {} +} + + +TI:: [hh:mm:ss:mss] Got install request + { + "projectName": "/dev/null/inferredProject1*", + "fileNames": [ + "/a/lib/lib.d.ts", + "/home/src/projects/project/src/index.d.ts" + ], + "compilerOptions": { + "target": 1, + "jsx": 1, + "allowNonTsExtensions": true, + "allowJs": true, + "noEmitForJsFiles": true + }, + "typeAcquisition": { + "enable": true, + "include": [], + "exclude": [] + }, + "unresolvedImports": [], + "projectRootPath": "/home/src/projects/project/src", + "kind": "discover" + } +TI:: [hh:mm:ss:mss] Failed to load safelist from types map file '/typesMap.json' +TI:: [hh:mm:ss:mss] Explicitly included types: [] +TI:: [hh:mm:ss:mss] Inferred typings from unresolved imports: [] +TI:: [hh:mm:ss:mss] Finished typings discovery: + { + "cachedTypingPaths": [], + "newTypingNames": [], + "filesToWatch": [ + "/home/src/projects/project/src/bower_components", + "/home/src/projects/project/src/node_modules" + ] + } +TI:: [hh:mm:ss:mss] Sending response: + { + "kind": "action::watchTypingLocations", + "projectName": "/dev/null/inferredProject1*", + "files": [ + "/home/src/projects/project/src/bower_components", + "/home/src/projects/project/src/node_modules" + ] + } +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src/bower_components 1 undefined Project: /dev/null/inferredProject1* WatchType: Directory location for typing installer +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src/bower_components 1 undefined Project: /dev/null/inferredProject1* WatchType: Directory location for typing installer +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src/node_modules 1 undefined Project: /dev/null/inferredProject1* WatchType: Directory location for typing installer +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/projects/project/src/node_modules 1 undefined Project: /dev/null/inferredProject1* WatchType: Directory location for typing installer +TI:: [hh:mm:ss:mss] Sending response: + { + "projectName": "/dev/null/inferredProject1*", + "typeAcquisition": { + "enable": true, + "include": [], + "exclude": [] + }, + "compilerOptions": { + "target": 1, + "jsx": 1, + "allowNonTsExtensions": true, + "allowJs": true, + "noEmitForJsFiles": true + }, + "typings": [], + "unresolvedImports": [], + "kind": "action::set" + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "setTypings", + "body": { + "projectName": "/dev/null/inferredProject1*", + "typeAcquisition": { + "enable": true, + "include": [], + "exclude": [] + }, + "compilerOptions": { + "target": 1, + "jsx": 1, + "allowNonTsExtensions": true, + "allowJs": true, + "noEmitForJsFiles": true + }, + "typings": [], + "unresolvedImports": [], + "kind": "action::set" + } + } +TI:: [hh:mm:ss:mss] No new typings were requested as a result of typings discovery +Info seq [hh:mm:ss:mss] Project '/home/src/projects/project/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (2) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Project '/home/src/projects/project/tsconfig.node.json' (Configured) +Info seq [hh:mm:ss:mss] Files (2) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred) +Info seq [hh:mm:ss:mss] Files (2) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /home/src/projects/project/src/index.d.ts ProjectRootPath: /home/src/projects/project +Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1* +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/home/src/projects/node_modules/@types: + {"pollingInterval":500} +/home/src/projects/project/jsconfig.json: + {"pollingInterval":2000} +/home/src/projects/project/node_modules/@types: + {"pollingInterval":500} +/home/src/projects/project/src/bower_components: *new* + {"pollingInterval":500} +/home/src/projects/project/src/jsconfig.json: + {"pollingInterval":2000} +/home/src/projects/project/src/node_modules: *new* + {"pollingInterval":500} +/home/src/projects/project/src/node_modules/@types: + {"pollingInterval":500} +/home/src/projects/project/src/tsconfig.json: + {"pollingInterval":2000} + +FsWatches:: +/a/lib/lib.d.ts: + {} +/home/src/projects/project/src: + {} +/home/src/projects/project/src/index.ts: + {} +/home/src/projects/project/tsconfig.json: + {} +/home/src/projects/project/tsconfig.node.json: + {} + +FsWatchesRecursive:: +/home/src/projects/project/src: + {} + +Projects:: +/dev/null/inferredProject1* (Inferred) *changed* + projectStateVersion: 1 + projectProgramVersion: 1 *changed* +/home/src/projects/project/tsconfig.json (Configured) + projectStateVersion: 1 + projectProgramVersion: 1 + noOpenRef: true +/home/src/projects/project/tsconfig.node.json (Configured) + projectStateVersion: 1 + projectProgramVersion: 1 + noOpenRef: true Before request @@ -256,50 +558,11 @@ Info seq [hh:mm:ss:mss] request: } Info seq [hh:mm:ss:mss] response: { - "response": [ - { - "file": "/home/src/projects/project/src/index.ts", - "highlightSpans": [ - { - "start": { - "line": 1, - "offset": 7 - }, - "end": { - "line": 1, - "offset": 10 - }, - "contextStart": { - "line": 1, - "offset": 1 - }, - "contextEnd": { - "line": 1, - "offset": 15 - }, - "kind": "writtenReference" - } - ] - } - ], + "response": [], "responseRequired": true } After request -ScriptInfos:: -/a/lib/lib.d.ts - version: Text-1 - containingProjects: 1 - /home/src/projects/project/tsconfig.json -/home/src/projects/project/src/index.d.ts (Open) *changed* - version: SVC-1-0 *changed* - containingProjects: 1 - /home/src/projects/project/tsconfig.json *default* -/home/src/projects/project/src/index.ts - version: Text-1 - containingProjects: 1 - /home/src/projects/project/tsconfig.json - Before request Info seq [hh:mm:ss:mss] request: @@ -318,9 +581,21 @@ Info seq [hh:mm:ss:mss] response: { "response": { "spans": [ + 8, + 6, + 1025, + 31, 6, + 769, + 48, + 8, + 2561, + 58, + 11, + 1536, + 78, 3, - 2057 + 2561 ], "endOfLineState": 0 },