mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-04 14:56:16 -05:00
Split tests and fix issues with missing referenced files (#10599)
* Split tsserver project system tests to seperate files * fix issues with missing references * Incorporate changes in tsserverVS-WIP branch * drop ts prefix
This commit is contained in:
@@ -209,6 +209,9 @@ var harnessSources = harnessCoreSources.concat([
|
||||
"convertCompilerOptionsFromJson.ts",
|
||||
"convertTypingOptionsFromJson.ts",
|
||||
"tsserverProjectSystem.ts",
|
||||
"compileOnSave.ts",
|
||||
"typingsInstaller.ts",
|
||||
"projectErrors.ts",
|
||||
"matchFiles.ts",
|
||||
"initializeTSConfig.ts",
|
||||
].map(function (f) {
|
||||
|
||||
@@ -90,6 +90,9 @@
|
||||
"./unittests/convertTypingOptionsFromJson.ts",
|
||||
"./unittests/tsserverProjectSystem.ts",
|
||||
"./unittests/matchFiles.ts",
|
||||
"./unittests/initializeTSConfig.ts"
|
||||
"./unittests/initializeTSConfig.ts",
|
||||
"./unittests/compileOnSave.ts",
|
||||
"./unittests/typingsInstaller.ts",
|
||||
"./unittests/projectErrors.ts"
|
||||
]
|
||||
}
|
||||
|
||||
495
src/harness/unittests/compileOnSave.ts
Normal file
495
src/harness/unittests/compileOnSave.ts
Normal file
@@ -0,0 +1,495 @@
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="./tsserverProjectSystem.ts" />
|
||||
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
|
||||
|
||||
namespace ts.projectSystem {
|
||||
describe("CompileOnSave affected list", () => {
|
||||
function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) {
|
||||
const response: server.protocol.CompileOnSaveAffectedFileListSingleProject[] = session.executeCommand(request).response;
|
||||
const actualResult = response.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
|
||||
expectedFileList = expectedFileList.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
|
||||
|
||||
assert.equal(actualResult.length, expectedFileList.length, `Actual result project number is different from the expected project number`);
|
||||
|
||||
for (let i = 0; i < actualResult.length; i++) {
|
||||
const actualResultSingleProject = actualResult[i];
|
||||
const expectedResultSingleProject = expectedFileList[i];
|
||||
assert.equal(actualResultSingleProject.projectFileName, expectedResultSingleProject.projectFileName, `Actual result contains different projects than the expected result`);
|
||||
|
||||
const actualResultSingleProjectFileNameList = actualResultSingleProject.fileNames.sort();
|
||||
const expectedResultSingleProjectFileNameList = map(expectedResultSingleProject.files, f => f.path).sort();
|
||||
assert.isTrue(
|
||||
arrayIsEqualTo(actualResultSingleProjectFileNameList, expectedResultSingleProjectFileNameList),
|
||||
`For project ${actualResultSingleProject.projectFileName}, the actual result is ${actualResultSingleProjectFileNameList}, while expected ${expectedResultSingleProjectFileNameList}`);
|
||||
}
|
||||
}
|
||||
|
||||
describe("for configured projects", () => {
|
||||
let moduleFile1: FileOrFolder;
|
||||
let file1Consumer1: FileOrFolder;
|
||||
let file1Consumer2: FileOrFolder;
|
||||
let moduleFile2: FileOrFolder;
|
||||
let globalFile3: FileOrFolder;
|
||||
let configFile: FileOrFolder;
|
||||
let changeModuleFile1ShapeRequest1: server.protocol.Request;
|
||||
let changeModuleFile1InternalRequest1: server.protocol.Request;
|
||||
let changeModuleFile1ShapeRequest2: server.protocol.Request;
|
||||
// A compile on save affected file request using file1
|
||||
let moduleFile1FileListRequest: server.protocol.Request;
|
||||
|
||||
function createSession(host: server.ServerHost) {
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
return new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
moduleFile1 = {
|
||||
path: "/a/b/moduleFile1.ts",
|
||||
content: "export function Foo() { };"
|
||||
};
|
||||
|
||||
file1Consumer1 = {
|
||||
path: "/a/b/file1Consumer1.ts",
|
||||
content: `import {Foo} from "./moduleFile1"; export var y = 10;`
|
||||
};
|
||||
|
||||
file1Consumer2 = {
|
||||
path: "/a/b/file1Consumer2.ts",
|
||||
content: `import {Foo} from "./moduleFile1"; let z = 10;`
|
||||
};
|
||||
|
||||
moduleFile2 = {
|
||||
path: "/a/b/moduleFile2.ts",
|
||||
content: `export var Foo4 = 10;`
|
||||
};
|
||||
|
||||
globalFile3 = {
|
||||
path: "/a/b/globalFile3.ts",
|
||||
content: `interface GlobalFoo { age: number }`
|
||||
};
|
||||
|
||||
configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compileOnSave": true
|
||||
}`
|
||||
};
|
||||
|
||||
// Change the content of file1 to `export var T: number;export function Foo() { };`
|
||||
changeModuleFile1ShapeRequest1 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: `export var T: number;`
|
||||
});
|
||||
|
||||
// Change the content of file1 to `export var T: number;export function Foo() { };`
|
||||
changeModuleFile1InternalRequest1 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: `var T1: number;`
|
||||
});
|
||||
|
||||
// Change the content of file1 to `export var T: number;export function Foo() { };`
|
||||
changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: `export var T2: number;`
|
||||
});
|
||||
|
||||
moduleFile1FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path, projectFileName: configFile.path });
|
||||
});
|
||||
|
||||
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([moduleFile1, file1Consumer1], session);
|
||||
|
||||
// Send an initial compileOnSave request
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
|
||||
|
||||
// Change the content of file1 to `export var T: number;export function Foo() { console.log('hi'); };`
|
||||
const changeFile1InternalRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 46,
|
||||
endLine: 1,
|
||||
endOffset: 46,
|
||||
insertString: `console.log('hi');`
|
||||
});
|
||||
session.executeCommand(changeFile1InternalRequest);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
|
||||
});
|
||||
|
||||
it("should be up-to-date with the reference map changes", () => {
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([moduleFile1, file1Consumer1], session);
|
||||
|
||||
// Send an initial compileOnSave request
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
|
||||
|
||||
// Change file2 content to `let y = Foo();`
|
||||
const removeFile1Consumer1ImportRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: file1Consumer1.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 28,
|
||||
insertString: ""
|
||||
});
|
||||
session.executeCommand(removeFile1Consumer1ImportRequest);
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer2] }]);
|
||||
|
||||
// Add the import statements back to file2
|
||||
const addFile2ImportRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: file1Consumer1.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: `import {Foo} from "./moduleFile1";`
|
||||
});
|
||||
session.executeCommand(addFile2ImportRequest);
|
||||
|
||||
// Change the content of file1 to `export var T2: string;export var T: number;export function Foo() { };`
|
||||
const changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: `export var T2: string;`
|
||||
});
|
||||
session.executeCommand(changeModuleFile1ShapeRequest2);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
|
||||
});
|
||||
|
||||
it("should be up-to-date with changes made in non-open files", () => {
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([moduleFile1], session);
|
||||
|
||||
// Send an initial compileOnSave request
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
|
||||
|
||||
file1Consumer1.content = `let y = 10;`;
|
||||
host.reloadFS([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
|
||||
host.triggerFileWatcherCallback(file1Consumer1.path, /*removed*/ false);
|
||||
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer2] }]);
|
||||
});
|
||||
|
||||
it("should be up-to-date with deleted files", () => {
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([moduleFile1], session);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
|
||||
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
// Delete file1Consumer2
|
||||
host.reloadFS([moduleFile1, file1Consumer1, configFile, libFile]);
|
||||
host.triggerFileWatcherCallback(file1Consumer2.path, /*removed*/ true);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
|
||||
});
|
||||
|
||||
it("should be up-to-date with newly created files", () => {
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([moduleFile1], session);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2] }]);
|
||||
|
||||
const file1Consumer3: FileOrFolder = {
|
||||
path: "/a/b/file1Consumer3.ts",
|
||||
content: `import {Foo} from "./moduleFile1"; let y = Foo();`
|
||||
};
|
||||
host.reloadFS([moduleFile1, file1Consumer1, file1Consumer2, file1Consumer3, globalFile3, configFile, libFile]);
|
||||
host.triggerDirectoryWatcherCallback(ts.getDirectoryPath(file1Consumer3.path), file1Consumer3.path);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2, file1Consumer3] }]);
|
||||
});
|
||||
|
||||
it("should detect changes in non-root files", () => {
|
||||
moduleFile1 = {
|
||||
path: "/a/b/moduleFile1.ts",
|
||||
content: "export function Foo() { };"
|
||||
};
|
||||
|
||||
file1Consumer1 = {
|
||||
path: "/a/b/file1Consumer1.ts",
|
||||
content: `import {Foo} from "./moduleFile1"; let y = Foo();`
|
||||
};
|
||||
|
||||
configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compileOnSave": true,
|
||||
"files": ["${file1Consumer1.path}"]
|
||||
}`
|
||||
};
|
||||
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([moduleFile1, file1Consumer1], session);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
|
||||
|
||||
// change file1 shape now, and verify both files are affected
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1] }]);
|
||||
|
||||
// change file1 internal, and verify only file1 is affected
|
||||
session.executeCommand(changeModuleFile1InternalRequest1);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
|
||||
});
|
||||
|
||||
it("should return all files if a global file changed shape", () => {
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([globalFile3], session);
|
||||
const changeGlobalFile3ShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: globalFile3.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: `var T2: string;`
|
||||
});
|
||||
|
||||
// check after file1 shape changes
|
||||
session.executeCommand(changeGlobalFile3ShapeRequest);
|
||||
const globalFile3FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: globalFile3.path });
|
||||
sendAffectedFileRequestAndCheckResult(session, globalFile3FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2] }]);
|
||||
});
|
||||
|
||||
it("should return empty array if CompileOnSave is not enabled", () => {
|
||||
configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{}`
|
||||
};
|
||||
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
openFilesForSession([moduleFile1], session);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, []);
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--isolatedModules' is specified", () => {
|
||||
configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"isolatedModules": true
|
||||
}
|
||||
}`
|
||||
};
|
||||
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
openFilesForSession([moduleFile1], session);
|
||||
|
||||
const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 27,
|
||||
endLine: 1,
|
||||
endOffset: 27,
|
||||
insertString: `Point,`
|
||||
});
|
||||
session.executeCommand(file1ChangeShapeRequest);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--out' or '--outFile' is specified", () => {
|
||||
configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"module": "system",
|
||||
"outFile": "/a/b/out.js"
|
||||
}
|
||||
}`
|
||||
};
|
||||
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
openFilesForSession([moduleFile1], session);
|
||||
|
||||
const file1ChangeShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 27,
|
||||
endLine: 1,
|
||||
endOffset: 27,
|
||||
insertString: `Point,`
|
||||
});
|
||||
session.executeCommand(file1ChangeShapeRequest);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1] }]);
|
||||
});
|
||||
|
||||
it("should return cascaded affected file list", () => {
|
||||
const file1Consumer1Consumer1: FileOrFolder = {
|
||||
path: "/a/b/file1Consumer1Consumer1.ts",
|
||||
content: `import {y} from "./file1Consumer1";`
|
||||
};
|
||||
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer1Consumer1, globalFile3, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([moduleFile1, file1Consumer1], session);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer1Consumer1] }]);
|
||||
|
||||
const changeFile1Consumer1ShapeRequest = makeSessionRequest<server.protocol.ChangeRequestArgs>(server.CommandNames.Change, {
|
||||
file: file1Consumer1.path,
|
||||
line: 2,
|
||||
offset: 1,
|
||||
endLine: 2,
|
||||
endOffset: 1,
|
||||
insertString: `export var T: number;`
|
||||
});
|
||||
session.executeCommand(changeModuleFile1ShapeRequest1);
|
||||
session.executeCommand(changeFile1Consumer1ShapeRequest);
|
||||
sendAffectedFileRequestAndCheckResult(session, moduleFile1FileListRequest, [{ projectFileName: configFile.path, files: [moduleFile1, file1Consumer1, file1Consumer1Consumer1] }]);
|
||||
});
|
||||
|
||||
it("should work fine for files with circular references", () => {
|
||||
const file1: FileOrFolder = {
|
||||
path: "/a/b/file1.ts",
|
||||
content: `
|
||||
/// <reference path="./file2.ts" />
|
||||
export var t1 = 10;`
|
||||
};
|
||||
const file2: FileOrFolder = {
|
||||
path: "/a/b/file2.ts",
|
||||
content: `
|
||||
/// <reference path="./file1.ts" />
|
||||
export var t2 = 10;`
|
||||
};
|
||||
const host = createServerHost([file1, file2, configFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([file1, file2], session);
|
||||
const file1AffectedListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: file1.path });
|
||||
sendAffectedFileRequestAndCheckResult(session, file1AffectedListRequest, [{ projectFileName: configFile.path, files: [file1, file2] }]);
|
||||
});
|
||||
|
||||
it("should return results for all projects if not specifying projectFileName", () => {
|
||||
const file1: FileOrFolder = { path: "/a/b/file1.ts", content: "export var t = 10;" };
|
||||
const file2: FileOrFolder = { path: "/a/b/file2.ts", content: `import {t} from "./file1"; var t2 = 11;` };
|
||||
const file3: FileOrFolder = { path: "/a/c/file2.ts", content: `import {t} from "../b/file1"; var t3 = 11;` };
|
||||
const configFile1: FileOrFolder = { path: "/a/b/tsconfig.json", content: `{ "compileOnSave": true }` };
|
||||
const configFile2: FileOrFolder = { path: "/a/c/tsconfig.json", content: `{ "compileOnSave": true }` };
|
||||
|
||||
const host = createServerHost([file1, file2, file3, configFile1, configFile2]);
|
||||
const session = createSession(host);
|
||||
|
||||
openFilesForSession([file1, file2, file3], session);
|
||||
const file1AffectedListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: file1.path });
|
||||
|
||||
sendAffectedFileRequestAndCheckResult(session, file1AffectedListRequest, [
|
||||
{ projectFileName: configFile1.path, files: [file1, file2] },
|
||||
{ projectFileName: configFile2.path, files: [file1, file3] }
|
||||
]);
|
||||
});
|
||||
|
||||
it("should detect removed code file", () => {
|
||||
const referenceFile1: FileOrFolder = {
|
||||
path: "/a/b/referenceFile1.ts",
|
||||
content: `
|
||||
/// <reference path="./moduleFile1.ts" />
|
||||
export var x = Foo();`
|
||||
};
|
||||
const host = createServerHost([moduleFile1, referenceFile1, configFile]);
|
||||
const session = createSession(host);
|
||||
|
||||
openFilesForSession([referenceFile1], session);
|
||||
host.reloadFS([referenceFile1, configFile]);
|
||||
host.triggerFileWatcherCallback(moduleFile1.path, /*removed*/ true);
|
||||
|
||||
const request = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: referenceFile1.path });
|
||||
sendAffectedFileRequestAndCheckResult(session, request, [
|
||||
{ projectFileName: configFile.path, files: [referenceFile1] }
|
||||
]);
|
||||
const requestForMissingFile = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path });
|
||||
sendAffectedFileRequestAndCheckResult(session, requestForMissingFile, []);
|
||||
});
|
||||
|
||||
it("should detect non-existing code file", () => {
|
||||
const referenceFile1: FileOrFolder = {
|
||||
path: "/a/b/referenceFile1.ts",
|
||||
content: `
|
||||
/// <reference path="./moduleFile2.ts" />
|
||||
export var x = Foo();`
|
||||
};
|
||||
const host = createServerHost([referenceFile1, configFile]);
|
||||
const session = createSession(host);
|
||||
|
||||
openFilesForSession([referenceFile1], session);
|
||||
const request = makeSessionRequest<server.protocol.FileRequestArgs>(server.CommandNames.CompileOnSaveAffectedFileList, { file: referenceFile1.path });
|
||||
sendAffectedFileRequestAndCheckResult(session, request, [
|
||||
{ projectFileName: configFile.path, files: [referenceFile1] }
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("EmitFile test", () => {
|
||||
it("should emit specified file", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/f1.ts",
|
||||
content: `export function Foo() { return 10; }`
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/f2.ts",
|
||||
content: `import {Foo} from "./f1"; let y = Foo();`
|
||||
};
|
||||
const configFile = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{}`
|
||||
};
|
||||
const host = createServerHost([file1, file2, configFile, libFile]);
|
||||
const typingsInstaller = new TestTypingsInstaller("/a/data/", host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
openFilesForSession([file1, file2], session);
|
||||
const compileFileRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(server.CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path });
|
||||
session.executeCommand(compileFileRequest);
|
||||
|
||||
const expectedEmittedFileName = "/a/b/f1.js";
|
||||
assert.isTrue(host.fileExists(expectedEmittedFileName));
|
||||
assert.equal(host.readFile(expectedEmittedFileName), `"use strict";\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\n`);
|
||||
});
|
||||
});
|
||||
}
|
||||
186
src/harness/unittests/projectErrors.ts
Normal file
186
src/harness/unittests/projectErrors.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="./tsserverProjectSystem.ts" />
|
||||
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
|
||||
|
||||
namespace ts.projectSystem {
|
||||
describe("Project errors", () => {
|
||||
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
|
||||
assert.isTrue(projectFiles !== undefined, "missing project files");
|
||||
const errors = projectFiles.projectErrors;
|
||||
assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
|
||||
if (expectedErrors.length) {
|
||||
for (let i = 0; i < errors.length; i++) {
|
||||
const actualMessage = flattenDiagnosticMessageText(errors[i].messageText, "\n");
|
||||
const expectedMessage = expectedErrors[i];
|
||||
assert.isTrue(actualMessage.indexOf(expectedMessage) === 0, `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("external project - diagnostics for missing files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
// only file1 exists - expect error
|
||||
const host = createServerHost([file1]);
|
||||
const projectService = createProjectService(host);
|
||||
const projectFileName = "/a/b/test.csproj";
|
||||
|
||||
{
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: toExternalFiles([file1.path, file2.path])
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
const knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkProjectErrors(knownProjects[0], ["File '/a/b/lib.ts' not found."]);
|
||||
}
|
||||
// only file2 exists - expect error
|
||||
host.reloadFS([file2]);
|
||||
{
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: toExternalFiles([file1.path, file2.path])
|
||||
});
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
const knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkProjectErrors(knownProjects[0], ["File '/a/b/app.ts' not found."]);
|
||||
}
|
||||
|
||||
// both files exist - expect no errors
|
||||
host.reloadFS([file1, file2]);
|
||||
{
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: toExternalFiles([file1.path, file2.path])
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
const knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkProjectErrors(knownProjects[0], []);
|
||||
}
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for missing files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
|
||||
};
|
||||
const host = createServerHost([file1, config]);
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectErrors(projectService.synchronizeProjectList([])[0], ["File '/a/b/lib.ts' not found."]);
|
||||
|
||||
host.reloadFS([file1, file2, config]);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectErrors(projectService.synchronizeProjectList([])[0], []);
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for corrupted config 1", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const correctConfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
|
||||
};
|
||||
const corruptedConfig = {
|
||||
path: correctConfig.path,
|
||||
content: correctConfig.content.substr(1)
|
||||
};
|
||||
const host = createServerHost([file1, file2, corruptedConfig]);
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, [
|
||||
"')' expected.",
|
||||
"Declaration or statement expected.",
|
||||
"Declaration or statement expected.",
|
||||
"Failed to parse file '/a/b/tsconfig.json'"
|
||||
]);
|
||||
}
|
||||
// fix config and trigger watcher
|
||||
host.reloadFS([file1, file2, correctConfig]);
|
||||
host.triggerFileWatcherCallback(correctConfig.path, /*false*/);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, []);
|
||||
}
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for corrupted config 2", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const correctConfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
|
||||
};
|
||||
const corruptedConfig = {
|
||||
path: correctConfig.path,
|
||||
content: correctConfig.content.substr(1)
|
||||
};
|
||||
const host = createServerHost([file1, file2, correctConfig]);
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, []);
|
||||
}
|
||||
// break config and trigger watcher
|
||||
host.reloadFS([file1, file2, corruptedConfig]);
|
||||
host.triggerFileWatcherCallback(corruptedConfig.path, /*false*/);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, [
|
||||
"')' expected.",
|
||||
"Declaration or statement expected.",
|
||||
"Declaration or statement expected.",
|
||||
"Failed to parse file '/a/b/tsconfig.json'"
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
190
src/harness/unittests/typingsInstaller.ts
Normal file
190
src/harness/unittests/typingsInstaller.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="./tsserverProjectSystem.ts" />
|
||||
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
|
||||
|
||||
namespace ts.projectSystem {
|
||||
describe("typings installer", () => {
|
||||
it("configured projects (tsd installed) 1", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.js",
|
||||
content: ""
|
||||
};
|
||||
const tsconfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({
|
||||
compilerOptions: {
|
||||
allowJs: true
|
||||
},
|
||||
typingOptions: {
|
||||
enableAutoDiscovery: true
|
||||
}
|
||||
})
|
||||
};
|
||||
const packageJson = {
|
||||
path: "/a/b/package.json",
|
||||
content: JSON.stringify({
|
||||
name: "test",
|
||||
dependencies: {
|
||||
jquery: "^3.1.0"
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
const jquery = {
|
||||
path: "/a/data/typings/jquery/jquery.d.ts",
|
||||
content: "declare const $: { x: number }"
|
||||
};
|
||||
|
||||
const host = createServerHost([file1, tsconfig, packageJson]);
|
||||
const installer = new TestTypingsInstaller("/a/data/", host);
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
|
||||
projectService.openClientFile(file1.path);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [file1.path]);
|
||||
|
||||
assert(host.fileExists(combinePaths(installer.globalTypingsCacheLocation, "tsd.json")));
|
||||
|
||||
installer.runPostInstallActions(t => {
|
||||
assert.deepEqual(t, ["jquery"]);
|
||||
host.createFileOrFolder(jquery, /*createParentDirectory*/ true);
|
||||
return ["jquery/jquery.d.ts"];
|
||||
});
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
});
|
||||
|
||||
it("inferred project (tsd installed)", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.js",
|
||||
content: ""
|
||||
};
|
||||
const packageJson = {
|
||||
path: "/a/b/package.json",
|
||||
content: JSON.stringify({
|
||||
name: "test",
|
||||
dependencies: {
|
||||
jquery: "^3.1.0"
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
const jquery = {
|
||||
path: "/a/data/typings/jquery/jquery.d.ts",
|
||||
content: "declare const $: { x: number }"
|
||||
};
|
||||
const host = createServerHost([file1, packageJson]);
|
||||
const installer = new TestTypingsInstaller("/a/data/", host);
|
||||
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
|
||||
projectService.openClientFile(file1.path);
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
const p = projectService.inferredProjects[0];
|
||||
checkProjectActualFiles(p, [file1.path]);
|
||||
|
||||
assert(host.fileExists(combinePaths(installer.globalTypingsCacheLocation, "tsd.json")));
|
||||
|
||||
installer.runPostInstallActions(t => {
|
||||
assert.deepEqual(t, ["jquery"]);
|
||||
host.createFileOrFolder(jquery, /*createParentDirectory*/ true);
|
||||
return ["jquery/jquery.d.ts"];
|
||||
});
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
});
|
||||
|
||||
it("external project - no typing options, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([file1]);
|
||||
const installer = new (class extends TestTypingsInstaller {
|
||||
constructor() {
|
||||
super("", host);
|
||||
}
|
||||
enqueueInstallTypingsRequest() {
|
||||
assert(false, "auto discovery should not be enabled");
|
||||
}
|
||||
})();
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: [toExternalFile(file1.path)]
|
||||
});
|
||||
// by default auto discovery will kick in if project contain only .js/.d.ts files
|
||||
// in this case project contain only ts files - no auto discovery
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
it("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([file1]);
|
||||
const installer = new (class extends TestTypingsInstaller {
|
||||
constructor() {
|
||||
super("", host);
|
||||
}
|
||||
enqueueInstallTypingsRequest() {
|
||||
assert(false, "auto discovery should not be enabled");
|
||||
}
|
||||
})();
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: [toExternalFile(file1.path)],
|
||||
typingOptions: { include: ["jquery"] }
|
||||
});
|
||||
// by default auto discovery will kick in if project contain only .js/.d.ts files
|
||||
// in this case project contain only ts files - no auto discovery even if typing options is set
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
it("external project - autoDiscovery = true, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([file1]);
|
||||
let enqueueIsCalled = false;
|
||||
let runTsdIsCalled = false;
|
||||
const installer = new (class extends TestTypingsInstaller {
|
||||
constructor() {
|
||||
super("", host);
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
|
||||
enqueueIsCalled = true;
|
||||
super.enqueueInstallTypingsRequest(project, typingOptions);
|
||||
}
|
||||
runTsd(cachePath: string, typingsToInstall: string[], postInstallAction: (installedTypings: string[]) => void): void {
|
||||
assert.deepEqual(typingsToInstall, ["node"]);
|
||||
runTsdIsCalled = true;
|
||||
super.runTsd(cachePath, typingsToInstall, postInstallAction);
|
||||
}
|
||||
})();
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: [toExternalFile(file1.path)],
|
||||
typingOptions: { enableAutoDiscovery: true, include: ["node"] }
|
||||
});
|
||||
// autoDiscovery is set in typing options - use it even if project contains only .ts files
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
assert.isTrue(enqueueIsCalled, "expected 'enqueueIsCalled' to be true");
|
||||
assert.isTrue(runTsdIsCalled, "expected 'runTsdIsCalled' to be true");
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -442,7 +442,7 @@ namespace ts.server {
|
||||
// but that will only be one dependency.
|
||||
// To avoid invernal conversion, the key of the referencedFiles map must be of type Path
|
||||
const referencedFiles = createMap<boolean>();
|
||||
if (sourceFile.imports) {
|
||||
if (sourceFile.imports && sourceFile.imports.length > 0) {
|
||||
const checker: TypeChecker = this.program.getTypeChecker();
|
||||
for (const importName of sourceFile.imports) {
|
||||
const symbol = checker.getSymbolAtLocation(importName);
|
||||
@@ -458,7 +458,7 @@ namespace ts.server {
|
||||
const currentDirectory = getDirectoryPath(path);
|
||||
const getCanonicalFileName = createGetCanonicalFileName(this.projectService.host.useCaseSensitiveFileNames);
|
||||
// Handle triple slash references
|
||||
if (sourceFile.referencedFiles) {
|
||||
if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) {
|
||||
for (const referencedFile of sourceFile.referencedFiles) {
|
||||
const referencedPath = toPath(referencedFile.fileName, currentDirectory, getCanonicalFileName);
|
||||
referencedFiles[referencedPath] = true;
|
||||
@@ -479,7 +479,8 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
return map(Object.keys(referencedFiles), key => <Path>key);
|
||||
const allFileNames = map(Object.keys(referencedFiles), key => <Path>key);
|
||||
return filter(allFileNames, file => this.projectService.host.fileExists(file));
|
||||
}
|
||||
|
||||
// remove a root file from project
|
||||
|
||||
@@ -951,6 +951,10 @@ namespace ts.server {
|
||||
const info = this.projectService.getScriptInfo(args.file);
|
||||
const result: protocol.CompileOnSaveAffectedFileListSingleProject[] = [];
|
||||
|
||||
if (!info) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// if specified a project, we only return affected file list in this project
|
||||
const projectsToSearch = args.projectFileName ? [this.projectService.findProject(args.projectFileName)] : info.containingProjects;
|
||||
for (const project of projectsToSearch) {
|
||||
|
||||
Reference in New Issue
Block a user