• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts {
2    export function checkResolvedModule(actual: ResolvedModuleFull | undefined, expected: ResolvedModuleFull | undefined): boolean {
3        if (!expected) {
4            if (actual) {
5                assert.fail(actual, expected, "expected resolved module to be undefined");
6                return false;
7            }
8            return true;
9        }
10        else if (!actual) {
11            assert.fail(actual, expected, "expected resolved module to be defined");
12            return false;
13        }
14
15        assert.isTrue(actual.resolvedFileName === expected.resolvedFileName, `'resolvedFileName': expected '${actual.resolvedFileName}' to be equal to '${expected.resolvedFileName}'`);
16        assert.isTrue(actual.extension === expected.extension, `'ext': expected '${actual.extension}' to be equal to '${expected.extension}'`);
17        assert.isTrue(actual.isExternalLibraryImport === expected.isExternalLibraryImport, `'isExternalLibraryImport': expected '${actual.isExternalLibraryImport}' to be equal to '${expected.isExternalLibraryImport}'`);
18        return true;
19    }
20
21    export function checkResolvedModuleWithFailedLookupLocations(actual: ResolvedModuleWithFailedLookupLocations, expectedResolvedModule: ResolvedModuleFull, expectedFailedLookupLocations: string[]): void {
22        assert.isTrue(actual.resolvedModule !== undefined, "module should be resolved");
23        checkResolvedModule(actual.resolvedModule, expectedResolvedModule);
24        assert.deepEqual(actual.failedLookupLocations, expectedFailedLookupLocations, `Failed lookup locations should match - expected has ${expectedFailedLookupLocations.length}, actual has ${actual.failedLookupLocations.length}`);
25    }
26
27    export function createResolvedModule(resolvedFileName: string, isExternalLibraryImport = false): ResolvedModuleFull {
28        return { resolvedFileName, extension: extensionFromPath(resolvedFileName), isExternalLibraryImport };
29    }
30
31    interface File {
32        name: string;
33        content?: string;
34        symlinks?: string[];
35    }
36
37    function createModuleResolutionHost(hasDirectoryExists: boolean, ...files: File[]): ModuleResolutionHost {
38        const map = new Map<string, File>();
39        for (const file of files) {
40            map.set(file.name, file);
41            if (file.symlinks) {
42                for (const symlink of file.symlinks) {
43                    map.set(symlink, file);
44                }
45            }
46        }
47
48        if (hasDirectoryExists) {
49            const directories = new Map<string, string>();
50            for (const f of files) {
51                let name = getDirectoryPath(f.name);
52                while (true) {
53                    directories.set(name, name);
54                    const baseName = getDirectoryPath(name);
55                    if (baseName === name) {
56                        break;
57                    }
58                    name = baseName;
59                }
60            }
61            return {
62                readFile,
63                realpath,
64                directoryExists: path => directories.has(path),
65                fileExists: path => {
66                    assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`);
67                    return map.has(path);
68                }
69            };
70        }
71        else {
72            return { readFile, realpath, fileExists: path => map.has(path) };
73        }
74        function readFile(path: string): string | undefined {
75            const file = map.get(path);
76            return file && file.content;
77        }
78        function realpath(path: string): string {
79            return map.get(path)!.name;
80        }
81    }
82
83    describe("unittests:: moduleResolution:: Node module resolution - relative paths", () => {
84
85        function testLoadAsFile(containingFileName: string, moduleFileNameNoExt: string, moduleName: string): void {
86            for (const ext of supportedTSExtensions) {
87                if (ext !== Extension.Dets) {
88                    test(ext, /*hasDirectoryExists*/ false);
89                    test(ext, /*hasDirectoryExists*/ true);
90                }
91            }
92
93            function test(ext: string, hasDirectoryExists: boolean) {
94                const containingFile = { name: containingFileName };
95                const moduleFile = { name: moduleFileNameNoExt + ext };
96                const resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
97                checkResolvedModule(resolution.resolvedModule, createResolvedModule(moduleFile.name));
98
99                const failedLookupLocations: string[] = [];
100                const dir = getDirectoryPath(containingFileName);
101                for (const e of supportedTSExtensions) {
102                    if (e === ext) {
103                        break;
104                    }
105                    else {
106                        failedLookupLocations.push(normalizePath(getRootLength(moduleName) === 0 ? combinePaths(dir, moduleName) : moduleName) + e);
107                    }
108                }
109
110                assert.deepEqual(resolution.failedLookupLocations, failedLookupLocations);
111
112            }
113        }
114
115        it("module name that starts with './' resolved as relative file name", () => {
116            testLoadAsFile("/foo/bar/baz.ts", "/foo/bar/foo", "./foo");
117        });
118
119        it("module name that starts with '../' resolved as relative file name", () => {
120            testLoadAsFile("/foo/bar/baz.ts", "/foo/foo", "../foo");
121        });
122
123        it("module name that starts with '/' script extension resolved as relative file name", () => {
124            testLoadAsFile("/foo/bar/baz.ts", "/foo", "/foo");
125        });
126
127        it("module name that starts with 'c:/' script extension resolved as relative file name", () => {
128            testLoadAsFile("c:/foo/bar/baz.ts", "c:/foo", "c:/foo");
129        });
130
131        function testLoadingFromPackageJson(containingFileName: string, packageJsonFileName: string, fieldRef: string, moduleFileName: string, moduleName: string): void {
132            test(/*hasDirectoryExists*/ false);
133            test(/*hasDirectoryExists*/ true);
134
135            function test(hasDirectoryExists: boolean) {
136                const containingFile = { name: containingFileName };
137                const packageJson = { name: packageJsonFileName, content: JSON.stringify({ typings: fieldRef }) };
138                const moduleFile = { name: moduleFileName };
139                const resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, moduleFile));
140                checkResolvedModule(resolution.resolvedModule, createResolvedModule(moduleFile.name));
141                // expect three failed lookup location - attempt to load module as file with all supported extensions
142                // Minus 1 is because the test cannot read the ets configuration in tsconfig.json. The. d.ets extension should not be supported here.
143                assert.equal(resolution.failedLookupLocations.length, supportedTSExtensions.length - 1);
144            }
145        }
146
147        it("module name as directory - load from 'typings'", () => {
148            testLoadingFromPackageJson("/a/b/c/d.ts", "/a/b/c/bar/package.json", "c/d/e.d.ts", "/a/b/c/bar/c/d/e.d.ts", "./bar");
149            testLoadingFromPackageJson("/a/b/c/d.ts", "/a/bar/package.json", "e.d.ts", "/a/bar/e.d.ts", "../../bar");
150            testLoadingFromPackageJson("/a/b/c/d.ts", "/bar/package.json", "e.d.ts", "/bar/e.d.ts", "/bar");
151            testLoadingFromPackageJson("c:/a/b/c/d.ts", "c:/bar/package.json", "e.d.ts", "c:/bar/e.d.ts", "c:/bar");
152        });
153
154        function testTypingsIgnored(typings: any): void {
155            test(/*hasDirectoryExists*/ false);
156            test(/*hasDirectoryExists*/ true);
157
158            function test(hasDirectoryExists: boolean) {
159                const containingFile = { name: "/a/b.ts" };
160                const packageJson = { name: "/node_modules/b/package.json", content: JSON.stringify({ typings }) };
161                const moduleFile = { name: "/a/b.d.ts" };
162
163                const indexPath = "/node_modules/b/index.d.ts";
164                const indexFile = { name: indexPath };
165
166                const resolution = nodeModuleNameResolver("b", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, moduleFile, indexFile));
167
168                checkResolvedModule(resolution.resolvedModule, createResolvedModule(indexPath, /*isExternalLibraryImport*/ true));
169            }
170        }
171
172        it("module name as directory - handle invalid 'typings'", () => {
173            testTypingsIgnored(["a", "b"]);
174            testTypingsIgnored({ a: "b" });
175            testTypingsIgnored(/*typings*/ true);
176            testTypingsIgnored(/*typings*/ null); // eslint-disable-line no-null/no-null
177            testTypingsIgnored(/*typings*/ undefined);
178        });
179        it("module name as directory - load index.d.ts", () => {
180            test(/*hasDirectoryExists*/ false);
181            test(/*hasDirectoryExists*/ true);
182
183            function test(hasDirectoryExists: boolean) {
184                const containingFile = { name: "/a/b/c.ts" };
185                const packageJson = { name: "/a/b/foo/package.json", content: JSON.stringify({ main: "/c/d" }) };
186                const indexFile = { name: "/a/b/foo/index.d.ts" };
187                const resolution = nodeModuleNameResolver("./foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, indexFile));
188                checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(indexFile.name), [
189                    "/a/b/foo.ts",
190                    "/a/b/foo.tsx",
191                    "/a/b/foo.d.ts",
192                    "/a/b/foo.ets",
193                    "/c/d",
194                    "/c/d.ts",
195                    "/c/d.tsx",
196                    "/c/d.d.ts",
197                    "/c/d.ets",
198                    "/c/d/index.ts",
199                    "/c/d/index.tsx",
200                    "/c/d/index.d.ts",
201                    "/c/d/index.ets",
202                    "/a/b/foo/index.ts",
203                    "/a/b/foo/index.tsx",
204                ]);
205            }
206        });
207    });
208
209    describe("unittests:: moduleResolution:: Node module resolution - non-relative paths", () => {
210        it("computes correct commonPrefix for moduleName cache", () => {
211            const resolutionCache = createModuleResolutionCache("/", (f) => f);
212            let cache = resolutionCache.getOrCreateCacheForModuleName("a");
213            cache.set("/sub", {
214                resolvedModule: {
215                    originalPath: undefined,
216                    resolvedFileName: "/sub/node_modules/a/index.ts",
217                    isExternalLibraryImport: true,
218                    extension: Extension.Ts,
219                },
220                failedLookupLocations: [],
221            });
222            assert.isDefined(cache.get("/sub"));
223            assert.isUndefined(cache.get("/"));
224
225            cache = resolutionCache.getOrCreateCacheForModuleName("b");
226            cache.set("/sub/dir/foo", {
227                resolvedModule: {
228                    originalPath: undefined,
229                    resolvedFileName: "/sub/directory/node_modules/b/index.ts",
230                    isExternalLibraryImport: true,
231                    extension: Extension.Ts,
232                },
233                failedLookupLocations: [],
234            });
235            assert.isDefined(cache.get("/sub/dir/foo"));
236            assert.isDefined(cache.get("/sub/dir"));
237            assert.isDefined(cache.get("/sub"));
238            assert.isUndefined(cache.get("/"));
239
240            cache = resolutionCache.getOrCreateCacheForModuleName("c");
241            cache.set("/foo/bar", {
242                resolvedModule: {
243                    originalPath: undefined,
244                    resolvedFileName: "/bar/node_modules/c/index.ts",
245                    isExternalLibraryImport: true,
246                    extension: Extension.Ts,
247                },
248                failedLookupLocations: [],
249            });
250            assert.isDefined(cache.get("/foo/bar"));
251            assert.isDefined(cache.get("/foo"));
252            assert.isDefined(cache.get("/"));
253
254            cache = resolutionCache.getOrCreateCacheForModuleName("d");
255            cache.set("/foo", {
256                resolvedModule: {
257                    originalPath: undefined,
258                    resolvedFileName: "/foo/index.ts",
259                    isExternalLibraryImport: true,
260                    extension: Extension.Ts,
261                },
262                failedLookupLocations: [],
263            });
264            assert.isDefined(cache.get("/foo"));
265            assert.isUndefined(cache.get("/"));
266
267            cache = resolutionCache.getOrCreateCacheForModuleName("e");
268            cache.set("c:/foo", {
269                resolvedModule: {
270                    originalPath: undefined,
271                    resolvedFileName: "d:/bar/node_modules/e/index.ts",
272                    isExternalLibraryImport: true,
273                    extension: Extension.Ts,
274                },
275                failedLookupLocations: [],
276            });
277            assert.isDefined(cache.get("c:/foo"));
278            assert.isDefined(cache.get("c:/"));
279            assert.isUndefined(cache.get("d:/"));
280
281            cache = resolutionCache.getOrCreateCacheForModuleName("f");
282            cache.set("/foo/bar/baz", {
283                resolvedModule: undefined,
284                failedLookupLocations: [],
285            });
286            assert.isDefined(cache.get("/foo/bar/baz"));
287            assert.isDefined(cache.get("/foo/bar"));
288            assert.isDefined(cache.get("/foo"));
289            assert.isDefined(cache.get("/"));
290        });
291
292        it("load module as file - ts files not loaded", () => {
293            test(/*hasDirectoryExists*/ false);
294            test(/*hasDirectoryExists*/ true);
295
296            function test(hasDirectoryExists: boolean) {
297                const containingFile = { name: "/a/b/c/d/e.ts" };
298                const moduleFile = { name: "/a/b/node_modules/foo.ts" };
299                const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
300                checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
301                    "/a/b/c/d/node_modules/foo/package.json",
302                    "/a/b/c/d/node_modules/foo.ts",
303                    "/a/b/c/d/node_modules/foo.tsx",
304                    "/a/b/c/d/node_modules/foo.d.ts",
305                    "/a/b/c/d/node_modules/foo.ets",
306
307                    "/a/b/c/d/node_modules/foo/index.ts",
308                    "/a/b/c/d/node_modules/foo/index.tsx",
309                    "/a/b/c/d/node_modules/foo/index.d.ts",
310                    "/a/b/c/d/node_modules/foo/index.ets",
311
312                    "/a/b/c/d/node_modules/@types/foo/package.json",
313                    "/a/b/c/d/node_modules/@types/foo.d.ts",
314
315                    "/a/b/c/d/node_modules/@types/foo/index.d.ts",
316
317                    "/a/b/c/node_modules/foo/package.json",
318                    "/a/b/c/node_modules/foo.ts",
319                    "/a/b/c/node_modules/foo.tsx",
320                    "/a/b/c/node_modules/foo.d.ts",
321                    "/a/b/c/node_modules/foo.ets",
322
323                    "/a/b/c/node_modules/foo/index.ts",
324                    "/a/b/c/node_modules/foo/index.tsx",
325                    "/a/b/c/node_modules/foo/index.d.ts",
326                    "/a/b/c/node_modules/foo/index.ets",
327
328                    "/a/b/c/node_modules/@types/foo/package.json",
329                    "/a/b/c/node_modules/@types/foo.d.ts",
330
331                    "/a/b/c/node_modules/@types/foo/index.d.ts",
332                    "/a/b/node_modules/foo/package.json",
333                ]);
334            }
335        });
336
337        it("load module as file", () => {
338            test(/*hasDirectoryExists*/ false);
339            test(/*hasDirectoryExists*/ true);
340
341            function test(hasDirectoryExists: boolean) {
342                const containingFile = { name: "/a/b/c/d/e.ts" };
343                const moduleFile = { name: "/a/b/node_modules/foo.d.ts" };
344                const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
345                checkResolvedModule(resolution.resolvedModule, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true));
346            }
347        });
348
349        it("load module as directory", () => {
350            test(/*hasDirectoryExists*/ false);
351            test(/*hasDirectoryExists*/ true);
352
353            function test(hasDirectoryExists: boolean) {
354                const containingFile: File = { name: "/a/node_modules/b/c/node_modules/d/e.ts" };
355                const moduleFile: File = { name: "/a/node_modules/foo/index.d.ts" };
356                const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
357                checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
358                    "/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json",
359                    "/a/node_modules/b/c/node_modules/d/node_modules/foo.ts",
360                    "/a/node_modules/b/c/node_modules/d/node_modules/foo.tsx",
361                    "/a/node_modules/b/c/node_modules/d/node_modules/foo.d.ts",
362                    "/a/node_modules/b/c/node_modules/d/node_modules/foo.ets",
363
364                    "/a/node_modules/b/c/node_modules/d/node_modules/foo/index.ts",
365                    "/a/node_modules/b/c/node_modules/d/node_modules/foo/index.tsx",
366                    "/a/node_modules/b/c/node_modules/d/node_modules/foo/index.d.ts",
367                    "/a/node_modules/b/c/node_modules/d/node_modules/foo/index.ets",
368
369                    "/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/package.json",
370                    "/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.d.ts",
371
372                    "/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.d.ts",
373
374                    "/a/node_modules/b/c/node_modules/foo/package.json",
375                    "/a/node_modules/b/c/node_modules/foo.ts",
376                    "/a/node_modules/b/c/node_modules/foo.tsx",
377                    "/a/node_modules/b/c/node_modules/foo.d.ts",
378                    "/a/node_modules/b/c/node_modules/foo.ets",
379
380                    "/a/node_modules/b/c/node_modules/foo/index.ts",
381                    "/a/node_modules/b/c/node_modules/foo/index.tsx",
382                    "/a/node_modules/b/c/node_modules/foo/index.d.ts",
383                    "/a/node_modules/b/c/node_modules/foo/index.ets",
384
385                    "/a/node_modules/b/c/node_modules/@types/foo/package.json",
386                    "/a/node_modules/b/c/node_modules/@types/foo.d.ts",
387
388                    "/a/node_modules/b/c/node_modules/@types/foo/index.d.ts",
389
390                    "/a/node_modules/b/node_modules/foo/package.json",
391                    "/a/node_modules/b/node_modules/foo.ts",
392                    "/a/node_modules/b/node_modules/foo.tsx",
393                    "/a/node_modules/b/node_modules/foo.d.ts",
394                    "/a/node_modules/b/node_modules/foo.ets",
395
396                    "/a/node_modules/b/node_modules/foo/index.ts",
397                    "/a/node_modules/b/node_modules/foo/index.tsx",
398                    "/a/node_modules/b/node_modules/foo/index.d.ts",
399                    "/a/node_modules/b/node_modules/foo/index.ets",
400
401                    "/a/node_modules/b/node_modules/@types/foo/package.json",
402                    "/a/node_modules/b/node_modules/@types/foo.d.ts",
403
404                    "/a/node_modules/b/node_modules/@types/foo/index.d.ts",
405
406                    "/a/node_modules/foo/package.json",
407                    "/a/node_modules/foo.ts",
408                    "/a/node_modules/foo.tsx",
409                    "/a/node_modules/foo.d.ts",
410                    "/a/node_modules/foo.ets",
411
412                    "/a/node_modules/foo/index.ts",
413                    "/a/node_modules/foo/index.tsx"
414                ]);
415            }
416        });
417
418        testPreserveSymlinks(/*preserveSymlinks*/ false);
419        testPreserveSymlinks(/*preserveSymlinks*/ true);
420        function testPreserveSymlinks(preserveSymlinks: boolean) {
421            it(`preserveSymlinks: ${preserveSymlinks}`, () => {
422                const realFileName = "/linked/index.d.ts";
423                const symlinkFileName = "/app/node_modules/linked/index.d.ts";
424                const host = createModuleResolutionHost(
425                    /*hasDirectoryExists*/ true,
426                    { name: realFileName, symlinks: [symlinkFileName] },
427                    { name: "/app/node_modules/linked/package.json", content: '{"version": "0.0.0", "main": "./index"}' },
428                );
429                const resolution = nodeModuleNameResolver("linked", "/app/app.ts", { preserveSymlinks }, host);
430                const resolvedFileName = preserveSymlinks ? symlinkFileName : realFileName;
431                checkResolvedModule(resolution.resolvedModule, createResolvedModule(resolvedFileName, /*isExternalLibraryImport*/ true));
432            });
433        }
434
435        it("uses originalPath for caching", () => {
436            const host = createModuleResolutionHost(
437                /*hasDirectoryExists*/ true,
438                {
439                    name: "/modules/a.ts",
440                    symlinks: ["/sub/node_modules/a/index.ts"],
441                },
442                {
443                    name: "/sub/node_modules/a/package.json",
444                    content: '{"version": "0.0.0", "main": "./index"}'
445                }
446            );
447            const compilerOptions: CompilerOptions = { moduleResolution: ModuleResolutionKind.NodeJs };
448            const cache = createModuleResolutionCache("/", (f) => f);
449            let resolution = resolveModuleName("a", "/sub/dir/foo.ts", compilerOptions, host, cache);
450            checkResolvedModule(resolution.resolvedModule, createResolvedModule("/modules/a.ts", /*isExternalLibraryImport*/ true));
451
452            resolution = resolveModuleName("a", "/sub/foo.ts", compilerOptions, host, cache);
453            checkResolvedModule(resolution.resolvedModule, createResolvedModule("/modules/a.ts", /*isExternalLibraryImport*/ true));
454
455            resolution = resolveModuleName("a", "/foo.ts", compilerOptions, host, cache);
456            assert.isUndefined(resolution.resolvedModule, "lookup in parent directory doesn't hit the cache");
457        });
458
459        it("preserves originalPath on cache hit", () => {
460            const host = createModuleResolutionHost(
461                /*hasDirectoryExists*/ true,
462                { name: "/linked/index.d.ts", symlinks: ["/app/node_modules/linked/index.d.ts"] },
463                { name: "/app/node_modules/linked/package.json", content: '{"version": "0.0.0", "main": "./index"}' },
464            );
465            const cache = createModuleResolutionCache("/", (f) => f);
466            const compilerOptions: CompilerOptions = { moduleResolution: ModuleResolutionKind.NodeJs };
467            checkResolution(resolveModuleName("linked", "/app/src/app.ts", compilerOptions, host, cache));
468            checkResolution(resolveModuleName("linked", "/app/lib/main.ts", compilerOptions, host, cache));
469
470            function checkResolution(resolution: ResolvedModuleWithFailedLookupLocations) {
471                checkResolvedModule(resolution.resolvedModule, createResolvedModule("/linked/index.d.ts", /*isExternalLibraryImport*/ true));
472                assert.strictEqual(resolution.resolvedModule!.originalPath, "/app/node_modules/linked/index.d.ts");
473            }
474        });
475    });
476
477    describe("unittests:: moduleResolution:: Relative imports", () => {
478        function test(files: ESMap<string, string>, currentDirectory: string, rootFiles: string[], expectedFilesCount: number, relativeNamesToCheck: string[]) {
479            const options: CompilerOptions = { module: ModuleKind.CommonJS };
480            const host: CompilerHost = {
481                getSourceFile: (fileName: string, languageVersion: ScriptTarget) => {
482                    const path = normalizePath(combinePaths(currentDirectory, fileName));
483                    const file = files.get(path);
484                    return file ? createSourceFile(fileName, file, languageVersion) : undefined;
485                },
486                getDefaultLibFileName: () => "lib.d.ts",
487                writeFile: notImplemented,
488                getCurrentDirectory: () => currentDirectory,
489                getDirectories: () => [],
490                getCanonicalFileName: fileName => fileName.toLowerCase(),
491                getNewLine: () => "\r\n",
492                useCaseSensitiveFileNames: () => false,
493                fileExists: fileName => {
494                    const path = normalizePath(combinePaths(currentDirectory, fileName));
495                    return files.has(path);
496                },
497                readFile: notImplemented,
498            };
499
500            const program = createProgram(rootFiles, options, host);
501
502            assert.equal(program.getSourceFiles().length, expectedFilesCount);
503            const syntacticDiagnostics = program.getSyntacticDiagnostics();
504            assert.equal(syntacticDiagnostics.length, 0, `expect no syntactic diagnostics, got: ${JSON.stringify(Harness.Compiler.minimalDiagnosticsToString(syntacticDiagnostics))}`);
505            const semanticDiagnostics = program.getSemanticDiagnostics();
506            assert.equal(semanticDiagnostics.length, 0, `expect no semantic diagnostics, got: ${JSON.stringify(Harness.Compiler.minimalDiagnosticsToString(semanticDiagnostics))}`);
507
508            // try to get file using a relative name
509            for (const relativeFileName of relativeNamesToCheck) {
510                assert.isTrue(program.getSourceFile(relativeFileName) !== undefined, `expected to get file by relative name, got undefined`);
511            }
512        }
513
514        it("should find all modules", () => {
515            const files = new Map(getEntries({
516                "/a/b/c/first/shared.ts": `
517class A {}
518export = A`,
519                "/a/b/c/first/second/class_a.ts": `
520import Shared = require('../shared');
521import C = require('../../third/class_c');
522class B {}
523export = B;`,
524                "/a/b/c/third/class_c.ts": `
525import Shared = require('../first/shared');
526class C {}
527export = C;
528                `
529            }));
530            test(files, "/a/b/c/first/second", ["class_a.ts"], 3, ["../../../c/third/class_c.ts"]);
531        });
532
533        it("should find modules in node_modules", () => {
534            const files = new Map(getEntries({
535                "/parent/node_modules/mod/index.d.ts": "export var x",
536                "/parent/app/myapp.ts": `import {x} from "mod"`
537            }));
538            test(files, "/parent/app", ["myapp.ts"], 2, []);
539        });
540
541        it("should find file referenced via absolute and relative names", () => {
542            const files = new Map(getEntries({
543                "/a/b/c.ts": `/// <reference path="b.ts"/>`,
544                "/a/b/b.ts": "var x"
545            }));
546            test(files, "/a/b", ["c.ts", "/a/b/b.ts"], 2, []);
547        });
548    });
549
550    describe("unittests:: moduleResolution:: Files with different casing with forceConsistentCasingInFileNames", () => {
551        let library: SourceFile;
552        function test(
553            files: ESMap<string, string>,
554            options: CompilerOptions,
555            currentDirectory: string,
556            useCaseSensitiveFileNames: boolean,
557            rootFiles: string[],
558            expectedDiagnostics: (program: Program) => readonly Diagnostic[]
559        ): void {
560            const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
561            if (!useCaseSensitiveFileNames) {
562                const oldFiles = files;
563                files = new Map<string, string>();
564                oldFiles.forEach((file, fileName) => {
565                    files.set(getCanonicalFileName(fileName), file);
566                });
567            }
568
569            const host: CompilerHost = {
570                getSourceFile: (fileName: string, languageVersion: ScriptTarget) => {
571                    if (fileName === "lib.d.ts") {
572                        if (!library) {
573                            library = createSourceFile("lib.d.ts", "", ScriptTarget.ES5);
574                        }
575                        return library;
576                    }
577                    const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
578                    const file = files.get(path);
579                    return file ? createSourceFile(fileName, file, languageVersion) : undefined;
580                },
581                getDefaultLibFileName: () => "lib.d.ts",
582                writeFile: notImplemented,
583                getCurrentDirectory: () => currentDirectory,
584                getDirectories: () => [],
585                getCanonicalFileName,
586                getNewLine: () => "\r\n",
587                useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
588                fileExists: fileName => {
589                    const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
590                    return files.has(path);
591                },
592                readFile: notImplemented,
593            };
594            const program = createProgram(rootFiles, options, host);
595            const diagnostics = sortAndDeduplicateDiagnostics([...program.getSemanticDiagnostics(), ...program.getOptionsDiagnostics()]);
596            assert.deepEqual(diagnostics, sortAndDeduplicateDiagnostics(expectedDiagnostics(program)));
597        }
598
599        it("should succeed when the same file is referenced using absolute and relative names", () => {
600            const files = new Map(getEntries({
601                "/a/b/c.ts": `/// <reference path="d.ts"/>`,
602                "/a/b/d.ts": "var x"
603            }));
604            test(
605                files,
606                { module: ModuleKind.AMD },
607                "/a/b",
608                /*useCaseSensitiveFileNames*/ false,
609                ["c.ts", "/a/b/d.ts"],
610                () => emptyArray
611            );
612        });
613
614        it("should fail when two files used in program differ only in casing (tripleslash references)", () => {
615            const files = new Map(getEntries({
616                "/a/b/c.ts": `/// <reference path="D.ts"/>`,
617                "/a/b/d.ts": "var x"
618            }));
619            test(
620                files,
621                { module: ModuleKind.AMD, forceConsistentCasingInFileNames: true },
622                "/a/b",
623                /*useCaseSensitiveFileNames*/ false,
624                ["c.ts", "d.ts"],
625                program => [{
626                    ...tscWatch.getDiagnosticOfFileFromProgram(
627                        program,
628                        "c.ts",
629                        `/// <reference path="D.ts"/>`.indexOf(`D.ts`),
630                        "D.ts".length,
631                        tscWatch.getDiagnosticMessageChain(
632                            Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
633                            ["D.ts", "d.ts"],
634                            [
635                                tscWatch.getDiagnosticMessageChain(
636                                    Diagnostics.The_file_is_in_the_program_because_Colon,
637                                    emptyArray,
638                                    [
639                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Referenced_via_0_from_file_1, ["D.ts", "c.ts"]),
640                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
641                                    ]
642                                )
643                            ],
644                        )
645                    ),
646                    relatedInformation: undefined,
647                }]
648            );
649        });
650
651        it("should fail when two files used in program differ only in casing (imports)", () => {
652            const files = new Map(getEntries({
653                "/a/b/c.ts": `import {x} from "D"`,
654                "/a/b/d.ts": "export var x"
655            }));
656            test(
657                files,
658                { module: ModuleKind.AMD, forceConsistentCasingInFileNames: true },
659                "/a/b",
660                /*useCaseSensitiveFileNames*/ false,
661                ["c.ts", "d.ts"],
662                program => [{
663                    ...tscWatch.getDiagnosticOfFileFromProgram(
664                        program,
665                        "c.ts",
666                        `import {x} from "D"`.indexOf(`"D"`),
667                        `"D"`.length,
668                        tscWatch.getDiagnosticMessageChain(
669                            Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
670                            ["/a/b/D.ts", "d.ts"],
671                            [
672                                tscWatch.getDiagnosticMessageChain(
673                                    Diagnostics.The_file_is_in_the_program_because_Colon,
674                                    emptyArray,
675                                    [
676                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"D"`, "c.ts"]),
677                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
678                                    ]
679                                )
680                            ],
681                        )
682                    ),
683                    relatedInformation: undefined,
684                }]
685            );
686        });
687
688        it("should fail when two files used in program differ only in casing (imports, relative module names)", () => {
689            const files = new Map(getEntries({
690                "moduleA.ts": `import {x} from "./ModuleB"`,
691                "moduleB.ts": "export var x"
692            }));
693            test(
694                files,
695                { module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true },
696                "",
697                /*useCaseSensitiveFileNames*/ false,
698                ["moduleA.ts", "moduleB.ts"],
699                program => [{
700                    ...tscWatch.getDiagnosticOfFileFromProgram(
701                        program,
702                        "moduleA.ts",
703                        `import {x} from "./ModuleB"`.indexOf(`"./ModuleB"`),
704                        `"./ModuleB"`.length,
705                        tscWatch.getDiagnosticMessageChain(
706                            Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
707                            ["ModuleB.ts", "moduleB.ts"],
708                            [
709                                tscWatch.getDiagnosticMessageChain(
710                                    Diagnostics.The_file_is_in_the_program_because_Colon,
711                                    emptyArray,
712                                    [
713                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleB"`, "moduleA.ts"]),
714                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
715                                    ]
716                                )
717                            ],
718                        )
719                    ),
720                    relatedInformation: undefined
721                }]
722            );
723        });
724
725        it("should fail when two files exist on disk that differs only in casing", () => {
726            const files = new Map(getEntries({
727                "/a/b/c.ts": `import {x} from "D"`,
728                "/a/b/D.ts": "export var x",
729                "/a/b/d.ts": "export var y"
730            }));
731            test(
732                files,
733                { module: ModuleKind.AMD },
734                "/a/b",
735                /*useCaseSensitiveFileNames*/ true,
736                ["c.ts", "d.ts"],
737                program => [{
738                    ...tscWatch.getDiagnosticOfFileFromProgram(
739                        program,
740                        "c.ts",
741                        `import {x} from "D"`.indexOf(`"D"`),
742                        `"D"`.length,
743                        tscWatch.getDiagnosticMessageChain(
744                            Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
745                            ["/a/b/D.ts", "d.ts"],
746                            [
747                                tscWatch.getDiagnosticMessageChain(
748                                    Diagnostics.The_file_is_in_the_program_because_Colon,
749                                    emptyArray,
750                                    [
751                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"D"`, "c.ts"]),
752                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)
753                                    ]
754                                )
755                            ],
756                        )
757                    ),
758                    relatedInformation: undefined
759                }]
760            );
761        });
762
763        it("should fail when module name in 'require' calls has inconsistent casing", () => {
764            const files = new Map(getEntries({
765                "moduleA.ts": `import a = require("./ModuleC")`,
766                "moduleB.ts": `import a = require("./moduleC")`,
767                "moduleC.ts": "export var x"
768            }));
769            test(
770                files,
771                { module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true },
772                "",
773                /*useCaseSensitiveFileNames*/ false,
774                ["moduleA.ts", "moduleB.ts", "moduleC.ts"],
775                program => {
776                    const importInA = {
777                        ...tscWatch.getDiagnosticOfFileFromProgram(
778                            program,
779                            "moduleA.ts",
780                            `import a = require("./ModuleC")`.indexOf(`"./ModuleC"`),
781                            `"./ModuleC"`.length,
782                            Diagnostics.File_is_included_via_import_here,
783                        ),
784                        reportsUnnecessary: undefined,
785                        reportsDeprecated: undefined
786                    };
787                    const importInB = {
788                        ...tscWatch.getDiagnosticOfFileFromProgram(
789                            program,
790                            "moduleB.ts",
791                            `import a = require("./moduleC")`.indexOf(`"./moduleC"`),
792                            `"./moduleC"`.length,
793                            Diagnostics.File_is_included_via_import_here,
794                        ),
795                        reportsUnnecessary: undefined,
796                        reportsDeprecated: undefined
797                    };
798                    const importHereInA = tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleC"`, "moduleA.ts"]);
799                    const importHereInB = tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./moduleC"`, "moduleB.ts"]);
800                    const details = [tscWatch.getDiagnosticMessageChain(
801                        Diagnostics.The_file_is_in_the_program_because_Colon,
802                        emptyArray,
803                        [importHereInA, importHereInB, tscWatch.getDiagnosticMessageChain(Diagnostics.Root_file_specified_for_compilation)]
804                    )];
805                    return [
806                        {
807                            ...tscWatch.getDiagnosticOfFileFrom(
808                                importInA.file,
809                                importInA.start,
810                                importInA.length,
811                                tscWatch.getDiagnosticMessageChain(
812                                    Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing,
813                                    ["ModuleC.ts", "moduleC.ts" ],
814                                    details,
815                                )
816                            ),
817                            relatedInformation: [importInB]
818                        },
819                        {
820                            ...tscWatch.getDiagnosticOfFileFrom(
821                                importInB.file,
822                                importInB.start,
823                                importInB.length,
824                                tscWatch.getDiagnosticMessageChain(
825                                    Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
826                                    ["moduleC.ts", "ModuleC.ts"],
827                                    details,
828                                )
829                            ),
830                            relatedInformation: [importInA]
831                        }
832                    ];
833                }
834            );
835        });
836
837        it("should fail when module names in 'require' calls has inconsistent casing and current directory has uppercase chars", () => {
838            const files = new Map(getEntries({
839                "/a/B/c/moduleA.ts": `import a = require("./ModuleC")`,
840                "/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
841                "/a/B/c/moduleC.ts": "export var x",
842                "/a/B/c/moduleD.ts": `
843import a = require("./moduleA");
844import b = require("./moduleB");
845                `
846            }));
847            test(
848                files,
849                { module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true },
850                "/a/B/c",
851                /*useCaseSensitiveFileNames*/ false,
852                ["moduleD.ts"],
853                program => [{
854                    ...tscWatch.getDiagnosticOfFileFromProgram(
855                        program,
856                        "moduleB.ts",
857                        `import a = require("./moduleC")`.indexOf(`"./moduleC"`),
858                        `"./moduleC"`.length,
859                        tscWatch.getDiagnosticMessageChain(
860                            Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
861                            ["/a/B/c/moduleC.ts", "/a/B/c/ModuleC.ts"],
862                            [
863                                tscWatch.getDiagnosticMessageChain(
864                                    Diagnostics.The_file_is_in_the_program_because_Colon,
865                                    emptyArray,
866                                    [
867                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./ModuleC"`, "/a/B/c/moduleA.ts"]),
868                                        tscWatch.getDiagnosticMessageChain(Diagnostics.Imported_via_0_from_file_1, [`"./moduleC"`, "/a/B/c/moduleB.ts"])
869                                    ]
870                                )
871                            ],
872                        )
873                    ),
874                    relatedInformation: [
875                        {
876                            ...tscWatch.getDiagnosticOfFileFromProgram(
877                                program,
878                                "moduleA.ts",
879                                `import a = require("./ModuleC")`.indexOf(`"./ModuleC"`),
880                                `"./ModuleC"`.length,
881                                Diagnostics.File_is_included_via_import_here,
882                            ),
883                            reportsUnnecessary: undefined,
884                            reportsDeprecated: undefined
885                        }
886                    ]
887                }]
888            );
889        });
890        it("should not fail when module names in 'require' calls has consistent casing and current directory has uppercase chars", () => {
891            const files = new Map(getEntries({
892                "/a/B/c/moduleA.ts": `import a = require("./moduleC")`,
893                "/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
894                "/a/B/c/moduleC.ts": "export var x",
895                "/a/B/c/moduleD.ts": `
896import a = require("./moduleA");
897import b = require("./moduleB");
898                `
899            }));
900            test(
901                files,
902                { module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true },
903                "/a/B/c",
904                /*useCaseSensitiveFileNames*/ false,
905                ["moduleD.ts"],
906                () => emptyArray
907            );
908        });
909
910        it("should succeed when the two files in program differ only in drive letter in their names", () => {
911            const files = new Map(getEntries({
912                "d:/someFolder/moduleA.ts": `import a = require("D:/someFolder/moduleC")`,
913                "d:/someFolder/moduleB.ts": `import a = require("./moduleC")`,
914                "D:/someFolder/moduleC.ts": "export const x = 10",
915            }));
916            test(
917                files,
918                { module: ModuleKind.CommonJS, forceConsistentCasingInFileNames: true },
919                "d:/someFolder",
920                /*useCaseSensitiveFileNames*/ false,
921                ["d:/someFolder/moduleA.ts", "d:/someFolder/moduleB.ts"],
922                () => emptyArray
923            );
924        });
925    });
926
927    describe("unittests:: moduleResolution:: baseUrl augmented module resolution", () => {
928
929        it("module resolution without path mappings/rootDirs", () => {
930            test(/*hasDirectoryExists*/ false);
931            test(/*hasDirectoryExists*/ true);
932
933            function test(hasDirectoryExists: boolean) {
934                const file1: File = { name: "/root/folder1/file1.ts" };
935                const file2: File = { name: "/root/folder2/file2.ts" };
936                const file3: File = { name: "/root/folder2/file3.ts" };
937                const host = createModuleResolutionHost(hasDirectoryExists, file1, file2, file3);
938                for (const moduleResolution of [ ModuleResolutionKind.NodeJs, ModuleResolutionKind.Classic ]) {
939                    const options: CompilerOptions = { moduleResolution, baseUrl: "/root" };
940                    {
941                        const result = resolveModuleName("folder2/file2", file1.name, options, host);
942                        checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(file2.name), []);
943                    }
944                    {
945                        const result = resolveModuleName("./file3", file2.name, options, host);
946                        checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(file3.name), []);
947                    }
948                    {
949                        const result = resolveModuleName("/root/folder1/file1", file2.name, options, host);
950                        checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(file1.name), []);
951                    }
952                }
953            }
954            // add failure tests
955        });
956
957        it("node + baseUrl", () => {
958            test(/*hasDirectoryExists*/ false);
959            test(/*hasDirectoryExists*/ true);
960
961            function test(hasDirectoryExists: boolean) {
962                const main: File = { name: "/root/a/b/main.ts" };
963                const m1: File = { name: "/root/m1.ts" }; // load file as module
964                const m2: File = { name: "/root/m2/index.d.ts" }; // load folder as module
965                const m3: File = { name: "/root/m3/package.json", content: JSON.stringify({ typings: "dist/typings.d.ts" }) };
966                const m3Typings: File = { name: "/root/m3/dist/typings.d.ts" };
967                const m4: File = { name: "/root/node_modules/m4.ts" }; // fallback to node
968
969                const options: CompilerOptions = { moduleResolution: ModuleResolutionKind.NodeJs, baseUrl: "/root" };
970                const host = createModuleResolutionHost(hasDirectoryExists, main, m1, m2, m3, m3Typings, m4);
971
972                check("m1", main, m1);
973                check("m2", main, m2);
974                check("m3", main, m3Typings);
975                check("m4", main, m4, /*isExternalLibraryImport*/ true);
976
977                function check(name: string, caller: File, expected: File, isExternalLibraryImport = false) {
978                    const result = resolveModuleName(name, caller.name, options, host);
979                    checkResolvedModule(result.resolvedModule, createResolvedModule(expected.name, isExternalLibraryImport));
980                }
981            }
982        });
983
984        it("classic + baseUrl", () => {
985            test(/*hasDirectoryExists*/ false);
986            test(/*hasDirectoryExists*/ true);
987
988            function test(hasDirectoryExists: boolean) {
989                const main: File = { name: "/root/a/b/main.ts" };
990                const m1: File = { name: "/root/x/m1.ts" }; // load from base url
991                const m2: File = { name: "/m2.ts" }; // fallback to classic
992
993                const options: CompilerOptions = { moduleResolution: ModuleResolutionKind.Classic, baseUrl: "/root/x", jsx: JsxEmit.React };
994                const host = createModuleResolutionHost(hasDirectoryExists, main, m1, m2);
995
996                check("m1", main, m1);
997                check("m2", main, m2);
998
999                function check(name: string, caller: File, expected: File) {
1000                    const result = resolveModuleName(name, caller.name, options, host);
1001                    checkResolvedModule(result.resolvedModule, createResolvedModule(expected.name));
1002                }
1003            }
1004        });
1005
1006        it("node + baseUrl + path mappings", () => {
1007            test(/*hasDirectoryExists*/ false);
1008            test(/*hasDirectoryExists*/ true);
1009
1010            function test(hasDirectoryExists: boolean) {
1011                const main: File = { name: "/root/folder1/main.ts" };
1012
1013                const file1: File = { name: "/root/folder1/file1.ts" };
1014                const file2: File = { name: "/root/generated/folder1/file2.ts" }; // load remapped file as module
1015                const file3: File = { name: "/root/generated/folder2/file3/index.d.ts" }; // load folder a module
1016                const file4Typings: File = { name: "/root/generated/folder2/file4/package.json", content: JSON.stringify({ typings: "dist/types.d.ts" }) };
1017                const file4: File = { name: "/root/generated/folder2/file4/dist/types.d.ts" }; // load file pointed by typings
1018                const file5: File = { name: "/root/someanotherfolder/file5/index.d.ts" }; // load remapped module from folder
1019                const file6: File = { name: "/root/node_modules/file6.ts" }; // fallback to node
1020                const host = createModuleResolutionHost(hasDirectoryExists, file1, file2, file3, file4, file4Typings, file5, file6);
1021
1022                const options: CompilerOptions = {
1023                    moduleResolution: ModuleResolutionKind.NodeJs,
1024                    baseUrl: "/root",
1025                    jsx: JsxEmit.React,
1026                    paths: {
1027                        "*": [
1028                            "*",
1029                            "generated/*"
1030                        ],
1031                        "somefolder/*": [
1032                            "someanotherfolder/*"
1033                        ],
1034                        "/rooted/*": [
1035                            "generated/*"
1036                        ]
1037                    }
1038                };
1039                check("folder1/file1", file1, []);
1040                check("folder1/file2", file2, [
1041                    // first try the '*'
1042                    "/root/folder1/file2.ts",
1043                    "/root/folder1/file2.tsx",
1044                    "/root/folder1/file2.d.ts",
1045                    "/root/folder1/file2.ets",
1046                    "/root/folder1/file2/package.json",
1047
1048                    "/root/folder1/file2/index.ts",
1049                    "/root/folder1/file2/index.tsx",
1050                    "/root/folder1/file2/index.d.ts",
1051                    "/root/folder1/file2/index.ets",
1052                    // then first attempt on 'generated/*' was successful
1053                ]);
1054                check("/rooted/folder1/file2", file2, []);
1055                check("folder2/file3", file3, [
1056                    // first try '*'
1057                    "/root/folder2/file3.ts",
1058                    "/root/folder2/file3.tsx",
1059                    "/root/folder2/file3.d.ts",
1060                    "/root/folder2/file3.ets",
1061                    "/root/folder2/file3/package.json",
1062
1063                    "/root/folder2/file3/index.ts",
1064                    "/root/folder2/file3/index.tsx",
1065                    "/root/folder2/file3/index.d.ts",
1066                    "/root/folder2/file3/index.ets",
1067
1068                    // then use remapped location
1069                    "/root/generated/folder2/file3.ts",
1070                    "/root/generated/folder2/file3.tsx",
1071                    "/root/generated/folder2/file3.d.ts",
1072                    "/root/generated/folder2/file3.ets",
1073                    "/root/generated/folder2/file3/package.json",
1074
1075                    "/root/generated/folder2/file3/index.ts",
1076                    "/root/generated/folder2/file3/index.tsx",
1077                    // success on index.d.ts
1078                ]);
1079                check("folder2/file4", file4, [
1080                    // first try '*'
1081                    "/root/folder2/file4.ts",
1082                    "/root/folder2/file4.tsx",
1083                    "/root/folder2/file4.d.ts",
1084                    "/root/folder2/file4.ets",
1085                    "/root/folder2/file4/package.json",
1086
1087                    "/root/folder2/file4/index.ts",
1088                    "/root/folder2/file4/index.tsx",
1089                    "/root/folder2/file4/index.d.ts",
1090                    "/root/folder2/file4/index.ets",
1091
1092                    // try to load from file from remapped location
1093                    "/root/generated/folder2/file4.ts",
1094                    "/root/generated/folder2/file4.tsx",
1095                    "/root/generated/folder2/file4.d.ts",
1096                    "/root/generated/folder2/file4.ets"
1097                    // success on loading as from folder
1098                ]);
1099                check("somefolder/file5", file5, [
1100                    // load from remapped location
1101                    // first load from fle
1102                    "/root/someanotherfolder/file5.ts",
1103                    "/root/someanotherfolder/file5.tsx",
1104                    "/root/someanotherfolder/file5.d.ts",
1105                    "/root/someanotherfolder/file5.ets",
1106
1107                    // load from folder
1108                    "/root/someanotherfolder/file5/package.json",
1109                    "/root/someanotherfolder/file5/index.ts",
1110                    "/root/someanotherfolder/file5/index.tsx",
1111                    // success on index.d.ts
1112                ]);
1113                check("file6", file6, [
1114                    // first try *
1115                    // load from file
1116                    "/root/file6.ts",
1117                    "/root/file6.tsx",
1118                    "/root/file6.d.ts",
1119                    "/root/file6.ets",
1120
1121                    // load from folder
1122                    "/root/file6/package.json",
1123                    "/root/file6/index.ts",
1124                    "/root/file6/index.tsx",
1125                    "/root/file6/index.d.ts",
1126                    "/root/file6/index.ets",
1127
1128                    // then try 'generated/*'
1129                    // load from file
1130                    "/root/generated/file6.ts",
1131                    "/root/generated/file6.tsx",
1132                    "/root/generated/file6.d.ts",
1133                    "/root/generated/file6.ets",
1134
1135                    // load from folder
1136                    "/root/generated/file6/package.json",
1137                    "/root/generated/file6/index.ts",
1138                    "/root/generated/file6/index.tsx",
1139                    "/root/generated/file6/index.d.ts",
1140                    "/root/generated/file6/index.ets",
1141
1142                    // fallback to standard node behavior
1143                    "/root/folder1/node_modules/file6/package.json",
1144
1145                    // load from file
1146                    "/root/folder1/node_modules/file6.ts",
1147                    "/root/folder1/node_modules/file6.tsx",
1148                    "/root/folder1/node_modules/file6.d.ts",
1149                    "/root/folder1/node_modules/file6.ets",
1150
1151                    // load from folder
1152                    "/root/folder1/node_modules/file6/index.ts",
1153                    "/root/folder1/node_modules/file6/index.tsx",
1154                    "/root/folder1/node_modules/file6/index.d.ts",
1155                    "/root/folder1/node_modules/file6/index.ets",
1156
1157                    "/root/folder1/node_modules/@types/file6/package.json",
1158                    "/root/folder1/node_modules/@types/file6.d.ts",
1159                    "/root/folder1/node_modules/@types/file6/index.d.ts",
1160
1161                    "/root/node_modules/file6/package.json",
1162                    // success on /root/node_modules/file6.ts
1163                ], /*isExternalLibraryImport*/ true);
1164
1165                function check(name: string, expected: File, expectedFailedLookups: string[], isExternalLibraryImport = false) {
1166                    const result = resolveModuleName(name, main.name, options, host);
1167                    checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(expected.name, isExternalLibraryImport), expectedFailedLookups);
1168                }
1169            }
1170        });
1171
1172        it ("classic + baseUrl + path mappings", () => {
1173            // classic mode does not use directoryExists
1174            test(/*hasDirectoryExists*/ false);
1175
1176            function test(hasDirectoryExists: boolean) {
1177                const main: File = { name: "/root/folder1/main.ts" };
1178
1179                const file1: File = { name: "/root/folder1/file1.ts" };
1180                const file2: File = { name: "/root/generated/folder1/file2.ts" };
1181                const file3: File = { name: "/folder1/file3.ts" }; // fallback to classic
1182                const host = createModuleResolutionHost(hasDirectoryExists, file1, file2, file3);
1183
1184                const options: CompilerOptions = {
1185                    moduleResolution: ModuleResolutionKind.Classic,
1186                    baseUrl: "/root",
1187                    jsx: JsxEmit.React,
1188                    paths: {
1189                        "*": [
1190                            "*",
1191                            "generated/*"
1192                        ],
1193                        "somefolder/*": [
1194                            "someanotherfolder/*"
1195                        ],
1196                        "/rooted/*": [
1197                            "generated/*"
1198                        ]
1199                    }
1200                };
1201                check("folder1/file1", file1, []);
1202                check("folder1/file2", file2, [
1203                    // first try '*'
1204                    "/root/folder1/file2.ts",
1205                    "/root/folder1/file2.tsx",
1206                    "/root/folder1/file2.d.ts",
1207                    "/root/folder1/file2.ets",
1208                    // success when using 'generated/*'
1209                ]);
1210                check("/rooted/folder1/file2", file2, []);
1211                check("folder1/file3", file3, [
1212                    // first try '*'
1213                    "/root/folder1/file3.ts",
1214                    "/root/folder1/file3.tsx",
1215                    "/root/folder1/file3.d.ts",
1216                    "/root/folder1/file3.ets",
1217                    // then try 'generated/*'
1218                    "/root/generated/folder1/file3.ts",
1219                    "/root/generated/folder1/file3.tsx",
1220                    "/root/generated/folder1/file3.d.ts",
1221                    "/root/generated/folder1/file3.ets",
1222                    // fallback to classic
1223                    "/root/folder1/folder1/file3.ts",
1224                    "/root/folder1/folder1/file3.tsx",
1225                    "/root/folder1/folder1/file3.d.ts",
1226                    "/root/folder1/folder1/file3.ets",
1227                    "/root/folder1/file3.ts",
1228                    "/root/folder1/file3.tsx",
1229                    "/root/folder1/file3.d.ts",
1230                    "/root/folder1/file3.ets"
1231                ]);
1232
1233                function check(name: string, expected: File, expectedFailedLookups: string[]) {
1234                    const result = resolveModuleName(name, main.name, options, host);
1235                    checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(expected.name), expectedFailedLookups);
1236                }
1237            }
1238        });
1239
1240        it ("node + rootDirs", () => {
1241            test(/*hasDirectoryExists*/ false);
1242            test(/*hasDirectoryExists*/ true);
1243
1244            function test(hasDirectoryExists: boolean) {
1245                const file1: File = { name: "/root/folder1/file1.ts" };
1246                const file1_1: File = { name: "/root/folder1/file1_1/index.d.ts" }; // eslint-disable-line @typescript-eslint/naming-convention
1247                const file2: File = { name: "/root/generated/folder1/file2.ts" };
1248                const file3: File = { name: "/root/generated/folder2/file3.ts" };
1249                const host = createModuleResolutionHost(hasDirectoryExists, file1, file1_1, file2, file3);
1250                const options: CompilerOptions = {
1251                    moduleResolution: ModuleResolutionKind.NodeJs,
1252                    rootDirs: [
1253                        "/root",
1254                        "/root/generated/"
1255                    ]
1256                };
1257                check("./file2", file1, file2, [
1258                    // first try current location
1259                    // load from file
1260                    "/root/folder1/file2.ts",
1261                    "/root/folder1/file2.tsx",
1262                    "/root/folder1/file2.d.ts",
1263                    "/root/folder1/file2.ets",
1264                    // load from folder
1265                    "/root/folder1/file2/package.json",
1266                    "/root/folder1/file2/index.ts",
1267                    "/root/folder1/file2/index.tsx",
1268                    "/root/folder1/file2/index.d.ts",
1269                    "/root/folder1/file2/index.ets",
1270                    // success after using alternative rootDir entry
1271                ]);
1272                check("../folder1/file1", file3, file1, [
1273                    // first try current location
1274                    // load from file
1275                    "/root/generated/folder1/file1.ts",
1276                    "/root/generated/folder1/file1.tsx",
1277                    "/root/generated/folder1/file1.d.ts",
1278                    "/root/generated/folder1/file1.ets",
1279                    // load from module
1280                    "/root/generated/folder1/file1/package.json",
1281                    "/root/generated/folder1/file1/index.ts",
1282                    "/root/generated/folder1/file1/index.tsx",
1283                    "/root/generated/folder1/file1/index.d.ts",
1284                    "/root/generated/folder1/file1/index.ets"
1285                    // success after using alternative rootDir entry
1286                ]);
1287                check("../folder1/file1_1", file3, file1_1, [
1288                    // first try current location
1289                    // load from file
1290                    "/root/generated/folder1/file1_1.ts",
1291                    "/root/generated/folder1/file1_1.tsx",
1292                    "/root/generated/folder1/file1_1.d.ts",
1293                    "/root/generated/folder1/file1_1.ets",
1294                    // load from folder
1295                    "/root/generated/folder1/file1_1/package.json",
1296                    "/root/generated/folder1/file1_1/index.ts",
1297                    "/root/generated/folder1/file1_1/index.tsx",
1298                    "/root/generated/folder1/file1_1/index.d.ts",
1299                    "/root/generated/folder1/file1_1/index.ets",
1300                    // try alternative rootDir entry
1301                    // load from file
1302                    "/root/folder1/file1_1.ts",
1303                    "/root/folder1/file1_1.tsx",
1304                    "/root/folder1/file1_1.d.ts",
1305                    "/root/folder1/file1_1.ets",
1306                    // load from directory
1307                    "/root/folder1/file1_1/package.json",
1308                    "/root/folder1/file1_1/index.ts",
1309                    "/root/folder1/file1_1/index.tsx",
1310                    // success on loading '/root/folder1/file1_1/index.d.ts'
1311                ]);
1312
1313                function check(name: string, container: File, expected: File, expectedFailedLookups: string[]) {
1314                    const result = resolveModuleName(name, container.name, options, host);
1315                    checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(expected.name), expectedFailedLookups);
1316                }
1317            }
1318        });
1319
1320        it ("classic + rootDirs", () => {
1321            test(/*hasDirectoryExists*/ false);
1322
1323            function test(hasDirectoryExists: boolean) {
1324                const file1: File = { name: "/root/folder1/file1.ts" };
1325                const file2: File = { name: "/root/generated/folder1/file2.ts" };
1326                const file3: File = { name: "/root/generated/folder2/file3.ts" };
1327                const file4: File = { name: "/folder1/file1_1.ts" };
1328                const host = createModuleResolutionHost(hasDirectoryExists, file1, file2, file3, file4);
1329                const options: CompilerOptions = {
1330                    moduleResolution: ModuleResolutionKind.Classic,
1331                    jsx: JsxEmit.React,
1332                    rootDirs: [
1333                        "/root",
1334                        "/root/generated/"
1335                    ]
1336                };
1337                check("./file2", file1, file2, [
1338                    // first load from current location
1339                    "/root/folder1/file2.ts",
1340                    "/root/folder1/file2.tsx",
1341                    "/root/folder1/file2.d.ts",
1342                    "/root/folder1/file2.ets"
1343                    // then try alternative rootDir entry
1344                ]);
1345                check("../folder1/file1", file3, file1, [
1346                    // first load from current location
1347                    "/root/generated/folder1/file1.ts",
1348                    "/root/generated/folder1/file1.tsx",
1349                    "/root/generated/folder1/file1.d.ts",
1350                    "/root/generated/folder1/file1.ets"
1351                    // then try alternative rootDir entry
1352                ]);
1353                check("folder1/file1_1", file3, file4, [
1354                    // current location
1355                    "/root/generated/folder2/folder1/file1_1.ts",
1356                    "/root/generated/folder2/folder1/file1_1.tsx",
1357                    "/root/generated/folder2/folder1/file1_1.d.ts",
1358                    "/root/generated/folder2/folder1/file1_1.ets",
1359                    // other entry in rootDirs
1360                    "/root/generated/folder1/file1_1.ts",
1361                    "/root/generated/folder1/file1_1.tsx",
1362                    "/root/generated/folder1/file1_1.d.ts",
1363                    "/root/generated/folder1/file1_1.ets",
1364                    // fallback
1365                    "/root/folder1/file1_1.ts",
1366                    "/root/folder1/file1_1.tsx",
1367                    "/root/folder1/file1_1.d.ts",
1368                    "/root/folder1/file1_1.ets"
1369                    // found one
1370                ]);
1371
1372                function check(name: string, container: File, expected: File, expectedFailedLookups: string[]) {
1373                    const result = resolveModuleName(name, container.name, options, host);
1374                    checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(expected.name), expectedFailedLookups);
1375                }
1376            }
1377        });
1378
1379        it ("nested node module", () => {
1380            test(/*hasDirectoryExists*/ false);
1381            test(/*hasDirectoryExists*/ true);
1382
1383            function test(hasDirectoryExists: boolean) {
1384                const app: File = { name: "/root/src/app.ts" };
1385                const libsPackage: File = { name: "/root/src/libs/guid/package.json", content: JSON.stringify({ typings: "dist/guid.d.ts" }) };
1386                const libsTypings: File = { name: "/root/src/libs/guid/dist/guid.d.ts" };
1387                const host = createModuleResolutionHost(hasDirectoryExists, app, libsPackage, libsTypings);
1388                const options: CompilerOptions = {
1389                    moduleResolution: ModuleResolutionKind.NodeJs,
1390                    baseUrl: "/root",
1391                    paths: {
1392                        "libs/guid": [ "src/libs/guid" ]
1393                    }
1394                };
1395                const result = resolveModuleName("libs/guid", app.name, options, host);
1396                checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(libsTypings.name), [
1397                    // first try to load module as file
1398                    "/root/src/libs/guid.ts",
1399                    "/root/src/libs/guid.tsx",
1400                    "/root/src/libs/guid.d.ts",
1401                    "/root/src/libs/guid.ets",
1402                ]);
1403            }
1404        });
1405    });
1406
1407    describe("unittests:: moduleResolution:: ModuleResolutionHost.directoryExists", () => {
1408        it("No 'fileExists' calls if containing directory is missing", () => {
1409            const host: ModuleResolutionHost = {
1410                readFile: notImplemented,
1411                fileExists: notImplemented,
1412                directoryExists: _ => false
1413            };
1414
1415            const result = resolveModuleName("someName", "/a/b/c/d", { moduleResolution: ModuleResolutionKind.NodeJs }, host);
1416            assert(!result.resolvedModule);
1417        });
1418    });
1419
1420    describe("unittests:: moduleResolution:: Type reference directive resolution: ", () => {
1421        function testWorker(hasDirectoryExists: boolean, typesRoot: string | undefined, typeDirective: string, primary: boolean, initialFile: File, targetFile: File, ...otherFiles: File[]) {
1422            const host = createModuleResolutionHost(hasDirectoryExists, ...[initialFile, targetFile].concat(...otherFiles));
1423            const result = resolveTypeReferenceDirective(typeDirective, initialFile.name, typesRoot ? { typeRoots: [typesRoot] } : {}, host);
1424            assert(result.resolvedTypeReferenceDirective!.resolvedFileName !== undefined, "expected type directive to be resolved");
1425            assert.equal(result.resolvedTypeReferenceDirective!.resolvedFileName, targetFile.name, "unexpected result of type reference resolution");
1426            assert.equal(result.resolvedTypeReferenceDirective!.primary, primary, "unexpected 'primary' value");
1427        }
1428
1429        function test(typesRoot: string, typeDirective: string, primary: boolean, initialFile: File, targetFile: File, ...otherFiles: File[]) {
1430            testWorker(/*hasDirectoryExists*/ false, typesRoot, typeDirective, primary, initialFile, targetFile, ...otherFiles);
1431        }
1432
1433        it("Can be resolved from primary location", () => {
1434            {
1435                const f1 = { name: "/root/src/app.ts" };
1436                const f2 = { name: "/root/src/types/lib/index.d.ts" };
1437                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ true, f1, f2);
1438            }
1439            {
1440                const f1 = { name: "/root/src/app.ts" };
1441                const f2 = { name: "/root/src/types/lib/typings/lib.d.ts" };
1442                const packageFile = { name: "/root/src/types/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
1443                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ true, f1, f2, packageFile);
1444            }
1445            {
1446                const f1 = { name: "/root/src/app.ts" };
1447                const f2 = { name: "/root/src/node_modules/lib/index.d.ts" };
1448                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2);
1449            }
1450            {
1451                const f1 = { name: "/root/src/app.ts" };
1452                const f2 = { name: "/root/src/node_modules/lib/typings/lib.d.ts" };
1453                const packageFile = { name: "/root/src/node_modules/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
1454                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, packageFile);
1455            }
1456            {
1457                const f1 = { name: "/root/src/app.ts" };
1458                const f2 = { name: "/root/src/node_modules/@types/lib/index.d.ts" };
1459                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2);
1460            }
1461            {
1462                const f1 = { name: "/root/src/app.ts" };
1463                const f2 = { name: "/root/src/node_modules/@types/lib/typings/lib.d.ts" };
1464                const packageFile = { name: "/root/src/node_modules/@types/lib/package.json", content: JSON.stringify({ types: "typings/lib.d.ts" }) };
1465                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, packageFile);
1466            }
1467        });
1468        it("Can be resolved from secondary location", () => {
1469            {
1470                const f1 = { name: "/root/src/app.ts" };
1471                const f2 = { name: "/root/node_modules/lib.d.ts" };
1472                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2);
1473            }
1474            {
1475                const f1 = { name: "/root/src/app.ts" };
1476                const f2 = { name: "/root/node_modules/lib/index.d.ts" };
1477                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2);
1478            }
1479            {
1480                const f1 = { name: "/root/src/app.ts" };
1481                const f2 = { name: "/root/node_modules/lib/typings/lib.d.ts" };
1482                const packageFile = { name: "/root/node_modules/lib/package.json", content: JSON.stringify({ typings: "typings/lib.d.ts" }) };
1483                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, packageFile);
1484            }
1485            {
1486                const f1 = { name: "/root/src/app.ts" };
1487                const f2 = { name: "/root/node_modules/@types/lib/index.d.ts" };
1488                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2);
1489            }
1490            {
1491                const f1 = { name: "/root/src/app.ts" };
1492                const f2 = { name: "/root/node_modules/@types/lib/typings/lib.d.ts" };
1493                const packageFile = { name: "/root/node_modules/@types/lib/package.json", content: JSON.stringify({ typings: "typings/lib.d.ts" }) };
1494                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ false, f1, f2, packageFile);
1495            }
1496        });
1497        it("Primary resolution overrides secondary resolutions", () => {
1498            {
1499                const f1 = { name: "/root/src/a/b/c/app.ts" };
1500                const f2 = { name: "/root/src/types/lib/index.d.ts" };
1501                const f3 = { name: "/root/src/a/b/node_modules/lib.d.ts" };
1502                test(/*typesRoot*/"/root/src/types", /* typeDirective */"lib", /*primary*/ true, f1, f2, f3);
1503            }
1504        });
1505        it("Reused program keeps errors", () => {
1506            const f1 = { name: "/root/src/a/b/c/d/e/app.ts", content: `/// <reference types="lib"/>` };
1507            const f2 = { name: "/root/src/a/b/c/d/node_modules/lib/index.d.ts", content: `declare var x: number;` };
1508            const f3 = { name: "/root/src/a/b/c/d/f/g/app.ts", content: `/// <reference types="lib"/>` };
1509            const f4 = { name: "/root/src/a/b/c/d/f/node_modules/lib/index.d.ts", content: `declare var x: number;` };
1510            const files = [f1, f2, f3, f4];
1511
1512            const names = map(files, f => f.name);
1513            const sourceFiles = arrayToMap(map(files, f => createSourceFile(f.name, f.content, ScriptTarget.ES2015)), f => f.fileName);
1514            const compilerHost: CompilerHost = {
1515                fileExists: fileName => sourceFiles.has(fileName),
1516                getSourceFile: fileName => sourceFiles.get(fileName),
1517                getDefaultLibFileName: () => "lib.d.ts",
1518                writeFile: notImplemented,
1519                getCurrentDirectory: () => "/",
1520                getDirectories: () => [],
1521                getCanonicalFileName: f => f.toLowerCase(),
1522                getNewLine: () => "\r\n",
1523                useCaseSensitiveFileNames: () => false,
1524                readFile: fileName => {
1525                    const file = sourceFiles.get(fileName);
1526                    return file && file.text;
1527                },
1528            };
1529            const program1 = createProgram(names, {}, compilerHost);
1530            const diagnostics1 = program1.getOptionsDiagnostics();
1531            assert.equal(diagnostics1.length, 1, "expected one diagnostic");
1532
1533            const program2 = createProgram(names, {}, compilerHost, program1);
1534            assert.isTrue(program2.structureIsReused === StructureIsReused.Completely);
1535            const diagnostics2 = program2.getOptionsDiagnostics();
1536            assert.equal(diagnostics2.length, 1, "expected one diagnostic");
1537            assert.deepEqual(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
1538        });
1539
1540        it("Modules in the same .d.ts file are preferred to external files", () => {
1541            const f = {
1542                name: "/a/b/c/c/app.d.ts",
1543                content: `
1544                declare module "fs" {
1545                    export interface Stat { id: number }
1546                }
1547                declare module "fs-client" {
1548                    import { Stat } from "fs";
1549                    export function foo(): Stat;
1550                }`
1551            };
1552            const file = createSourceFile(f.name, f.content, ScriptTarget.ES2015);
1553            const compilerHost: CompilerHost = {
1554                fileExists: fileName => fileName === file.fileName,
1555                getSourceFile: fileName => fileName === file.fileName ? file : undefined,
1556                getDefaultLibFileName: () => "lib.d.ts",
1557                writeFile: notImplemented,
1558                getCurrentDirectory: () => "/",
1559                getDirectories: () => [],
1560                getCanonicalFileName: f => f.toLowerCase(),
1561                getNewLine: () => "\r\n",
1562                useCaseSensitiveFileNames: () => false,
1563                readFile: fileName => fileName === file.fileName ? file.text : undefined,
1564                resolveModuleNames: notImplemented,
1565            };
1566            createProgram([f.name], {}, compilerHost);
1567        });
1568
1569        it("Modules in .ts file are not checked in the same file", () => {
1570            const f = {
1571                name: "/a/b/c/c/app.ts",
1572                content: `
1573                declare module "fs" {
1574                    export interface Stat { id: number }
1575                }
1576                declare module "fs-client" {
1577                    import { Stat } from "fs";
1578                    export function foo(): Stat;
1579                }`
1580            };
1581            const file = createSourceFile(f.name, f.content, ScriptTarget.ES2015);
1582            const compilerHost: CompilerHost = {
1583                fileExists: fileName => fileName === file.fileName,
1584                getSourceFile: fileName => fileName === file.fileName ? file : undefined,
1585                getDefaultLibFileName: () => "lib.d.ts",
1586                writeFile: notImplemented,
1587                getCurrentDirectory: () => "/",
1588                getDirectories: () => [],
1589                getCanonicalFileName: f => f.toLowerCase(),
1590                getNewLine: () => "\r\n",
1591                useCaseSensitiveFileNames: () => false,
1592                readFile: fileName => fileName === file.fileName ? file.text : undefined,
1593                resolveModuleNames(moduleNames: string[], _containingFile: string) {
1594                    assert.deepEqual(moduleNames, ["fs"]);
1595                    return [undefined!]; // TODO: GH#18217
1596                }
1597            };
1598            createProgram([f.name], {}, compilerHost);
1599        });
1600        describe("can be resolved when typeReferenceDirective is relative and in a sibling folder", () => {
1601            const initialFile = { name: "/root/src/background/app.ts" };
1602            const targetFile = { name: "/root/src/typedefs/filesystem.d.ts" };
1603            it("when host doesnt have directoryExists", () => {
1604                testWorker(/*hasDirectoryExists*/ false, /*typesRoot*/ undefined, /*typeDirective*/ "../typedefs/filesystem", /*primary*/ false, initialFile, targetFile);
1605            });
1606            it("when host has directoryExists", () => {
1607                testWorker(/*hasDirectoryExists*/ true, /*typesRoot*/ undefined, /*typeDirective*/ "../typedefs/filesystem", /*primary*/ false, initialFile, targetFile);
1608            });
1609        });
1610    });
1611}
1612