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