mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
Merge pull request #23597 from Microsoft/inferredProjects
Do not remove inferred project till next file open so we can reuse them
This commit is contained in:
commit
96b2cf8aba
@ -1762,6 +1762,10 @@ namespace ts {
|
||||
return compareComparableValues(a, b);
|
||||
}
|
||||
|
||||
export function getStringComparer(ignoreCase?: boolean) {
|
||||
return ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string comparer for use with string collation in the UI.
|
||||
*/
|
||||
@ -2274,7 +2278,7 @@ namespace ts {
|
||||
const aComponents = getNormalizedPathComponents(a, currentDirectory);
|
||||
const bComponents = getNormalizedPathComponents(b, currentDirectory);
|
||||
const sharedLength = Math.min(aComponents.length, bComponents.length);
|
||||
const comparer = ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive;
|
||||
const comparer = getStringComparer(ignoreCase);
|
||||
for (let i = 0; i < sharedLength; i++) {
|
||||
const result = comparer(aComponents[i], bComponents[i]);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
@ -2615,7 +2619,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Sort the offsets array using either the literal or canonical path representations.
|
||||
includeBasePaths.sort(useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive);
|
||||
includeBasePaths.sort(getStringComparer(!useCaseSensitiveFileNames));
|
||||
|
||||
// Iterate over each include base path and include unique base paths that are not a
|
||||
// subpath of an existing base path
|
||||
|
||||
@ -698,7 +698,7 @@ namespace ts {
|
||||
createWatchDirectoryUsing(dynamicPollingWatchFile || createDynamicPriorityPollingWatchFile({ getModifiedTime, setTimeout })) :
|
||||
watchDirectoryUsingFsWatch;
|
||||
const watchDirectoryRecursively = createRecursiveDirectoryWatcher({
|
||||
filePathComparer: useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive,
|
||||
filePathComparer: getStringComparer(!useCaseSensitiveFileNames),
|
||||
directoryExists,
|
||||
getAccessibleSortedChildDirectories: path => getAccessibleFileSystemEntries(path).directories,
|
||||
watchDirectory,
|
||||
|
||||
@ -678,7 +678,10 @@ namespace ts.projectSystem {
|
||||
projectService.openClientFile(commonFile1.path);
|
||||
projectService.openClientFile(commonFile2.path);
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 2);
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 2 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [commonFile1.path, libFile.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [commonFile2.path, libFile.path]);
|
||||
|
||||
const configFileLocations = ["/", "/a/", "/a/b/"];
|
||||
const watchedFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]).concat(libFile.path);
|
||||
checkWatchedFiles(host, watchedFiles);
|
||||
@ -686,18 +689,26 @@ namespace ts.projectSystem {
|
||||
// Add a tsconfig file
|
||||
host.reloadFS(filesWithConfig);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 2, configuredProjects: 1 });
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [commonFile2.path, libFile.path]);
|
||||
checkProjectActualFiles(projectService.configuredProjects.get(configFile.path), [libFile.path, commonFile1.path, configFile.path]);
|
||||
|
||||
checkWatchedFiles(host, watchedFiles);
|
||||
|
||||
// remove the tsconfig file
|
||||
host.reloadFS(filesWithoutConfig);
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 2 });
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [commonFile2.path, libFile.path]);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Refresh inferred projects
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 2);
|
||||
checkNumberOfConfiguredProjects(projectService, 0);
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 2 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [commonFile1.path, libFile.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [commonFile2.path, libFile.path]);
|
||||
checkWatchedFiles(host, watchedFiles);
|
||||
});
|
||||
|
||||
@ -934,6 +945,10 @@ namespace ts.projectSystem {
|
||||
path: "/a/module1.ts",
|
||||
content: `export interface T {}`
|
||||
};
|
||||
const randomFile: FileOrFolder = {
|
||||
path: "/a/file1.ts",
|
||||
content: `export interface T {}`
|
||||
};
|
||||
const configFile: FileOrFolder = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
@ -943,17 +958,18 @@ namespace ts.projectSystem {
|
||||
"files": ["${file1.path}"]
|
||||
}`
|
||||
};
|
||||
const files = [file1, nodeModuleFile, classicModuleFile, configFile];
|
||||
const files = [file1, nodeModuleFile, classicModuleFile, configFile, randomFile];
|
||||
const host = createServerHost(files);
|
||||
const projectService = createProjectService(host);
|
||||
projectService.openClientFile(file1.path);
|
||||
projectService.openClientFile(nodeModuleFile.path);
|
||||
projectService.openClientFile(classicModuleFile.path);
|
||||
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
|
||||
const project = configuredProjectAt(projectService, 0);
|
||||
const inferredProject0 = projectService.inferredProjects[0];
|
||||
checkProjectActualFiles(project, [file1.path, nodeModuleFile.path, configFile.path]);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [classicModuleFile.path]);
|
||||
|
||||
configFile.content = `{
|
||||
"compilerOptions": {
|
||||
@ -963,8 +979,22 @@ namespace ts.projectSystem {
|
||||
}`;
|
||||
host.reloadFS(files);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 }); // will not remove project 1
|
||||
checkProjectActualFiles(project, [file1.path, classicModuleFile.path, configFile.path]);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProject0);
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
const inferredProject1 = projectService.inferredProjects[1];
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [nodeModuleFile.path]);
|
||||
|
||||
// Open random file and it will reuse first inferred project
|
||||
projectService.openClientFile(randomFile.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
|
||||
checkProjectActualFiles(project, [file1.path, classicModuleFile.path, configFile.path]);
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProject0);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [randomFile.path]); // Reuses first inferred project
|
||||
assert.strictEqual(projectService.inferredProjects[1], inferredProject1);
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [nodeModuleFile.path]);
|
||||
});
|
||||
|
||||
it("should keep the configured project when the opened file is referenced by the project but not its root", () => {
|
||||
@ -1304,20 +1334,21 @@ namespace ts.projectSystem {
|
||||
assert.isUndefined(projectService.configuredProjects.get(config2.path));
|
||||
|
||||
projectService.closeClientFile(file3.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1);
|
||||
assert.isUndefined(projectService.configuredProjects.get(config2.path));
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
|
||||
projectService.closeClientFile(file1.path);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1);
|
||||
assert.isUndefined(projectService.configuredProjects.get(config2.path));
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
|
||||
projectService.openClientFile(file2.path, file2.content);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
assert.isUndefined(projectService.configuredProjects.get(config1.path));
|
||||
assert.isDefined(projectService.configuredProjects.get(config2.path));
|
||||
|
||||
});
|
||||
|
||||
describe("ignoreConfigFiles", () => {
|
||||
@ -1579,12 +1610,15 @@ namespace ts.projectSystem {
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
const inferredProject0 = projectService.inferredProjects[0];
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path, file2.path]);
|
||||
|
||||
projectService.openClientFile(file3.path);
|
||||
checkNumberOfInferredProjects(projectService, 2);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProject0);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path, file2.path]);
|
||||
const inferredProject1 = projectService.inferredProjects[1];
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [file3.path]);
|
||||
|
||||
const modifiedFile2 = {
|
||||
@ -1594,8 +1628,11 @@ namespace ts.projectSystem {
|
||||
|
||||
host.reloadFS([file1, modifiedFile2, file3]);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProject0);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path, modifiedFile2.path, file3.path]);
|
||||
assert.strictEqual(projectService.inferredProjects[1], inferredProject1);
|
||||
assert.isTrue(inferredProject1.isOrphan());
|
||||
});
|
||||
|
||||
it("deleted files affect project structure", () => {
|
||||
@ -1767,8 +1804,10 @@ namespace ts.projectSystem {
|
||||
|
||||
host.reloadFS([file1, file2, file3, configFile]);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
|
||||
checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, file3.path, configFile.path]);
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
assert.isTrue(projectService.inferredProjects[1].isOrphan());
|
||||
});
|
||||
|
||||
it("correctly migrate files between projects", () => {
|
||||
@ -1792,19 +1831,46 @@ namespace ts.projectSystem {
|
||||
projectService.openClientFile(file2.path);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file2.path]);
|
||||
let inferredProjects = projectService.inferredProjects.slice();
|
||||
|
||||
projectService.openClientFile(file3.path);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProjects[0]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file2.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [file3.path]);
|
||||
inferredProjects = projectService.inferredProjects.slice();
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
assert.notStrictEqual(projectService.inferredProjects[0], inferredProjects[0]);
|
||||
assert.notStrictEqual(projectService.inferredProjects[0], inferredProjects[1]);
|
||||
checkProjectRootFiles(projectService.inferredProjects[0], [file1.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path, file2.path, file3.path]);
|
||||
inferredProjects = projectService.inferredProjects.slice();
|
||||
|
||||
projectService.closeClientFile(file1.path);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 3 });
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProjects[0]);
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [file2.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[2], [file3.path]);
|
||||
inferredProjects = projectService.inferredProjects.slice();
|
||||
|
||||
projectService.closeClientFile(file3.path);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 3 });
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProjects[0]);
|
||||
assert.strictEqual(projectService.inferredProjects[1], inferredProjects[1]);
|
||||
assert.strictEqual(projectService.inferredProjects[2], inferredProjects[2]);
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [file2.path]);
|
||||
assert.isTrue(projectService.inferredProjects[2].isOrphan());
|
||||
|
||||
projectService.openClientFile(file3.path);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProjects[2]);
|
||||
assert.strictEqual(projectService.inferredProjects[1], inferredProjects[1]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file3.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [file2.path]);
|
||||
});
|
||||
|
||||
it("can correctly update configured project when set of root files has changed (new file on disk)", () => {
|
||||
@ -2286,10 +2352,17 @@ namespace ts.projectSystem {
|
||||
projectService.openClientFile(modFile.path);
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
const inferredProjects = projectService.inferredProjects.slice();
|
||||
checkProjectActualFiles(inferredProjects[0], [file1.path]);
|
||||
checkProjectActualFiles(inferredProjects[1], [modFile.path]);
|
||||
|
||||
projectService.setCompilerOptionsForInferredProjects({ moduleResolution: ModuleResolutionKind.Classic });
|
||||
host.checkTimeoutQueueLengthAndRun(3);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProjects[0]);
|
||||
assert.strictEqual(projectService.inferredProjects[1], inferredProjects[1]);
|
||||
checkProjectActualFiles(inferredProjects[0], [file1.path, modFile.path]);
|
||||
assert.isTrue(inferredProjects[1].isOrphan());
|
||||
});
|
||||
|
||||
it("syntax tree cache handles changes in project settings", () => {
|
||||
@ -2423,9 +2496,9 @@ namespace ts.projectSystem {
|
||||
|
||||
verifyScriptInfos();
|
||||
checkOpenFiles(projectService, files);
|
||||
verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true); // file1, file2, file3
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
const inferredProject3 = projectService.inferredProjects[0];
|
||||
verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true, 2); // file1, file2, file3
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
const inferredProject3 = projectService.inferredProjects[1];
|
||||
checkProjectActualFiles(inferredProject3, [file4.path]);
|
||||
assert.strictEqual(inferredProject3, inferredProject2);
|
||||
|
||||
@ -2435,22 +2508,21 @@ namespace ts.projectSystem {
|
||||
|
||||
verifyScriptInfos();
|
||||
checkOpenFiles(projectService, [file3]);
|
||||
verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true); // file3
|
||||
checkNumberOfInferredProjects(projectService, 0);
|
||||
verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true, 2); // file3
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
assert.isTrue(projectService.inferredProjects[1].isOrphan());
|
||||
|
||||
projectService.openClientFile(file4.path);
|
||||
verifyScriptInfos();
|
||||
checkOpenFiles(projectService, [file3, file4]);
|
||||
verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true); // file3
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ true, 1); // file3
|
||||
const inferredProject4 = projectService.inferredProjects[0];
|
||||
checkProjectActualFiles(inferredProject4, [file4.path]);
|
||||
|
||||
projectService.closeClientFile(file3.path);
|
||||
verifyScriptInfos();
|
||||
checkOpenFiles(projectService, [file4]);
|
||||
verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ false); // No open files
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
verifyConfiguredProjectStateAfterUpdate(/*hasOpenRef*/ false, 1); // No open files
|
||||
const inferredProject5 = projectService.inferredProjects[0];
|
||||
checkProjectActualFiles(inferredProject4, [file4.path]);
|
||||
assert.strictEqual(inferredProject5, inferredProject4);
|
||||
@ -2465,7 +2537,9 @@ namespace ts.projectSystem {
|
||||
assert.strictEqual(projectService.getScriptInfoForPath(file4.path as Path), find(infos, info => info.path === file4.path));
|
||||
assert.isDefined(projectService.getScriptInfoForPath(file5.path as Path));
|
||||
checkOpenFiles(projectService, [file4, file5]);
|
||||
checkNumberOfConfiguredProjects(projectService, 0);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 2 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file4.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [file5.path]);
|
||||
|
||||
function verifyScriptInfos() {
|
||||
infos.forEach(info => assert.strictEqual(projectService.getScriptInfoForPath(info.path), info));
|
||||
@ -2477,8 +2551,8 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}
|
||||
|
||||
function verifyConfiguredProjectStateAfterUpdate(hasOpenRef: boolean) {
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
function verifyConfiguredProjectStateAfterUpdate(hasOpenRef: boolean, inferredProjects: number) {
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects });
|
||||
const configProject2 = projectService.configuredProjects.get(configFile.path);
|
||||
assert.strictEqual(configProject2, configProject1);
|
||||
checkProjectActualFiles(configProject2, [file1.path, file2.path, file3.path, configFile.path]);
|
||||
@ -2543,11 +2617,13 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(inferredProject2, [file4.path]);
|
||||
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 2 });
|
||||
assert.strictEqual(projectService.configuredProjects.get(configFile.path), configuredProject);
|
||||
assert.isTrue(configuredProject.hasOpenRef()); // file2
|
||||
checkProjectActualFiles(configuredProject, [file1.path, file2.path, file3.path, configFile.path]);
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProject2);
|
||||
assert.strictEqual(projectService.inferredProjects[0], inferredProject1);
|
||||
assert.isTrue(inferredProject1.isOrphan());
|
||||
assert.strictEqual(projectService.inferredProjects[1], inferredProject2);
|
||||
checkProjectActualFiles(inferredProject2, [file4.path]);
|
||||
});
|
||||
|
||||
@ -4903,7 +4979,8 @@ namespace ts.projectSystem {
|
||||
command: server.protocol.CommandTypes.Close,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(0, f1.content, "contents of closed file");
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// Can reload contents of the file when its not open and has no project
|
||||
// reload from temp file
|
||||
@ -4911,21 +4988,23 @@ namespace ts.projectSystem {
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path, tmpfile: tmp.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(0, tmp.content, "contents of temp file");
|
||||
checkScriptInfoAndProjects(tmp.content, "contents of temp file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from own file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(0, f1.content, "contents of closed file");
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// Open file again without setting its content
|
||||
session.executeCommandSeq(<server.protocol.OpenRequest>{
|
||||
command: server.protocol.CommandTypes.Open,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(1, f1.content, "contents of file when opened without specifying contents");
|
||||
checkScriptInfoAndProjects(f1.content, "contents of file when opened without specifying contents");
|
||||
const snap = info.getSnapshot();
|
||||
|
||||
// send close request
|
||||
@ -4933,27 +5012,35 @@ namespace ts.projectSystem {
|
||||
command: server.protocol.CommandTypes.Close,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(0, f1.content, "contents of closed file");
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
assert.strictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from temp file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path, tmpfile: tmp.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(0, tmp.content, "contents of temp file");
|
||||
checkScriptInfoAndProjects(tmp.content, "contents of temp file");
|
||||
assert.notStrictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
// reload from own file
|
||||
session.executeCommandSeq(<server.protocol.ReloadRequest>{
|
||||
command: server.protocol.CommandTypes.Reload,
|
||||
arguments: { file: f1.path }
|
||||
});
|
||||
checkScriptInfoAndProjects(0, f1.content, "contents of closed file");
|
||||
checkScriptInfoAndProjects(f1.content, "contents of closed file");
|
||||
assert.notStrictEqual(info.getSnapshot(), snap);
|
||||
checkInferredProjectIsOrphan();
|
||||
|
||||
function checkScriptInfoAndProjects(inferredProjects: number, contentsOfInfo: string, captionForContents: string) {
|
||||
checkNumberOfProjects(projectService, { inferredProjects });
|
||||
function checkInferredProjectIsOrphan() {
|
||||
assert.isTrue(projectService.inferredProjects[0].isOrphan());
|
||||
assert.equal(info.containingProjects.length, 0);
|
||||
}
|
||||
|
||||
function checkScriptInfoAndProjects(contentsOfInfo: string, captionForContents: string) {
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
assert.strictEqual(projectService.getScriptInfo(f1.path), info);
|
||||
checkScriptInfoContents(contentsOfInfo, captionForContents);
|
||||
}
|
||||
|
||||
@ -708,17 +708,11 @@ namespace ts.server {
|
||||
this.handleDeletedFile(info);
|
||||
}
|
||||
else if (!info.isScriptOpen()) {
|
||||
if (info.containingProjects.length === 0) {
|
||||
// Orphan script info, remove it as we can always reload it on next open file request
|
||||
this.stopWatchingScriptInfo(info);
|
||||
this.deleteScriptInfo(info);
|
||||
}
|
||||
else {
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.delayReloadNonMixedContentFile();
|
||||
this.delayUpdateProjectGraphs(info.containingProjects);
|
||||
}
|
||||
Debug.assert(info.containingProjects.length !== 0);
|
||||
// file has been changed which might affect the set of referenced files in projects that include
|
||||
// this file and set of inferred projects
|
||||
info.delayReloadNonMixedContentFile();
|
||||
this.delayUpdateProjectGraphs(info.containingProjects);
|
||||
}
|
||||
}
|
||||
|
||||
@ -851,15 +845,23 @@ namespace ts.server {
|
||||
|
||||
const project = this.getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath) ||
|
||||
this.getOrCreateSingleInferredProjectIfEnabled() ||
|
||||
this.createInferredProject(info.isDynamic ? this.currentDirectory : getDirectoryPath(info.path));
|
||||
this.getOrCreateSingleInferredWithoutProjectRoot(info.isDynamic ? this.currentDirectory : getDirectoryPath(info.path));
|
||||
|
||||
project.addRoot(info);
|
||||
if (info.containingProjects[0] !== project) {
|
||||
// Ensure this is first project, we could be in this scenario because info could be part of orphan project
|
||||
info.detachFromProject(project);
|
||||
info.containingProjects.unshift(project);
|
||||
}
|
||||
project.updateGraph();
|
||||
|
||||
if (!this.useSingleInferredProject && !project.projectRootPath) {
|
||||
// Note that we need to create a copy of the array since the list of project can change
|
||||
for (const inferredProject of this.inferredProjects.slice(0, this.inferredProjects.length - 1)) {
|
||||
Debug.assert(inferredProject !== project);
|
||||
for (const inferredProject of this.inferredProjects) {
|
||||
if (inferredProject === project || inferredProject.isOrphan()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the inferred project if the root of it is now part of newly created inferred project
|
||||
// e.g through references
|
||||
// Which means if any root of inferred project is part of more than 1 project can be removed
|
||||
@ -870,8 +872,8 @@ namespace ts.server {
|
||||
// instead of scanning all open files
|
||||
const roots = inferredProject.getRootScriptInfos();
|
||||
Debug.assert(roots.length === 1 || !!inferredProject.projectRootPath);
|
||||
if (roots.length === 1 && roots[0].containingProjects.length > 1) {
|
||||
this.removeProject(inferredProject);
|
||||
if (roots.length === 1 && forEach(roots[0].containingProjects, p => p !== roots[0].containingProjects[0] && !p.isOrphan())) {
|
||||
inferredProject.removeFile(roots[0], /*fileExists*/ true, /*detachFromProject*/ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -891,15 +893,13 @@ namespace ts.server {
|
||||
info.close(fileExists);
|
||||
this.stopWatchingConfigFilesForClosedScriptInfo(info);
|
||||
|
||||
this.openFiles.delete(info.path);
|
||||
const canonicalFileName = this.toCanonicalFileName(info.fileName);
|
||||
if (this.openFilesWithNonRootedDiskPath.get(canonicalFileName) === info) {
|
||||
this.openFilesWithNonRootedDiskPath.delete(canonicalFileName);
|
||||
}
|
||||
|
||||
|
||||
// collect all projects that should be removed
|
||||
let projectsToRemove: Project[];
|
||||
let ensureProjectsForOpenFiles = false;
|
||||
for (const p of info.containingProjects) {
|
||||
if (p.projectKind === ProjectKind.Configured) {
|
||||
if (info.hasMixedContent) {
|
||||
@ -909,15 +909,14 @@ namespace ts.server {
|
||||
// if it would need to be re-created with next file open
|
||||
}
|
||||
else if (p.projectKind === ProjectKind.Inferred && p.isRoot(info)) {
|
||||
// If this was the open root file of inferred project
|
||||
// If this was the last open root file of inferred project
|
||||
if ((p as InferredProject).isProjectWithSingleRoot()) {
|
||||
// - when useSingleInferredProject is not set, we can guarantee that this will be the only root
|
||||
// - other wise remove the project if it is the only root
|
||||
(projectsToRemove || (projectsToRemove = [])).push(p);
|
||||
}
|
||||
else {
|
||||
p.removeFile(info, fileExists, /*detachFromProject*/ true);
|
||||
ensureProjectsForOpenFiles = true;
|
||||
}
|
||||
|
||||
p.removeFile(info, fileExists, /*detachFromProject*/ true);
|
||||
// Do not remove the project even if this was last root of the inferred project
|
||||
// so that we can reuse this project, if it would need to be re-created with next file open
|
||||
}
|
||||
|
||||
if (!p.languageServiceEnabled) {
|
||||
@ -927,24 +926,24 @@ namespace ts.server {
|
||||
p.markAsDirty();
|
||||
}
|
||||
}
|
||||
if (projectsToRemove) {
|
||||
for (const project of projectsToRemove) {
|
||||
this.removeProject(project);
|
||||
}
|
||||
|
||||
this.openFiles.delete(info.path);
|
||||
|
||||
if (ensureProjectsForOpenFiles) {
|
||||
// collect orphaned files and assign them to inferred project just like we treat open of a file
|
||||
this.openFiles.forEach((projectRootPath, path) => {
|
||||
const f = this.getScriptInfoForPath(path as Path);
|
||||
if (f.isOrphan()) {
|
||||
this.assignOrphanScriptInfoToInferredProject(f, projectRootPath);
|
||||
const info = this.getScriptInfoForPath(path as Path);
|
||||
// collect all orphaned script infos from open files
|
||||
if (info.isOrphan()) {
|
||||
this.assignOrphanScriptInfoToInferredProject(info, projectRootPath);
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup script infos that arent part of any project (eg. those could be closed script infos not referenced by any project)
|
||||
// is postponed to next file open so that if file from same project is opened,
|
||||
// we wont end up creating same script infos
|
||||
}
|
||||
|
||||
// Cleanup script infos that arent part of any project (eg. those could be closed script infos not referenced by any project)
|
||||
// is postponed to next file open so that if file from same project is opened,
|
||||
// we wont end up creating same script infos
|
||||
|
||||
// If the current info is being just closed - add the watcher file to track changes
|
||||
// But if file was deleted, handle that part
|
||||
if (fileExists) {
|
||||
@ -955,16 +954,6 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private deleteOrphanScriptInfoNotInAnyProject() {
|
||||
this.filenameToScriptInfo.forEach(info => {
|
||||
if (!info.isScriptOpen() && info.isOrphan()) {
|
||||
// if there are not projects that include this script info - delete it
|
||||
this.stopWatchingScriptInfo(info);
|
||||
this.deleteScriptInfo(info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private deleteScriptInfo(info: ScriptInfo) {
|
||||
this.filenameToScriptInfo.delete(info.path);
|
||||
const realpath = info.getRealpathIfDifferent();
|
||||
@ -1141,7 +1130,7 @@ namespace ts.server {
|
||||
* This is called by inferred project whenever script info is added as a root
|
||||
*/
|
||||
/* @internal */
|
||||
startWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo, projectRootPath: NormalizedPath | undefined) {
|
||||
startWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) {
|
||||
Debug.assert(info.isScriptOpen());
|
||||
this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => {
|
||||
let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
@ -1163,7 +1152,7 @@ namespace ts.server {
|
||||
!this.getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath)) {
|
||||
this.createConfigFileWatcherOfConfigFileExistence(configFileName, canonicalConfigFilePath, configFileExistenceInfo);
|
||||
}
|
||||
}, projectRootPath);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1194,14 +1183,14 @@ namespace ts.server {
|
||||
* The server must start searching from the directory containing
|
||||
* the newly opened file.
|
||||
*/
|
||||
private forEachConfigFileLocation(info: ScriptInfo,
|
||||
action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void,
|
||||
projectRootPath?: NormalizedPath) {
|
||||
|
||||
private forEachConfigFileLocation(info: ScriptInfo, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) {
|
||||
if (this.syntaxOnly) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Debug.assert(this.openFiles.has(info.path));
|
||||
const projectRootPath = this.openFiles.get(info.path);
|
||||
|
||||
let searchPath = asNormalizedPath(getDirectoryPath(info.fileName));
|
||||
|
||||
while (!projectRootPath || containsPath(projectRootPath, searchPath, this.currentDirectory, !this.host.useCaseSensitiveFileNames)) {
|
||||
@ -1236,13 +1225,12 @@ namespace ts.server {
|
||||
* The server must start searching from the directory containing
|
||||
* the newly opened file.
|
||||
*/
|
||||
private getConfigFileNameForFile(info: ScriptInfo, projectRootPath: NormalizedPath | undefined) {
|
||||
private getConfigFileNameForFile(info: ScriptInfo) {
|
||||
Debug.assert(info.isScriptOpen());
|
||||
this.logger.info(`Search path: ${getDirectoryPath(info.fileName)}`);
|
||||
const configFileName = this.forEachConfigFileLocation(info,
|
||||
(configFileName, canonicalConfigFilePath) =>
|
||||
this.configFileExists(configFileName, canonicalConfigFilePath, info),
|
||||
projectRootPath
|
||||
);
|
||||
if (configFileName) {
|
||||
this.logger.info(`For info: ${info.fileName} :: Config file name: ${configFileName}`);
|
||||
@ -1670,6 +1658,21 @@ namespace ts.server {
|
||||
return this.createInferredProject(/*currentDirectory*/ undefined, /*isSingleInferredProject*/ true);
|
||||
}
|
||||
|
||||
private getOrCreateSingleInferredWithoutProjectRoot(currentDirectory: string | undefined): InferredProject {
|
||||
Debug.assert(!this.useSingleInferredProject);
|
||||
const expectedCurrentDirectory = this.toCanonicalFileName(this.getNormalizedAbsolutePath(currentDirectory || ""));
|
||||
// Reuse the project with same current directory but no roots
|
||||
for (const inferredProject of this.inferredProjects) {
|
||||
if (!inferredProject.projectRootPath &&
|
||||
inferredProject.isOrphan() &&
|
||||
inferredProject.canonicalCurrentDirectory === expectedCurrentDirectory) {
|
||||
return inferredProject;
|
||||
}
|
||||
}
|
||||
|
||||
return this.createInferredProject(currentDirectory);
|
||||
}
|
||||
|
||||
private createInferredProject(currentDirectory: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: NormalizedPath): InferredProject {
|
||||
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
|
||||
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, currentDirectory);
|
||||
@ -1716,6 +1719,7 @@ namespace ts.server {
|
||||
for (const project of toAddInfo.containingProjects) {
|
||||
// Add the projects only if they can use symLink targets and not already in the list
|
||||
if (project.languageServiceEnabled &&
|
||||
!project.isOrphan() &&
|
||||
!project.getCompilerOptions().preserveSymlinks &&
|
||||
!contains(info.containingProjects, project)) {
|
||||
if (!projects) {
|
||||
@ -1905,7 +1909,7 @@ namespace ts.server {
|
||||
// we first detect if there is already a configured project created for it: if so,
|
||||
// we re- read the tsconfig file content and update the project only if we havent already done so
|
||||
// otherwise we create a new one.
|
||||
const configFileName = this.getConfigFileNameForFile(info, this.openFiles.get(path));
|
||||
const configFileName = this.getConfigFileNameForFile(info);
|
||||
if (configFileName) {
|
||||
const project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
@ -1944,16 +1948,14 @@ namespace ts.server {
|
||||
// so it will be added to inferred project as a root. (for sake of this example assume single inferred project is false)
|
||||
// So at this poing a.ts is part of first inferred project and second inferred project (of which c.ts is root)
|
||||
// And hence it needs to be removed from the first inferred project.
|
||||
if (info.containingProjects.length > 1 &&
|
||||
info.containingProjects[0].projectKind === ProjectKind.Inferred &&
|
||||
info.containingProjects[0].isRoot(info)) {
|
||||
const inferredProject = info.containingProjects[0] as InferredProject;
|
||||
if (inferredProject.isProjectWithSingleRoot()) {
|
||||
this.removeProject(inferredProject);
|
||||
}
|
||||
else {
|
||||
inferredProject.removeFile(info, /*fileExists*/ true, /*detachFromProject*/ true);
|
||||
}
|
||||
Debug.assert(info.containingProjects.length > 0);
|
||||
const firstProject = info.containingProjects[0];
|
||||
|
||||
if (!firstProject.isOrphan() &&
|
||||
firstProject.projectKind === ProjectKind.Inferred &&
|
||||
firstProject.isRoot(info) &&
|
||||
forEach(info.containingProjects, p => p !== firstProject && !p.isOrphan())) {
|
||||
firstProject.removeFile(info, /*fileExists*/ true, /*detachFromProject*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2008,9 +2010,10 @@ namespace ts.server {
|
||||
let configFileErrors: ReadonlyArray<Diagnostic>;
|
||||
|
||||
const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, projectRootPath ? this.getNormalizedAbsolutePath(projectRootPath) : this.currentDirectory, fileContent, scriptKind, hasMixedContent);
|
||||
this.openFiles.set(info.path, projectRootPath);
|
||||
let project: ConfiguredProject | ExternalProject | undefined = this.findExternalProjectContainingOpenScriptInfo(info);
|
||||
if (!project && !this.syntaxOnly) { // Checking syntaxOnly is an optimization
|
||||
configFileName = this.getConfigFileNameForFile(info, projectRootPath);
|
||||
configFileName = this.getConfigFileNameForFile(info);
|
||||
if (configFileName) {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
@ -2046,9 +2049,8 @@ namespace ts.server {
|
||||
if (info.isOrphan()) {
|
||||
this.assignOrphanScriptInfoToInferredProject(info, projectRootPath);
|
||||
}
|
||||
|
||||
Debug.assert(!info.isOrphan());
|
||||
this.openFiles.set(info.path, projectRootPath);
|
||||
|
||||
|
||||
// Remove the configured projects that have zero references from open files.
|
||||
// This was postponed from closeOpenFile to after opening next file,
|
||||
@ -2059,11 +2061,26 @@ namespace ts.server {
|
||||
}
|
||||
});
|
||||
|
||||
// Remove orphan inferred projects now that we have reused projects
|
||||
// We need to create a duplicate because we cant guarantee order after removal
|
||||
for (const inferredProject of this.inferredProjects.slice()) {
|
||||
if (inferredProject.isOrphan()) {
|
||||
this.removeProject(inferredProject);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the orphan files here because there might be orphan script infos (which are not part of project)
|
||||
// when some file/s were closed which resulted in project removal.
|
||||
// It was then postponed to cleanup these script infos so that they can be reused if
|
||||
// the file from that old project is reopened because of opening file from here.
|
||||
this.deleteOrphanScriptInfoNotInAnyProject();
|
||||
this.filenameToScriptInfo.forEach(info => {
|
||||
if (!info.isScriptOpen() && info.isOrphan()) {
|
||||
// if there are not projects that include this script info - delete it
|
||||
this.stopWatchingScriptInfo(info);
|
||||
this.deleteScriptInfo(info);
|
||||
}
|
||||
});
|
||||
|
||||
this.printProjects();
|
||||
|
||||
return { configFileName, configFileErrors };
|
||||
|
||||
@ -591,6 +591,11 @@ namespace ts.server {
|
||||
return this.rootFiles && this.rootFiles.length > 0;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
isOrphan() {
|
||||
return false;
|
||||
}
|
||||
|
||||
getRootFiles() {
|
||||
return this.rootFiles && this.rootFiles.map(info => info.fileName);
|
||||
}
|
||||
@ -834,6 +839,10 @@ namespace ts.server {
|
||||
|
||||
/*@internal*/
|
||||
updateTypingFiles(typingFiles: SortedReadonlyArray<string>) {
|
||||
enumerateInsertsAndDeletes(typingFiles, this.typingFiles, getStringComparer(!this.useCaseSensitiveFileNames()),
|
||||
/*inserted*/ noop,
|
||||
removed => this.detachScriptInfoFromProject(removed)
|
||||
);
|
||||
this.typingFiles = typingFiles;
|
||||
// Invalidate files with unresolved imports
|
||||
this.resolutionCache.setFilesWithInvalidatedNonRelativeUnresolvedImports(this.cachedUnresolvedImportsPerFile);
|
||||
@ -894,7 +903,7 @@ namespace ts.server {
|
||||
|
||||
const oldExternalFiles = this.externalFiles || emptyArray as SortedReadonlyArray<string>;
|
||||
this.externalFiles = this.getExternalFiles();
|
||||
enumerateInsertsAndDeletes(this.externalFiles, oldExternalFiles, compareStringsCaseSensitive,
|
||||
enumerateInsertsAndDeletes(this.externalFiles, oldExternalFiles, getStringComparer(!this.useCaseSensitiveFileNames()),
|
||||
// Ensure a ScriptInfo is created for new external files. This is performed indirectly
|
||||
// by the LSHost for files in the program when the program is retrieved above but
|
||||
// the program doesn't contain external files so this must be done explicitly.
|
||||
@ -1169,6 +1178,10 @@ namespace ts.server {
|
||||
/** this is canonical project root path */
|
||||
readonly projectRootPath: string | undefined;
|
||||
|
||||
/*@internal*/
|
||||
/** stored only if their is no projectRootPath and this isnt single inferred project */
|
||||
readonly canonicalCurrentDirectory: string | undefined;
|
||||
|
||||
/*@internal*/
|
||||
constructor(
|
||||
projectService: ProjectService,
|
||||
@ -1187,12 +1200,15 @@ namespace ts.server {
|
||||
projectService.host,
|
||||
currentDirectory);
|
||||
this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath);
|
||||
if (!projectRootPath && !projectService.useSingleInferredProject) {
|
||||
this.canonicalCurrentDirectory = projectService.toCanonicalFileName(this.currentDirectory);
|
||||
}
|
||||
this.enableGlobalPlugins();
|
||||
}
|
||||
|
||||
addRoot(info: ScriptInfo) {
|
||||
Debug.assert(info.isScriptOpen());
|
||||
this.projectService.startWatchingConfigFilesForInferredProjectRoot(info, this.projectService.openFiles.get(info.path));
|
||||
this.projectService.startWatchingConfigFilesForInferredProjectRoot(info);
|
||||
if (!this._isJsInferredProject && info.isJavaScript()) {
|
||||
this.toggleJsInferredProject(/*isJsInferredProject*/ true);
|
||||
}
|
||||
@ -1209,6 +1225,11 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
isOrphan() {
|
||||
return !this.hasRoots();
|
||||
}
|
||||
|
||||
isProjectWithSingleRoot() {
|
||||
// - when useSingleInferredProject is not set and projectRootPath is not set,
|
||||
// we can guarantee that this will be the only root
|
||||
|
||||
@ -460,7 +460,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
isOrphan() {
|
||||
return this.containingProjects.length === 0;
|
||||
return !forEach(this.containingProjects, p => !p.isOrphan());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -865,7 +865,7 @@ namespace ts.server {
|
||||
symLinkedProjects = this.projectService.getSymlinkedProjects(scriptInfo);
|
||||
}
|
||||
// filter handles case when 'projects' is undefined
|
||||
projects = filter(projects, p => p.languageServiceEnabled);
|
||||
projects = filter(projects, p => p.languageServiceEnabled && !p.isOrphan());
|
||||
if ((!projects || !projects.length) && !symLinkedProjects) {
|
||||
return Errors.ThrowNoProject();
|
||||
}
|
||||
@ -1336,7 +1336,7 @@ namespace ts.server {
|
||||
symLinkedProjects ? { projects, symLinkedProjects } : projects,
|
||||
(project, info) => {
|
||||
let result: protocol.CompileOnSaveAffectedFileListSingleProject;
|
||||
if (project.compileOnSaveEnabled && project.languageServiceEnabled && !project.getCompilationSettings().noEmit) {
|
||||
if (project.compileOnSaveEnabled && project.languageServiceEnabled && !project.isOrphan() && !project.getCompilationSettings().noEmit) {
|
||||
result = {
|
||||
projectFileName: project.getProjectName(),
|
||||
fileNames: project.getCompileOnSaveAffectedFileList(info),
|
||||
|
||||
@ -8074,7 +8074,6 @@ declare namespace ts.server {
|
||||
* @param info The file that has been closed or newly configured
|
||||
*/
|
||||
private closeOpenFile;
|
||||
private deleteOrphanScriptInfoNotInAnyProject;
|
||||
private deleteScriptInfo;
|
||||
private configFileExists;
|
||||
private setConfigFileExistenceByNewConfiguredProject;
|
||||
@ -8131,6 +8130,7 @@ declare namespace ts.server {
|
||||
private sendConfigFileDiagEvent;
|
||||
private getOrCreateInferredProjectForProjectRootPathIfEnabled;
|
||||
private getOrCreateSingleInferredProjectIfEnabled;
|
||||
private getOrCreateSingleInferredWithoutProjectRoot;
|
||||
private createInferredProject;
|
||||
getScriptInfo(uncheckedFileName: string): ScriptInfo;
|
||||
private watchClosedScriptInfo;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user