Skip to content
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

Handle already released source files that dont match scriptKind #54401

Merged
merged 1 commit into from
May 25, 2023
Merged
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
8 changes: 7 additions & 1 deletion src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1713,9 +1713,13 @@ export function createLanguageService(
// calculate this early so it's not undefined if downleveled to a var (or, if emitted
// as a const variable without downleveling, doesn't crash).
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
let releasedScriptKinds: Set<Path> | undefined = new Set();

// If the program is already up-to-date, we can reuse it
if (isProgramUptoDate(program, rootFileNames, newSettings, (_path, fileName) => host.getScriptVersion(fileName), fileName => compilerHost!.fileExists(fileName), hasInvalidatedResolutions, hasInvalidatedLibResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
compilerHost = undefined;
parsedCommandLines = undefined;
releasedScriptKinds = undefined;
return;
}

Expand All @@ -1738,6 +1742,7 @@ export function createLanguageService(
// After this point, the cache needs to be cleared to allow all collected snapshots to be released
compilerHost = undefined;
parsedCommandLines = undefined;
releasedScriptKinds = undefined;

// We reset this cache on structure invalidation so we don't hold on to outdated files for long; however we can't use the `compilerHost` above,
// Because it only functions until `hostCache` is cleared, while we'll potentially need the functionality to lazily read sourcemap files during
Expand Down Expand Up @@ -1841,12 +1846,13 @@ export function createLanguageService(
// We do not support the scenario where a host can modify a registered
// file's script kind, i.e. in one project some file is treated as ".ts"
// and in another as ".js"
if (scriptKind === oldSourceFile.scriptKind) {
if (scriptKind === oldSourceFile.scriptKind || releasedScriptKinds!.has(oldSourceFile.resolvedPath)) {
return documentRegistry.updateDocumentWithKey(fileName, path, host, documentRegistryBucketKey, scriptSnapshot, scriptVersion, scriptKind, languageVersionOrOptions);
}
else {
// Release old source file and fall through to aquire new file with new script kind
documentRegistry.releaseDocumentWithKey(oldSourceFile.resolvedPath, documentRegistry.getKeyForCompilationSettings(program.getCompilerOptions()), oldSourceFile.scriptKind, oldSourceFile.impliedNodeFormat);
releasedScriptKinds!.add(oldSourceFile.resolvedPath);
}
}

Expand Down
39 changes: 39 additions & 0 deletions src/testRunner/unittests/tsserver/documentRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
closeFilesForSession,
createLoggerWithInMemoryLogs,
createProjectService,
createSession,
openFilesForSession,
TestProjectService,
} from "../helpers/tsserver";
import {
Expand Down Expand Up @@ -103,3 +106,39 @@ describe("unittests:: tsserver:: documentRegistry:: document registry in project
baselineTsserverLogs("documentRegistry", "Caches the source file if script info is orphan, and orphan script info changes", service);
});
});

describe("unittests:: tsserver:: documentRegistry:: works when reusing orphan script info with different scriptKind", () => {
it("works when reusing orphan script info with different scriptKind", () => {
const host = createServerHost({});
const session = createSession(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
const newText = "exrpot const x = 10;";
const content = `import x from 'react';\n${newText}`;
openFilesForSession([
{ file: "^/inmemory/model/6", content, scriptKindName: "TSX", projectRootPath: "/users/user/projects/san" },
{ file: "^/inmemory/model/4", content, scriptKindName: "TSX", projectRootPath: "/users/user/projects/san" },
], session);
closeFilesForSession(["^/inmemory/model/4"], session);
session.executeCommandSeq<ts.server.protocol.UpdateOpenRequest>({
command: ts.server.protocol.CommandTypes.UpdateOpen,
arguments: {
changedFiles: [{
fileName: "^/inmemory/model/6",
textChanges: [{
newText,
start: { line: 1, offset: 1 },
end: { line: 2, offset: newText.length + 1 } // Remove the import so that structure is not reused
}]
}],
openFiles: [
{
file: "^/inmemory/model/4",
fileContent: newText,
projectRootPath: "/users/user/projects/san", // Add same document with different script kind
scriptKindName: "TS"
},
]
}
});
baselineTsserverLogs("documentRegistry", "works when reusing orphan script info with different scriptKind", session);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/a/lib/typesMap.json" doesn't exist
Before request

Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "^/inmemory/model/6",
"projectRootPath": "/users/user/projects/san",
"fileContent": "import x from 'react';\nexrpot const x = 10;",
"scriptKindName": "TSX"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: ^/inmemory/model
Info seq [hh:mm:ss:mss] For info: ^/inmemory/model/6 :: No config files found.
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/san/^ 1 undefined Project: /dev/null/inferredProject1* WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/san/^ 1 undefined Project: /dev/null/inferredProject1* WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/san/node_modules 1 undefined Project: /dev/null/inferredProject1* WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/san/node_modules 1 undefined Project: /dev/null/inferredProject1* WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/node_modules 1 undefined Project: /dev/null/inferredProject1* WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/node_modules 1 undefined Project: /dev/null/inferredProject1* WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/san/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/san/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /users/user/projects/node_modules/@types 1 undefined Project: /dev/null/inferredProject1* WatchType: Type roots
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/user/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* Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
^/inmemory/model/6 SVC-1-0 "import x from 'react';\nexrpot const x = 10;"


^/inmemory/model/6
Root file specified for compilation

Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)

Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: ^/inmemory/model/6 ProjectRootPath: /users/user/projects/san
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request

PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
/users/user/projects/node_modules: *new*
{"pollingInterval":500}
/users/user/projects/node_modules/@types: *new*
{"pollingInterval":500}
/users/user/projects/san/^: *new*
{"pollingInterval":500}
/users/user/projects/san/node_modules: *new*
{"pollingInterval":500}
/users/user/projects/san/node_modules/@types: *new*
{"pollingInterval":500}

Before request

Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "^/inmemory/model/4",
"projectRootPath": "/users/user/projects/san",
"fileContent": "import x from 'react';\nexrpot const x = 10;",
"scriptKindName": "TSX"
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: ^/inmemory/model
Info seq [hh:mm:ss:mss] For info: ^/inmemory/model/4 :: No config files found.
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 2 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)
^/inmemory/model/6 SVC-1-0 "import x from 'react';\nexrpot const x = 10;"
^/inmemory/model/4 SVC-1-0 "import x from 'react';\nexrpot const x = 10;"


^/inmemory/model/6
Root file specified for compilation
^/inmemory/model/4
Root file specified for compilation

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: ^/inmemory/model/6 ProjectRootPath: /users/user/projects/san
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] FileName: ^/inmemory/model/4 ProjectRootPath: /users/user/projects/san
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request

Before request

Info seq [hh:mm:ss:mss] request:
{
"command": "close",
"arguments": {
"file": "^/inmemory/model/4"
},
"seq": 3,
"type": "request"
}
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: ^/inmemory/model/6 ProjectRootPath: /users/user/projects/san
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request

Before request

Info seq [hh:mm:ss:mss] request:
{
"command": "updateOpen",
"arguments": {
"changedFiles": [
{
"fileName": "^/inmemory/model/6",
"textChanges": [
{
"newText": "exrpot const x = 10;",
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 2,
"offset": 21
}
}
]
}
],
"openFiles": [
{
"file": "^/inmemory/model/4",
"fileContent": "exrpot const x = 10;",
"projectRootPath": "/users/user/projects/san",
"scriptKindName": "TS"
}
]
},
"seq": 4,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: ^/inmemory/model
Info seq [hh:mm:ss:mss] For info: ^/inmemory/model/4 :: No config files found.
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 3 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (2)
^/inmemory/model/6 SVC-1-1 "exrpot const x = 10;"
^/inmemory/model/4 SVC-2-0 "exrpot const x = 10;"


^/inmemory/model/6
Root file specified for compilation
^/inmemory/model/4
Root file specified for compilation

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: ^/inmemory/model/6 ProjectRootPath: /users/user/projects/san
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] FileName: ^/inmemory/model/4 ProjectRootPath: /users/user/projects/san
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] response:
{
"response": true,
"responseRequired": true
}
After request
Loading