more tests for module resolution change and exclude
This commit is contained in:
parent
3052913b92
commit
37949a3d65
|
@ -1143,6 +1143,7 @@ namespace ts {
|
||||||
// if any of these properties has changed - structure cannot be reused
|
// if any of these properties has changed - structure cannot be reused
|
||||||
const oldOptions = oldProgram.getCompilerOptions();
|
const oldOptions = oldProgram.getCompilerOptions();
|
||||||
if ((oldOptions.module !== options.module) ||
|
if ((oldOptions.module !== options.module) ||
|
||||||
|
(oldOptions.moduleResolution !== options.moduleResolution) ||
|
||||||
(oldOptions.noResolve !== options.noResolve) ||
|
(oldOptions.noResolve !== options.noResolve) ||
|
||||||
(oldOptions.target !== options.target) ||
|
(oldOptions.target !== options.target) ||
|
||||||
(oldOptions.noLib !== options.noLib) ||
|
(oldOptions.noLib !== options.noLib) ||
|
||||||
|
|
|
@ -268,7 +268,7 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRoot(info: ScriptInfo) {
|
removeRoot(info: ScriptInfo) {
|
||||||
if (!this.filenameToScript.contains(info.path)) {
|
if (this.filenameToScript.contains(info.path)) {
|
||||||
this.filenameToScript.remove(info.path);
|
this.filenameToScript.remove(info.path);
|
||||||
this.roots = copyListRemovingItem(info, this.roots);
|
this.roots = copyListRemovingItem(info, this.roots);
|
||||||
this.resolvedModuleNames.remove(info.path);
|
this.resolvedModuleNames.remove(info.path);
|
||||||
|
|
|
@ -2919,6 +2919,7 @@ namespace ts {
|
||||||
const changesInCompilationSettingsAffectSyntax = oldSettings &&
|
const changesInCompilationSettingsAffectSyntax = oldSettings &&
|
||||||
(oldSettings.target !== newSettings.target ||
|
(oldSettings.target !== newSettings.target ||
|
||||||
oldSettings.module !== newSettings.module ||
|
oldSettings.module !== newSettings.module ||
|
||||||
|
oldSettings.moduleResolution !== newSettings.moduleResolution ||
|
||||||
oldSettings.noResolve !== newSettings.noResolve ||
|
oldSettings.noResolve !== newSettings.noResolve ||
|
||||||
oldSettings.jsx !== newSettings.jsx ||
|
oldSettings.jsx !== newSettings.jsx ||
|
||||||
oldSettings.allowJs !== newSettings.allowJs);
|
oldSettings.allowJs !== newSettings.allowJs);
|
||||||
|
|
|
@ -107,11 +107,11 @@ namespace ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkConfiguredProjectNumber(projectService: server.ProjectService, expected: number) {
|
function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
|
||||||
assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`);
|
assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkInferredProjectNumber(projectService: server.ProjectService, expected: number) {
|
function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) {
|
||||||
assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`);
|
assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +131,8 @@ namespace ts {
|
||||||
checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), expectedFiles);
|
checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), expectedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TimeOutCallback = () => any;
|
||||||
|
|
||||||
class TestServerHost implements server.ServerHost {
|
class TestServerHost implements server.ServerHost {
|
||||||
args: string[] = [];
|
args: string[] = [];
|
||||||
newLine: "\n";
|
newLine: "\n";
|
||||||
|
@ -138,6 +140,7 @@ namespace ts {
|
||||||
private fs: ts.FileMap<FSEntry>;
|
private fs: ts.FileMap<FSEntry>;
|
||||||
private getCanonicalFileName: (s: string) => string;
|
private getCanonicalFileName: (s: string) => string;
|
||||||
private toPath: (f: string) => Path;
|
private toPath: (f: string) => Path;
|
||||||
|
private callbackQueue: TimeOutCallback[] = [];
|
||||||
readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {};
|
readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {};
|
||||||
readonly watchedFiles: Map<FileWatcherCallback[]> = {};
|
readonly watchedFiles: Map<FileWatcherCallback[]> = {};
|
||||||
|
|
||||||
|
@ -222,12 +225,12 @@ namespace ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerFileWatcherCallback(fileName: string): void {
|
triggerFileWatcherCallback(fileName: string, removed?: boolean): void {
|
||||||
const path = this.toPath(fileName);
|
const path = this.toPath(fileName);
|
||||||
const callbacks = lookUp(this.watchedFiles, path);
|
const callbacks = lookUp(this.watchedFiles, path);
|
||||||
if (callbacks) {
|
if (callbacks) {
|
||||||
for (const callback of callbacks) {
|
for (const callback of callbacks) {
|
||||||
callback(path, /*removed*/ true);
|
callback(path, removed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,8 +251,27 @@ namespace ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TOOD: record and invoke callbacks to simulate timer events
|
// TOOD: record and invoke callbacks to simulate timer events
|
||||||
readonly setTimeout = setTimeout;
|
readonly setTimeout = (callback: TimeOutCallback, time: number) => {
|
||||||
readonly clearTimeout = (timeoutId: any): void => void 0;
|
this.callbackQueue.push(callback);
|
||||||
|
return this.callbackQueue.length - 1;
|
||||||
|
};
|
||||||
|
readonly clearTimeout = (timeoutId: any): void => {
|
||||||
|
if (typeof timeoutId === "number") {
|
||||||
|
this.callbackQueue.splice(timeoutId, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkTimeoutQueueLength(expected: number) {
|
||||||
|
assert.equal(this.callbackQueue.length, expected, `expected ${expected} timeout callbacks queued but found ${this.callbackQueue.length}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
runQueuedTimeoutCallbacks() {
|
||||||
|
for (const callback of this.callbackQueue) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
this.callbackQueue = [];
|
||||||
|
}
|
||||||
|
|
||||||
readonly readFile = (s: string) => (<File>this.fs.get(this.toPath(s))).content;
|
readonly readFile = (s: string) => (<File>this.fs.get(this.toPath(s))).content;
|
||||||
readonly resolvePath = (s: string) => s;
|
readonly resolvePath = (s: string) => s;
|
||||||
readonly getExecutingFilePath = () => this.executingFilePath;
|
readonly getExecutingFilePath = () => this.executingFilePath;
|
||||||
|
@ -292,8 +314,8 @@ namespace ts {
|
||||||
const { configFileName } = projectService.openClientFile(appFile.path);
|
const { configFileName } = projectService.openClientFile(appFile.path);
|
||||||
|
|
||||||
assert(!configFileName, `should not find config, got: '${configFileName}`);
|
assert(!configFileName, `should not find config, got: '${configFileName}`);
|
||||||
checkConfiguredProjectNumber(projectService, 0);
|
checkNumberOfConfiguredProjects(projectService, 0);
|
||||||
checkInferredProjectNumber(projectService, 1);
|
checkNumberOfInferredProjects(projectService, 1);
|
||||||
|
|
||||||
const project = projectService.inferredProjects[0];
|
const project = projectService.inferredProjects[0];
|
||||||
|
|
||||||
|
@ -331,8 +353,8 @@ namespace ts {
|
||||||
|
|
||||||
assert(configFileName, "should find config file");
|
assert(configFileName, "should find config file");
|
||||||
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
|
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
|
||||||
checkInferredProjectNumber(projectService, 0);
|
checkNumberOfInferredProjects(projectService, 0);
|
||||||
checkConfiguredProjectNumber(projectService, 1);
|
checkNumberOfConfiguredProjects(projectService, 1);
|
||||||
|
|
||||||
const project = projectService.configuredProjects[0];
|
const project = projectService.configuredProjects[0];
|
||||||
checkConfiguredProjectActualFiles(project, [file1.path, libFile.path, file2.path]);
|
checkConfiguredProjectActualFiles(project, [file1.path, libFile.path, file2.path]);
|
||||||
|
@ -356,27 +378,28 @@ namespace ts {
|
||||||
projectService.openClientFile(commonFile1.path);
|
projectService.openClientFile(commonFile1.path);
|
||||||
projectService.openClientFile(commonFile2.path);
|
projectService.openClientFile(commonFile2.path);
|
||||||
|
|
||||||
checkInferredProjectNumber(projectService, 2);
|
checkNumberOfInferredProjects(projectService, 2);
|
||||||
checkWatchedDirectories(host, ["/a/b", "/a"]);
|
checkWatchedDirectories(host, ["/a/b", "/a"]);
|
||||||
|
|
||||||
// Add a tsconfig file
|
// Add a tsconfig file
|
||||||
host.reloadFS(filesWithConfig);
|
host.reloadFS(filesWithConfig);
|
||||||
host.triggerDirectoryWatcherCallback("/a/b", configFile.path);
|
host.triggerDirectoryWatcherCallback("/a/b", configFile.path);
|
||||||
|
|
||||||
checkInferredProjectNumber(projectService, 1);
|
checkNumberOfInferredProjects(projectService, 1);
|
||||||
checkConfiguredProjectNumber(projectService, 1);
|
checkNumberOfConfiguredProjects(projectService, 1);
|
||||||
// watching all files except one that was open
|
// watching all files except one that was open
|
||||||
checkWatchedFiles(host, [libFile.path, configFile.path]);
|
checkWatchedFiles(host, [libFile.path, configFile.path]);
|
||||||
|
|
||||||
// remove the tsconfig file
|
// remove the tsconfig file
|
||||||
host.reloadFS(filesWithoutConfig);
|
host.reloadFS(filesWithoutConfig);
|
||||||
host.triggerFileWatcherCallback(configFile.path);
|
host.triggerFileWatcherCallback(configFile.path);
|
||||||
checkInferredProjectNumber(projectService, 2);
|
|
||||||
checkConfiguredProjectNumber(projectService, 0);
|
checkNumberOfInferredProjects(projectService, 2);
|
||||||
|
checkNumberOfConfiguredProjects(projectService, 0);
|
||||||
checkWatchedDirectories(host, ["/a/b", "/a"]);
|
checkWatchedDirectories(host, ["/a/b", "/a"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("add new files to a configured project without file list", (done: () => void) => {
|
it("add new files to a configured project without file list", () => {
|
||||||
const configFile: FileOrFolder = {
|
const configFile: FileOrFolder = {
|
||||||
path: "/a/b/tsconfig.json",
|
path: "/a/b/tsconfig.json",
|
||||||
content: `{}`
|
content: `{}`
|
||||||
|
@ -385,7 +408,7 @@ namespace ts {
|
||||||
const projectService = new server.ProjectService(host, nullLogger);
|
const projectService = new server.ProjectService(host, nullLogger);
|
||||||
projectService.openClientFile(commonFile1.path);
|
projectService.openClientFile(commonFile1.path);
|
||||||
checkWatchedDirectories(host, ["/a/b"]);
|
checkWatchedDirectories(host, ["/a/b"]);
|
||||||
checkConfiguredProjectNumber(projectService, 1);
|
checkNumberOfConfiguredProjects(projectService, 1);
|
||||||
|
|
||||||
const project = projectService.configuredProjects[0];
|
const project = projectService.configuredProjects[0];
|
||||||
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
|
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
|
||||||
|
@ -393,11 +416,9 @@ namespace ts {
|
||||||
// add a new ts file
|
// add a new ts file
|
||||||
host.reloadFS([commonFile1, commonFile2, libFile, configFile]);
|
host.reloadFS([commonFile1, commonFile2, libFile, configFile]);
|
||||||
host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
|
host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
|
||||||
|
host.runQueuedTimeoutCallbacks();
|
||||||
// project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer.
|
// project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer.
|
||||||
setTimeout(() => {
|
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
||||||
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
|
||||||
done();
|
|
||||||
}, 1000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should ignore non-existing files specified in the config file", () => {
|
it("should ignore non-existing files specified in the config file", () => {
|
||||||
|
@ -416,13 +437,13 @@ namespace ts {
|
||||||
projectService.openClientFile(commonFile1.path);
|
projectService.openClientFile(commonFile1.path);
|
||||||
projectService.openClientFile(commonFile2.path);
|
projectService.openClientFile(commonFile2.path);
|
||||||
|
|
||||||
checkConfiguredProjectNumber(projectService, 1);
|
checkNumberOfConfiguredProjects(projectService, 1);
|
||||||
const project = projectService.configuredProjects[0];
|
const project = projectService.configuredProjects[0];
|
||||||
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
|
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
|
||||||
checkInferredProjectNumber(projectService, 1);
|
checkNumberOfInferredProjects(projectService, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handle recreated files correctly", (done: () => void) => {
|
it("handle recreated files correctly", () => {
|
||||||
const configFile: FileOrFolder = {
|
const configFile: FileOrFolder = {
|
||||||
path: "/a/b/tsconfig.json",
|
path: "/a/b/tsconfig.json",
|
||||||
content: `{}`
|
content: `{}`
|
||||||
|
@ -431,23 +452,120 @@ namespace ts {
|
||||||
const projectService = new server.ProjectService(host, nullLogger);
|
const projectService = new server.ProjectService(host, nullLogger);
|
||||||
projectService.openClientFile(commonFile1.path);
|
projectService.openClientFile(commonFile1.path);
|
||||||
|
|
||||||
checkConfiguredProjectNumber(projectService, 1);
|
checkNumberOfConfiguredProjects(projectService, 1);
|
||||||
const project = projectService.configuredProjects[0];
|
const project = projectService.configuredProjects[0];
|
||||||
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
||||||
|
|
||||||
// delete commonFile1
|
// delete commonFile2
|
||||||
projectService.closeClientFile(commonFile1.path);
|
host.reloadFS([commonFile1, configFile]);
|
||||||
host.reloadFS([configFile]);
|
host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
|
||||||
host.triggerDirectoryWatcherCallback("/a/b", commonFile1.path);
|
host.runQueuedTimeoutCallbacks();
|
||||||
host.setTimeout(() => {
|
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
|
||||||
// re-add commonFile1
|
|
||||||
host.reloadFS([commonFile1, configFile]);
|
// re-add commonFile2
|
||||||
projectService.openClientFile(commonFile1.path);
|
host.reloadFS([commonFile1, commonFile2, configFile]);
|
||||||
host.setTimeout(() => {
|
host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
|
||||||
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
host.runQueuedTimeoutCallbacks();
|
||||||
done();
|
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
||||||
}, 500);
|
|
||||||
}, 500);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should create new inferred projects for files excluded from a configured project", () => {
|
||||||
|
const configFile: FileOrFolder = {
|
||||||
|
path: "/a/b/tsconfig.json",
|
||||||
|
content: `{
|
||||||
|
"compilerOptions": {},
|
||||||
|
"files": ["${commonFile1.path}", "${commonFile2.path}"]
|
||||||
|
}`
|
||||||
|
};
|
||||||
|
const files = [commonFile1, commonFile2, configFile];
|
||||||
|
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", files);
|
||||||
|
const projectService = new server.ProjectService(host, nullLogger);
|
||||||
|
projectService.openClientFile(commonFile1.path);
|
||||||
|
|
||||||
|
const project = projectService.configuredProjects[0];
|
||||||
|
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
||||||
|
configFile.content = `{
|
||||||
|
"compilerOptions": {},
|
||||||
|
"files": ["${commonFile1.path}"]
|
||||||
|
}`;
|
||||||
|
host.reloadFS(files);
|
||||||
|
host.triggerFileWatcherCallback(configFile.path);
|
||||||
|
|
||||||
|
checkNumberOfConfiguredProjects(projectService, 1);
|
||||||
|
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
|
||||||
|
|
||||||
|
projectService.openClientFile(commonFile2.path);
|
||||||
|
checkNumberOfInferredProjects(projectService, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("files explicitly excluded in config file", () => {
|
||||||
|
const configFile: FileOrFolder = {
|
||||||
|
path: "/a/b/tsconfig.json",
|
||||||
|
content: `{
|
||||||
|
"compilerOptions": {},
|
||||||
|
"exclude": ["/a/c"]
|
||||||
|
}`
|
||||||
|
};
|
||||||
|
const excludedFile1: FileOrFolder = {
|
||||||
|
path: "/a/c/excluedFile1.ts",
|
||||||
|
content: `let t = 1;`
|
||||||
|
};
|
||||||
|
|
||||||
|
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, excludedFile1, configFile]);
|
||||||
|
const projectService = new server.ProjectService(host, nullLogger);
|
||||||
|
|
||||||
|
projectService.openClientFile(commonFile1.path);
|
||||||
|
checkNumberOfConfiguredProjects(projectService, 1);
|
||||||
|
const project = projectService.configuredProjects[0];
|
||||||
|
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
||||||
|
projectService.openClientFile(excludedFile1.path);
|
||||||
|
checkNumberOfInferredProjects(projectService, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should properly handle module resolution changes in config file", () => {
|
||||||
|
const file1: FileOrFolder = {
|
||||||
|
path: "/a/b/file1.ts",
|
||||||
|
content: `import { T } from "module1";`
|
||||||
|
}
|
||||||
|
const nodeModuleFile: FileOrFolder = {
|
||||||
|
path: "/a/b/node_modules/module1.ts",
|
||||||
|
content: `export interface T {}`
|
||||||
|
}
|
||||||
|
const classicModuleFile: FileOrFolder = {
|
||||||
|
path: "/a/module1.ts",
|
||||||
|
content: `export interface T {}`
|
||||||
|
}
|
||||||
|
const configFile: FileOrFolder = {
|
||||||
|
path: "/a/b/tsconfig.json",
|
||||||
|
content: `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"files": ["${file1.path}"]
|
||||||
|
}`
|
||||||
|
};
|
||||||
|
const files = [file1, nodeModuleFile, classicModuleFile, configFile];
|
||||||
|
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", files);
|
||||||
|
const projectService = new server.ProjectService(host, nullLogger);
|
||||||
|
projectService.openClientFile(file1.path);
|
||||||
|
projectService.openClientFile(nodeModuleFile.path);
|
||||||
|
projectService.openClientFile(classicModuleFile.path);
|
||||||
|
|
||||||
|
checkNumberOfConfiguredProjects(projectService, 1);
|
||||||
|
const project = projectService.configuredProjects[0];
|
||||||
|
checkConfiguredProjectActualFiles(project, [file1.path, nodeModuleFile.path]);
|
||||||
|
checkNumberOfInferredProjects(projectService, 1);
|
||||||
|
|
||||||
|
configFile.content = `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "classic"
|
||||||
|
},
|
||||||
|
"files": ["${file1.path}"]
|
||||||
|
}`;
|
||||||
|
host.reloadFS(files);
|
||||||
|
host.triggerFileWatcherCallback(configFile.path);
|
||||||
|
checkConfiguredProjectActualFiles(project, [file1.path, classicModuleFile.path]);
|
||||||
|
checkNumberOfInferredProjects(projectService, 1);
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
Loading…
Reference in a new issue