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