1namespace ts { 2 /* @internal */ 3 export function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void; 4 export function trace(host: ModuleResolutionHost): void { 5 host.trace!(formatMessage.apply(undefined, arguments)); 6 } 7 8 /* @internal */ 9 export function isTraceEnabled(compilerOptions: CompilerOptions, host: ModuleResolutionHost): boolean { 10 return !!compilerOptions.traceResolution && host.trace !== undefined; 11 } 12 13 function withPackageId(packageInfo: PackageJsonInfo | undefined, r: PathAndExtension | undefined): Resolved | undefined { 14 let packageId: PackageId | undefined; 15 if (r && packageInfo) { 16 const packageJsonContent = packageInfo.packageJsonContent as PackageJson; 17 if (typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string") { 18 packageId = { 19 name: packageJsonContent.name, 20 subModuleName: r.path.slice(packageInfo.packageDirectory.length + directorySeparator.length), 21 version: packageJsonContent.version 22 }; 23 } 24 } 25 return r && { path: r.path, extension: r.ext, packageId }; 26 } 27 28 function noPackageId(r: PathAndExtension | undefined): Resolved | undefined { 29 return withPackageId(/*packageInfo*/ undefined, r); 30 } 31 32 function removeIgnoredPackageId(r: Resolved | undefined): PathAndExtension | undefined { 33 if (r) { 34 Debug.assert(r.packageId === undefined); 35 return { path: r.path, ext: r.extension }; 36 } 37 } 38 39 /** Result of trying to resolve a module. */ 40 interface Resolved { 41 path: string; 42 extension: Extension; 43 packageId: PackageId | undefined; 44 /** 45 * When the resolved is not created from cache, the value is 46 * - string if original Path if it is symbolic link to the resolved path 47 * - undefined if path is not a symbolic link 48 * When the resolved is created using value from cache of ResolvedModuleWithFailedLookupLocations, the value is: 49 * - string if original Path if it is symbolic link to the resolved path 50 * - true if path is not a symbolic link - this indicates that the originalPath calculation is already done and needs to be skipped 51 */ 52 originalPath?: string | true; 53 } 54 55 /** Result of trying to resolve a module at a file. Needs to have 'packageId' added later. */ 56 interface PathAndExtension { 57 path: string; 58 // (Use a different name than `extension` to make sure Resolved isn't assignable to PathAndExtension.) 59 ext: Extension; 60 } 61 62 /** 63 * Kinds of file that we are currently looking for. 64 * Typically there is one pass with Extensions.TypeScript, then a second pass with Extensions.JavaScript. 65 */ 66 enum Extensions { 67 TypeScript, /** '.ts', '.tsx', '.d.ts', or '.ets */ 68 JavaScript, /** '.js' or '.jsx' */ 69 Json, /** '.json' */ 70 TSConfig, /** '.json' with `tsconfig` used instead of `index` */ 71 DtsOnly /** Only '.d.ts' */ 72 } 73 74 interface PathAndPackageId { 75 readonly fileName: string; 76 readonly packageId: PackageId | undefined; 77 } 78 /** Used with `Extensions.DtsOnly` to extract the path from TypeScript results. */ 79 function resolvedTypeScriptOnly(resolved: Resolved | undefined): PathAndPackageId | undefined { 80 if (!resolved) { 81 return undefined; 82 } 83 Debug.assert(extensionIsTS(resolved.extension)); 84 return { fileName: resolved.path, packageId: resolved.packageId }; 85 } 86 87 function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean | undefined, failedLookupLocations: string[], resultFromCache: ResolvedModuleWithFailedLookupLocations | undefined): ResolvedModuleWithFailedLookupLocations { 88 if (resultFromCache) { 89 resultFromCache.failedLookupLocations.push(...failedLookupLocations); 90 return resultFromCache; 91 } 92 return { 93 resolvedModule: resolved && { resolvedFileName: resolved.path, originalPath: resolved.originalPath === true ? undefined : resolved.originalPath, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId }, 94 failedLookupLocations 95 }; 96 } 97 98 interface ModuleResolutionState { 99 host: ModuleResolutionHost; 100 compilerOptions: CompilerOptions; 101 traceEnabled: boolean; 102 failedLookupLocations: Push<string>; 103 resultFromCache?: ResolvedModuleWithFailedLookupLocations; 104 } 105 106 /** Just the fields that we use for module resolution. */ 107 interface PackageJsonPathFields { 108 typings?: string; 109 types?: string; 110 typesVersions?: MapLike<MapLike<string[]>>; 111 main?: string; 112 tsconfig?: string; 113 } 114 115 interface PackageJson extends PackageJsonPathFields { 116 name?: string; 117 version?: string; 118 } 119 120 function readPackageJsonField<TMatch, K extends MatchingKeys<PackageJson, string | undefined>>(jsonContent: PackageJson, fieldName: K, typeOfTag: "string", state: ModuleResolutionState): PackageJson[K] | undefined; 121 function readPackageJsonField<K extends MatchingKeys<PackageJson, object | undefined>>(jsonContent: PackageJson, fieldName: K, typeOfTag: "object", state: ModuleResolutionState): PackageJson[K] | undefined; 122 function readPackageJsonField<K extends keyof PackageJson>(jsonContent: PackageJson, fieldName: K, typeOfTag: "string" | "object", state: ModuleResolutionState): PackageJson[K] | undefined { 123 if (!hasProperty(jsonContent, fieldName)) { 124 if (state.traceEnabled) { 125 trace(state.host, Diagnostics.package_json_does_not_have_a_0_field, fieldName); 126 } 127 return; 128 } 129 const value = jsonContent[fieldName]; 130 if (typeof value !== typeOfTag || value === null) { // eslint-disable-line no-null/no-null 131 if (state.traceEnabled) { 132 // eslint-disable-next-line no-null/no-null 133 trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2, fieldName, typeOfTag, value === null ? "null" : typeof value); 134 } 135 return; 136 } 137 return value; 138 } 139 140 function readPackageJsonPathField<K extends "typings" | "types" | "main" | "tsconfig">(jsonContent: PackageJson, fieldName: K, baseDirectory: string, state: ModuleResolutionState): PackageJson[K] | undefined { 141 const fileName = readPackageJsonField(jsonContent, fieldName, "string", state); 142 if (fileName === undefined) { 143 return; 144 } 145 if (!fileName) { 146 if (state.traceEnabled) { 147 trace(state.host, Diagnostics.package_json_had_a_falsy_0_field, fieldName); 148 } 149 return; 150 } 151 const path = normalizePath(combinePaths(baseDirectory, fileName)); 152 if (state.traceEnabled) { 153 trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, fileName, path); 154 } 155 return path; 156 } 157 158 function readPackageJsonTypesFields(jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState) { 159 return readPackageJsonPathField(jsonContent, "typings", baseDirectory, state) 160 || readPackageJsonPathField(jsonContent, "types", baseDirectory, state); 161 } 162 163 function readPackageJsonTSConfigField(jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState) { 164 return readPackageJsonPathField(jsonContent, "tsconfig", baseDirectory, state); 165 } 166 167 function readPackageJsonMainField(jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState) { 168 return readPackageJsonPathField(jsonContent, "main", baseDirectory, state); 169 } 170 171 function readPackageJsonTypesVersionsField(jsonContent: PackageJson, state: ModuleResolutionState) { 172 const typesVersions = readPackageJsonField(jsonContent, "typesVersions", "object", state); 173 if (typesVersions === undefined) return; 174 175 if (state.traceEnabled) { 176 trace(state.host, Diagnostics.package_json_has_a_typesVersions_field_with_version_specific_path_mappings); 177 } 178 179 return typesVersions; 180 } 181 182 interface VersionPaths { 183 version: string; 184 paths: MapLike<string[]>; 185 } 186 187 function readPackageJsonTypesVersionPaths(jsonContent: PackageJson, state: ModuleResolutionState): VersionPaths | undefined { 188 const typesVersions = readPackageJsonTypesVersionsField(jsonContent, state); 189 if (typesVersions === undefined) return; 190 191 if (state.traceEnabled) { 192 for (const key in typesVersions) { 193 if (hasProperty(typesVersions, key) && !VersionRange.tryParse(key)) { 194 trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_is_not_a_valid_semver_range, key); 195 } 196 } 197 } 198 199 const result = getPackageJsonTypesVersionsPaths(typesVersions); 200 if (!result) { 201 if (state.traceEnabled) { 202 trace(state.host, Diagnostics.package_json_does_not_have_a_typesVersions_entry_that_matches_version_0, versionMajorMinor); 203 } 204 return; 205 } 206 207 const { version: bestVersionKey, paths: bestVersionPaths } = result; 208 if (typeof bestVersionPaths !== "object") { 209 if (state.traceEnabled) { 210 trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2, `typesVersions['${bestVersionKey}']`, "object", typeof bestVersionPaths); 211 } 212 return; 213 } 214 215 return result; 216 } 217 218 let typeScriptVersion: Version | undefined; 219 220 /* @internal */ 221 export function getPackageJsonTypesVersionsPaths(typesVersions: MapLike<MapLike<string[]>>) { 222 if (!typeScriptVersion) typeScriptVersion = new Version(version); 223 224 for (const key in typesVersions) { 225 if (!hasProperty(typesVersions, key)) continue; 226 227 const keyRange = VersionRange.tryParse(key); 228 if (keyRange === undefined) { 229 continue; 230 } 231 232 // return the first entry whose range matches the current compiler version. 233 if (keyRange.test(typeScriptVersion)) { 234 return { version: key, paths: typesVersions[key] }; 235 } 236 } 237 } 238 239 export function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined { 240 if (options.typeRoots) { 241 return options.typeRoots; 242 } 243 244 let currentDirectory: string | undefined; 245 if (options.configFilePath) { 246 currentDirectory = getDirectoryPath(options.configFilePath); 247 } 248 else if (host.getCurrentDirectory) { 249 currentDirectory = host.getCurrentDirectory(); 250 } 251 252 if (currentDirectory !== undefined) { 253 return getDefaultTypeRoots(currentDirectory, host); 254 } 255 } 256 257 /** 258 * Returns the path to every node_modules/@types directory from some ancestor directory. 259 * Returns undefined if there are none. 260 */ 261 function getDefaultTypeRoots(currentDirectory: string, host: { directoryExists?: (directoryName: string) => boolean }): string[] | undefined { 262 if (!host.directoryExists) { 263 return [combinePaths(currentDirectory, nodeModulesAtTypes)]; 264 // And if it doesn't exist, tough. 265 } 266 267 let typeRoots: string[] | undefined; 268 forEachAncestorDirectory(normalizePath(currentDirectory), directory => { 269 const atTypes = combinePaths(directory, nodeModulesAtTypes); 270 if (host.directoryExists!(atTypes)) { 271 (typeRoots || (typeRoots = [])).push(atTypes); 272 } 273 return undefined; 274 }); 275 return typeRoots; 276 } 277 const nodeModulesAtTypes = combinePaths("node_modules", "@types"); 278 279 /** 280 * @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown. 281 * This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups 282 * is assumed to be the same as root directory of the project. 283 */ 284 export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): ResolvedTypeReferenceDirectiveWithFailedLookupLocations { 285 const traceEnabled = isTraceEnabled(options, host); 286 if (redirectedReference) { 287 options = redirectedReference.commandLine.options; 288 } 289 const failedLookupLocations: string[] = []; 290 const moduleResolutionState: ModuleResolutionState = { compilerOptions: options, host, traceEnabled, failedLookupLocations }; 291 292 const typeRoots = getEffectiveTypeRoots(options, host); 293 if (traceEnabled) { 294 if (containingFile === undefined) { 295 if (typeRoots === undefined) { 296 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName); 297 } 298 else { 299 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots); 300 } 301 } 302 else { 303 if (typeRoots === undefined) { 304 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile); 305 } 306 else { 307 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots); 308 } 309 } 310 if (redirectedReference) { 311 trace(host, Diagnostics.Using_compiler_options_of_project_reference_redirect_0, redirectedReference.sourceFile.fileName); 312 } 313 } 314 315 let resolved = primaryLookup(); 316 let primary = true; 317 if (!resolved) { 318 resolved = secondaryLookup(); 319 primary = false; 320 } 321 322 let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; 323 if (resolved) { 324 const { fileName, packageId } = resolved; 325 const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled); 326 if (traceEnabled) { 327 if (packageId) { 328 trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_with_Package_ID_2_primary_Colon_3, typeReferenceDirectiveName, resolvedFileName, packageIdToString(packageId), primary); 329 } 330 else { 331 trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFileName, primary); 332 } 333 } 334 resolvedTypeReferenceDirective = { primary, resolvedFileName, packageId, isExternalLibraryImport: pathContainsNodeModules(fileName) }; 335 } 336 337 return { resolvedTypeReferenceDirective, failedLookupLocations }; 338 339 function primaryLookup(): PathAndPackageId | undefined { 340 // Check primary library paths 341 if (typeRoots && typeRoots.length) { 342 if (traceEnabled) { 343 trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", ")); 344 } 345 return firstDefined(typeRoots, typeRoot => { 346 const candidate = combinePaths(typeRoot, typeReferenceDirectiveName); 347 const candidateDirectory = getDirectoryPath(candidate); 348 const directoryExists = directoryProbablyExists(candidateDirectory, host); 349 if (!directoryExists && traceEnabled) { 350 trace(host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, candidateDirectory); 351 } 352 return resolvedTypeScriptOnly( 353 loadNodeModuleFromDirectory(Extensions.DtsOnly, candidate, 354 !directoryExists, moduleResolutionState)); 355 }); 356 } 357 else { 358 if (traceEnabled) { 359 trace(host, Diagnostics.Root_directory_cannot_be_determined_skipping_primary_search_paths); 360 } 361 } 362 } 363 364 function secondaryLookup(): PathAndPackageId | undefined { 365 const initialLocationForSecondaryLookup = containingFile && getDirectoryPath(containingFile); 366 367 if (initialLocationForSecondaryLookup !== undefined) { 368 // check secondary locations 369 if (traceEnabled) { 370 trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup); 371 } 372 let result: Resolved | undefined; 373 if (!isExternalModuleNameRelative(typeReferenceDirectiveName)) { 374 const searchResult = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined, /*redirectedReference*/ undefined); 375 result = searchResult && searchResult.value; 376 } 377 else { 378 const { path: candidate } = normalizePathAndParts(combinePaths(initialLocationForSecondaryLookup, typeReferenceDirectiveName)); 379 result = nodeLoadModuleByRelativeName(Extensions.DtsOnly, candidate, /*onlyRecordFailures*/ false, moduleResolutionState, /*considerPackageJson*/ true); 380 } 381 const resolvedFile = resolvedTypeScriptOnly(result); 382 if (!resolvedFile && traceEnabled) { 383 trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName); 384 } 385 return resolvedFile; 386 } 387 else { 388 if (traceEnabled) { 389 trace(host, Diagnostics.Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_modules_folder); 390 } 391 } 392 } 393 } 394 395 /** 396 * Given a set of options, returns the set of type directive names 397 * that should be included for this program automatically. 398 * This list could either come from the config file, 399 * or from enumerating the types root + initial secondary types lookup location. 400 * More type directives might appear in the program later as a result of loading actual source files; 401 * this list is only the set of defaults that are implicitly included. 402 */ 403 export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] { 404 // Use explicit type list from tsconfig.json 405 if (options.types) { 406 return options.types; 407 } 408 409 // Walk the primary type lookup locations 410 const result: string[] = []; 411 if (host.directoryExists && host.getDirectories) { 412 const typeRoots = getEffectiveTypeRoots(options, host); 413 if (typeRoots) { 414 for (const root of typeRoots) { 415 if (host.directoryExists(root)) { 416 for (const typeDirectivePath of host.getDirectories(root)) { 417 const normalized = normalizePath(typeDirectivePath); 418 const packageJsonPath = combinePaths(root, normalized, "package.json"); 419 // `types-publisher` sometimes creates packages with `"typings": null` for packages that don't provide their own types. 420 // See `createNotNeededPackageJSON` in the types-publisher` repo. 421 // eslint-disable-next-line no-null/no-null 422 const isNotNeededPackage = host.fileExists(packageJsonPath) && (readJson(packageJsonPath, host) as PackageJson).typings === null; 423 if (!isNotNeededPackage) { 424 const baseFileName = getBaseFileName(normalized); 425 426 // At this stage, skip results with leading dot. 427 if (baseFileName.charCodeAt(0) !== CharacterCodes.dot) { 428 // Return just the type directive names 429 result.push(baseFileName); 430 } 431 } 432 } 433 } 434 } 435 } 436 } 437 return result; 438 } 439 440 /** 441 * Cached module resolutions per containing directory. 442 * This assumes that any module id will have the same resolution for sibling files located in the same folder. 443 */ 444 export interface ModuleResolutionCache extends NonRelativeModuleNameResolutionCache { 445 getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map<ResolvedModuleWithFailedLookupLocations>; 446 /*@internal*/ directoryToModuleNameMap: CacheWithRedirects<ESMap<string, ResolvedModuleWithFailedLookupLocations>>; 447 } 448 449 /** 450 * Stored map from non-relative module name to a table: directory -> result of module lookup in this directory 451 * We support only non-relative module names because resolution of relative module names is usually more deterministic and thus less expensive. 452 */ 453 export interface NonRelativeModuleNameResolutionCache { 454 getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache; 455 /*@internal*/ moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>; 456 } 457 458 export interface PerModuleNameCache { 459 get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined; 460 set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void; 461 } 462 463 export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string, options?: CompilerOptions): ModuleResolutionCache { 464 return createModuleResolutionCacheWithMaps( 465 createCacheWithRedirects(options), 466 createCacheWithRedirects(options), 467 currentDirectory, 468 getCanonicalFileName 469 ); 470 } 471 472 473 /*@internal*/ 474 export interface CacheWithRedirects<T> { 475 ownMap: ESMap<string, T>; 476 redirectsMap: ESMap<Path, ESMap<string, T>>; 477 getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): ESMap<string, T>; 478 clear(): void; 479 setOwnOptions(newOptions: CompilerOptions): void; 480 setOwnMap(newOwnMap: ESMap<string, T>): void; 481 } 482 483 /*@internal*/ 484 export function createCacheWithRedirects<T>(options?: CompilerOptions): CacheWithRedirects<T> { 485 let ownMap: ESMap<string, T> = new Map(); 486 const redirectsMap = new Map<Path, ESMap<string, T>>(); 487 return { 488 ownMap, 489 redirectsMap, 490 getOrCreateMapOfCacheRedirects, 491 clear, 492 setOwnOptions, 493 setOwnMap 494 }; 495 496 function setOwnOptions(newOptions: CompilerOptions) { 497 options = newOptions; 498 } 499 500 function setOwnMap(newOwnMap: ESMap<string, T>) { 501 ownMap = newOwnMap; 502 } 503 504 function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined) { 505 if (!redirectedReference) { 506 return ownMap; 507 } 508 const path = redirectedReference.sourceFile.path; 509 let redirects = redirectsMap.get(path); 510 if (!redirects) { 511 // Reuse map if redirected reference map uses same resolution 512 redirects = !options || optionsHaveModuleResolutionChanges(options, redirectedReference.commandLine.options) ? new Map() : ownMap; 513 redirectsMap.set(path, redirects); 514 } 515 return redirects; 516 } 517 518 function clear() { 519 ownMap.clear(); 520 redirectsMap.clear(); 521 } 522 } 523 524 /*@internal*/ 525 export function createModuleResolutionCacheWithMaps( 526 directoryToModuleNameMap: CacheWithRedirects<ESMap<string, ResolvedModuleWithFailedLookupLocations>>, 527 moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>, 528 currentDirectory: string, 529 getCanonicalFileName: GetCanonicalFileName): ModuleResolutionCache { 530 531 return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName, directoryToModuleNameMap, moduleNameToDirectoryMap }; 532 533 function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) { 534 const path = toPath(directoryName, currentDirectory, getCanonicalFileName); 535 return getOrCreateCache<ESMap<string, ResolvedModuleWithFailedLookupLocations>>(directoryToModuleNameMap, redirectedReference, path, () => new Map()); 536 } 537 538 function getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache { 539 Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName)); 540 return getOrCreateCache(moduleNameToDirectoryMap, redirectedReference, nonRelativeModuleName, createPerModuleNameCache); 541 } 542 543 function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirectedReference: ResolvedProjectReference | undefined, key: string, create: () => T): T { 544 const cache = cacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference); 545 let result = cache.get(key); 546 if (!result) { 547 result = create(); 548 cache.set(key, result); 549 } 550 return result; 551 } 552 553 function createPerModuleNameCache(): PerModuleNameCache { 554 const directoryPathMap = new Map<string, ResolvedModuleWithFailedLookupLocations>(); 555 556 return { get, set }; 557 558 function get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined { 559 return directoryPathMap.get(toPath(directory, currentDirectory, getCanonicalFileName)); 560 } 561 562 /** 563 * At first this function add entry directory -> module resolution result to the table. 564 * Then it computes the set of parent folders for 'directory' that should have the same module resolution result 565 * and for every parent folder in set it adds entry: parent -> module resolution. . 566 * Lets say we first directory name: /a/b/c/d/e and resolution result is: /a/b/bar.ts. 567 * Set of parent folders that should have the same result will be: 568 * [ 569 * /a/b/c/d, /a/b/c, /a/b 570 * ] 571 * this means that request for module resolution from file in any of these folder will be immediately found in cache. 572 */ 573 function set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void { 574 const path = toPath(directory, currentDirectory, getCanonicalFileName); 575 // if entry is already in cache do nothing 576 if (directoryPathMap.has(path)) { 577 return; 578 } 579 directoryPathMap.set(path, result); 580 581 const resolvedFileName = result.resolvedModule && 582 (result.resolvedModule.originalPath || result.resolvedModule.resolvedFileName); 583 // find common prefix between directory and resolved file name 584 // this common prefix should be the shortest path that has the same resolution 585 // directory: /a/b/c/d/e 586 // resolvedFileName: /a/b/foo.d.ts 587 // commonPrefix: /a/b 588 // for failed lookups cache the result for every directory up to root 589 const commonPrefix = resolvedFileName && getCommonPrefix(path, resolvedFileName); 590 let current = path; 591 while (current !== commonPrefix) { 592 const parent = getDirectoryPath(current); 593 if (parent === current || directoryPathMap.has(parent)) { 594 break; 595 } 596 directoryPathMap.set(parent, result); 597 current = parent; 598 } 599 } 600 601 function getCommonPrefix(directory: Path, resolution: string) { 602 const resolutionDirectory = toPath(getDirectoryPath(resolution), currentDirectory, getCanonicalFileName); 603 604 // find first position where directory and resolution differs 605 let i = 0; 606 const limit = Math.min(directory.length, resolutionDirectory.length); 607 while (i < limit && directory.charCodeAt(i) === resolutionDirectory.charCodeAt(i)) { 608 i++; 609 } 610 if (i === directory.length && (resolutionDirectory.length === i || resolutionDirectory[i] === directorySeparator)) { 611 return directory; 612 } 613 const rootLength = getRootLength(directory); 614 if (i < rootLength) { 615 return undefined; 616 } 617 const sep = directory.lastIndexOf(directorySeparator, i - 1); 618 if (sep === -1) { 619 return undefined; 620 } 621 return directory.substr(0, Math.max(sep, rootLength)); 622 } 623 } 624 } 625 626 export function resolveModuleNameFromCache(moduleName: string, containingFile: string, cache: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations | undefined { 627 const containingDirectory = getDirectoryPath(containingFile); 628 const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory); 629 return perFolderCache && perFolderCache.get(moduleName); 630 } 631 632 export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations { 633 const traceEnabled = isTraceEnabled(compilerOptions, host); 634 if (redirectedReference) { 635 compilerOptions = redirectedReference.commandLine.options; 636 } 637 if (traceEnabled) { 638 trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile); 639 if (redirectedReference) { 640 trace(host, Diagnostics.Using_compiler_options_of_project_reference_redirect_0, redirectedReference.sourceFile.fileName); 641 } 642 } 643 const containingDirectory = getDirectoryPath(containingFile); 644 const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory, redirectedReference); 645 let result = perFolderCache && perFolderCache.get(moduleName); 646 647 if (result) { 648 if (traceEnabled) { 649 trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory); 650 } 651 } 652 else { 653 let moduleResolution = compilerOptions.moduleResolution; 654 if (moduleResolution === undefined) { 655 moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic; 656 if (traceEnabled) { 657 trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]); 658 } 659 } 660 else { 661 if (traceEnabled) { 662 trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]); 663 } 664 } 665 666 perfLogger.logStartResolveModule(moduleName /* , containingFile, ModuleResolutionKind[moduleResolution]*/); 667 switch (moduleResolution) { 668 case ModuleResolutionKind.NodeJs: 669 result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference); 670 break; 671 case ModuleResolutionKind.Classic: 672 result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference); 673 break; 674 default: 675 return Debug.fail(`Unexpected moduleResolution: ${moduleResolution}`); 676 } 677 if (result && result.resolvedModule) perfLogger.logInfoEvent(`Module "${moduleName}" resolved to "${result.resolvedModule.resolvedFileName}"`); 678 perfLogger.logStopResolveModule((result && result.resolvedModule) ? "" + result.resolvedModule.resolvedFileName : "null"); 679 680 if (perFolderCache) { 681 perFolderCache.set(moduleName, result); 682 if (!isExternalModuleNameRelative(moduleName)) { 683 // put result in per-module name cache 684 cache!.getOrCreateCacheForModuleName(moduleName, redirectedReference).set(containingDirectory, result); 685 } 686 } 687 } 688 689 if (traceEnabled) { 690 if (result.resolvedModule) { 691 if (result.resolvedModule.packageId) { 692 trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1_with_Package_ID_2, moduleName, result.resolvedModule.resolvedFileName, packageIdToString(result.resolvedModule.packageId)); 693 } 694 else { 695 trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName); 696 } 697 } 698 else { 699 trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName); 700 } 701 } 702 703 return result; 704 } 705 706 /* 707 * Every module resolution kind can has its specific understanding how to load module from a specific path on disk 708 * I.e. for path '/a/b/c': 709 * - Node loader will first to try to check if '/a/b/c' points to a file with some supported extension and if this fails 710 * it will try to load module from directory: directory '/a/b/c' should exist and it should have either 'package.json' with 711 * 'typings' entry or file 'index' with some supported extension 712 * - Classic loader will only try to interpret '/a/b/c' as file. 713 */ 714 type ResolutionKindSpecificLoader = (extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState) => Resolved | undefined; 715 716 /** 717 * Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to 718 * mitigate differences between design time structure of the project and its runtime counterpart so the same import name 719 * can be resolved successfully by TypeScript compiler and runtime module loader. 720 * If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will 721 * fallback to standard resolution routine. 722 * 723 * - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative 724 * names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will 725 * be '/a/b/c/d' 726 * - paths - this setting can only be used when baseUrl is specified. allows to tune how non-relative module names 727 * will be resolved based on the content of the module name. 728 * Structure of 'paths' compiler options 729 * 'paths': { 730 * pattern-1: [...substitutions], 731 * pattern-2: [...substitutions], 732 * ... 733 * pattern-n: [...substitutions] 734 * } 735 * Pattern here is a string that can contain zero or one '*' character. During module resolution module name will be matched against 736 * all patterns in the list. Matching for patterns that don't contain '*' means that module name must be equal to pattern respecting the case. 737 * If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>. 738 * <MatchedStar> denotes part of the module name between <prefix> and <suffix>. 739 * If module name can be matches with multiple patterns then pattern with the longest prefix will be picked. 740 * After selecting pattern we'll use list of substitutions to get candidate locations of the module and the try to load module 741 * from the candidate location. 742 * Substitution is a string that can contain zero or one '*'. To get candidate location from substitution we'll pick every 743 * substitution in the list and replace '*' with <MatchedStar> string. If candidate location is not rooted it 744 * will be converted to absolute using baseUrl. 745 * For example: 746 * baseUrl: /a/b/c 747 * "paths": { 748 * // match all module names 749 * "*": [ 750 * "*", // use matched name as is, 751 * // <matched name> will be looked as /a/b/c/<matched name> 752 * 753 * "folder1/*" // substitution will convert matched name to 'folder1/<matched name>', 754 * // since it is not rooted then final candidate location will be /a/b/c/folder1/<matched name> 755 * ], 756 * // match module names that start with 'components/' 757 * "components/*": [ "/root/components/*" ] // substitution will convert /components/folder1/<matched name> to '/root/components/folder1/<matched name>', 758 * // it is rooted so it will be final candidate location 759 * } 760 * 761 * 'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if 762 * they were in the same location. For example lets say there are two files 763 * '/local/src/content/file1.ts' 764 * '/shared/components/contracts/src/content/protocols/file2.ts' 765 * After bundling content of '/shared/components/contracts/src' will be merged with '/local/src' so 766 * if file1 has the following import 'import {x} from "./protocols/file2"' it will be resolved successfully in runtime. 767 * 'rootDirs' provides the way to tell compiler that in order to get the whole project it should behave as if content of all 768 * root dirs were merged together. 769 * I.e. for the example above 'rootDirs' will have two entries: [ '/local/src', '/shared/components/contracts/src' ]. 770 * Compiler will first convert './protocols/file2' into absolute path relative to the location of containing file: 771 * '/local/src/content/protocols/file2' and try to load it - failure. 772 * Then it will search 'rootDirs' looking for a longest matching prefix of this absolute path and if such prefix is found - absolute path will 773 * be converted to a path relative to found rootDir entry './content/protocols/file2' (*). As a last step compiler will check all remaining 774 * entries in 'rootDirs', use them to build absolute path out of (*) and try to resolve module from this location. 775 */ 776 function tryLoadModuleUsingOptionalResolutionSettings(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, 777 state: ModuleResolutionState): Resolved | undefined { 778 779 const resolved = tryLoadModuleUsingPathsIfEligible(extensions, moduleName, loader, state); 780 if (resolved) return resolved.value; 781 782 if (!isExternalModuleNameRelative(moduleName)) { 783 return tryLoadModuleUsingBaseUrl(extensions, moduleName, loader, state); 784 } 785 else { 786 return tryLoadModuleUsingRootDirs(extensions, moduleName, containingDirectory, loader, state); 787 } 788 } 789 790 function tryLoadModuleUsingPathsIfEligible(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState) { 791 const { baseUrl, paths } = state.compilerOptions; 792 if (paths && !pathIsRelative(moduleName)) { 793 if (state.traceEnabled) { 794 if (baseUrl) { 795 trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, baseUrl, moduleName); 796 } 797 trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); 798 } 799 const baseDirectory = getPathsBasePath(state.compilerOptions, state.host)!; // Always defined when 'paths' is defined 800 return tryLoadModuleUsingPaths(extensions, moduleName, baseDirectory, paths, loader, /*onlyRecordFailures*/ false, state); 801 } 802 } 803 804 function tryLoadModuleUsingRootDirs(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, 805 state: ModuleResolutionState): Resolved | undefined { 806 807 if (!state.compilerOptions.rootDirs) { 808 return undefined; 809 } 810 811 if (state.traceEnabled) { 812 trace(state.host, Diagnostics.rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName); 813 } 814 815 const candidate = normalizePath(combinePaths(containingDirectory, moduleName)); 816 817 let matchedRootDir: string | undefined; 818 let matchedNormalizedPrefix: string | undefined; 819 for (const rootDir of state.compilerOptions.rootDirs) { 820 // rootDirs are expected to be absolute 821 // in case of tsconfig.json this will happen automatically - compiler will expand relative names 822 // using location of tsconfig.json as base location 823 let normalizedRoot = normalizePath(rootDir); 824 if (!endsWith(normalizedRoot, directorySeparator)) { 825 normalizedRoot += directorySeparator; 826 } 827 const isLongestMatchingPrefix = 828 startsWith(candidate, normalizedRoot) && 829 (matchedNormalizedPrefix === undefined || matchedNormalizedPrefix.length < normalizedRoot.length); 830 831 if (state.traceEnabled) { 832 trace(state.host, Diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2, normalizedRoot, candidate, isLongestMatchingPrefix); 833 } 834 835 if (isLongestMatchingPrefix) { 836 matchedNormalizedPrefix = normalizedRoot; 837 matchedRootDir = rootDir; 838 } 839 } 840 if (matchedNormalizedPrefix) { 841 if (state.traceEnabled) { 842 trace(state.host, Diagnostics.Longest_matching_prefix_for_0_is_1, candidate, matchedNormalizedPrefix); 843 } 844 const suffix = candidate.substr(matchedNormalizedPrefix.length); 845 846 // first - try to load from a initial location 847 if (state.traceEnabled) { 848 trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, matchedNormalizedPrefix, candidate); 849 } 850 const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(containingDirectory, state.host), state); 851 if (resolvedFileName) { 852 return resolvedFileName; 853 } 854 855 if (state.traceEnabled) { 856 trace(state.host, Diagnostics.Trying_other_entries_in_rootDirs); 857 } 858 // then try to resolve using remaining entries in rootDirs 859 for (const rootDir of state.compilerOptions.rootDirs) { 860 if (rootDir === matchedRootDir) { 861 // skip the initially matched entry 862 continue; 863 } 864 const candidate = combinePaths(normalizePath(rootDir), suffix); 865 if (state.traceEnabled) { 866 trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, rootDir, candidate); 867 } 868 const baseDirectory = getDirectoryPath(candidate); 869 const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(baseDirectory, state.host), state); 870 if (resolvedFileName) { 871 return resolvedFileName; 872 } 873 } 874 if (state.traceEnabled) { 875 trace(state.host, Diagnostics.Module_resolution_using_rootDirs_has_failed); 876 } 877 } 878 return undefined; 879 } 880 881 function tryLoadModuleUsingBaseUrl(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined { 882 const { baseUrl } = state.compilerOptions; 883 if (!baseUrl) { 884 return undefined; 885 } 886 if (state.traceEnabled) { 887 trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, baseUrl, moduleName); 888 } 889 const candidate = normalizePath(combinePaths(baseUrl, moduleName)); 890 if (state.traceEnabled) { 891 trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, baseUrl, candidate); 892 } 893 return loader(extensions, candidate, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state); 894 } 895 896 /** 897 * Expose resolution logic to allow us to use Node module resolution logic from arbitrary locations. 898 * No way to do this with `require()`: https://github.com/nodejs/node/issues/5963 899 * Throws an error if the module can't be resolved. 900 */ 901 /* @internal */ 902 export function resolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string { 903 const { resolvedModule, failedLookupLocations } = tryResolveJSModuleWorker(moduleName, initialDir, host); 904 if (!resolvedModule) { 905 throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`); 906 } 907 return resolvedModule.resolvedFileName; 908 } 909 910 /* @internal */ 911 export function tryResolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string | undefined { 912 const { resolvedModule } = tryResolveJSModuleWorker(moduleName, initialDir, host); 913 return resolvedModule && resolvedModule.resolvedFileName; 914 } 915 916 const jsOnlyExtensions = [Extensions.JavaScript]; 917 const tsExtensions = [Extensions.TypeScript, Extensions.JavaScript]; 918 const tsPlusJsonExtensions = [...tsExtensions, Extensions.Json]; 919 const tsconfigExtensions = [Extensions.TSConfig]; 920 function tryResolveJSModuleWorker(moduleName: string, initialDir: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { 921 return nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, jsOnlyExtensions, /*redirectedReferences*/ undefined); 922 } 923 924 export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations; 925 /* @internal */ export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, lookupConfig?: boolean): ResolvedModuleWithFailedLookupLocations; // eslint-disable-line @typescript-eslint/unified-signatures 926 export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, lookupConfig?: boolean): ResolvedModuleWithFailedLookupLocations { 927 return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, lookupConfig ? tsconfigExtensions : (compilerOptions.resolveJsonModule ? tsPlusJsonExtensions : tsExtensions), redirectedReference); 928 } 929 930 function nodeModuleNameResolverWorker(moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, extensions: Extensions[], redirectedReference: ResolvedProjectReference | undefined): ResolvedModuleWithFailedLookupLocations { 931 const traceEnabled = isTraceEnabled(compilerOptions, host); 932 933 const failedLookupLocations: string[] = []; 934 const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations }; 935 936 const result = forEach(extensions, ext => tryResolve(ext)); 937 return createResolvedModuleWithFailedLookupLocations(result?.value?.resolved, result?.value?.isExternalLibraryImport, failedLookupLocations, state.resultFromCache); 938 939 function tryResolve(extensions: Extensions): SearchResult<{ resolved: Resolved, isExternalLibraryImport: boolean }> { 940 const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => nodeLoadModuleByRelativeName(extensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ true); 941 const resolved = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loader, state); 942 if (resolved) { 943 return toSearchResult({ resolved, isExternalLibraryImport: pathContainsNodeModules(resolved.path) }); 944 } 945 946 if (!isExternalModuleNameRelative(moduleName)) { 947 if (traceEnabled) { 948 trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]); 949 } 950 const resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache, redirectedReference); 951 if (!resolved) return undefined; 952 953 let resolvedValue = resolved.value; 954 if (!compilerOptions.preserveSymlinks && resolvedValue && !resolvedValue.originalPath) { 955 const path = realPath(resolvedValue.path, host, traceEnabled); 956 const originalPath = path === resolvedValue.path ? undefined : resolvedValue.path; 957 resolvedValue = { ...resolvedValue, path, originalPath }; 958 } 959 // For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files. 960 return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } }; 961 } 962 else { 963 const { path: candidate, parts } = normalizePathAndParts(combinePaths(containingDirectory, moduleName)); 964 const resolved = nodeLoadModuleByRelativeName(extensions, candidate, /*onlyRecordFailures*/ false, state, /*considerPackageJson*/ true); 965 // Treat explicit "node_modules" import as an external library import. 966 return resolved && toSearchResult({ resolved, isExternalLibraryImport: contains(parts, "node_modules") }); 967 } 968 } 969 } 970 971 function realPath(path: string, host: ModuleResolutionHost, traceEnabled: boolean): string { 972 if (!host.realpath) { 973 return path; 974 } 975 976 const real = normalizePath(host.realpath(path)); 977 if (traceEnabled) { 978 trace(host, Diagnostics.Resolving_real_path_for_0_result_1, path, real); 979 } 980 Debug.assert(host.fileExists(real), `${path} linked to nonexistent file ${real}`); 981 return real; 982 } 983 984 function nodeLoadModuleByRelativeName(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson: boolean): Resolved | undefined { 985 if (state.traceEnabled) { 986 trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0_target_file_type_1, candidate, Extensions[extensions]); 987 } 988 if (!hasTrailingDirectorySeparator(candidate)) { 989 if (!onlyRecordFailures) { 990 const parentOfCandidate = getDirectoryPath(candidate); 991 if (!directoryProbablyExists(parentOfCandidate, state.host)) { 992 if (state.traceEnabled) { 993 trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, parentOfCandidate); 994 } 995 onlyRecordFailures = true; 996 } 997 } 998 const resolvedFromFile = loadModuleFromFile(extensions, candidate, onlyRecordFailures, state); 999 if (resolvedFromFile) { 1000 const packageDirectory = considerPackageJson ? parseNodeModuleFromPath(resolvedFromFile) : undefined; 1001 const packageInfo = packageDirectory ? getPackageJsonInfo(packageDirectory, /*onlyRecordFailures*/ false, state) : undefined; 1002 return withPackageId(packageInfo, resolvedFromFile); 1003 } 1004 } 1005 if (!onlyRecordFailures) { 1006 const candidateExists = directoryProbablyExists(candidate, state.host); 1007 if (!candidateExists) { 1008 if (state.traceEnabled) { 1009 trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, candidate); 1010 } 1011 onlyRecordFailures = true; 1012 } 1013 } 1014 return loadNodeModuleFromDirectory(extensions, candidate, onlyRecordFailures, state, considerPackageJson); 1015 } 1016 1017 /*@internal*/ 1018 export const nodeModulesPathPart = "/node_modules/"; 1019 /*@internal*/ 1020 export function pathContainsNodeModules(path: string): boolean { 1021 return stringContains(path, nodeModulesPathPart); 1022 } 1023 1024 /** 1025 * This will be called on the successfully resolved path from `loadModuleFromFile`. 1026 * (Not needed for `loadModuleFromNodeModules` as that looks up the `package.json` as part of resolution.) 1027 * 1028 * packageDirectory is the directory of the package itself. 1029 * For `blah/node_modules/foo/index.d.ts` this is packageDirectory: "foo" 1030 * For `/node_modules/foo/bar.d.ts` this is packageDirectory: "foo" 1031 * For `/node_modules/@types/foo/bar/index.d.ts` this is packageDirectory: "@types/foo" 1032 * For `/node_modules/foo/bar/index.d.ts` this is packageDirectory: "foo" 1033 */ 1034 function parseNodeModuleFromPath(resolved: PathAndExtension): string | undefined { 1035 const path = normalizePath(resolved.path); 1036 const idx = path.lastIndexOf(nodeModulesPathPart); 1037 if (idx === -1) { 1038 return undefined; 1039 } 1040 1041 const indexAfterNodeModules = idx + nodeModulesPathPart.length; 1042 let indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterNodeModules); 1043 if (path.charCodeAt(indexAfterNodeModules) === CharacterCodes.at) { 1044 indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName); 1045 } 1046 return path.slice(0, indexAfterPackageName); 1047 } 1048 1049 function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number { 1050 const nextSeparatorIndex = path.indexOf(directorySeparator, prevSeparatorIndex + 1); 1051 return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex; 1052 } 1053 1054 function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined { 1055 return noPackageId(loadModuleFromFile(extensions, candidate, onlyRecordFailures, state)); 1056 } 1057 1058 /** 1059 * @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary 1060 * in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations. 1061 */ 1062 function loadModuleFromFile(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { 1063 if (extensions === Extensions.Json || extensions === Extensions.TSConfig) { 1064 const extensionLess = tryRemoveExtension(candidate, Extension.Json); 1065 return (extensionLess === undefined && extensions === Extensions.Json) ? undefined : tryAddingExtensions(extensionLess || candidate, extensions, onlyRecordFailures, state); 1066 } 1067 1068 // First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts" 1069 const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, onlyRecordFailures, state); 1070 if (resolvedByAddingExtension) { 1071 return resolvedByAddingExtension; 1072 } 1073 1074 // If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one; 1075 // e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" 1076 if (hasJSFileExtension(candidate)) { 1077 const extensionless = removeFileExtension(candidate); 1078 if (state.traceEnabled) { 1079 const extension = candidate.substring(extensionless.length); 1080 trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension); 1081 } 1082 return tryAddingExtensions(extensionless, extensions, onlyRecordFailures, state); 1083 } 1084 } 1085 1086 /** Try to return an existing file that adds one of the `extensions` to `candidate`. */ 1087 function tryAddingExtensions(candidate: string, extensions: Extensions, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { 1088 if (!onlyRecordFailures) { 1089 // check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing 1090 const directory = getDirectoryPath(candidate); 1091 if (directory) { 1092 onlyRecordFailures = !directoryProbablyExists(directory, state.host); 1093 } 1094 } 1095 1096 switch (extensions) { 1097 case Extensions.DtsOnly: 1098 return tryExtension(Extension.Dts); 1099 case Extensions.TypeScript: 1100 if (state.compilerOptions.ets) { 1101 return tryExtension(Extension.Ets) || tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts); 1102 } 1103 else { 1104 return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts) || tryExtension(Extension.Ets); 1105 } 1106 case Extensions.JavaScript: 1107 return tryExtension(Extension.Js) || tryExtension(Extension.Jsx); 1108 case Extensions.TSConfig: 1109 case Extensions.Json: 1110 return tryExtension(Extension.Json); 1111 } 1112 1113 function tryExtension(ext: Extension): PathAndExtension | undefined { 1114 const path = tryFile(candidate + ext, onlyRecordFailures, state); 1115 return path === undefined ? undefined : { path, ext }; 1116 } 1117 } 1118 1119 /** Return the file if it exists. */ 1120 function tryFile(fileName: string, onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined { 1121 if (!onlyRecordFailures) { 1122 if (state.host.fileExists(fileName)) { 1123 if (state.traceEnabled) { 1124 trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName); 1125 } 1126 return fileName; 1127 } 1128 else { 1129 if (state.traceEnabled) { 1130 trace(state.host, Diagnostics.File_0_does_not_exist, fileName); 1131 } 1132 } 1133 } 1134 state.failedLookupLocations.push(fileName); 1135 return undefined; 1136 } 1137 1138 function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) { 1139 const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, onlyRecordFailures, state) : undefined; 1140 const packageJsonContent = packageInfo && packageInfo.packageJsonContent; 1141 const versionPaths = packageInfo && packageInfo.versionPaths; 1142 return withPackageId(packageInfo, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths)); 1143 } 1144 1145 interface PackageJsonInfo { 1146 packageDirectory: string; 1147 packageJsonContent: PackageJsonPathFields; 1148 versionPaths: VersionPaths | undefined; 1149 } 1150 1151 function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined { 1152 const { host, traceEnabled } = state; 1153 const directoryExists = !onlyRecordFailures && directoryProbablyExists(packageDirectory, host); 1154 const packageJsonPath = combinePaths(packageDirectory, "package.json"); 1155 if (directoryExists && host.fileExists(packageJsonPath)) { 1156 const packageJsonContent = readJson(packageJsonPath, host) as PackageJson; 1157 if (traceEnabled) { 1158 trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath); 1159 } 1160 const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state); 1161 return { packageDirectory, packageJsonContent, versionPaths }; 1162 } 1163 else { 1164 if (directoryExists && traceEnabled) { 1165 trace(host, Diagnostics.File_0_does_not_exist, packageJsonPath); 1166 } 1167 1168 // record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results 1169 state.failedLookupLocations.push(packageJsonPath); 1170 } 1171 } 1172 1173 function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, jsonContent: PackageJsonPathFields | undefined, versionPaths: VersionPaths | undefined): PathAndExtension | undefined { 1174 let packageFile: string | undefined; 1175 if (jsonContent) { 1176 switch (extensions) { 1177 case Extensions.JavaScript: 1178 case Extensions.Json: 1179 packageFile = readPackageJsonMainField(jsonContent, candidate, state); 1180 break; 1181 case Extensions.TypeScript: 1182 // When resolving typescript modules, try resolving using main field as well 1183 packageFile = readPackageJsonTypesFields(jsonContent, candidate, state) || readPackageJsonMainField(jsonContent, candidate, state); 1184 break; 1185 case Extensions.DtsOnly: 1186 packageFile = readPackageJsonTypesFields(jsonContent, candidate, state); 1187 break; 1188 case Extensions.TSConfig: 1189 packageFile = readPackageJsonTSConfigField(jsonContent, candidate, state); 1190 break; 1191 default: 1192 return Debug.assertNever(extensions); 1193 } 1194 } 1195 1196 const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => { 1197 const fromFile = tryFile(candidate, onlyRecordFailures, state); 1198 if (fromFile) { 1199 const resolved = resolvedIfExtensionMatches(extensions, fromFile); 1200 if (resolved) { 1201 return noPackageId(resolved); 1202 } 1203 if (state.traceEnabled) { 1204 trace(state.host, Diagnostics.File_0_has_an_unsupported_extension_so_skipping_it, fromFile); 1205 } 1206 } 1207 1208 // Even if extensions is DtsOnly, we can still look up a .ts file as a result of package.json "types" 1209 const nextExtensions = extensions === Extensions.DtsOnly ? Extensions.TypeScript : extensions; 1210 // Don't do package.json lookup recursively, because Node.js' package lookup doesn't. 1211 return nodeLoadModuleByRelativeName(nextExtensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ false); 1212 }; 1213 1214 const onlyRecordFailuresForPackageFile = packageFile ? !directoryProbablyExists(getDirectoryPath(packageFile), state.host) : undefined; 1215 const onlyRecordFailuresForIndex = onlyRecordFailures || !directoryProbablyExists(candidate, state.host); 1216 const indexPath = combinePaths(candidate, extensions === Extensions.TSConfig ? "tsconfig" : "index"); 1217 1218 if (versionPaths && (!packageFile || containsPath(candidate, packageFile))) { 1219 const moduleName = getRelativePathFromDirectory(candidate, packageFile || indexPath, /*ignoreCase*/ false); 1220 if (state.traceEnabled) { 1221 trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, versionPaths.version, version, moduleName); 1222 } 1223 const result = tryLoadModuleUsingPaths(extensions, moduleName, candidate, versionPaths.paths, loader, onlyRecordFailuresForPackageFile || onlyRecordFailuresForIndex, state); 1224 if (result) { 1225 return removeIgnoredPackageId(result.value); 1226 } 1227 } 1228 1229 // It won't have a `packageId` set, because we disabled `considerPackageJson`. 1230 const packageFileResult = packageFile && removeIgnoredPackageId(loader(extensions, packageFile, onlyRecordFailuresForPackageFile!, state)); 1231 if (packageFileResult) return packageFileResult; 1232 1233 return loadModuleFromFile(extensions, indexPath, onlyRecordFailuresForIndex, state); 1234 } 1235 1236 /** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */ 1237 function resolvedIfExtensionMatches(extensions: Extensions, path: string): PathAndExtension | undefined { 1238 const ext = tryGetExtensionFromPath(path); 1239 return ext !== undefined && extensionIsOk(extensions, ext) ? { path, ext } : undefined; 1240 } 1241 1242 /** True if `extension` is one of the supported `extensions`. */ 1243 function extensionIsOk(extensions: Extensions, extension: Extension): boolean { 1244 switch (extensions) { 1245 case Extensions.JavaScript: 1246 return extension === Extension.Js || extension === Extension.Jsx; 1247 case Extensions.TSConfig: 1248 case Extensions.Json: 1249 return extension === Extension.Json; 1250 case Extensions.TypeScript: 1251 return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Dts || extension === Extension.Ets; 1252 case Extensions.DtsOnly: 1253 return extension === Extension.Dts; 1254 } 1255 } 1256 1257 /* @internal */ 1258 export function parsePackageName(moduleName: string): { packageName: string, rest: string } { 1259 let idx = moduleName.indexOf(directorySeparator); 1260 if (moduleName[0] === "@") { 1261 idx = moduleName.indexOf(directorySeparator, idx + 1); 1262 } 1263 return idx === -1 ? { packageName: moduleName, rest: "" } : { packageName: moduleName.slice(0, idx), rest: moduleName.slice(idx + 1) }; 1264 } 1265 1266 function loadModuleFromNearestNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> { 1267 return loadModuleFromNearestNodeModulesDirectoryWorker(extensions, moduleName, directory, state, /*typesScopeOnly*/ false, cache, redirectedReference); 1268 } 1269 1270 function loadModuleFromNearestNodeModulesDirectoryTypesScope(moduleName: string, directory: string, state: ModuleResolutionState): SearchResult<Resolved> { 1271 // Extensions parameter here doesn't actually matter, because typesOnly ensures we're just doing @types lookup, which is always DtsOnly. 1272 return loadModuleFromNearestNodeModulesDirectoryWorker(Extensions.DtsOnly, moduleName, directory, state, /*typesScopeOnly*/ true, /*cache*/ undefined, /*redirectedReference*/ undefined); 1273 } 1274 1275 function loadModuleFromNearestNodeModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> { 1276 const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference); 1277 return forEachAncestorDirectory(normalizeSlashes(directory), ancestorDirectory => { 1278 if (getBaseFileName(ancestorDirectory) !== "node_modules") { 1279 const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, ancestorDirectory, state); 1280 if (resolutionFromCache) { 1281 return resolutionFromCache; 1282 } 1283 return toSearchResult(loadModuleFromImmediateNodeModulesDirectory(extensions, moduleName, ancestorDirectory, state, typesScopeOnly)); 1284 } 1285 }); 1286 } 1287 1288 function loadModuleFromImmediateNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean): Resolved | undefined { 1289 const nodeModulesFolder = combinePaths(directory, "node_modules"); 1290 const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host); 1291 if (!nodeModulesFolderExists && state.traceEnabled) { 1292 trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, nodeModulesFolder); 1293 } 1294 1295 const packageResult = typesScopeOnly ? undefined : loadModuleFromSpecificNodeModulesDirectory(extensions, moduleName, nodeModulesFolder, nodeModulesFolderExists, state); 1296 if (packageResult) { 1297 return packageResult; 1298 } 1299 if (extensions === Extensions.TypeScript || extensions === Extensions.DtsOnly) { 1300 const nodeModulesAtTypes = combinePaths(nodeModulesFolder, "@types"); 1301 let nodeModulesAtTypesExists = nodeModulesFolderExists; 1302 if (nodeModulesFolderExists && !directoryProbablyExists(nodeModulesAtTypes, state.host)) { 1303 if (state.traceEnabled) { 1304 trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, nodeModulesAtTypes); 1305 } 1306 nodeModulesAtTypesExists = false; 1307 } 1308 return loadModuleFromSpecificNodeModulesDirectory(Extensions.DtsOnly, mangleScopedPackageNameWithTrace(moduleName, state), nodeModulesAtTypes, nodeModulesAtTypesExists, state); 1309 } 1310 } 1311 1312 function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, moduleName: string, nodeModulesDirectory: string, nodeModulesDirectoryExists: boolean, state: ModuleResolutionState): Resolved | undefined { 1313 const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName)); 1314 1315 // First look for a nested package.json, as in `node_modules/foo/bar/package.json`. 1316 let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state); 1317 if (packageInfo) { 1318 const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state); 1319 if (fromFile) { 1320 return noPackageId(fromFile); 1321 } 1322 1323 const fromDirectory = loadNodeModuleFromDirectoryWorker( 1324 extensions, 1325 candidate, 1326 !nodeModulesDirectoryExists, 1327 state, 1328 packageInfo.packageJsonContent, 1329 packageInfo.versionPaths 1330 ); 1331 return withPackageId(packageInfo, fromDirectory); 1332 } 1333 1334 const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => { 1335 const pathAndExtension = 1336 loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) || 1337 loadNodeModuleFromDirectoryWorker( 1338 extensions, 1339 candidate, 1340 onlyRecordFailures, 1341 state, 1342 packageInfo && packageInfo.packageJsonContent, 1343 packageInfo && packageInfo.versionPaths 1344 ); 1345 return withPackageId(packageInfo, pathAndExtension); 1346 }; 1347 1348 const { packageName, rest } = parsePackageName(moduleName); 1349 if (rest !== "") { // If "rest" is empty, we just did this search above. 1350 const packageDirectory = combinePaths(nodeModulesDirectory, packageName); 1351 1352 // Don't use a "types" or "main" from here because we're not loading the root, but a subdirectory -- just here for the packageId and path mappings. 1353 packageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state); 1354 if (packageInfo && packageInfo.versionPaths) { 1355 if (state.traceEnabled) { 1356 trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, packageInfo.versionPaths.version, version, rest); 1357 } 1358 const packageDirectoryExists = nodeModulesDirectoryExists && directoryProbablyExists(packageDirectory, state.host); 1359 const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.versionPaths.paths, loader, !packageDirectoryExists, state); 1360 if (fromPaths) { 1361 return fromPaths.value; 1362 } 1363 } 1364 } 1365 1366 return loader(extensions, candidate, !nodeModulesDirectoryExists, state); 1367 } 1368 1369 function tryLoadModuleUsingPaths(extensions: Extensions, moduleName: string, baseDirectory: string, paths: MapLike<string[]>, loader: ResolutionKindSpecificLoader, onlyRecordFailures: boolean, state: ModuleResolutionState): SearchResult<Resolved> { 1370 const matchedPattern = matchPatternOrExact(getOwnKeys(paths), moduleName); 1371 if (matchedPattern) { 1372 const matchedStar = isString(matchedPattern) ? undefined : matchedText(matchedPattern, moduleName); 1373 const matchedPatternText = isString(matchedPattern) ? matchedPattern : patternText(matchedPattern); 1374 if (state.traceEnabled) { 1375 trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); 1376 } 1377 const resolved = forEach(paths[matchedPatternText], subst => { 1378 const path = matchedStar ? subst.replace("*", matchedStar) : subst; 1379 // When baseUrl is not specified, the command line parser resolves relative paths to the config file location. 1380 const candidate = normalizePath(combinePaths(baseDirectory, path)); 1381 if (state.traceEnabled) { 1382 trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path); 1383 } 1384 // A path mapping may have an extension, in contrast to an import, which should omit it. 1385 const extension = tryGetExtensionFromPath(subst); 1386 if (extension !== undefined) { 1387 const path = tryFile(candidate, onlyRecordFailures, state); 1388 if (path !== undefined) { 1389 return noPackageId({ path, ext: extension }); 1390 } 1391 } 1392 return loader(extensions, candidate, onlyRecordFailures || !directoryProbablyExists(getDirectoryPath(candidate), state.host), state); 1393 }); 1394 return { value: resolved }; 1395 } 1396 } 1397 1398 /** Double underscores are used in DefinitelyTyped to delimit scoped packages. */ 1399 const mangledScopedPackageSeparator = "__"; 1400 1401 /** For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. */ 1402 function mangleScopedPackageNameWithTrace(packageName: string, state: ModuleResolutionState): string { 1403 const mangled = mangleScopedPackageName(packageName); 1404 if (state.traceEnabled && mangled !== packageName) { 1405 trace(state.host, Diagnostics.Scoped_package_detected_looking_in_0, mangled); 1406 } 1407 return mangled; 1408 } 1409 1410 /* @internal */ 1411 export function getTypesPackageName(packageName: string): string { 1412 return `@types/${mangleScopedPackageName(packageName)}`; 1413 } 1414 1415 /* @internal */ 1416 export function mangleScopedPackageName(packageName: string): string { 1417 if (startsWith(packageName, "@")) { 1418 const replaceSlash = packageName.replace(directorySeparator, mangledScopedPackageSeparator); 1419 if (replaceSlash !== packageName) { 1420 return replaceSlash.slice(1); // Take off the "@" 1421 } 1422 } 1423 return packageName; 1424 } 1425 1426 /* @internal */ 1427 export function getPackageNameFromTypesPackageName(mangledName: string): string { 1428 const withoutAtTypePrefix = removePrefix(mangledName, "@types/"); 1429 if (withoutAtTypePrefix !== mangledName) { 1430 return unmangleScopedPackageName(withoutAtTypePrefix); 1431 } 1432 return mangledName; 1433 } 1434 1435 /* @internal */ 1436 export function unmangleScopedPackageName(typesPackageName: string): string { 1437 return stringContains(typesPackageName, mangledScopedPackageSeparator) ? 1438 "@" + typesPackageName.replace(mangledScopedPackageSeparator, directorySeparator) : 1439 typesPackageName; 1440 } 1441 1442 function tryFindNonRelativeModuleNameInCache(cache: PerModuleNameCache | undefined, moduleName: string, containingDirectory: string, state: ModuleResolutionState): SearchResult<Resolved> { 1443 const result = cache && cache.get(containingDirectory); 1444 if (result) { 1445 if (state.traceEnabled) { 1446 trace(state.host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory); 1447 } 1448 state.resultFromCache = result; 1449 return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, originalPath: result.resolvedModule.originalPath || true, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } }; 1450 } 1451 } 1452 1453 export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations { 1454 const traceEnabled = isTraceEnabled(compilerOptions, host); 1455 const failedLookupLocations: string[] = []; 1456 const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations }; 1457 const containingDirectory = getDirectoryPath(containingFile); 1458 1459 const resolved = tryResolve(Extensions.TypeScript) || tryResolve(Extensions.JavaScript); 1460 // No originalPath because classic resolution doesn't resolve realPath 1461 return createResolvedModuleWithFailedLookupLocations(resolved && resolved.value, /*isExternalLibraryImport*/ false, failedLookupLocations, state.resultFromCache); 1462 1463 function tryResolve(extensions: Extensions): SearchResult<Resolved> { 1464 const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFileNoPackageId, state); 1465 if (resolvedUsingSettings) { 1466 return { value: resolvedUsingSettings }; 1467 } 1468 1469 if (!isExternalModuleNameRelative(moduleName)) { 1470 const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference); 1471 // Climb up parent directories looking for a module. 1472 const resolved = forEachAncestorDirectory(containingDirectory, directory => { 1473 const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, directory, state); 1474 if (resolutionFromCache) { 1475 return resolutionFromCache; 1476 } 1477 const searchName = normalizePath(combinePaths(directory, moduleName)); 1478 return toSearchResult(loadModuleFromFileNoPackageId(extensions, searchName, /*onlyRecordFailures*/ false, state)); 1479 }); 1480 if (resolved) { 1481 return resolved; 1482 } 1483 if (extensions === Extensions.TypeScript) { 1484 // If we didn't find the file normally, look it up in @types. 1485 return loadModuleFromNearestNodeModulesDirectoryTypesScope(moduleName, containingDirectory, state); 1486 } 1487 } 1488 else { 1489 const candidate = normalizePath(combinePaths(containingDirectory, moduleName)); 1490 return toSearchResult(loadModuleFromFileNoPackageId(extensions, candidate, /*onlyRecordFailures*/ false, state)); 1491 } 1492 } 1493 } 1494 1495 /** 1496 * A host may load a module from a global cache of typings. 1497 * This is the minumum code needed to expose that functionality; the rest is in the host. 1498 */ 1499 /* @internal */ 1500 export function loadModuleFromGlobalCache(moduleName: string, projectName: string | undefined, compilerOptions: CompilerOptions, host: ModuleResolutionHost, globalCache: string): ResolvedModuleWithFailedLookupLocations { 1501 const traceEnabled = isTraceEnabled(compilerOptions, host); 1502 if (traceEnabled) { 1503 trace(host, Diagnostics.Auto_discovery_for_typings_is_enabled_in_project_0_Running_extra_resolution_pass_for_module_1_using_cache_location_2, projectName, moduleName, globalCache); 1504 } 1505 const failedLookupLocations: string[] = []; 1506 const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations }; 1507 const resolved = loadModuleFromImmediateNodeModulesDirectory(Extensions.DtsOnly, moduleName, globalCache, state, /*typesScopeOnly*/ false); 1508 return createResolvedModuleWithFailedLookupLocations(resolved, /*isExternalLibraryImport*/ true, failedLookupLocations, state.resultFromCache); 1509 } 1510 1511 /** 1512 * Represents result of search. Normally when searching among several alternatives we treat value `undefined` as indicator 1513 * that search fails and we should try another option. 1514 * However this does not allow us to represent final result that should be used instead of further searching (i.e. a final result that was found in cache). 1515 * SearchResult is used to deal with this issue, its values represents following outcomes: 1516 * - undefined - not found, continue searching 1517 * - { value: undefined } - not found - stop searching 1518 * - { value: <some-value> } - found - stop searching 1519 */ 1520 type SearchResult<T> = { value: T | undefined } | undefined; 1521 1522 /** 1523 * Wraps value to SearchResult. 1524 * @returns undefined if value is undefined or { value } otherwise 1525 */ 1526 function toSearchResult<T>(value: T | undefined): SearchResult<T> { 1527 return value !== undefined ? { value } : undefined; 1528 } 1529} 1530