Skip to content

Commit 37949a3

Browse files
author
Zhengbo Li
committed
more tests for module resolution change and exclude
1 parent 3052913 commit 37949a3

4 files changed

Lines changed: 159 additions & 39 deletions

File tree

src/compiler/program.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,7 @@ namespace ts {
11431143
// if any of these properties has changed - structure cannot be reused
11441144
const oldOptions = oldProgram.getCompilerOptions();
11451145
if ((oldOptions.module !== options.module) ||
1146+
(oldOptions.moduleResolution !== options.moduleResolution) ||
11461147
(oldOptions.noResolve !== options.noResolve) ||
11471148
(oldOptions.target !== options.target) ||
11481149
(oldOptions.noLib !== options.noLib) ||

src/server/editorServices.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ namespace ts.server {
268268
}
269269

270270
removeRoot(info: ScriptInfo) {
271-
if (!this.filenameToScript.contains(info.path)) {
271+
if (this.filenameToScript.contains(info.path)) {
272272
this.filenameToScript.remove(info.path);
273273
this.roots = copyListRemovingItem(info, this.roots);
274274
this.resolvedModuleNames.remove(info.path);

src/services/services.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2919,6 +2919,7 @@ namespace ts {
29192919
const changesInCompilationSettingsAffectSyntax = oldSettings &&
29202920
(oldSettings.target !== newSettings.target ||
29212921
oldSettings.module !== newSettings.module ||
2922+
oldSettings.moduleResolution !== newSettings.moduleResolution ||
29222923
oldSettings.noResolve !== newSettings.noResolve ||
29232924
oldSettings.jsx !== newSettings.jsx ||
29242925
oldSettings.allowJs !== newSettings.allowJs);

tests/cases/unittests/tsserverProjectSystem.ts

Lines changed: 156 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,11 @@ namespace ts {
107107
}
108108
}
109109

110-
function checkConfiguredProjectNumber(projectService: server.ProjectService, expected: number) {
110+
function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
111111
assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`);
112112
}
113113

114-
function checkInferredProjectNumber(projectService: server.ProjectService, expected: number) {
114+
function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) {
115115
assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`);
116116
}
117117

@@ -131,13 +131,16 @@ namespace ts {
131131
checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), expectedFiles);
132132
}
133133

134+
type TimeOutCallback = () => any;
135+
134136
class TestServerHost implements server.ServerHost {
135137
args: string[] = [];
136138
newLine: "\n";
137139

138140
private fs: ts.FileMap<FSEntry>;
139141
private getCanonicalFileName: (s: string) => string;
140142
private toPath: (f: string) => Path;
143+
private callbackQueue: TimeOutCallback[] = [];
141144
readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {};
142145
readonly watchedFiles: Map<FileWatcherCallback[]> = {};
143146

@@ -222,12 +225,12 @@ namespace ts {
222225
}
223226
}
224227

225-
triggerFileWatcherCallback(fileName: string): void {
228+
triggerFileWatcherCallback(fileName: string, removed?: boolean): void {
226229
const path = this.toPath(fileName);
227230
const callbacks = lookUp(this.watchedFiles, path);
228231
if (callbacks) {
229232
for (const callback of callbacks) {
230-
callback(path, /*removed*/ true);
233+
callback(path, removed);
231234
}
232235
}
233236
}
@@ -248,8 +251,27 @@ namespace ts {
248251
}
249252

250253
// TOOD: record and invoke callbacks to simulate timer events
251-
readonly setTimeout = setTimeout;
252-
readonly clearTimeout = (timeoutId: any): void => void 0;
254+
readonly setTimeout = (callback: TimeOutCallback, time: number) => {
255+
this.callbackQueue.push(callback);
256+
return this.callbackQueue.length - 1;
257+
};
258+
readonly clearTimeout = (timeoutId: any): void => {
259+
if (typeof timeoutId === "number") {
260+
this.callbackQueue.splice(timeoutId, 1);
261+
}
262+
};
263+
264+
checkTimeoutQueueLength(expected: number) {
265+
assert.equal(this.callbackQueue.length, expected, `expected ${expected} timeout callbacks queued but found ${this.callbackQueue.length}.`);
266+
}
267+
268+
runQueuedTimeoutCallbacks() {
269+
for (const callback of this.callbackQueue) {
270+
callback();
271+
}
272+
this.callbackQueue = [];
273+
}
274+
253275
readonly readFile = (s: string) => (<File>this.fs.get(this.toPath(s))).content;
254276
readonly resolvePath = (s: string) => s;
255277
readonly getExecutingFilePath = () => this.executingFilePath;
@@ -292,8 +314,8 @@ namespace ts {
292314
const { configFileName } = projectService.openClientFile(appFile.path);
293315

294316
assert(!configFileName, `should not find config, got: '${configFileName}`);
295-
checkConfiguredProjectNumber(projectService, 0);
296-
checkInferredProjectNumber(projectService, 1);
317+
checkNumberOfConfiguredProjects(projectService, 0);
318+
checkNumberOfInferredProjects(projectService, 1);
297319

298320
const project = projectService.inferredProjects[0];
299321

@@ -331,8 +353,8 @@ namespace ts {
331353

332354
assert(configFileName, "should find config file");
333355
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
334-
checkInferredProjectNumber(projectService, 0);
335-
checkConfiguredProjectNumber(projectService, 1);
356+
checkNumberOfInferredProjects(projectService, 0);
357+
checkNumberOfConfiguredProjects(projectService, 1);
336358

337359
const project = projectService.configuredProjects[0];
338360
checkConfiguredProjectActualFiles(project, [file1.path, libFile.path, file2.path]);
@@ -356,27 +378,28 @@ namespace ts {
356378
projectService.openClientFile(commonFile1.path);
357379
projectService.openClientFile(commonFile2.path);
358380

359-
checkInferredProjectNumber(projectService, 2);
381+
checkNumberOfInferredProjects(projectService, 2);
360382
checkWatchedDirectories(host, ["/a/b", "/a"]);
361383

362384
// Add a tsconfig file
363385
host.reloadFS(filesWithConfig);
364386
host.triggerDirectoryWatcherCallback("/a/b", configFile.path);
365387

366-
checkInferredProjectNumber(projectService, 1);
367-
checkConfiguredProjectNumber(projectService, 1);
388+
checkNumberOfInferredProjects(projectService, 1);
389+
checkNumberOfConfiguredProjects(projectService, 1);
368390
// watching all files except one that was open
369391
checkWatchedFiles(host, [libFile.path, configFile.path]);
370392

371393
// remove the tsconfig file
372394
host.reloadFS(filesWithoutConfig);
373395
host.triggerFileWatcherCallback(configFile.path);
374-
checkInferredProjectNumber(projectService, 2);
375-
checkConfiguredProjectNumber(projectService, 0);
396+
397+
checkNumberOfInferredProjects(projectService, 2);
398+
checkNumberOfConfiguredProjects(projectService, 0);
376399
checkWatchedDirectories(host, ["/a/b", "/a"]);
377400
});
378401

379-
it("add new files to a configured project without file list", (done: () => void) => {
402+
it("add new files to a configured project without file list", () => {
380403
const configFile: FileOrFolder = {
381404
path: "/a/b/tsconfig.json",
382405
content: `{}`
@@ -385,19 +408,17 @@ namespace ts {
385408
const projectService = new server.ProjectService(host, nullLogger);
386409
projectService.openClientFile(commonFile1.path);
387410
checkWatchedDirectories(host, ["/a/b"]);
388-
checkConfiguredProjectNumber(projectService, 1);
411+
checkNumberOfConfiguredProjects(projectService, 1);
389412

390413
const project = projectService.configuredProjects[0];
391414
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
392415

393416
// add a new ts file
394417
host.reloadFS([commonFile1, commonFile2, libFile, configFile]);
395418
host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
419+
host.runQueuedTimeoutCallbacks();
396420
// project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer.
397-
setTimeout(() => {
398-
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
399-
done();
400-
}, 1000);
421+
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
401422
});
402423

403424
it("should ignore non-existing files specified in the config file", () => {
@@ -416,13 +437,13 @@ namespace ts {
416437
projectService.openClientFile(commonFile1.path);
417438
projectService.openClientFile(commonFile2.path);
418439

419-
checkConfiguredProjectNumber(projectService, 1);
440+
checkNumberOfConfiguredProjects(projectService, 1);
420441
const project = projectService.configuredProjects[0];
421442
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
422-
checkInferredProjectNumber(projectService, 1);
443+
checkNumberOfInferredProjects(projectService, 1);
423444
});
424445

425-
it("handle recreated files correctly", (done: () => void) => {
446+
it("handle recreated files correctly", () => {
426447
const configFile: FileOrFolder = {
427448
path: "/a/b/tsconfig.json",
428449
content: `{}`
@@ -431,23 +452,120 @@ namespace ts {
431452
const projectService = new server.ProjectService(host, nullLogger);
432453
projectService.openClientFile(commonFile1.path);
433454

434-
checkConfiguredProjectNumber(projectService, 1);
455+
checkNumberOfConfiguredProjects(projectService, 1);
456+
const project = projectService.configuredProjects[0];
457+
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
458+
459+
// delete commonFile2
460+
host.reloadFS([commonFile1, configFile]);
461+
host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
462+
host.runQueuedTimeoutCallbacks();
463+
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
464+
465+
// re-add commonFile2
466+
host.reloadFS([commonFile1, commonFile2, configFile]);
467+
host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
468+
host.runQueuedTimeoutCallbacks();
469+
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
470+
});
471+
472+
it("should create new inferred projects for files excluded from a configured project", () => {
473+
const configFile: FileOrFolder = {
474+
path: "/a/b/tsconfig.json",
475+
content: `{
476+
"compilerOptions": {},
477+
"files": ["${commonFile1.path}", "${commonFile2.path}"]
478+
}`
479+
};
480+
const files = [commonFile1, commonFile2, configFile];
481+
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", files);
482+
const projectService = new server.ProjectService(host, nullLogger);
483+
projectService.openClientFile(commonFile1.path);
484+
435485
const project = projectService.configuredProjects[0];
436486
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
487+
configFile.content = `{
488+
"compilerOptions": {},
489+
"files": ["${commonFile1.path}"]
490+
}`;
491+
host.reloadFS(files);
492+
host.triggerFileWatcherCallback(configFile.path);
437493

438-
// delete commonFile1
439-
projectService.closeClientFile(commonFile1.path);
440-
host.reloadFS([configFile]);
441-
host.triggerDirectoryWatcherCallback("/a/b", commonFile1.path);
442-
host.setTimeout(() => {
443-
// re-add commonFile1
444-
host.reloadFS([commonFile1, configFile]);
445-
projectService.openClientFile(commonFile1.path);
446-
host.setTimeout(() => {
447-
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
448-
done();
449-
}, 500);
450-
}, 500);
494+
checkNumberOfConfiguredProjects(projectService, 1);
495+
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
496+
497+
projectService.openClientFile(commonFile2.path);
498+
checkNumberOfInferredProjects(projectService, 1);
499+
});
500+
501+
it("files explicitly excluded in config file", () => {
502+
const configFile: FileOrFolder = {
503+
path: "/a/b/tsconfig.json",
504+
content: `{
505+
"compilerOptions": {},
506+
"exclude": ["/a/c"]
507+
}`
508+
};
509+
const excludedFile1: FileOrFolder = {
510+
path: "/a/c/excluedFile1.ts",
511+
content: `let t = 1;`
512+
};
513+
514+
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, excludedFile1, configFile]);
515+
const projectService = new server.ProjectService(host, nullLogger);
516+
517+
projectService.openClientFile(commonFile1.path);
518+
checkNumberOfConfiguredProjects(projectService, 1);
519+
const project = projectService.configuredProjects[0];
520+
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
521+
projectService.openClientFile(excludedFile1.path);
522+
checkNumberOfInferredProjects(projectService, 1);
451523
});
524+
525+
it("should properly handle module resolution changes in config file", () => {
526+
const file1: FileOrFolder = {
527+
path: "/a/b/file1.ts",
528+
content: `import { T } from "module1";`
529+
}
530+
const nodeModuleFile: FileOrFolder = {
531+
path: "/a/b/node_modules/module1.ts",
532+
content: `export interface T {}`
533+
}
534+
const classicModuleFile: FileOrFolder = {
535+
path: "/a/module1.ts",
536+
content: `export interface T {}`
537+
}
538+
const configFile: FileOrFolder = {
539+
path: "/a/b/tsconfig.json",
540+
content: `{
541+
"compilerOptions": {
542+
"moduleResolution": "node"
543+
},
544+
"files": ["${file1.path}"]
545+
}`
546+
};
547+
const files = [file1, nodeModuleFile, classicModuleFile, configFile];
548+
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", files);
549+
const projectService = new server.ProjectService(host, nullLogger);
550+
projectService.openClientFile(file1.path);
551+
projectService.openClientFile(nodeModuleFile.path);
552+
projectService.openClientFile(classicModuleFile.path);
553+
554+
checkNumberOfConfiguredProjects(projectService, 1);
555+
const project = projectService.configuredProjects[0];
556+
checkConfiguredProjectActualFiles(project, [file1.path, nodeModuleFile.path]);
557+
checkNumberOfInferredProjects(projectService, 1);
558+
559+
configFile.content = `{
560+
"compilerOptions": {
561+
"moduleResolution": "classic"
562+
},
563+
"files": ["${file1.path}"]
564+
}`;
565+
host.reloadFS(files);
566+
host.triggerFileWatcherCallback(configFile.path);
567+
checkConfiguredProjectActualFiles(project, [file1.path, classicModuleFile.path]);
568+
checkNumberOfInferredProjects(projectService, 1);
569+
})
452570
});
453571
}

0 commit comments

Comments
 (0)