Skip to content

Commit 55fb052

Browse files
committed
Allow creation of relative path file infos only for open script infos
Fixes #20476
1 parent 41b1930 commit 55fb052

File tree

2 files changed

+49
-11
lines changed

2 files changed

+49
-11
lines changed

src/server/editorServices.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2109,7 +2109,20 @@ namespace ts.server {
21092109
}
21102110

21112111
private getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName: NormalizedPath, currentDirectory: string, scriptKind: ScriptKind | undefined, hasMixedContent: boolean | undefined, hostToQueryFileExistsOn: DirectoryStructureHost | undefined) {
2112-
return this.getOrCreateScriptInfoWorker(fileName, currentDirectory, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
2112+
if (isRootedDiskPath(fileName) || isDynamicFileName(fileName)) {
2113+
return this.getOrCreateScriptInfoWorker(fileName, currentDirectory, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
2114+
}
2115+
2116+
// This is non rooted path with different current directory than project service current directory
2117+
// Only paths recognized are open relative file paths
2118+
const info = this.openFilesWithNonRootedDiskPath.get(this.toCanonicalFileName(fileName));
2119+
if (info) {
2120+
return info;
2121+
}
2122+
2123+
// This means triple slash references wont be resolved in dynamic and unsaved files
2124+
// which is intentional since we dont know what it means to be relative to non disk files
2125+
return undefined;
21132126
}
21142127

21152128
private getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, currentDirectory: string, fileContent: string | undefined, scriptKind: ScriptKind | undefined, hasMixedContent: boolean | undefined) {
@@ -2126,7 +2139,7 @@ namespace ts.server {
21262139
let info = this.getScriptInfoForPath(path);
21272140
if (!info) {
21282141
const isDynamic = isDynamicFileName(fileName);
2129-
Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nScript info with non-dynamic relative file name can only be open script info`);
2142+
Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nScript info with non-dynamic relative file name can only be open script info or in context of host currentDirectory`);
21302143
Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nOpen script files with non rooted disk path opened with current directory context cannot have same canonical names`);
21312144
Debug.assert(!isDynamic || this.currentDirectory === currentDirectory, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nDynamic files must always have current directory context since containing external project name will always match the script info name.`);
21322145
// If the file is not opened by client and the file doesnot exist on the disk, return
@@ -2139,7 +2152,7 @@ namespace ts.server {
21392152
if (!openedByClient) {
21402153
this.watchClosedScriptInfo(info);
21412154
}
2142-
else if (!isRootedDiskPath(fileName) && currentDirectory !== this.currentDirectory) {
2155+
else if (!isRootedDiskPath(fileName) && !isDynamic) {
21432156
// File that is opened by user but isn't rooted disk path
21442157
this.openFilesWithNonRootedDiskPath.set(this.toCanonicalFileName(fileName), info);
21452158
}

src/testRunner/unittests/tsserverProjectSystem.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3301,7 +3301,7 @@ namespace ts.projectSystem {
33013301
});
33023302
});
33033303

3304-
it("dynamic file with reference paths external project", () => {
3304+
it("dynamic file with reference paths without external project", () => {
33053305
const file: File = {
33063306
path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js",
33073307
content: `/// <reference path="../../../../../../typings/@epic/Core.d.ts" />
@@ -3899,25 +3899,39 @@ var x = 10;`
38993899

39003900
describe("when opening new file that doesnt exist on disk yet", () => {
39013901
function verifyNonExistentFile(useProjectRoot: boolean) {
3902-
const host = createServerHost([libFile]);
3902+
const folderPath = "/user/someuser/projects/someFolder";
3903+
const fileInRoot: File = {
3904+
path: `/src/somefile.d.ts`,
3905+
content: "class c { }"
3906+
};
3907+
const fileInProjectRoot: File = {
3908+
path: `${folderPath}/src/somefile.d.ts`,
3909+
content: "class c { }"
3910+
};
3911+
const host = createServerHost([libFile, fileInRoot, fileInProjectRoot]);
39033912
const { hasError, errorLogger } = createErrorLogger();
39043913
const session = createSession(host, { canUseEvents: true, logger: errorLogger, useInferredProjectPerProjectRoot: true });
39053914

3906-
const folderPath = "/user/someuser/projects/someFolder";
39073915
const projectService = session.getProjectService();
39083916
const untitledFile = "untitled:Untitled-1";
3917+
const refPathNotFound1 = "../../../../../../typings/@epic/Core.d.ts";
3918+
const refPathNotFound2 = "./src/somefile.d.ts";
3919+
const fileContent = `/// <reference path="${refPathNotFound1}" />
3920+
/// <reference path="${refPathNotFound2}" />`;
39093921
session.executeCommandSeq<protocol.OpenRequest>({
39103922
command: server.CommandNames.Open,
39113923
arguments: {
39123924
file: untitledFile,
3913-
fileContent: `/// <reference path="../../../../../../typings/@epic/Core.d.ts" />`,
3925+
fileContent,
39143926
scriptKindName: "TS",
39153927
projectRootPath: useProjectRoot ? folderPath : undefined
39163928
}
39173929
});
39183930
checkNumberOfProjects(projectService, { inferredProjects: 1 });
39193931
const infoForUntitledAtProjectRoot = projectService.getScriptInfoForPath(`${folderPath.toLowerCase()}/${untitledFile.toLowerCase()}` as Path);
39203932
const infoForUnitiledAtRoot = projectService.getScriptInfoForPath(`/${untitledFile.toLowerCase()}` as Path);
3933+
const infoForSomefileAtProjectRoot = projectService.getScriptInfoForPath(`/${folderPath.toLowerCase()}/src/somefile.d.ts` as Path);
3934+
const infoForSomefileAtRoot = projectService.getScriptInfoForPath(`${fileInRoot.path.toLowerCase()}` as Path);
39213935
if (useProjectRoot) {
39223936
assert.isDefined(infoForUntitledAtProjectRoot);
39233937
assert.isUndefined(infoForUnitiledAtRoot);
@@ -3926,7 +3940,11 @@ var x = 10;`
39263940
assert.isDefined(infoForUnitiledAtRoot);
39273941
assert.isUndefined(infoForUntitledAtProjectRoot);
39283942
}
3929-
host.checkTimeoutQueueLength(2);
3943+
assert.isUndefined(infoForSomefileAtRoot);
3944+
assert.isUndefined(infoForSomefileAtProjectRoot);
3945+
3946+
// Since this is not js project so no typings are queued
3947+
host.checkTimeoutQueueLength(0);
39303948

39313949
const newTimeoutId = host.getNextTimeoutId();
39323950
const expectedSequenceId = session.getNextSeq();
@@ -3937,19 +3955,26 @@ var x = 10;`
39373955
files: [untitledFile]
39383956
}
39393957
});
3940-
host.checkTimeoutQueueLength(3);
3958+
host.checkTimeoutQueueLength(1);
39413959

39423960
// Run the last one = get error request
39433961
host.runQueuedTimeoutCallbacks(newTimeoutId);
39443962

39453963
assert.isFalse(hasError());
3946-
host.checkTimeoutQueueLength(2);
3964+
host.checkTimeoutQueueLength(0);
39473965
checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] });
39483966
session.clearMessages();
39493967

39503968
host.runQueuedImmediateCallbacks();
39513969
assert.isFalse(hasError());
3952-
checkErrorMessage(session, "semanticDiag", { file: untitledFile, diagnostics: [] });
3970+
const errorOffset = fileContent.indexOf(refPathNotFound1) + 1;
3971+
checkErrorMessage(session, "semanticDiag", {
3972+
file: untitledFile,
3973+
diagnostics: [
3974+
createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"),
3975+
createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error")
3976+
]
3977+
});
39533978
session.clearMessages();
39543979

39553980
host.runQueuedImmediateCallbacks(1);

0 commit comments

Comments
 (0)