1import { 2 append, appendIfUnique, arrayFrom, changeAnyExtension, CharacterCodes, choosePathContainsModules, combinePaths, 3 comparePaths, Comparison, CompilerOptions, contains, containsPath, createCompilerDiagnostic, Debug, Diagnostic, 4 DiagnosticMessage, DiagnosticReporter, Diagnostics, directoryProbablyExists, directorySeparator, emptyArray, 5 endsWith, ensureTrailingDirectorySeparator, ESMap, every, Extension, extensionIsTS, fileExtensionIs, 6 fileExtensionIsOneOf, FileReference, filter, firstDefined, forEach, forEachAncestorDirectory, formatMessage, 7 getBaseFileName, GetCanonicalFileName, getCommonSourceDirectory, getDirectoryPath, GetEffectiveTypeRootsHost, 8 getEmitModuleKind, getEmitModuleResolutionKind, getModeForResolutionAtIndex, getModuleByPMType, 9 getModulePathPartByPMType, getNormalizedAbsolutePath, getOwnKeys, getPackageJsonByPMType, getPathComponents, 10 getPathFromPathComponents, getPathsBasePath, getPossibleOriginalInputExtensionForExtension, 11 getRelativePathFromDirectory, getRootLength, hasJSFileExtension, hasProperty, hasTrailingDirectorySeparator, 12 hasTSFileExtension, hostGetCanonicalFileName, isArray, isExternalModuleNameRelative, isOhpm, isRootedDiskPath, 13 isString, lastOrUndefined, length, Map, MapLike, matchedText, MatchingKeys, matchPatternOrExact, ModuleKind, 14 ModuleResolutionHost, ModuleResolutionKind, noop, noopPush, normalizePath, normalizeSlashes, 15 optionsHaveModuleResolutionChanges, PackageId, packageIdToString, ParsedCommandLine, Path, pathContainsOHModules, 16 pathIsRelative, Pattern, patternText, perfLogger, Push, readJson, removeExtension, removeFileExtension, 17 removePrefix, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirective, 18 ResolvedTypeReferenceDirectiveWithFailedLookupLocations, some, sort, SourceFile, startsWith, stringContains, 19 supportedTSExtensionsFlat, toPath, tryExtractTSExtension, tryGetExtensionFromPath, tryParsePatterns, 20 tryRemoveExtension, version, Version, versionMajorMinor, VersionRange, 21} from "./_namespaces/ts"; 22 23/** @internal */ 24export function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void; 25export function trace(host: ModuleResolutionHost): void { 26 host.trace!(formatMessage.apply(undefined, arguments)); 27} 28 29/** @internal */ 30export function isTraceEnabled(compilerOptions: CompilerOptions, host: ModuleResolutionHost): boolean { 31 return !!compilerOptions.traceResolution && host.trace !== undefined; 32} 33 34function withPackageId(packageInfo: PackageJsonInfo | undefined, r: PathAndExtension | undefined): Resolved | undefined { 35 let packageId: PackageId | undefined; 36 if (r && packageInfo) { 37 const packageJsonContent = packageInfo.contents.packageJsonContent as PackageJson; 38 if (typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string") { 39 packageId = { 40 name: packageJsonContent.name, 41 subModuleName: r.path.slice(packageInfo.packageDirectory.length + directorySeparator.length), 42 version: packageJsonContent.version 43 }; 44 } 45 } 46 return r && { path: r.path, extension: r.ext, packageId }; 47} 48 49function noPackageId(r: PathAndExtension | undefined): Resolved | undefined { 50 return withPackageId(/*packageInfo*/ undefined, r); 51} 52 53function removeIgnoredPackageId(r: Resolved | undefined): PathAndExtension | undefined { 54 if (r) { 55 Debug.assert(r.packageId === undefined); 56 return { path: r.path, ext: r.extension }; 57 } 58} 59 60/** Result of trying to resolve a module. */ 61interface Resolved { 62 path: string; 63 extension: Extension; 64 packageId: PackageId | undefined; 65 /** 66 * When the resolved is not created from cache, the value is 67 * - string if it is symbolic link to the resolved `path` 68 * - undefined if `path` is not a symbolic link 69 * When the resolved is created using value from cache of ResolvedModuleWithFailedLookupLocations, the value is: 70 * - string if it is symbolic link to the resolved `path` 71 * - true if `path` is not a symbolic link - this indicates that the `originalPath` calculation is already done and needs to be skipped 72 * Note: This is a file name with preserved original casing, not a normalized `Path`. 73 */ 74 originalPath?: string | true; 75} 76 77/** Result of trying to resolve a module at a file. Needs to have 'packageId' added later. */ 78interface PathAndExtension { 79 path: string; 80 // (Use a different name than `extension` to make sure Resolved isn't assignable to PathAndExtension.) 81 ext: Extension; 82} 83 84/** 85 * Kinds of file that we are currently looking for. 86 * Typically there is one pass with Extensions.TypeScript, then a second pass with Extensions.JavaScript. 87 */ 88enum Extensions { 89 TypeScript, /** '.ts', '.tsx', '.d.ts', '.ets' or '.d.ets' */ 90 JavaScript, /** '.js' or '.jsx' */ 91 Json, /** '.json' */ 92 TSConfig, /** '.json' with `tsconfig` used instead of `index` */ 93 DtsOnly, /** Only '.d.ts' */ 94 TsOnly, /** '.[cm]tsx?' but not .d.ts variants */ 95} 96 97interface PathAndPackageId { 98 readonly fileName: string; 99 readonly packageId: PackageId | undefined; 100} 101/** Used with `Extensions.DtsOnly` to extract the path from TypeScript results. */ 102function resolvedTypeScriptOnly(resolved: Resolved | undefined): PathAndPackageId | undefined { 103 if (!resolved) { 104 return undefined; 105 } 106 Debug.assert(extensionIsTS(resolved.extension)); 107 return { fileName: resolved.path, packageId: resolved.packageId }; 108} 109 110function createResolvedModuleWithFailedLookupLocations( 111 resolved: Resolved | undefined, 112 isExternalLibraryImport: boolean | undefined, 113 failedLookupLocations: string[], 114 affectingLocations: string[], 115 diagnostics: Diagnostic[], 116 resultFromCache: ResolvedModuleWithFailedLookupLocations | undefined 117): ResolvedModuleWithFailedLookupLocations { 118 if (resultFromCache) { 119 resultFromCache.failedLookupLocations.push(...failedLookupLocations); 120 resultFromCache.affectingLocations.push(...affectingLocations); 121 return resultFromCache; 122 } 123 return { 124 resolvedModule: resolved && { resolvedFileName: resolved.path, originalPath: resolved.originalPath === true ? undefined : resolved.originalPath, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId }, 125 failedLookupLocations, 126 affectingLocations, 127 resolutionDiagnostics: diagnostics, 128 }; 129} 130 131/** @internal */ 132export interface ModuleResolutionState { 133 host: ModuleResolutionHost; 134 compilerOptions: CompilerOptions; 135 traceEnabled: boolean; 136 failedLookupLocations: Push<string>; 137 affectingLocations: Push<string>; 138 resultFromCache?: ResolvedModuleWithFailedLookupLocations; 139 packageJsonInfoCache: PackageJsonInfoCache | undefined; 140 features: NodeResolutionFeatures; 141 conditions: readonly string[]; 142 requestContainingDirectory: string | undefined; 143 reportDiagnostic: DiagnosticReporter; 144} 145 146/** Just the fields that we use for module resolution. 147 * 148 * @internal 149 */ 150export interface PackageJsonPathFields { 151 typings?: string; 152 types?: string; 153 typesVersions?: MapLike<MapLike<string[]>>; 154 main?: string; 155 tsconfig?: string; 156 type?: string; 157 imports?: object; 158 exports?: object; 159 name?: string; 160} 161 162interface PackageJson extends PackageJsonPathFields { 163 name?: string; 164 version?: string; 165} 166 167function readPackageJsonField<TMatch, K extends MatchingKeys<PackageJson, string | undefined>>(jsonContent: PackageJson, fieldName: K, typeOfTag: "string", state: ModuleResolutionState): PackageJson[K] | undefined; 168function readPackageJsonField<K extends MatchingKeys<PackageJson, object | undefined>>(jsonContent: PackageJson, fieldName: K, typeOfTag: "object", state: ModuleResolutionState): PackageJson[K] | undefined; 169function readPackageJsonField<K extends keyof PackageJson>(jsonContent: PackageJson, fieldName: K, typeOfTag: "string" | "object", state: ModuleResolutionState): PackageJson[K] | undefined { 170 if (!hasProperty(jsonContent, fieldName)) { 171 if (state.traceEnabled) { 172 const message = isOhpm(state.compilerOptions.packageManagerType) ? Diagnostics.oh_package_json5_does_not_have_a_0_field : Diagnostics.package_json_does_not_have_a_0_field; 173 trace(state.host, message, fieldName); 174 } 175 return; 176 } 177 const value = jsonContent[fieldName]; 178 if (typeof value !== typeOfTag || value === null) { // eslint-disable-line no-null/no-null 179 if (state.traceEnabled) { 180 // eslint-disable-next-line no-null/no-null 181 trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2, fieldName, typeOfTag, value === null ? "null" : typeof value); 182 } 183 return; 184 } 185 return value; 186} 187 188function readPackageJsonPathField<K extends "typings" | "types" | "main" | "tsconfig">(jsonContent: PackageJson, fieldName: K, baseDirectory: string, state: ModuleResolutionState): PackageJson[K] | undefined { 189 const fileName = readPackageJsonField(jsonContent, fieldName, "string", state); 190 if (fileName === undefined) { 191 return; 192 } 193 if (!fileName) { 194 if (state.traceEnabled) { 195 trace(state.host, Diagnostics.package_json_had_a_falsy_0_field, fieldName); 196 } 197 return; 198 } 199 const path = normalizePath(combinePaths(baseDirectory, fileName)); 200 if (state.traceEnabled) { 201 const message = isOhpm(state.compilerOptions.packageManagerType) ? Diagnostics.oh_package_json5_has_0_field_1_that_references_2 : Diagnostics.package_json_has_0_field_1_that_references_2; 202 trace(state.host, message, fieldName, fileName, path); 203 } 204 return path; 205} 206 207function readPackageJsonTypesFields(jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState) { 208 return readPackageJsonPathField(jsonContent, "typings", baseDirectory, state) 209 || readPackageJsonPathField(jsonContent, "types", baseDirectory, state); 210} 211 212function readPackageJsonTSConfigField(jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState) { 213 return readPackageJsonPathField(jsonContent, "tsconfig", baseDirectory, state); 214} 215 216function readPackageJsonMainField(jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState) { 217 return readPackageJsonPathField(jsonContent, "main", baseDirectory, state); 218} 219 220function readPackageJsonTypesVersionsField(jsonContent: PackageJson, state: ModuleResolutionState) { 221 const typesVersions = readPackageJsonField(jsonContent, "typesVersions", "object", state); 222 if (typesVersions === undefined) return; 223 224 if (state.traceEnabled) { 225 trace(state.host, Diagnostics.package_json_has_a_typesVersions_field_with_version_specific_path_mappings); 226 } 227 228 return typesVersions; 229} 230 231/** @internal */ 232export interface VersionPaths { 233 version: string; 234 paths: MapLike<string[]>; 235} 236 237function readPackageJsonTypesVersionPaths(jsonContent: PackageJson, state: ModuleResolutionState): VersionPaths | undefined { 238 const typesVersions = readPackageJsonTypesVersionsField(jsonContent, state); 239 if (typesVersions === undefined) return; 240 241 if (state.traceEnabled) { 242 for (const key in typesVersions) { 243 if (hasProperty(typesVersions, key) && !VersionRange.tryParse(key)) { 244 trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_is_not_a_valid_semver_range, key); 245 } 246 } 247 } 248 249 const result = getPackageJsonTypesVersionsPaths(typesVersions); 250 if (!result) { 251 if (state.traceEnabled) { 252 trace(state.host, Diagnostics.package_json_does_not_have_a_typesVersions_entry_that_matches_version_0, versionMajorMinor); 253 } 254 return; 255 } 256 257 const { version: bestVersionKey, paths: bestVersionPaths } = result; 258 if (typeof bestVersionPaths !== "object") { 259 if (state.traceEnabled) { 260 trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2, `typesVersions['${bestVersionKey}']`, "object", typeof bestVersionPaths); 261 } 262 return; 263 } 264 265 return result; 266} 267 268let typeScriptVersion: Version | undefined; 269 270/** @internal */ 271export function getPackageJsonTypesVersionsPaths(typesVersions: MapLike<MapLike<string[]>>) { 272 if (!typeScriptVersion) typeScriptVersion = new Version(version); 273 274 for (const key in typesVersions) { 275 if (!hasProperty(typesVersions, key)) continue; 276 277 const keyRange = VersionRange.tryParse(key); 278 if (keyRange === undefined) { 279 continue; 280 } 281 282 // return the first entry whose range matches the current compiler version. 283 if (keyRange.test(typeScriptVersion)) { 284 return { version: key, paths: typesVersions[key] }; 285 } 286 } 287} 288 289export function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined { 290 if (options.typeRoots) { 291 return options.typeRoots; 292 } 293 294 let currentDirectory: string | undefined; 295 if (options.configFilePath) { 296 currentDirectory = getDirectoryPath(options.configFilePath); 297 } 298 else if (host.getCurrentDirectory) { 299 currentDirectory = host.getCurrentDirectory(); 300 } 301 302 if (currentDirectory !== undefined) { 303 return getDefaultTypeRoots(currentDirectory, host, options.packageManagerType); 304 } 305} 306 307/** 308 * Returns the path to every node_modules/@types or oh_modules/@types directory from some ancestor directory. 309 * Returns undefined if there are none. 310 */ 311function getDefaultTypeRoots(currentDirectory: string, host: { directoryExists?: (directoryName: string) => boolean }, packageManagerType?: string): string[] | undefined { 312 const modulesAtTypes = combinePaths(getModuleByPMType(packageManagerType), "@types"); 313 314 if (!host.directoryExists) { 315 return [combinePaths(currentDirectory, modulesAtTypes)]; 316 // And if it doesn't exist, tough. 317 } 318 319 let typeRoots: string[] | undefined; 320 forEachAncestorDirectory(normalizePath(currentDirectory), directory => { 321 const atTypes = combinePaths(directory, modulesAtTypes); 322 if (host.directoryExists!(atTypes)) { 323 (typeRoots || (typeRoots = [])).push(atTypes); 324 } 325 return undefined; 326 }); 327 return typeRoots; 328} 329 330function arePathsEqual(path1: string, path2: string, host: ModuleResolutionHost): boolean { 331 const useCaseSensitiveFileNames = typeof host.useCaseSensitiveFileNames === "function" ? host.useCaseSensitiveFileNames() : host.useCaseSensitiveFileNames; 332 return comparePaths(path1, path2, !useCaseSensitiveFileNames) === Comparison.EqualTo; 333} 334 335/** 336 * @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown. 337 * This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups 338 * is assumed to be the same as root directory of the project. 339 */ 340export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, cache?: TypeReferenceDirectiveResolutionCache, resolutionMode?: SourceFile["impliedNodeFormat"]): ResolvedTypeReferenceDirectiveWithFailedLookupLocations { 341 Debug.assert(typeof typeReferenceDirectiveName === "string", "Non-string value passed to `ts.resolveTypeReferenceDirective`, likely by a wrapping package working with an outdated `resolveTypeReferenceDirectives` signature. This is probably not a problem in TS itself."); 342 const traceEnabled = isTraceEnabled(options, host); 343 if (redirectedReference) { 344 options = redirectedReference.commandLine.options; 345 } 346 347 const containingDirectory = containingFile ? getDirectoryPath(containingFile) : undefined; 348 const perFolderCache = containingDirectory ? cache && cache.getOrCreateCacheForDirectory(containingDirectory, redirectedReference) : undefined; 349 let result = perFolderCache && perFolderCache.get(typeReferenceDirectiveName, /*mode*/ resolutionMode); 350 if (result) { 351 if (traceEnabled) { 352 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1, typeReferenceDirectiveName, containingFile); 353 if (redirectedReference) trace(host, Diagnostics.Using_compiler_options_of_project_reference_redirect_0, redirectedReference.sourceFile.fileName); 354 trace(host, Diagnostics.Resolution_for_type_reference_directive_0_was_found_in_cache_from_location_1, typeReferenceDirectiveName, containingDirectory); 355 traceResult(result); 356 } 357 return result; 358 } 359 360 const typeRoots = getEffectiveTypeRoots(options, host); 361 if (traceEnabled) { 362 if (containingFile === undefined) { 363 if (typeRoots === undefined) { 364 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName); 365 } 366 else { 367 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots); 368 } 369 } 370 else { 371 if (typeRoots === undefined) { 372 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile); 373 } 374 else { 375 trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots); 376 } 377 } 378 if (redirectedReference) { 379 trace(host, Diagnostics.Using_compiler_options_of_project_reference_redirect_0, redirectedReference.sourceFile.fileName); 380 } 381 } 382 383 const failedLookupLocations: string[] = []; 384 const affectingLocations: string[] = []; 385 let features = getDefaultNodeResolutionFeatures(options); 386 // Unlike `import` statements, whose mode-calculating APIs are all guaranteed to return `undefined` if we're in an un-mode-ed module resolution 387 // setting, type references will return their target mode regardless of options because of how the parser works, so we guard against the mode being 388 // set in a non-modal module resolution setting here. Do note that our behavior is not particularly well defined when these mode-overriding imports 389 // are present in a non-modal project; while in theory we'd like to either ignore the mode or provide faithful modern resolution, depending on what we feel is best, 390 // in practice, not every cache has the options available to intelligently make the choice to ignore the mode request, and it's unclear how modern "faithful modern 391 // resolution" should be (`node16`? `nodenext`?). As such, witnessing a mode-overriding triple-slash reference in a non-modal module resolution 392 // context should _probably_ be an error - and that should likely be handled by the `Program` (which is what we do). 393 if (resolutionMode === ModuleKind.ESNext && (getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node16 || getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext)) { 394 features |= NodeResolutionFeatures.EsmMode; 395 } 396 const conditions = features & NodeResolutionFeatures.Exports ? features & NodeResolutionFeatures.EsmMode ? ["node", "import", "types"] : ["node", "require", "types"] : []; 397 const diagnostics: Diagnostic[] = []; 398 const moduleResolutionState: ModuleResolutionState = { 399 compilerOptions: options, 400 host, 401 traceEnabled, 402 failedLookupLocations, 403 affectingLocations, 404 packageJsonInfoCache: cache, 405 features, 406 conditions, 407 requestContainingDirectory: containingDirectory, 408 reportDiagnostic: diag => void diagnostics.push(diag), 409 }; 410 let resolved = primaryLookup(); 411 let primary = true; 412 if (!resolved) { 413 resolved = secondaryLookup(); 414 primary = false; 415 } 416 417 let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; 418 if (resolved) { 419 const { fileName, packageId } = resolved; 420 const resolvedFileName = options.preserveSymlinks ? fileName : realPath(fileName, host, traceEnabled); 421 const pathsAreEqual = arePathsEqual(fileName, resolvedFileName, host); 422 resolvedTypeReferenceDirective = { 423 primary, 424 // If the fileName and realpath are differing only in casing prefer fileName so that we can issue correct errors for casing under forceConsistentCasingInFileNames 425 resolvedFileName: pathsAreEqual ? fileName : resolvedFileName, 426 originalPath: pathsAreEqual ? undefined : fileName, 427 packageId, 428 isExternalLibraryImport: choosePathContainsModules(options.packageManagerType, fileName), 429 }; 430 } 431 result = { resolvedTypeReferenceDirective, failedLookupLocations, affectingLocations, resolutionDiagnostics: diagnostics }; 432 perFolderCache?.set(typeReferenceDirectiveName, /*mode*/ resolutionMode, result); 433 if (traceEnabled) traceResult(result); 434 return result; 435 436 function traceResult(result: ResolvedTypeReferenceDirectiveWithFailedLookupLocations) { 437 if (!result.resolvedTypeReferenceDirective?.resolvedFileName) { 438 trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName); 439 } 440 else if (result.resolvedTypeReferenceDirective.packageId) { 441 trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_with_Package_ID_2_primary_Colon_3, typeReferenceDirectiveName, result.resolvedTypeReferenceDirective.resolvedFileName, packageIdToString(result.resolvedTypeReferenceDirective.packageId), result.resolvedTypeReferenceDirective.primary); 442 } 443 else { 444 trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, result.resolvedTypeReferenceDirective.resolvedFileName, result.resolvedTypeReferenceDirective.primary); 445 } 446 } 447 448 function primaryLookup(): PathAndPackageId | undefined { 449 // Check primary library paths 450 if (typeRoots && typeRoots.length) { 451 if (traceEnabled) { 452 trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", ")); 453 } 454 return firstDefined(typeRoots, typeRoot => { 455 const candidate = combinePaths(typeRoot, typeReferenceDirectiveName); 456 const candidateDirectory = getDirectoryPath(candidate); 457 const directoryExists = directoryProbablyExists(candidateDirectory, host); 458 if (!directoryExists && traceEnabled) { 459 trace(host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, candidateDirectory); 460 } 461 return resolvedTypeScriptOnly( 462 loadNodeModuleFromDirectory(Extensions.DtsOnly, candidate, 463 !directoryExists, moduleResolutionState)); 464 }); 465 } 466 else { 467 if (traceEnabled) { 468 trace(host, Diagnostics.Root_directory_cannot_be_determined_skipping_primary_search_paths); 469 } 470 } 471 } 472 473 function secondaryLookup(): PathAndPackageId | undefined { 474 const initialLocationForSecondaryLookup = containingFile && getDirectoryPath(containingFile); 475 const packageManagerType = options.packageManagerType; 476 if (initialLocationForSecondaryLookup !== undefined) { 477 // check secondary locations 478 if (traceEnabled) { 479 const message = isOhpm(packageManagerType) ? Diagnostics.Looking_up_in_oh_modules_folder_initial_location_0: Diagnostics.Looking_up_in_node_modules_folder_initial_location_0; 480 trace(host, message, initialLocationForSecondaryLookup); 481 } 482 let result: Resolved | undefined; 483 if (!isExternalModuleNameRelative(typeReferenceDirectiveName)) { 484 const searchResult = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined, /*redirectedReference*/ undefined); 485 result = searchResult && searchResult.value; 486 } 487 else { 488 const { path: candidate } = normalizePathForCJSResolution(initialLocationForSecondaryLookup, typeReferenceDirectiveName); 489 result = nodeLoadModuleByRelativeName(Extensions.DtsOnly, candidate, /*onlyRecordFailures*/ false, moduleResolutionState, /*considerPackageJson*/ true); 490 } 491 return resolvedTypeScriptOnly(result); 492 } 493 else { 494 if (traceEnabled) { 495 const message = isOhpm(packageManagerType) ? Diagnostics.Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_oh_modules_folder : 496 Diagnostics.Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_modules_folder; 497 trace(host, message); 498 } 499 } 500 } 501} 502 503function getDefaultNodeResolutionFeatures(options: CompilerOptions) { 504 return getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node16 ? NodeResolutionFeatures.Node16Default : 505 getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext ? NodeResolutionFeatures.NodeNextDefault : 506 NodeResolutionFeatures.None; 507} 508 509/** 510 * @internal 511 * Does not try `@types/${packageName}` - use a second pass if needed. 512 */ 513export function resolvePackageNameToPackageJson( 514 packageName: string, 515 containingDirectory: string, 516 options: CompilerOptions, 517 host: ModuleResolutionHost, 518 cache: ModuleResolutionCache | undefined, 519): PackageJsonInfo | undefined { 520 const moduleResolutionState = getTemporaryModuleResolutionState(cache?.getPackageJsonInfoCache(), host, options); 521 522 return forEachAncestorDirectory(containingDirectory, ancestorDirectory => { 523 if (getBaseFileName(ancestorDirectory) !== "node_modules" && getBaseFileName(ancestorDirectory) !== "oh_modules") { 524 const modulePathPart = getModuleByPMType(options.packageManagerType) 525 const nodeModulesFolder = combinePaths(ancestorDirectory, modulePathPart); 526 const candidate = combinePaths(nodeModulesFolder, packageName); 527 return getPackageJsonInfo(candidate, /*onlyRecordFailures*/ false, moduleResolutionState); 528 } 529 }); 530} 531 532/** 533 * Given a set of options, returns the set of type directive names 534 * that should be included for this program automatically. 535 * This list could either come from the config file, 536 * or from enumerating the types root + initial secondary types lookup location. 537 * More type directives might appear in the program later as a result of loading actual source files; 538 * this list is only the set of defaults that are implicitly included. 539 */ 540export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] { 541 // Use explicit type list from tsconfig.json 542 if (options.types) { 543 return options.types; 544 } 545 546 // Walk the primary type lookup locations 547 const result: string[] = []; 548 if (host.directoryExists && host.getDirectories) { 549 const typeRoots = getEffectiveTypeRoots(options, host); 550 if (typeRoots) { 551 for (const root of typeRoots) { 552 if (host.directoryExists(root)) { 553 for (const typeDirectivePath of host.getDirectories(root)) { 554 const normalized = normalizePath(typeDirectivePath); 555 const packageJsonPath = combinePaths(root, normalized, getPackageJsonByPMType(options.packageManagerType)); 556 // `types-publisher` sometimes creates packages with `"typings": null` for packages that don't provide their own types. 557 // See `createNotNeededPackageJSON` in the types-publisher` repo. 558 // eslint-disable-next-line no-null/no-null 559 let isNotNeededPackage: boolean; 560 if (isOhpm(options.packageManagerType)) { 561 isNotNeededPackage = host.fileExists(packageJsonPath) && require("json5").parse(host.readFile!(packageJsonPath)!).typings === null; 562 } 563 else { 564 isNotNeededPackage = host.fileExists(packageJsonPath) && (readJson(packageJsonPath, host) as PackageJson).typings === null; 565 } 566 if (!isNotNeededPackage) { 567 const baseFileName = getBaseFileName(normalized); 568 569 // At this stage, skip results with leading dot. 570 if (baseFileName.charCodeAt(0) !== CharacterCodes.dot) { 571 // Return just the type directive names 572 result.push(baseFileName); 573 } 574 } 575 } 576 } 577 } 578 } 579 } 580 return result; 581} 582 583export interface TypeReferenceDirectiveResolutionCache extends PerDirectoryResolutionCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>, PackageJsonInfoCache { 584 /** @internal */clearAllExceptPackageJsonInfoCache(): void; 585} 586 587export interface ModeAwareCache<T> { 588 get(key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): T | undefined; 589 set(key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, value: T): this; 590 delete(key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): this; 591 has(key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): boolean; 592 forEach(cb: (elem: T, key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined) => void): void; 593 size(): number; 594} 595 596/** 597 * Cached resolutions per containing directory. 598 * This assumes that any module id will have the same resolution for sibling files located in the same folder. 599 */ 600export interface PerDirectoryResolutionCache<T> { 601 getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): ModeAwareCache<T>; 602 clear(): void; 603 /** 604 * Updates with the current compilerOptions the cache will operate with. 605 * This updates the redirects map as well if needed so module resolutions are cached if they can across the projects 606 */ 607 update(options: CompilerOptions): void; 608} 609 610export interface ModuleResolutionCache extends PerDirectoryResolutionCache<ResolvedModuleWithFailedLookupLocations>, NonRelativeModuleNameResolutionCache, PackageJsonInfoCache { 611 getPackageJsonInfoCache(): PackageJsonInfoCache; 612 /** @internal */ clearAllExceptPackageJsonInfoCache(): void; 613} 614 615/** 616 * Stored map from non-relative module name to a table: directory -> result of module lookup in this directory 617 * We support only non-relative module names because resolution of relative module names is usually more deterministic and thus less expensive. 618 */ 619export interface NonRelativeModuleNameResolutionCache extends PackageJsonInfoCache { 620 getOrCreateCacheForModuleName(nonRelativeModuleName: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, redirectedReference?: ResolvedProjectReference): PerModuleNameCache; 621} 622 623export interface PackageJsonInfoCache { 624 /** @internal */ getPackageJsonInfo(packageJsonPath: string): PackageJsonInfo | boolean | undefined; 625 /** @internal */ setPackageJsonInfo(packageJsonPath: string, info: PackageJsonInfo | boolean): void; 626 /** @internal */ entries(): [Path, PackageJsonInfo | boolean][]; 627 /** @internal */ getInternalMap(): ESMap<Path, PackageJsonInfo | boolean> | undefined; 628 clear(): void; 629} 630 631export interface PerModuleNameCache { 632 get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined; 633 set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void; 634} 635 636/** @internal */ 637export interface CacheWithRedirects<T> { 638 getOwnMap: () => ESMap<string, T>; 639 redirectsMap: ESMap<Path, ESMap<string, T>>; 640 getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): ESMap<string, T>; 641 clear(): void; 642 setOwnOptions(newOptions: CompilerOptions): void; 643 setOwnMap(newOwnMap: ESMap<string, T>): void; 644} 645 646/** @internal */ 647export function createCacheWithRedirects<T>(options?: CompilerOptions): CacheWithRedirects<T> { 648 let ownMap: ESMap<string, T> = new Map(); 649 const redirectsMap = new Map<Path, ESMap<string, T>>(); 650 return { 651 getOwnMap, 652 redirectsMap, 653 getOrCreateMapOfCacheRedirects, 654 clear, 655 setOwnOptions, 656 setOwnMap 657 }; 658 659 function getOwnMap() { 660 return ownMap; 661 } 662 663 function setOwnOptions(newOptions: CompilerOptions) { 664 options = newOptions; 665 } 666 667 function setOwnMap(newOwnMap: ESMap<string, T>) { 668 ownMap = newOwnMap; 669 } 670 671 function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined) { 672 if (!redirectedReference) { 673 return ownMap; 674 } 675 const path = redirectedReference.sourceFile.path; 676 let redirects = redirectsMap.get(path); 677 if (!redirects) { 678 // Reuse map if redirected reference map uses same resolution 679 redirects = !options || optionsHaveModuleResolutionChanges(options, redirectedReference.commandLine.options) ? new Map() : ownMap; 680 redirectsMap.set(path, redirects); 681 } 682 return redirects; 683 } 684 685 function clear() { 686 ownMap.clear(); 687 redirectsMap.clear(); 688 } 689} 690 691function createPackageJsonInfoCache(currentDirectory: string, getCanonicalFileName: (s: string) => string): PackageJsonInfoCache { 692 let cache: ESMap<Path, PackageJsonInfo | boolean> | undefined; 693 return { getPackageJsonInfo, setPackageJsonInfo, clear, entries, getInternalMap }; 694 function getPackageJsonInfo(packageJsonPath: string) { 695 return cache?.get(toPath(packageJsonPath, currentDirectory, getCanonicalFileName)); 696 } 697 function setPackageJsonInfo(packageJsonPath: string, info: PackageJsonInfo | boolean) { 698 (cache ||= new Map()).set(toPath(packageJsonPath, currentDirectory, getCanonicalFileName), info); 699 } 700 function clear() { 701 cache = undefined; 702 } 703 function entries() { 704 const iter = cache?.entries(); 705 return iter ? arrayFrom(iter) : []; 706 } 707 function getInternalMap() { 708 return cache; 709 } 710} 711 712function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirectedReference: ResolvedProjectReference | undefined, key: string, create: () => T): T { 713 const cache = cacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference); 714 let result = cache.get(key); 715 if (!result) { 716 result = create(); 717 cache.set(key, result); 718 } 719 return result; 720} 721 722function updateRedirectsMap<T>( 723 options: CompilerOptions, 724 directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<T>>, 725 moduleNameToDirectoryMap?: CacheWithRedirects<PerModuleNameCache> 726) { 727 if (!options.configFile) return; 728 if (directoryToModuleNameMap.redirectsMap.size === 0) { 729 // The own map will be for projectCompilerOptions 730 Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.redirectsMap.size === 0); 731 Debug.assert(directoryToModuleNameMap.getOwnMap().size === 0); 732 Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.getOwnMap().size === 0); 733 directoryToModuleNameMap.redirectsMap.set(options.configFile.path, directoryToModuleNameMap.getOwnMap()); 734 moduleNameToDirectoryMap?.redirectsMap.set(options.configFile.path, moduleNameToDirectoryMap.getOwnMap()); 735 } 736 else { 737 // Set correct own map 738 Debug.assert(!moduleNameToDirectoryMap || moduleNameToDirectoryMap.redirectsMap.size > 0); 739 const ref: ResolvedProjectReference = { 740 sourceFile: options.configFile, 741 commandLine: { options } as ParsedCommandLine 742 }; 743 directoryToModuleNameMap.setOwnMap(directoryToModuleNameMap.getOrCreateMapOfCacheRedirects(ref)); 744 moduleNameToDirectoryMap?.setOwnMap(moduleNameToDirectoryMap.getOrCreateMapOfCacheRedirects(ref)); 745 } 746 directoryToModuleNameMap.setOwnOptions(options); 747 moduleNameToDirectoryMap?.setOwnOptions(options); 748} 749 750function createPerDirectoryResolutionCache<T>(currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<T>>): PerDirectoryResolutionCache<T> { 751 return { 752 getOrCreateCacheForDirectory, 753 clear, 754 update, 755 }; 756 757 function clear() { 758 directoryToModuleNameMap.clear(); 759 } 760 761 function update(options: CompilerOptions) { 762 updateRedirectsMap(options, directoryToModuleNameMap); 763 } 764 765 function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) { 766 const path = toPath(directoryName, currentDirectory, getCanonicalFileName); 767 return getOrCreateCache<ModeAwareCache<T>>(directoryToModuleNameMap, redirectedReference, path, () => createModeAwareCache()); 768 } 769} 770 771/** @internal */ 772export function createModeAwareCache<T>(): ModeAwareCache<T> { 773 const underlying = new Map<string, T>(); 774 const memoizedReverseKeys = new Map<string, [specifier: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined]>(); 775 776 const cache: ModeAwareCache<T> = { 777 get(specifier, mode) { 778 return underlying.get(getUnderlyingCacheKey(specifier, mode)); 779 }, 780 set(specifier, mode, value) { 781 underlying.set(getUnderlyingCacheKey(specifier, mode), value); 782 return cache; 783 }, 784 delete(specifier, mode) { 785 underlying.delete(getUnderlyingCacheKey(specifier, mode)); 786 return cache; 787 }, 788 has(specifier, mode) { 789 return underlying.has(getUnderlyingCacheKey(specifier, mode)); 790 }, 791 forEach(cb) { 792 return underlying.forEach((elem, key) => { 793 const [specifier, mode] = memoizedReverseKeys.get(key)!; 794 return cb(elem, specifier, mode); 795 }); 796 }, 797 size() { 798 return underlying.size; 799 } 800 }; 801 return cache; 802 803 function getUnderlyingCacheKey(specifier: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined) { 804 const result = mode === undefined ? specifier : `${mode}|${specifier}`; 805 memoizedReverseKeys.set(result, [specifier, mode]); 806 return result; 807 } 808} 809 810/** @internal */ 811export function zipToModeAwareCache<V>(file: SourceFile, keys: readonly string[] | readonly FileReference[], values: readonly V[]): ModeAwareCache<V> { 812 Debug.assert(keys.length === values.length); 813 const map = createModeAwareCache<V>(); 814 for (let i = 0; i < keys.length; ++i) { 815 const entry = keys[i]; 816 // We lower-case all type references because npm automatically lowercases all packages. See GH#9824. 817 const name = !isString(entry) ? entry.fileName.toLowerCase() : entry; 818 const mode = !isString(entry) ? entry.resolutionMode || file.impliedNodeFormat : getModeForResolutionAtIndex(file, i); 819 map.set(name, mode, values[i]); 820 } 821 return map; 822} 823 824export function createModuleResolutionCache( 825 currentDirectory: string, 826 getCanonicalFileName: (s: string) => string, 827 options?: CompilerOptions 828): ModuleResolutionCache; 829/** @internal */ 830export function createModuleResolutionCache( 831 currentDirectory: string, 832 getCanonicalFileName: GetCanonicalFileName, 833 options: undefined, 834 directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<ResolvedModuleWithFailedLookupLocations>>, 835 moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>, 836): ModuleResolutionCache; 837export function createModuleResolutionCache( 838 currentDirectory: string, 839 getCanonicalFileName: GetCanonicalFileName, 840 options?: CompilerOptions, 841 directoryToModuleNameMap?: CacheWithRedirects<ModeAwareCache<ResolvedModuleWithFailedLookupLocations>>, 842 moduleNameToDirectoryMap?: CacheWithRedirects<PerModuleNameCache>, 843): ModuleResolutionCache { 844 const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap ||= createCacheWithRedirects(options)); 845 moduleNameToDirectoryMap ||= createCacheWithRedirects(options); 846 const packageJsonInfoCache = createPackageJsonInfoCache(currentDirectory, getCanonicalFileName); 847 848 return { 849 ...packageJsonInfoCache, 850 ...perDirectoryResolutionCache, 851 getOrCreateCacheForModuleName, 852 clear, 853 update, 854 getPackageJsonInfoCache: () => packageJsonInfoCache, 855 clearAllExceptPackageJsonInfoCache, 856 }; 857 858 function clear() { 859 clearAllExceptPackageJsonInfoCache(); 860 packageJsonInfoCache.clear(); 861 } 862 863 function clearAllExceptPackageJsonInfoCache() { 864 perDirectoryResolutionCache.clear(); 865 moduleNameToDirectoryMap!.clear(); 866 } 867 868 function update(options: CompilerOptions) { 869 updateRedirectsMap(options, directoryToModuleNameMap!, moduleNameToDirectoryMap); 870 } 871 872 function getOrCreateCacheForModuleName(nonRelativeModuleName: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, redirectedReference?: ResolvedProjectReference): PerModuleNameCache { 873 Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName)); 874 return getOrCreateCache(moduleNameToDirectoryMap!, redirectedReference, mode === undefined ? nonRelativeModuleName : `${mode}|${nonRelativeModuleName}`, createPerModuleNameCache); 875 } 876 877 function createPerModuleNameCache(): PerModuleNameCache { 878 const directoryPathMap = new Map<string, ResolvedModuleWithFailedLookupLocations>(); 879 880 return { get, set }; 881 882 function get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined { 883 return directoryPathMap.get(toPath(directory, currentDirectory, getCanonicalFileName)); 884 } 885 886 /** 887 * At first this function add entry directory -> module resolution result to the table. 888 * Then it computes the set of parent folders for 'directory' that should have the same module resolution result 889 * and for every parent folder in set it adds entry: parent -> module resolution. . 890 * Lets say we first directory name: /a/b/c/d/e and resolution result is: /a/b/bar.ts. 891 * Set of parent folders that should have the same result will be: 892 * [ 893 * /a/b/c/d, /a/b/c, /a/b 894 * ] 895 * this means that request for module resolution from file in any of these folder will be immediately found in cache. 896 */ 897 function set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void { 898 const path = toPath(directory, currentDirectory, getCanonicalFileName); 899 // if entry is already in cache do nothing 900 if (directoryPathMap.has(path)) { 901 return; 902 } 903 directoryPathMap.set(path, result); 904 905 const resolvedFileName = result.resolvedModule && 906 (result.resolvedModule.originalPath || result.resolvedModule.resolvedFileName); 907 // find common prefix between directory and resolved file name 908 // this common prefix should be the shortest path that has the same resolution 909 // directory: /a/b/c/d/e 910 // resolvedFileName: /a/b/foo.d.ts 911 // commonPrefix: /a/b 912 // for failed lookups cache the result for every directory up to root 913 const commonPrefix = resolvedFileName && getCommonPrefix(path, resolvedFileName); 914 let current = path; 915 while (current !== commonPrefix) { 916 const parent = getDirectoryPath(current); 917 if (parent === current || directoryPathMap.has(parent)) { 918 break; 919 } 920 directoryPathMap.set(parent, result); 921 current = parent; 922 } 923 } 924 925 function getCommonPrefix(directory: Path, resolution: string) { 926 const resolutionDirectory = toPath(getDirectoryPath(resolution), currentDirectory, getCanonicalFileName); 927 928 // find first position where directory and resolution differs 929 let i = 0; 930 const limit = Math.min(directory.length, resolutionDirectory.length); 931 while (i < limit && directory.charCodeAt(i) === resolutionDirectory.charCodeAt(i)) { 932 i++; 933 } 934 if (i === directory.length && (resolutionDirectory.length === i || resolutionDirectory[i] === directorySeparator)) { 935 return directory; 936 } 937 const rootLength = getRootLength(directory); 938 if (i < rootLength) { 939 return undefined; 940 } 941 const sep = directory.lastIndexOf(directorySeparator, i - 1); 942 if (sep === -1) { 943 return undefined; 944 } 945 return directory.substr(0, Math.max(sep, rootLength)); 946 } 947 } 948} 949 950export function createTypeReferenceDirectiveResolutionCache( 951 currentDirectory: string, 952 getCanonicalFileName: (s: string) => string, 953 options?: CompilerOptions, 954 packageJsonInfoCache?: PackageJsonInfoCache, 955): TypeReferenceDirectiveResolutionCache; 956/** @internal */ 957export function createTypeReferenceDirectiveResolutionCache( 958 currentDirectory: string, 959 getCanonicalFileName: GetCanonicalFileName, 960 options: undefined, 961 packageJsonInfoCache: PackageJsonInfoCache | undefined, 962 directoryToModuleNameMap: CacheWithRedirects<ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>, 963): TypeReferenceDirectiveResolutionCache; 964export function createTypeReferenceDirectiveResolutionCache( 965 currentDirectory: string, 966 getCanonicalFileName: GetCanonicalFileName, 967 options?: CompilerOptions, 968 packageJsonInfoCache?: PackageJsonInfoCache | undefined, 969 directoryToModuleNameMap?: CacheWithRedirects<ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>, 970): TypeReferenceDirectiveResolutionCache { 971 const perDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap ||= createCacheWithRedirects(options)); 972 packageJsonInfoCache ||= createPackageJsonInfoCache(currentDirectory, getCanonicalFileName); 973 974 return { 975 ...packageJsonInfoCache, 976 ...perDirectoryResolutionCache, 977 clear, 978 clearAllExceptPackageJsonInfoCache, 979 }; 980 981 function clear() { 982 clearAllExceptPackageJsonInfoCache(); 983 packageJsonInfoCache!.clear(); 984 } 985 986 function clearAllExceptPackageJsonInfoCache() { 987 perDirectoryResolutionCache.clear(); 988 } 989} 990 991export function resolveModuleNameFromCache(moduleName: string, containingFile: string, cache: ModuleResolutionCache, mode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined { 992 const containingDirectory = getDirectoryPath(containingFile); 993 const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory); 994 if (!perFolderCache) return undefined; 995 return perFolderCache.get(moduleName, mode); 996} 997 998export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations { 999 const traceEnabled = isTraceEnabled(compilerOptions, host); 1000 if (redirectedReference) { 1001 compilerOptions = redirectedReference.commandLine.options; 1002 } 1003 if (traceEnabled) { 1004 trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile); 1005 if (redirectedReference) { 1006 trace(host, Diagnostics.Using_compiler_options_of_project_reference_redirect_0, redirectedReference.sourceFile.fileName); 1007 } 1008 } 1009 const containingDirectory = getDirectoryPath(containingFile); 1010 const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory, redirectedReference); 1011 let result = perFolderCache && perFolderCache.get(moduleName, resolutionMode); 1012 1013 if (result) { 1014 if (traceEnabled) { 1015 trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory); 1016 } 1017 } 1018 else { 1019 let moduleResolution = compilerOptions.moduleResolution; 1020 if (moduleResolution === undefined) { 1021 switch (getEmitModuleKind(compilerOptions)) { 1022 case ModuleKind.CommonJS: 1023 moduleResolution = ModuleResolutionKind.NodeJs; 1024 break; 1025 case ModuleKind.Node16: 1026 moduleResolution = ModuleResolutionKind.Node16; 1027 break; 1028 case ModuleKind.NodeNext: 1029 moduleResolution = ModuleResolutionKind.NodeNext; 1030 break; 1031 default: 1032 moduleResolution = ModuleResolutionKind.Classic; 1033 break; 1034 } 1035 if (traceEnabled) { 1036 trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]); 1037 } 1038 } 1039 else { 1040 if (traceEnabled) { 1041 trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]); 1042 } 1043 } 1044 1045 perfLogger.logStartResolveModule(moduleName /* , containingFile, ModuleResolutionKind[moduleResolution]*/); 1046 switch (moduleResolution) { 1047 case ModuleResolutionKind.Node16: 1048 result = node16ModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference, resolutionMode); 1049 break; 1050 case ModuleResolutionKind.NodeNext: 1051 result = nodeNextModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference, resolutionMode); 1052 break; 1053 case ModuleResolutionKind.NodeJs: 1054 result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference); 1055 break; 1056 case ModuleResolutionKind.Classic: 1057 result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference); 1058 break; 1059 default: 1060 return Debug.fail(`Unexpected moduleResolution: ${moduleResolution}`); 1061 } 1062 if (result && result.resolvedModule) perfLogger.logInfoEvent(`Module "${moduleName}" resolved to "${result.resolvedModule.resolvedFileName}"`); 1063 perfLogger.logStopResolveModule((result && result.resolvedModule) ? "" + result.resolvedModule.resolvedFileName : "null"); 1064 1065 if (perFolderCache) { 1066 perFolderCache.set(moduleName, resolutionMode, result); 1067 if (!isExternalModuleNameRelative(moduleName)) { 1068 // put result in per-module name cache 1069 cache.getOrCreateCacheForModuleName(moduleName, resolutionMode, redirectedReference).set(containingDirectory, result); 1070 } 1071 } 1072 } 1073 1074 if (traceEnabled) { 1075 if (result.resolvedModule) { 1076 if (result.resolvedModule.packageId) { 1077 trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1_with_Package_ID_2, moduleName, result.resolvedModule.resolvedFileName, packageIdToString(result.resolvedModule.packageId)); 1078 } 1079 else { 1080 trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName); 1081 } 1082 } 1083 else { 1084 trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName); 1085 } 1086 } 1087 1088 return result; 1089} 1090 1091/* 1092 * Every module resolution kind can has its specific understanding how to load module from a specific path on disk 1093 * I.e. for path '/a/b/c': 1094 * - Node loader will first to try to check if '/a/b/c' points to a file with some supported extension and if this fails 1095 * it will try to load module from directory: directory '/a/b/c' should exist and it should have either 'package.json' with 1096 * 'typings' entry or file 'index' with some supported extension 1097 * - Classic loader will only try to interpret '/a/b/c' as file. 1098 */ 1099type ResolutionKindSpecificLoader = (extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState) => Resolved | undefined; 1100 1101/** 1102 * Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to 1103 * mitigate differences between design time structure of the project and its runtime counterpart so the same import name 1104 * can be resolved successfully by TypeScript compiler and runtime module loader. 1105 * If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will 1106 * fallback to standard resolution routine. 1107 * 1108 * - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative 1109 * names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will 1110 * be '/a/b/c/d' 1111 * - paths - this setting can only be used when baseUrl is specified. allows to tune how non-relative module names 1112 * will be resolved based on the content of the module name. 1113 * Structure of 'paths' compiler options 1114 * 'paths': { 1115 * pattern-1: [...substitutions], 1116 * pattern-2: [...substitutions], 1117 * ... 1118 * pattern-n: [...substitutions] 1119 * } 1120 * Pattern here is a string that can contain zero or one '*' character. During module resolution module name will be matched against 1121 * all patterns in the list. Matching for patterns that don't contain '*' means that module name must be equal to pattern respecting the case. 1122 * If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>. 1123 * <MatchedStar> denotes part of the module name between <prefix> and <suffix>. 1124 * If module name can be matches with multiple patterns then pattern with the longest prefix will be picked. 1125 * After selecting pattern we'll use list of substitutions to get candidate locations of the module and the try to load module 1126 * from the candidate location. 1127 * Substitution is a string that can contain zero or one '*'. To get candidate location from substitution we'll pick every 1128 * substitution in the list and replace '*' with <MatchedStar> string. If candidate location is not rooted it 1129 * will be converted to absolute using baseUrl. 1130 * For example: 1131 * baseUrl: /a/b/c 1132 * "paths": { 1133 * // match all module names 1134 * "*": [ 1135 * "*", // use matched name as is, 1136 * // <matched name> will be looked as /a/b/c/<matched name> 1137 * 1138 * "folder1/*" // substitution will convert matched name to 'folder1/<matched name>', 1139 * // since it is not rooted then final candidate location will be /a/b/c/folder1/<matched name> 1140 * ], 1141 * // match module names that start with 'components/' 1142 * "components/*": [ "/root/components/*" ] // substitution will convert /components/folder1/<matched name> to '/root/components/folder1/<matched name>', 1143 * // it is rooted so it will be final candidate location 1144 * } 1145 * 1146 * 'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if 1147 * they were in the same location. For example lets say there are two files 1148 * '/local/src/content/file1.ts' 1149 * '/shared/components/contracts/src/content/protocols/file2.ts' 1150 * After bundling content of '/shared/components/contracts/src' will be merged with '/local/src' so 1151 * if file1 has the following import 'import {x} from "./protocols/file2"' it will be resolved successfully in runtime. 1152 * 'rootDirs' provides the way to tell compiler that in order to get the whole project it should behave as if content of all 1153 * root dirs were merged together. 1154 * I.e. for the example above 'rootDirs' will have two entries: [ '/local/src', '/shared/components/contracts/src' ]. 1155 * Compiler will first convert './protocols/file2' into absolute path relative to the location of containing file: 1156 * '/local/src/content/protocols/file2' and try to load it - failure. 1157 * Then it will search 'rootDirs' looking for a longest matching prefix of this absolute path and if such prefix is found - absolute path will 1158 * be converted to a path relative to found rootDir entry './content/protocols/file2' (*). As a last step compiler will check all remaining 1159 * entries in 'rootDirs', use them to build absolute path out of (*) and try to resolve module from this location. 1160 */ 1161function tryLoadModuleUsingOptionalResolutionSettings(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, 1162 state: ModuleResolutionState): Resolved | undefined { 1163 1164 const resolved = tryLoadModuleUsingPathsIfEligible(extensions, moduleName, loader, state); 1165 if (resolved) return resolved.value; 1166 1167 if (!isExternalModuleNameRelative(moduleName)) { 1168 return tryLoadModuleUsingBaseUrl(extensions, moduleName, loader, state); 1169 } 1170 else { 1171 return tryLoadModuleUsingRootDirs(extensions, moduleName, containingDirectory, loader, state); 1172 } 1173} 1174 1175function tryLoadModuleUsingPathsIfEligible(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState) { 1176 const { baseUrl, paths, configFile } = state.compilerOptions; 1177 if (paths && !pathIsRelative(moduleName)) { 1178 if (state.traceEnabled) { 1179 if (baseUrl) { 1180 trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, baseUrl, moduleName); 1181 } 1182 trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); 1183 } 1184 const baseDirectory = getPathsBasePath(state.compilerOptions, state.host)!; // Always defined when 'paths' is defined 1185 const pathPatterns = configFile?.configFileSpecs ? configFile.configFileSpecs.pathPatterns ||= tryParsePatterns(paths) : undefined; 1186 return tryLoadModuleUsingPaths(extensions, moduleName, baseDirectory, paths, pathPatterns, loader, /*onlyRecordFailures*/ false, state); 1187 } 1188} 1189 1190function tryLoadModuleUsingRootDirs(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader, 1191 state: ModuleResolutionState): Resolved | undefined { 1192 1193 if (!state.compilerOptions.rootDirs) { 1194 return undefined; 1195 } 1196 1197 if (state.traceEnabled) { 1198 trace(state.host, Diagnostics.rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName); 1199 } 1200 1201 const candidate = normalizePath(combinePaths(containingDirectory, moduleName)); 1202 1203 let matchedRootDir: string | undefined; 1204 let matchedNormalizedPrefix: string | undefined; 1205 for (const rootDir of state.compilerOptions.rootDirs) { 1206 // rootDirs are expected to be absolute 1207 // in case of tsconfig.json this will happen automatically - compiler will expand relative names 1208 // using location of tsconfig.json as base location 1209 let normalizedRoot = normalizePath(rootDir); 1210 if (!endsWith(normalizedRoot, directorySeparator)) { 1211 normalizedRoot += directorySeparator; 1212 } 1213 const isLongestMatchingPrefix = 1214 startsWith(candidate, normalizedRoot) && 1215 (matchedNormalizedPrefix === undefined || matchedNormalizedPrefix.length < normalizedRoot.length); 1216 1217 if (state.traceEnabled) { 1218 trace(state.host, Diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2, normalizedRoot, candidate, isLongestMatchingPrefix); 1219 } 1220 1221 if (isLongestMatchingPrefix) { 1222 matchedNormalizedPrefix = normalizedRoot; 1223 matchedRootDir = rootDir; 1224 } 1225 } 1226 if (matchedNormalizedPrefix) { 1227 if (state.traceEnabled) { 1228 trace(state.host, Diagnostics.Longest_matching_prefix_for_0_is_1, candidate, matchedNormalizedPrefix); 1229 } 1230 const suffix = candidate.substr(matchedNormalizedPrefix.length); 1231 1232 // first - try to load from a initial location 1233 if (state.traceEnabled) { 1234 trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, matchedNormalizedPrefix, candidate); 1235 } 1236 const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(containingDirectory, state.host), state); 1237 if (resolvedFileName) { 1238 return resolvedFileName; 1239 } 1240 1241 if (state.traceEnabled) { 1242 trace(state.host, Diagnostics.Trying_other_entries_in_rootDirs); 1243 } 1244 // then try to resolve using remaining entries in rootDirs 1245 for (const rootDir of state.compilerOptions.rootDirs) { 1246 if (rootDir === matchedRootDir) { 1247 // skip the initially matched entry 1248 continue; 1249 } 1250 const candidate = combinePaths(normalizePath(rootDir), suffix); 1251 if (state.traceEnabled) { 1252 trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, rootDir, candidate); 1253 } 1254 const baseDirectory = getDirectoryPath(candidate); 1255 const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(baseDirectory, state.host), state); 1256 if (resolvedFileName) { 1257 return resolvedFileName; 1258 } 1259 } 1260 if (state.traceEnabled) { 1261 trace(state.host, Diagnostics.Module_resolution_using_rootDirs_has_failed); 1262 } 1263 } 1264 return undefined; 1265} 1266 1267function tryLoadModuleUsingBaseUrl(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined { 1268 const { baseUrl } = state.compilerOptions; 1269 if (!baseUrl) { 1270 return undefined; 1271 } 1272 if (state.traceEnabled) { 1273 trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, baseUrl, moduleName); 1274 } 1275 const candidate = normalizePath(combinePaths(baseUrl, moduleName)); 1276 if (state.traceEnabled) { 1277 trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, baseUrl, candidate); 1278 } 1279 return loader(extensions, candidate, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state); 1280} 1281 1282/** 1283 * Expose resolution logic to allow us to use Node module resolution logic from arbitrary locations. 1284 * No way to do this with `require()`: https://github.com/nodejs/node/issues/5963 1285 * Throws an error if the module can't be resolved. 1286 * 1287 * @internal 1288 */ 1289export function resolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string { 1290 const { resolvedModule, failedLookupLocations } = tryResolveJSModuleWorker(moduleName, initialDir, host); 1291 if (!resolvedModule) { 1292 throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`); 1293 } 1294 return resolvedModule.resolvedFileName; 1295} 1296 1297/** @internal */ 1298export enum NodeResolutionFeatures { 1299 None = 0, 1300 // resolving `#local` names in your own package.json 1301 Imports = 1 << 1, 1302 // resolving `your-own-name` from your own package.json 1303 SelfName = 1 << 2, 1304 // respecting the `.exports` member of packages' package.json files and its (conditional) mappings of export names 1305 Exports = 1 << 3, 1306 // allowing `*` in the LHS of an export to be followed by more content, eg `"./whatever/*.js"` 1307 // not supported in node 12 - https://github.com/nodejs/Release/issues/690 1308 ExportsPatternTrailers = 1 << 4, 1309 AllFeatures = Imports | SelfName | Exports | ExportsPatternTrailers, 1310 1311 Node16Default = Imports | SelfName | Exports | ExportsPatternTrailers, 1312 1313 NodeNextDefault = AllFeatures, 1314 1315 EsmMode = 1 << 5, 1316} 1317 1318function node16ModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, 1319 host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, 1320 resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations { 1321 return nodeNextModuleNameResolverWorker( 1322 NodeResolutionFeatures.Node16Default, 1323 moduleName, 1324 containingFile, 1325 compilerOptions, 1326 host, 1327 cache, 1328 redirectedReference, 1329 resolutionMode 1330 ); 1331} 1332 1333function nodeNextModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, 1334 host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, 1335 resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations { 1336 return nodeNextModuleNameResolverWorker( 1337 NodeResolutionFeatures.NodeNextDefault, 1338 moduleName, 1339 containingFile, 1340 compilerOptions, 1341 host, 1342 cache, 1343 redirectedReference, 1344 resolutionMode 1345 ); 1346} 1347 1348const jsOnlyExtensions = [Extensions.JavaScript]; 1349const tsExtensions = [Extensions.TypeScript, Extensions.JavaScript]; 1350const tsPlusJsonExtensions = [...tsExtensions, Extensions.Json]; 1351const tsconfigExtensions = [Extensions.TSConfig]; 1352function nodeNextModuleNameResolverWorker(features: NodeResolutionFeatures, moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations { 1353 const containingDirectory = getDirectoryPath(containingFile); 1354 1355 // es module file or cjs-like input file, use a variant of the legacy cjs resolver that supports the selected modern features 1356 const esmMode = resolutionMode === ModuleKind.ESNext ? NodeResolutionFeatures.EsmMode : 0; 1357 let extensions = compilerOptions.noDtsResolution ? [Extensions.TsOnly, Extensions.JavaScript] : tsExtensions; 1358 if (compilerOptions.resolveJsonModule) { 1359 extensions = [...extensions, Extensions.Json]; 1360 } 1361 return nodeModuleNameResolverWorker(features | esmMode, moduleName, containingDirectory, compilerOptions, host, cache, extensions, redirectedReference); 1362} 1363 1364function tryResolveJSModuleWorker(moduleName: string, initialDir: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { 1365 return nodeModuleNameResolverWorker(NodeResolutionFeatures.None, moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, jsOnlyExtensions, /*redirectedReferences*/ undefined); 1366} 1367 1368export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations; 1369/** @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 1370export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, lookupConfig?: boolean): ResolvedModuleWithFailedLookupLocations { 1371 let extensions; 1372 if (lookupConfig) { 1373 extensions = tsconfigExtensions; 1374 } 1375 else if (compilerOptions.noDtsResolution) { 1376 extensions = [Extensions.TsOnly]; 1377 if (compilerOptions.allowJs) extensions.push(Extensions.JavaScript); 1378 if (compilerOptions.resolveJsonModule) extensions.push(Extensions.Json); 1379 } 1380 else { 1381 extensions = compilerOptions.resolveJsonModule ? tsPlusJsonExtensions : tsExtensions; 1382 } 1383 return nodeModuleNameResolverWorker(NodeResolutionFeatures.None, moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, extensions, redirectedReference); 1384} 1385 1386function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, extensions: Extensions[], redirectedReference: ResolvedProjectReference | undefined): ResolvedModuleWithFailedLookupLocations { 1387 const traceEnabled = isTraceEnabled(compilerOptions, host); 1388 1389 const failedLookupLocations: string[] = []; 1390 const affectingLocations: string[] = []; 1391 // conditions are only used by the node16/nodenext resolver - there's no priority order in the list, 1392 //it's essentially a set (priority is determined by object insertion order in the object we look at). 1393 const conditions = features & NodeResolutionFeatures.EsmMode ? ["node", "import", "types"] : ["node", "require", "types"]; 1394 if (compilerOptions.noDtsResolution) { 1395 conditions.pop(); 1396 } 1397 1398 const diagnostics: Diagnostic[] = []; 1399 const state: ModuleResolutionState = { 1400 compilerOptions, 1401 host, 1402 traceEnabled, 1403 failedLookupLocations, 1404 affectingLocations, 1405 packageJsonInfoCache: cache, 1406 features, 1407 conditions, 1408 requestContainingDirectory: containingDirectory, 1409 reportDiagnostic: diag => void diagnostics.push(diag), 1410 }; 1411 1412 if (traceEnabled && getEmitModuleResolutionKind(compilerOptions) >= ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(compilerOptions) <= ModuleResolutionKind.NodeNext) { 1413 trace(host, Diagnostics.Resolving_in_0_mode_with_conditions_1, features & NodeResolutionFeatures.EsmMode ? "ESM" : "CJS", conditions.map(c => `'${c}'`).join(", ")); 1414 } 1415 1416 const result = forEach(extensions, ext => tryResolve(ext)); 1417 return createResolvedModuleWithFailedLookupLocations( 1418 result?.value?.resolved, 1419 result?.value?.isExternalLibraryImport, 1420 failedLookupLocations, 1421 affectingLocations, 1422 diagnostics, 1423 state.resultFromCache 1424 ); 1425 1426 function tryResolve(extensions: Extensions): SearchResult<{ resolved: Resolved, isExternalLibraryImport: boolean }> { 1427 const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => nodeLoadModuleByRelativeName(extensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ true); 1428 const isOHModules: boolean = isOhpm(compilerOptions.packageManagerType); 1429 const resolved = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loader, state); 1430 if (resolved) { 1431 return toSearchResult({ resolved, isExternalLibraryImport: isOHModules ? pathContainsOHModules(resolved.path) : 1432 pathContainsNodeModules(resolved.path) }); 1433 } 1434 1435 if (!isExternalModuleNameRelative(moduleName)) { 1436 let resolved: SearchResult<Resolved> | undefined; 1437 if (features & NodeResolutionFeatures.Imports && startsWith(moduleName, "#")) { 1438 resolved = loadModuleFromImports(extensions, moduleName, containingDirectory, state, cache, redirectedReference); 1439 } 1440 if (!resolved && features & NodeResolutionFeatures.SelfName) { 1441 resolved = loadModuleFromSelfNameReference(extensions, moduleName, containingDirectory, state, cache, redirectedReference); 1442 } 1443 if (!resolved) { 1444 if (traceEnabled) { 1445 const message = isOHModules ? Diagnostics.Loading_module_0_from_oh_modules_folder_target_file_type_1 : Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1; 1446 trace(host, message, moduleName, Extensions[extensions]); 1447 } 1448 resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache, redirectedReference); 1449 } 1450 if (!resolved) return undefined; 1451 1452 let resolvedValue = resolved.value; 1453 if (!compilerOptions.preserveSymlinks && resolvedValue && !resolvedValue.originalPath) { 1454 const path = realPath(resolvedValue.path, host, traceEnabled); 1455 const pathsAreEqual = arePathsEqual(path, resolvedValue.path, host); 1456 const originalPath = pathsAreEqual ? undefined : resolvedValue.path; 1457 // If the path and realpath are differing only in casing prefer path so that we can issue correct errors for casing under forceConsistentCasingInFileNames 1458 resolvedValue = { ...resolvedValue, path: pathsAreEqual ? resolvedValue.path : path, originalPath }; 1459 } 1460 // For node_modules or oh_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files. 1461 return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } }; 1462 } 1463 else { 1464 const { path: candidate, parts } = normalizePathForCJSResolution(containingDirectory, moduleName); 1465 const resolved = nodeLoadModuleByRelativeName(extensions, candidate, /*onlyRecordFailures*/ false, state, /*considerPackageJson*/ true); 1466 // Treat explicit "node_modules" or "oh_modules" import as an external library import. 1467 return resolved && toSearchResult({ resolved, isExternalLibraryImport: contains(parts, getModuleByPMType(compilerOptions.packageManagerType)) }); 1468 } 1469 } 1470 1471} 1472 1473// If you import from "." inside a containing directory "/foo", the result of `normalizePath` 1474// would be "/foo", but this loses the information that `foo` is a directory and we intended 1475// to look inside of it. The Node CommonJS resolution algorithm doesn't call this out 1476// (https://nodejs.org/api/modules.html#all-together), but it seems that module paths ending 1477// in `.` are actually normalized to `./` before proceeding with the resolution algorithm. 1478function normalizePathForCJSResolution(containingDirectory: string, moduleName: string) { 1479 const combined = combinePaths(containingDirectory, moduleName); 1480 const parts = getPathComponents(combined); 1481 const lastPart = lastOrUndefined(parts); 1482 const path = lastPart === "." || lastPart === ".." ? ensureTrailingDirectorySeparator(normalizePath(combined)) : normalizePath(combined); 1483 return { path, parts }; 1484} 1485 1486function realPath(path: string, host: ModuleResolutionHost, traceEnabled: boolean): string { 1487 if (!host.realpath) { 1488 return path; 1489 } 1490 1491 const real = normalizePath(host.realpath(path)); 1492 if (traceEnabled) { 1493 trace(host, Diagnostics.Resolving_real_path_for_0_result_1, path, real); 1494 } 1495 Debug.assert(host.fileExists(real), `${path} linked to nonexistent file ${real}`); 1496 return real; 1497} 1498 1499function nodeLoadModuleByRelativeName(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson: boolean): Resolved | undefined { 1500 if (state.traceEnabled) { 1501 trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0_target_file_type_1, candidate, Extensions[extensions]); 1502 } 1503 if (!hasTrailingDirectorySeparator(candidate)) { 1504 if (!onlyRecordFailures) { 1505 const parentOfCandidate = getDirectoryPath(candidate); 1506 if (!directoryProbablyExists(parentOfCandidate, state.host)) { 1507 if (state.traceEnabled) { 1508 trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, parentOfCandidate); 1509 } 1510 onlyRecordFailures = true; 1511 } 1512 } 1513 const resolvedFromFile = loadModuleFromFile(extensions, candidate, onlyRecordFailures, state); 1514 if (resolvedFromFile) { 1515 const packageDirectory = considerPackageJson ? parseModuleFromPath(resolvedFromFile.path, state.compilerOptions.packageManagerType) : undefined; 1516 const packageInfo = packageDirectory ? getPackageJsonInfo(packageDirectory, /*onlyRecordFailures*/ false, state) : undefined; 1517 return withPackageId(packageInfo, resolvedFromFile); 1518 } 1519 } 1520 if (!onlyRecordFailures) { 1521 const candidateExists = directoryProbablyExists(candidate, state.host); 1522 if (!candidateExists) { 1523 if (state.traceEnabled) { 1524 trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, candidate); 1525 } 1526 onlyRecordFailures = true; 1527 } 1528 } 1529 // esm mode relative imports shouldn't do any directory lookups (either inside `package.json` 1530 // files or implicit `index.js`es). This is a notable depature from cjs norms, where `./foo/pkg` 1531 // could have been redirected by `./foo/pkg/package.json` to an arbitrary location! 1532 if (!(state.features & NodeResolutionFeatures.EsmMode)) { 1533 return loadNodeModuleFromDirectory(extensions, candidate, onlyRecordFailures, state, considerPackageJson); 1534 } 1535 return undefined; 1536} 1537 1538/** @internal */ 1539export const nodeModulesPathPart = "/node_modules/"; 1540 1541/** @internal */ 1542export function pathContainsNodeModules(path: string): boolean { 1543 return stringContains(path, nodeModulesPathPart); 1544} 1545 1546/** 1547 * This will be called on the successfully resolved path from `loadModuleFromFile`. 1548 * (Not needed for `loadModuleFromNodeModules` as that looks up the `package.json` or `oh-package.json5` as part of resolution.) 1549 * 1550 * packageDirectory is the directory of the package itself. 1551 * For `blah/node_modules/foo/index.d.ts` this is packageDirectory: "foo" 1552 * For `/node_modules/foo/bar.d.ts` this is packageDirectory: "foo" 1553 * For `/node_modules/@types/foo/bar/index.d.ts` this is packageDirectory: "@types/foo" 1554 * For `/node_modules/foo/bar/index.d.ts` this is packageDirectory: "foo" 1555 */ 1556export function parseModuleFromPath(resolved: string, packageManagerType?: string): string | undefined { 1557 const modulesPathPart = getModulePathPartByPMType(packageManagerType); 1558 const path = normalizePath(resolved); 1559 const idx = path.lastIndexOf(modulesPathPart); 1560 if (idx === -1) { 1561 return undefined; 1562 } 1563 1564 const indexAfterModules = idx + modulesPathPart.length; 1565 let indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterModules); 1566 if (path.charCodeAt(indexAfterModules) === CharacterCodes.at) { 1567 indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName); 1568 } 1569 return path.slice(0, indexAfterPackageName); 1570} 1571 1572function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number { 1573 const nextSeparatorIndex = path.indexOf(directorySeparator, prevSeparatorIndex + 1); 1574 return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex; 1575} 1576 1577function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined { 1578 return noPackageId(loadModuleFromFile(extensions, candidate, onlyRecordFailures, state)); 1579} 1580 1581/** 1582 * @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 1583 * 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. 1584 */ 1585function loadModuleFromFile(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { 1586 if (extensions === Extensions.Json || extensions === Extensions.TSConfig) { 1587 const extensionLess = tryRemoveExtension(candidate, Extension.Json); 1588 const extension = extensionLess ? candidate.substring(extensionLess.length) : ""; 1589 return (extensionLess === undefined && extensions === Extensions.Json) ? undefined : tryAddingExtensions(extensionLess || candidate, extensions, extension, onlyRecordFailures, state); 1590 } 1591 1592 // esm mode resolutions don't include automatic extension lookup (without additional flags, at least) 1593 if (!(state.features & NodeResolutionFeatures.EsmMode)) { 1594 // First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts" 1595 const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, "", onlyRecordFailures, state); 1596 if (resolvedByAddingExtension) { 1597 return resolvedByAddingExtension; 1598 } 1599 } 1600 1601 return loadModuleFromFileNoImplicitExtensions(extensions, candidate, onlyRecordFailures, state); 1602} 1603 1604function loadModuleFromFileNoImplicitExtensions(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { 1605 // If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one; 1606 // e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" 1607 if (hasJSFileExtension(candidate) || (fileExtensionIs(candidate, Extension.Json) && state.compilerOptions.resolveJsonModule)) { 1608 const extensionless = removeFileExtension(candidate); 1609 const extension = candidate.substring(extensionless.length); 1610 if (state.traceEnabled) { 1611 trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension); 1612 } 1613 return tryAddingExtensions(extensionless, extensions, extension, onlyRecordFailures, state); 1614 } 1615} 1616 1617function loadJSOrExactTSFileName(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { 1618 if ((extensions === Extensions.TypeScript || extensions === Extensions.DtsOnly) && fileExtensionIsOneOf(candidate, supportedTSExtensionsFlat)) { 1619 const result = tryFile(candidate, onlyRecordFailures, state); 1620 return result !== undefined ? { path: candidate, ext: tryExtractTSExtension(candidate) as Extension } : undefined; 1621 } 1622 1623 return loadModuleFromFileNoImplicitExtensions(extensions, candidate, onlyRecordFailures, state); 1624} 1625 1626/** Try to return an existing file that adds one of the `extensions` to `candidate`. */ 1627function tryAddingExtensions(candidate: string, extensions: Extensions, originalExtension: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { 1628 if (!onlyRecordFailures) { 1629 // check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing 1630 const directory = getDirectoryPath(candidate); 1631 if (directory) { 1632 onlyRecordFailures = !directoryProbablyExists(directory, state.host); 1633 } 1634 } 1635 1636 switch (extensions) { 1637 case Extensions.DtsOnly: 1638 switch (originalExtension) { 1639 case Extension.Mjs: 1640 case Extension.Mts: 1641 case Extension.Dmts: 1642 return tryExtension(Extension.Dmts); 1643 case Extension.Cjs: 1644 case Extension.Cts: 1645 case Extension.Dcts: 1646 return tryExtension(Extension.Dcts); 1647 case Extension.Json: 1648 candidate += Extension.Json; 1649 return tryExtension(Extension.Dts); 1650 default: return state.compilerOptions.ets ? tryExtension(Extension.Dets) || tryExtension(Extension.Dts) : tryExtension(Extension.Dts); 1651 } 1652 case Extensions.TypeScript: 1653 case Extensions.TsOnly: 1654 const useDts = extensions === Extensions.TypeScript; 1655 switch (originalExtension) { 1656 case Extension.Mjs: 1657 case Extension.Mts: 1658 case Extension.Dmts: 1659 return tryExtension(Extension.Mts) || (useDts ? tryExtension(Extension.Dmts) : undefined); 1660 case Extension.Cjs: 1661 case Extension.Cts: 1662 case Extension.Dcts: 1663 return tryExtension(Extension.Cts) || (useDts ? tryExtension(Extension.Dcts) : undefined); 1664 case Extension.Json: 1665 candidate += Extension.Json; 1666 return useDts ? tryExtension(Extension.Dts) : undefined; 1667 default: 1668 if (state.compilerOptions.ets) { 1669 return tryExtension(Extension.Ets) || tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || (useDts ? tryExtension(Extension.Dets) || tryExtension(Extension.Dts) : undefined); 1670 } 1671 else { 1672 return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || (useDts ? tryExtension(Extension.Dts) || tryExtension(Extension.Ets) || tryExtension(Extension.Dets) : undefined); 1673 } 1674 } 1675 case Extensions.JavaScript: 1676 switch (originalExtension) { 1677 case Extension.Mjs: 1678 case Extension.Mts: 1679 case Extension.Dmts: 1680 return tryExtension(Extension.Mjs); 1681 case Extension.Cjs: 1682 case Extension.Cts: 1683 case Extension.Dcts: 1684 return tryExtension(Extension.Cjs); 1685 case Extension.Json: 1686 return tryExtension(Extension.Json); 1687 default: 1688 return tryExtension(Extension.Js) || tryExtension(Extension.Jsx); 1689 } 1690 case Extensions.TSConfig: 1691 case Extensions.Json: 1692 return tryExtension(Extension.Json); 1693 } 1694 1695 function tryExtension(ext: Extension): PathAndExtension | undefined { 1696 const path = tryFile(candidate + ext, onlyRecordFailures, state); 1697 return path === undefined ? undefined : { path, ext }; 1698 } 1699} 1700 1701/** Return the file if it exists. */ 1702function tryFile(fileName: string, onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined { 1703 if (!state.compilerOptions.moduleSuffixes?.length) { 1704 return tryFileLookup(fileName, onlyRecordFailures, state); 1705 } 1706 1707 const ext = tryGetExtensionFromPath(fileName) ?? ""; 1708 const fileNameNoExtension = ext ? removeExtension(fileName, ext) : fileName; 1709 return forEach(state.compilerOptions.moduleSuffixes, suffix => tryFileLookup(fileNameNoExtension + suffix + ext, onlyRecordFailures, state)); 1710} 1711 1712function tryFileLookup(fileName: string, onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined { 1713 if (!onlyRecordFailures) { 1714 if (state.host.fileExists(fileName)) { 1715 if (state.traceEnabled) { 1716 trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName); 1717 } 1718 return fileName; 1719 } 1720 else { 1721 if (state.traceEnabled) { 1722 trace(state.host, Diagnostics.File_0_does_not_exist, fileName); 1723 } 1724 } 1725 } 1726 state.failedLookupLocations.push(fileName); 1727 return undefined; 1728} 1729 1730function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) { 1731 const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, onlyRecordFailures, state) : undefined; 1732 const packageJsonContent = packageInfo && packageInfo.contents.packageJsonContent; 1733 const versionPaths = packageInfo && packageInfo.contents.versionPaths; 1734 return withPackageId(packageInfo, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths)); 1735} 1736 1737/** @internal */ 1738export function getEntrypointsFromPackageJsonInfo( 1739 packageJsonInfo: PackageJsonInfo, 1740 options: CompilerOptions, 1741 host: ModuleResolutionHost, 1742 cache: ModuleResolutionCache | undefined, 1743 resolveJs?: boolean, 1744): string[] | false { 1745 if (!resolveJs && packageJsonInfo.contents.resolvedEntrypoints !== undefined) { 1746 // Cached value excludes resolutions to JS files - those could be 1747 // cached separately, but they're used rarely. 1748 return packageJsonInfo.contents.resolvedEntrypoints; 1749 } 1750 1751 let entrypoints: string[] | undefined; 1752 const extensions = resolveJs ? Extensions.JavaScript : Extensions.TypeScript; 1753 const features = getDefaultNodeResolutionFeatures(options); 1754 const requireState = getTemporaryModuleResolutionState(cache?.getPackageJsonInfoCache(), host, options); 1755 requireState.conditions = ["node", "require", "types"]; 1756 requireState.requestContainingDirectory = packageJsonInfo.packageDirectory; 1757 const requireResolution = loadNodeModuleFromDirectoryWorker( 1758 extensions, 1759 packageJsonInfo.packageDirectory, 1760 /*onlyRecordFailures*/ false, 1761 requireState, 1762 packageJsonInfo.contents.packageJsonContent, 1763 packageJsonInfo.contents.versionPaths); 1764 entrypoints = append(entrypoints, requireResolution?.path); 1765 1766 if (features & NodeResolutionFeatures.Exports && packageJsonInfo.contents.packageJsonContent.exports) { 1767 for (const conditions of [["node", "import", "types"], ["node", "require", "types"]]) { 1768 const exportState = { ...requireState, failedLookupLocations: [], conditions }; 1769 const exportResolutions = loadEntrypointsFromExportMap( 1770 packageJsonInfo, 1771 packageJsonInfo.contents.packageJsonContent.exports, 1772 exportState, 1773 extensions); 1774 if (exportResolutions) { 1775 for (const resolution of exportResolutions) { 1776 entrypoints = appendIfUnique(entrypoints, resolution.path); 1777 } 1778 } 1779 } 1780 } 1781 1782 return packageJsonInfo.contents.resolvedEntrypoints = entrypoints || false; 1783} 1784 1785function loadEntrypointsFromExportMap( 1786 scope: PackageJsonInfo, 1787 exports: object, 1788 state: ModuleResolutionState, 1789 extensions: Extensions, 1790): PathAndExtension[] | undefined { 1791 let entrypoints: PathAndExtension[] | undefined; 1792 if (isArray(exports)) { 1793 for (const target of exports) { 1794 loadEntrypointsFromTargetExports(target); 1795 } 1796 } 1797 // eslint-disable-next-line no-null/no-null 1798 else if (typeof exports === "object" && exports !== null && allKeysStartWithDot(exports as MapLike<unknown>)) { 1799 for (const key in exports) { 1800 loadEntrypointsFromTargetExports((exports as MapLike<unknown>)[key]); 1801 } 1802 } 1803 else { 1804 loadEntrypointsFromTargetExports(exports); 1805 } 1806 return entrypoints; 1807 1808 function loadEntrypointsFromTargetExports(target: unknown): boolean | undefined { 1809 if (typeof target === "string" && startsWith(target, "./") && target.indexOf("*") === -1) { 1810 const partsAfterFirst = getPathComponents(target).slice(2); 1811 if (partsAfterFirst.indexOf("..") >= 0 || partsAfterFirst.indexOf(".") >= 0 || partsAfterFirst.indexOf("node_modules") >= 0 || partsAfterFirst.indexOf("oh_modules") >= 0) { 1812 return false; 1813 } 1814 const resolvedTarget = combinePaths(scope.packageDirectory, target); 1815 const finalPath = getNormalizedAbsolutePath(resolvedTarget, state.host.getCurrentDirectory?.()); 1816 const result = loadJSOrExactTSFileName(extensions, finalPath, /*recordOnlyFailures*/ false, state); 1817 if (result) { 1818 entrypoints = appendIfUnique(entrypoints, result, (a, b) => a.path === b.path); 1819 return true; 1820 } 1821 } 1822 else if (Array.isArray(target)) { 1823 for (const t of target) { 1824 const success = loadEntrypointsFromTargetExports(t); 1825 if (success) { 1826 return true; 1827 } 1828 } 1829 } 1830 // eslint-disable-next-line no-null/no-null 1831 else if (typeof target === "object" && target !== null) { 1832 return forEach(getOwnKeys(target as MapLike<unknown>), key => { 1833 if (key === "default" || contains(state.conditions, key) || isApplicableVersionedTypesKey(state.conditions, key)) { 1834 loadEntrypointsFromTargetExports((target as MapLike<unknown>)[key]); 1835 return true; 1836 } 1837 }); 1838 } 1839 } 1840} 1841 1842/** @internal */ 1843export function getTemporaryModuleResolutionState(packageJsonInfoCache: PackageJsonInfoCache | undefined, host: ModuleResolutionHost, options: CompilerOptions): ModuleResolutionState { 1844 return { 1845 host, 1846 compilerOptions: options, 1847 traceEnabled: isTraceEnabled(options, host), 1848 failedLookupLocations: noopPush, 1849 affectingLocations: noopPush, 1850 packageJsonInfoCache, 1851 features: NodeResolutionFeatures.None, 1852 conditions: emptyArray, 1853 requestContainingDirectory: undefined, 1854 reportDiagnostic: noop 1855 }; 1856} 1857 1858/** @internal */ 1859export interface PackageJsonInfo { 1860 packageDirectory: string; 1861 contents: PackageJsonInfoContents; 1862} 1863/** @internal */ 1864export interface PackageJsonInfoContents { 1865 packageJsonContent: PackageJsonPathFields; 1866 versionPaths: VersionPaths | undefined; 1867 /** false: resolved to nothing. undefined: not yet resolved */ 1868 resolvedEntrypoints: string[] | false | undefined; 1869} 1870 1871/** 1872 * A function for locating the package.json scope for a given path 1873 * 1874 * @internal 1875 */ 1876 export function getPackageScopeForPath(fileName: string, state: ModuleResolutionState): PackageJsonInfo | undefined { 1877 const parts = getPathComponents(fileName); 1878 parts.pop(); 1879 while (parts.length > 0) { 1880 const pkg = getPackageJsonInfo(getPathFromPathComponents(parts), /*onlyRecordFailures*/ false, state); 1881 if (pkg) { 1882 return pkg; 1883 } 1884 parts.pop(); 1885 } 1886 return undefined; 1887} 1888 1889/** @internal */ 1890export function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined { 1891 const { host, traceEnabled } = state; 1892 const packageJsonPath = combinePaths(packageDirectory, getPackageJsonByPMType(state.compilerOptions.packageManagerType)); 1893 if (onlyRecordFailures) { 1894 state.failedLookupLocations.push(packageJsonPath); 1895 return undefined; 1896 } 1897 1898 const existing = state.packageJsonInfoCache?.getPackageJsonInfo(packageJsonPath); 1899 if (existing !== undefined) { 1900 if (typeof existing !== "boolean") { 1901 if (traceEnabled) trace(host, Diagnostics.File_0_exists_according_to_earlier_cached_lookups, packageJsonPath); 1902 state.affectingLocations.push(packageJsonPath); 1903 return existing.packageDirectory === packageDirectory ? 1904 existing : 1905 { packageDirectory, contents: existing.contents }; 1906 } 1907 else { 1908 if (existing && traceEnabled) trace(host, Diagnostics.File_0_does_not_exist_according_to_earlier_cached_lookups, packageJsonPath); 1909 state.failedLookupLocations.push(packageJsonPath); 1910 return undefined; 1911 } 1912 } 1913 const directoryExists = directoryProbablyExists(packageDirectory, host); 1914 if (directoryExists && host.fileExists(packageJsonPath)) { 1915 const isOHModules: boolean = isOhpm(state.compilerOptions.packageManagerType); 1916 const packageJsonContent = isOHModules ? require("json5").parse(host.readFile!(packageJsonPath)!) : readJson(packageJsonPath, host) as PackageJson; 1917 if (traceEnabled) { 1918 const message = isOHModules ? Diagnostics.Found_oh_package_json5_at_0 : Diagnostics.Found_package_json_at_0; 1919 trace(host, message, packageJsonPath); 1920 } 1921 const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state); 1922 const result: PackageJsonInfo = { packageDirectory, contents: { packageJsonContent, versionPaths, resolvedEntrypoints: undefined } }; 1923 state.packageJsonInfoCache?.setPackageJsonInfo(packageJsonPath, result); 1924 state.affectingLocations.push(packageJsonPath); 1925 return result; 1926 } 1927 else { 1928 if (directoryExists && traceEnabled) { 1929 trace(host, Diagnostics.File_0_does_not_exist, packageJsonPath); 1930 } 1931 state.packageJsonInfoCache?.setPackageJsonInfo(packageJsonPath, directoryExists); 1932 // record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results 1933 state.failedLookupLocations.push(packageJsonPath); 1934 } 1935} 1936 1937function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, jsonContent: PackageJsonPathFields | undefined, versionPaths: VersionPaths | undefined): PathAndExtension | undefined { 1938 let packageFile: string | undefined; 1939 if (jsonContent) { 1940 switch (extensions) { 1941 case Extensions.JavaScript: 1942 case Extensions.Json: 1943 case Extensions.TsOnly: 1944 packageFile = readPackageJsonMainField(jsonContent, candidate, state); 1945 break; 1946 case Extensions.TypeScript: 1947 // When resolving typescript modules, try resolving using main field as well 1948 packageFile = readPackageJsonTypesFields(jsonContent, candidate, state) || readPackageJsonMainField(jsonContent, candidate, state); 1949 break; 1950 case Extensions.DtsOnly: 1951 packageFile = readPackageJsonTypesFields(jsonContent, candidate, state); 1952 break; 1953 case Extensions.TSConfig: 1954 packageFile = readPackageJsonTSConfigField(jsonContent, candidate, state); 1955 break; 1956 default: 1957 return Debug.assertNever(extensions); 1958 } 1959 } 1960 1961 const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => { 1962 const fromFile = tryFile(candidate, onlyRecordFailures, state); 1963 if (fromFile) { 1964 const resolved = resolvedIfExtensionMatches(extensions, fromFile); 1965 if (resolved) { 1966 return noPackageId(resolved); 1967 } 1968 if (state.traceEnabled) { 1969 trace(state.host, Diagnostics.File_0_has_an_unsupported_extension_so_skipping_it, fromFile); 1970 } 1971 } 1972 1973 // Even if extensions is DtsOnly, we can still look up a .ts file as a result of package.json "types" 1974 const nextExtensions = extensions === Extensions.DtsOnly ? Extensions.TypeScript : extensions; 1975 // Don't do package.json lookup recursively, because Node.js' package lookup doesn't. 1976 1977 // Disable `EsmMode` for the resolution of the package path for cjs-mode packages (so the `main` field can omit extensions) 1978 // (technically it only emits a deprecation warning in esm packages right now, but that's probably 1979 // enough to mean we don't need to support it) 1980 const features = state.features; 1981 if (jsonContent?.type !== "module") { 1982 state.features &= ~NodeResolutionFeatures.EsmMode; 1983 } 1984 const result = nodeLoadModuleByRelativeName(nextExtensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ false); 1985 state.features = features; 1986 return result; 1987 }; 1988 1989 const onlyRecordFailuresForPackageFile = packageFile ? !directoryProbablyExists(getDirectoryPath(packageFile), state.host) : undefined; 1990 const onlyRecordFailuresForIndex = onlyRecordFailures || !directoryProbablyExists(candidate, state.host); 1991 const indexPath = combinePaths(candidate, extensions === Extensions.TSConfig ? "tsconfig" : "index"); 1992 1993 if (versionPaths && (!packageFile || containsPath(candidate, packageFile))) { 1994 const moduleName = getRelativePathFromDirectory(candidate, packageFile || indexPath, /*ignoreCase*/ false); 1995 if (state.traceEnabled) { 1996 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); 1997 } 1998 const result = tryLoadModuleUsingPaths(extensions, moduleName, candidate, versionPaths.paths, /*pathPatterns*/ undefined, loader, onlyRecordFailuresForPackageFile || onlyRecordFailuresForIndex, state); 1999 if (result) { 2000 return removeIgnoredPackageId(result.value); 2001 } 2002 } 2003 2004 // It won't have a `packageId` set, because we disabled `considerPackageJson`. 2005 const packageFileResult = packageFile && removeIgnoredPackageId(loader(extensions, packageFile, onlyRecordFailuresForPackageFile!, state)); 2006 if (packageFileResult) return packageFileResult; 2007 2008 // esm mode resolutions don't do package `index` lookups 2009 if (!(state.features & NodeResolutionFeatures.EsmMode)) { 2010 return loadModuleFromFile(extensions, indexPath, onlyRecordFailuresForIndex, state); 2011 } 2012} 2013 2014/** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */ 2015function resolvedIfExtensionMatches(extensions: Extensions, path: string): PathAndExtension | undefined { 2016 const ext = tryGetExtensionFromPath(path); 2017 return ext !== undefined && extensionIsOk(extensions, ext) ? { path, ext } : undefined; 2018} 2019 2020/** True if `extension` is one of the supported `extensions`. */ 2021function extensionIsOk(extensions: Extensions, extension: Extension): boolean { 2022 switch (extensions) { 2023 case Extensions.JavaScript: 2024 return extension === Extension.Js || extension === Extension.Jsx || extension === Extension.Mjs || extension === Extension.Cjs; 2025 case Extensions.TSConfig: 2026 case Extensions.Json: 2027 return extension === Extension.Json; 2028 case Extensions.TypeScript: 2029 return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Mts || extension === Extension.Cts || extension === Extension.Dts || extension === Extension.Dmts || extension === Extension.Dcts || extension === Extension.Ets || extension === Extension.Dets; 2030 case Extensions.TsOnly: 2031 return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Mts || extension === Extension.Cts || extension === Extension.Ets; 2032 case Extensions.DtsOnly: 2033 return extension === Extension.Dts || extension === Extension.Dmts || extension === Extension.Dcts || extension === Extension.Dets; 2034 } 2035} 2036 2037/** @internal */ 2038export function parsePackageName(moduleName: string): { packageName: string, rest: string } { 2039 let idx = moduleName.indexOf(directorySeparator); 2040 if (moduleName[0] === "@") { 2041 idx = moduleName.indexOf(directorySeparator, idx + 1); 2042 } 2043 return idx === -1 ? { packageName: moduleName, rest: "" } : { packageName: moduleName.slice(0, idx), rest: moduleName.slice(idx + 1) }; 2044} 2045 2046/** @internal */ 2047export function allKeysStartWithDot(obj: MapLike<unknown>) { 2048 return every(getOwnKeys(obj), k => startsWith(k, ".")); 2049} 2050 2051function noKeyStartsWithDot(obj: MapLike<unknown>) { 2052 return !some(getOwnKeys(obj), k => startsWith(k, ".")); 2053} 2054 2055function loadModuleFromSelfNameReference(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> { 2056 const directoryPath = getNormalizedAbsolutePath(combinePaths(directory, "dummy"), state.host.getCurrentDirectory?.()); 2057 const scope = getPackageScopeForPath(directoryPath, state); 2058 if (!scope || !scope.contents.packageJsonContent.exports) { 2059 return undefined; 2060 } 2061 if (typeof scope.contents.packageJsonContent.name !== "string") { 2062 return undefined; 2063 } 2064 const parts = getPathComponents(moduleName); // unrooted paths should have `""` as their 0th entry 2065 const nameParts = getPathComponents(scope.contents.packageJsonContent.name); 2066 if (!every(nameParts, (p, i) => parts[i] === p)) { 2067 return undefined; 2068 } 2069 const trailingParts = parts.slice(nameParts.length); 2070 return loadModuleFromExports(scope, extensions, !length(trailingParts) ? "." : `.${directorySeparator}${trailingParts.join(directorySeparator)}`, state, cache, redirectedReference); 2071} 2072 2073function loadModuleFromExports(scope: PackageJsonInfo, extensions: Extensions, subpath: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> { 2074 if (!scope.contents.packageJsonContent.exports) { 2075 return undefined; 2076 } 2077 2078 if (subpath === ".") { 2079 let mainExport; 2080 if (typeof scope.contents.packageJsonContent.exports === "string" || Array.isArray(scope.contents.packageJsonContent.exports) || (typeof scope.contents.packageJsonContent.exports === "object" && noKeyStartsWithDot(scope.contents.packageJsonContent.exports as MapLike<unknown>))) { 2081 mainExport = scope.contents.packageJsonContent.exports; 2082 } 2083 else if (hasProperty(scope.contents.packageJsonContent.exports as MapLike<unknown>, ".")) { 2084 mainExport = (scope.contents.packageJsonContent.exports as MapLike<unknown>)["."]; 2085 } 2086 if (mainExport) { 2087 const loadModuleFromTargetImportOrExport = getLoadModuleFromTargetImportOrExport(extensions, state, cache, redirectedReference, subpath, scope, /*isImports*/ false); 2088 return loadModuleFromTargetImportOrExport(mainExport, "", /*pattern*/ false, "."); 2089 } 2090 } 2091 else if (allKeysStartWithDot(scope.contents.packageJsonContent.exports as MapLike<unknown>)) { 2092 if (typeof scope.contents.packageJsonContent.exports !== "object") { 2093 if (state.traceEnabled) { 2094 trace(state.host, Diagnostics.Export_specifier_0_does_not_exist_in_package_json_scope_at_path_1, subpath, scope.packageDirectory); 2095 } 2096 return toSearchResult(/*value*/ undefined); 2097 } 2098 const result = loadModuleFromImportsOrExports(extensions, state, cache, redirectedReference, subpath, scope.contents.packageJsonContent.exports, scope, /*isImports*/ false); 2099 if (result) { 2100 return result; 2101 } 2102 } 2103 2104 if (state.traceEnabled) { 2105 trace(state.host, Diagnostics.Export_specifier_0_does_not_exist_in_package_json_scope_at_path_1, subpath, scope.packageDirectory); 2106 } 2107 return toSearchResult(/*value*/ undefined); 2108} 2109 2110function loadModuleFromImports(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> { 2111 if (moduleName === "#" || startsWith(moduleName, "#/")) { 2112 if (state.traceEnabled) { 2113 trace(state.host, Diagnostics.Invalid_import_specifier_0_has_no_possible_resolutions, moduleName); 2114 } 2115 return toSearchResult(/*value*/ undefined); 2116 } 2117 const directoryPath = getNormalizedAbsolutePath(combinePaths(directory, "dummy"), state.host.getCurrentDirectory?.()); 2118 const scope = getPackageScopeForPath(directoryPath, state); 2119 if (!scope) { 2120 if (state.traceEnabled) { 2121 trace(state.host, Diagnostics.Directory_0_has_no_containing_package_json_scope_Imports_will_not_resolve, directoryPath); 2122 } 2123 return toSearchResult(/*value*/ undefined); 2124 } 2125 if (!scope.contents.packageJsonContent.imports) { 2126 if (state.traceEnabled) { 2127 trace(state.host, Diagnostics.package_json_scope_0_has_no_imports_defined, scope.packageDirectory); 2128 } 2129 return toSearchResult(/*value*/ undefined); 2130 } 2131 2132 const result = loadModuleFromImportsOrExports(extensions, state, cache, redirectedReference, moduleName, scope.contents.packageJsonContent.imports, scope, /*isImports*/ true); 2133 if (result) { 2134 return result; 2135 } 2136 2137 if (state.traceEnabled) { 2138 trace(state.host, Diagnostics.Import_specifier_0_does_not_exist_in_package_json_scope_at_path_1, moduleName, scope.packageDirectory); 2139 } 2140 return toSearchResult(/*value*/ undefined); 2141} 2142 2143/** 2144 * @internal 2145 * From https://github.com/nodejs/node/blob/8f39f51cbbd3b2de14b9ee896e26421cc5b20121/lib/internal/modules/esm/resolve.js#L722 - 2146 * "longest" has some nuance as to what "longest" means in the presence of pattern trailers 2147 */ 2148export function comparePatternKeys(a: string, b: string) { 2149 const aPatternIndex = a.indexOf("*"); 2150 const bPatternIndex = b.indexOf("*"); 2151 const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; 2152 const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; 2153 if (baseLenA > baseLenB) return -1; 2154 if (baseLenB > baseLenA) return 1; 2155 if (aPatternIndex === -1) return 1; 2156 if (bPatternIndex === -1) return -1; 2157 if (a.length > b.length) return -1; 2158 if (b.length > a.length) return 1; 2159 return 0; 2160} 2161 2162function loadModuleFromImportsOrExports(extensions: Extensions, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined, moduleName: string, lookupTable: object, scope: PackageJsonInfo, isImports: boolean): SearchResult<Resolved> | undefined { 2163 const loadModuleFromTargetImportOrExport = getLoadModuleFromTargetImportOrExport(extensions, state, cache, redirectedReference, moduleName, scope, isImports); 2164 2165 if (!endsWith(moduleName, directorySeparator) && moduleName.indexOf("*") === -1 && hasProperty(lookupTable, moduleName)) { 2166 const target = (lookupTable as {[idx: string]: unknown})[moduleName]; 2167 return loadModuleFromTargetImportOrExport(target, /*subpath*/ "", /*pattern*/ false, moduleName); 2168 } 2169 const expandingKeys = sort(filter(getOwnKeys(lookupTable as MapLike<unknown>), k => k.indexOf("*") !== -1 || endsWith(k, "/")), comparePatternKeys); 2170 for (const potentialTarget of expandingKeys) { 2171 if (state.features & NodeResolutionFeatures.ExportsPatternTrailers && matchesPatternWithTrailer(potentialTarget, moduleName)) { 2172 const target = (lookupTable as {[idx: string]: unknown})[potentialTarget]; 2173 const starPos = potentialTarget.indexOf("*"); 2174 const subpath = moduleName.substring(potentialTarget.substring(0, starPos).length, moduleName.length - (potentialTarget.length - 1 - starPos)); 2175 return loadModuleFromTargetImportOrExport(target, subpath, /*pattern*/ true, potentialTarget); 2176 } 2177 else if (endsWith(potentialTarget, "*") && startsWith(moduleName, potentialTarget.substring(0, potentialTarget.length - 1))) { 2178 const target = (lookupTable as {[idx: string]: unknown})[potentialTarget]; 2179 const subpath = moduleName.substring(potentialTarget.length - 1); 2180 return loadModuleFromTargetImportOrExport(target, subpath, /*pattern*/ true, potentialTarget); 2181 } 2182 else if (startsWith(moduleName, potentialTarget)) { 2183 const target = (lookupTable as {[idx: string]: unknown})[potentialTarget]; 2184 const subpath = moduleName.substring(potentialTarget.length); 2185 return loadModuleFromTargetImportOrExport(target, subpath, /*pattern*/ false, potentialTarget); 2186 } 2187 } 2188 2189 function matchesPatternWithTrailer(target: string, name: string) { 2190 if (endsWith(target, "*")) return false; // handled by next case in loop 2191 const starPos = target.indexOf("*"); 2192 if (starPos === -1) return false; // handled by last case in loop 2193 return startsWith(name, target.substring(0, starPos)) && endsWith(name, target.substring(starPos + 1)); 2194 } 2195} 2196 2197/** 2198 * Gets the self-recursive function specialized to retrieving the targeted import/export element for the given resolution configuration 2199 */ 2200function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined, moduleName: string, scope: PackageJsonInfo, isImports: boolean) { 2201 return loadModuleFromTargetImportOrExport; 2202 function loadModuleFromTargetImportOrExport(target: unknown, subpath: string, pattern: boolean, key: string): SearchResult<Resolved> | undefined { 2203 if (typeof target === "string") { 2204 if (!pattern && subpath.length > 0 && !endsWith(target, "/")) { 2205 if (state.traceEnabled) { 2206 trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); 2207 } 2208 return toSearchResult(/*value*/ undefined); 2209 } 2210 if (!startsWith(target, "./")) { 2211 if (isImports && !startsWith(target, "../") && !startsWith(target, "/") && !isRootedDiskPath(target)) { 2212 const combinedLookup = pattern ? target.replace(/\*/g, subpath) : target + subpath; 2213 traceIfEnabled(state, Diagnostics.Using_0_subpath_1_with_target_2, "imports", key, combinedLookup); 2214 traceIfEnabled(state, Diagnostics.Resolving_module_0_from_1, combinedLookup, scope.packageDirectory + "/"); 2215 const result = nodeModuleNameResolverWorker(state.features, combinedLookup, scope.packageDirectory + "/", state.compilerOptions, state.host, cache, [extensions], redirectedReference); 2216 return toSearchResult(result.resolvedModule ? { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId, originalPath: result.resolvedModule.originalPath } : undefined); 2217 } 2218 if (state.traceEnabled) { 2219 trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); 2220 } 2221 return toSearchResult(/*value*/ undefined); 2222 } 2223 const parts = pathIsRelative(target) ? getPathComponents(target).slice(1) : getPathComponents(target); 2224 const partsAfterFirst = parts.slice(1); 2225 if (partsAfterFirst.indexOf("..") >= 0 || partsAfterFirst.indexOf(".") >= 0 || partsAfterFirst.indexOf("node_modules") >= 0 || partsAfterFirst.indexOf("oh_modules") >= 0) { 2226 if (state.traceEnabled) { 2227 trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); 2228 } 2229 return toSearchResult(/*value*/ undefined); 2230 } 2231 const resolvedTarget = combinePaths(scope.packageDirectory, target); 2232 // TODO: Assert that `resolvedTarget` is actually within the package directory? That's what the spec says.... but I'm not sure we need 2233 // to be in the business of validating everyone's import and export map correctness. 2234 const subpathParts = getPathComponents(subpath); 2235 if (subpathParts.indexOf("..") >= 0 || subpathParts.indexOf(".") >= 0 || subpathParts.indexOf("node_modules") >= 0 || subpathParts.indexOf("oh_modules") >= 0) { 2236 if (state.traceEnabled) { 2237 trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); 2238 } 2239 return toSearchResult(/*value*/ undefined); 2240 } 2241 2242 if (state.traceEnabled) { 2243 trace(state.host, 2244 Diagnostics.Using_0_subpath_1_with_target_2, 2245 isImports ? "imports" : "exports", 2246 key, 2247 pattern ? target.replace(/\*/g, subpath) : target + subpath); 2248 } 2249 const finalPath = toAbsolutePath(pattern ? resolvedTarget.replace(/\*/g, subpath) : resolvedTarget + subpath); 2250 const inputLink = tryLoadInputFileForPath(finalPath, subpath, combinePaths(scope.packageDirectory, "package.json"), isImports); 2251 if (inputLink) return inputLink; 2252 return toSearchResult(withPackageId(scope, loadJSOrExactTSFileName(extensions, finalPath, /*onlyRecordFailures*/ false, state))); 2253 } 2254 else if (typeof target === "object" && target !== null) { // eslint-disable-line no-null/no-null 2255 if (!Array.isArray(target)) { 2256 for (const condition of getOwnKeys(target as MapLike<unknown>)) { 2257 if (condition === "default" || state.conditions.indexOf(condition) >= 0 || isApplicableVersionedTypesKey(state.conditions, condition)) { 2258 traceIfEnabled(state, Diagnostics.Matched_0_condition_1, isImports ? "imports" : "exports", condition); 2259 const subTarget = (target as MapLike<unknown>)[condition]; 2260 const result = loadModuleFromTargetImportOrExport(subTarget, subpath, pattern, key); 2261 if (result) { 2262 return result; 2263 } 2264 } 2265 else { 2266 traceIfEnabled(state, Diagnostics.Saw_non_matching_condition_0, condition); 2267 } 2268 } 2269 return undefined; 2270 } 2271 else { 2272 if (!length(target)) { 2273 if (state.traceEnabled) { 2274 trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); 2275 } 2276 return toSearchResult(/*value*/ undefined); 2277 } 2278 for (const elem of target) { 2279 const result = loadModuleFromTargetImportOrExport(elem, subpath, pattern, key); 2280 if (result) { 2281 return result; 2282 } 2283 } 2284 } 2285 } 2286 else if (target === null) { // eslint-disable-line no-null/no-null 2287 if (state.traceEnabled) { 2288 trace(state.host, Diagnostics.package_json_scope_0_explicitly_maps_specifier_1_to_null, scope.packageDirectory, moduleName); 2289 } 2290 return toSearchResult(/*value*/ undefined); 2291 } 2292 if (state.traceEnabled) { 2293 trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); 2294 } 2295 return toSearchResult(/*value*/ undefined); 2296 2297 function toAbsolutePath(path: string): string; 2298 function toAbsolutePath(path: string | undefined): string | undefined; 2299 function toAbsolutePath(path: string | undefined): string | undefined { 2300 if (path === undefined) return path; 2301 return getNormalizedAbsolutePath(path, state.host.getCurrentDirectory?.()); 2302 } 2303 2304 function combineDirectoryPath(root: string, dir: string) { 2305 return ensureTrailingDirectorySeparator(combinePaths(root, dir)); 2306 } 2307 2308 function useCaseSensitiveFileNames() { 2309 return !state.host.useCaseSensitiveFileNames ? true : 2310 typeof state.host.useCaseSensitiveFileNames === "boolean" ? state.host.useCaseSensitiveFileNames : 2311 state.host.useCaseSensitiveFileNames(); 2312 } 2313 2314 function tryLoadInputFileForPath(finalPath: string, entry: string, packagePath: string, isImports: boolean) { 2315 // Replace any references to outputs for files in the program with the input files to support package self-names used with outDir 2316 // PROBLEM: We don't know how to calculate the output paths yet, because the "common source directory" we use as the base of the file structure 2317 // we reproduce into the output directory is based on the set of input files, which we're still in the process of traversing and resolving! 2318 // _Given that_, we have to guess what the base of the output directory is (obviously the user wrote the export map, so has some idea what it is!). 2319 // We are going to probe _so many_ possible paths. We limit where we'll do this to try to reduce the possibilities of false positive lookups. 2320 if ((extensions === Extensions.TypeScript || extensions === Extensions.JavaScript || extensions === Extensions.Json) 2321 && (state.compilerOptions.declarationDir || state.compilerOptions.outDir) 2322 && (finalPath.indexOf("/node_modules/") === -1 && finalPath.indexOf("/oh_modules/") === -1) 2323 && (state.compilerOptions.configFile ? containsPath(scope.packageDirectory, toAbsolutePath(state.compilerOptions.configFile.fileName), !useCaseSensitiveFileNames()) : true) 2324 ) { 2325 // So that all means we'll only try these guesses for files outside `node_modules` in a directory where the `package.json` and `tsconfig.json` are siblings. 2326 // Even with all that, we still don't know if the root of the output file structure will be (relative to the package file) 2327 // `.`, `./src` or any other deeper directory structure. (If project references are used, it's definitely `.` by fiat, so that should be pretty common.) 2328 2329 const getCanonicalFileName = hostGetCanonicalFileName({ useCaseSensitiveFileNames }); 2330 const commonSourceDirGuesses: string[] = []; 2331 // A `rootDir` compiler option strongly indicates the root location 2332 // A `composite` project is using project references and has it's common src dir set to `.`, so it shouldn't need to check any other locations 2333 if (state.compilerOptions.rootDir || (state.compilerOptions.composite && state.compilerOptions.configFilePath)) { 2334 const commonDir = toAbsolutePath(getCommonSourceDirectory(state.compilerOptions, () => [], state.host.getCurrentDirectory?.() || "", getCanonicalFileName)); 2335 commonSourceDirGuesses.push(commonDir); 2336 } 2337 else if (state.requestContainingDirectory) { 2338 // However without either of those set we're in the dark. Let's say you have 2339 // 2340 // ./tools/index.ts 2341 // ./src/index.ts 2342 // ./dist/index.js 2343 // ./package.json <-- references ./dist/index.js 2344 // ./tsconfig.json <-- loads ./src/index.ts 2345 // 2346 // How do we know `./src` is the common src dir, and not `./tools`, given only the `./dist` out dir and `./dist/index.js` filename? 2347 // Answer: We... don't. We know we're looking for an `index.ts` input file, but we have _no clue_ which subfolder it's supposed to be loaded from 2348 // without more context. 2349 // But we do have more context! Just a tiny bit more! We're resolving an import _for some other input file_! And that input file, too 2350 // must be inside the common source directory! So we propagate that tidbit of info all the way to here via state.requestContainingDirectory 2351 2352 const requestingFile = toAbsolutePath(combinePaths(state.requestContainingDirectory, "index.ts")); 2353 // And we can try every folder above the common folder for the request folder and the config/package base directory 2354 // This technically can be wrong - we may load ./src/index.ts when ./src/sub/index.ts was right because we don't 2355 // know if only `./src/sub` files were loaded by the program; but this has the best chance to be right of just about anything 2356 // else we have. And, given that we're about to load `./src/index.ts` because we choose it as likely correct, there will then 2357 // be a file outside of `./src/sub` in the program (the file we resolved to), making us de-facto right. So this fallback lookup 2358 // logic may influence what files are pulled in by self-names, which in turn influences the output path shape, but it's all 2359 // internally consistent so the paths should be stable so long as we prefer the "most general" (meaning: top-most-level directory) possible results first. 2360 const commonDir = toAbsolutePath(getCommonSourceDirectory(state.compilerOptions, () => [requestingFile, toAbsolutePath(packagePath)], state.host.getCurrentDirectory?.() || "", getCanonicalFileName)); 2361 commonSourceDirGuesses.push(commonDir); 2362 2363 let fragment = ensureTrailingDirectorySeparator(commonDir); 2364 while (fragment && fragment.length > 1) { 2365 const parts = getPathComponents(fragment); 2366 parts.pop(); // remove a directory 2367 const commonDir = getPathFromPathComponents(parts); 2368 commonSourceDirGuesses.unshift(commonDir); 2369 fragment = ensureTrailingDirectorySeparator(commonDir); 2370 } 2371 } 2372 if (commonSourceDirGuesses.length > 1) { 2373 state.reportDiagnostic(createCompilerDiagnostic( 2374 isImports 2375 ? Diagnostics.The_project_root_is_ambiguous_but_is_required_to_resolve_import_map_entry_0_in_file_1_Supply_the_rootDir_compiler_option_to_disambiguate 2376 : Diagnostics.The_project_root_is_ambiguous_but_is_required_to_resolve_export_map_entry_0_in_file_1_Supply_the_rootDir_compiler_option_to_disambiguate, 2377 entry === "" ? "." : entry, // replace empty string with `.` - the reverse of the operation done when entries are built - so main entrypoint errors don't look weird 2378 packagePath 2379 )); 2380 } 2381 for (const commonSourceDirGuess of commonSourceDirGuesses) { 2382 const candidateDirectories = getOutputDirectoriesForBaseDirectory(commonSourceDirGuess); 2383 for (const candidateDir of candidateDirectories) { 2384 if (containsPath(candidateDir, finalPath, !useCaseSensitiveFileNames())) { 2385 // The matched export is looking up something in either the out declaration or js dir, now map the written path back into the source dir and source extension 2386 const pathFragment = finalPath.slice(candidateDir.length + 1); // +1 to also remove directory seperator 2387 const possibleInputBase = combinePaths(commonSourceDirGuess, pathFragment); 2388 const jsAndDtsExtensions = [Extension.Mjs, Extension.Cjs, Extension.Js, Extension.Json, Extension.Dmts, Extension.Dcts, Extension.Dts]; 2389 for (const ext of jsAndDtsExtensions) { 2390 if (fileExtensionIs(possibleInputBase, ext)) { 2391 const inputExts = getPossibleOriginalInputExtensionForExtension(possibleInputBase); 2392 for (const possibleExt of inputExts) { 2393 const possibleInputWithInputExtension = changeAnyExtension(possibleInputBase, possibleExt, ext, !useCaseSensitiveFileNames()); 2394 if ((extensions === Extensions.TypeScript && hasJSFileExtension(possibleInputWithInputExtension)) || 2395 (extensions === Extensions.JavaScript && hasTSFileExtension(possibleInputWithInputExtension))) { 2396 continue; 2397 } 2398 if (state.host.fileExists(possibleInputWithInputExtension)) { 2399 return toSearchResult(withPackageId(scope, loadJSOrExactTSFileName(extensions, possibleInputWithInputExtension, /*onlyRecordFailures*/ false, state))); 2400 } 2401 } 2402 } 2403 } 2404 } 2405 } 2406 } 2407 } 2408 return undefined; 2409 2410 function getOutputDirectoriesForBaseDirectory(commonSourceDirGuess: string) { 2411 // Config file ouput paths are processed to be relative to the host's current directory, while 2412 // otherwise the paths are resolved relative to the common source dir the compiler puts together 2413 const currentDir = state.compilerOptions.configFile ? state.host.getCurrentDirectory?.() || "" : commonSourceDirGuess; 2414 const candidateDirectories = []; 2415 if (state.compilerOptions.declarationDir) { 2416 candidateDirectories.push(toAbsolutePath(combineDirectoryPath(currentDir, state.compilerOptions.declarationDir))); 2417 } 2418 if (state.compilerOptions.outDir && state.compilerOptions.outDir !== state.compilerOptions.declarationDir) { 2419 candidateDirectories.push(toAbsolutePath(combineDirectoryPath(currentDir, state.compilerOptions.outDir))); 2420 } 2421 return candidateDirectories; 2422 } 2423 } 2424 } 2425} 2426 2427/** @internal */ 2428export function isApplicableVersionedTypesKey(conditions: readonly string[], key: string) { 2429 if (conditions.indexOf("types") === -1) return false; // only apply versioned types conditions if the types condition is applied 2430 if (!startsWith(key, "types@")) return false; 2431 const range = VersionRange.tryParse(key.substring("types@".length)); 2432 if (!range) return false; 2433 return range.test(version); 2434} 2435 2436function loadModuleFromNearestNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> { 2437 return loadModuleFromNearestNodeModulesDirectoryWorker(extensions, moduleName, directory, state, /*typesScopeOnly*/ false, cache, redirectedReference); 2438} 2439 2440function loadModuleFromNearestModulesDirectoryTypesScope(moduleName: string, directory: string, state: ModuleResolutionState): SearchResult<Resolved> { 2441 // Extensions parameter here doesn't actually matter, because typesOnly ensures we're just doing @types lookup, which is always DtsOnly. 2442 return loadModuleFromNearestNodeModulesDirectoryWorker(Extensions.DtsOnly, moduleName, directory, state, /*typesScopeOnly*/ true, /*cache*/ undefined, /*redirectedReference*/ undefined); 2443} 2444 2445function loadModuleFromNearestNodeModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> { 2446 const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, state.features === 0 ? undefined : state.features & NodeResolutionFeatures.EsmMode ? ModuleKind.ESNext : ModuleKind.CommonJS, redirectedReference); 2447 const packageManagerType = state.compilerOptions.packageManagerType; 2448 const modulePathPart = getModuleByPMType(packageManagerType); 2449 return forEachAncestorDirectory(normalizeSlashes(directory), ancestorDirectory => { 2450 if (getBaseFileName(ancestorDirectory) !== modulePathPart) { 2451 const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, ancestorDirectory, state); 2452 if (resolutionFromCache) { 2453 return resolutionFromCache; 2454 } 2455 return toSearchResult(loadModuleFromImmediateNodeModulesDirectory(extensions, moduleName, ancestorDirectory, state, typesScopeOnly, cache, redirectedReference)); 2456 } 2457 }); 2458} 2459 2460function loadModuleFromImmediateNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): Resolved | undefined { 2461 const nodeModulesFolder = combinePaths(directory, getModuleByPMType(state.compilerOptions.packageManagerType)); 2462 const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host); 2463 if (!nodeModulesFolderExists && state.traceEnabled) { 2464 trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, nodeModulesFolder); 2465 } 2466 2467 const packageResult = typesScopeOnly ? undefined : loadModuleFromSpecificNodeModulesDirectory(extensions, moduleName, nodeModulesFolder, nodeModulesFolderExists, state, cache, redirectedReference); 2468 if (packageResult) { 2469 return packageResult; 2470 } 2471 if (extensions === Extensions.TypeScript || extensions === Extensions.DtsOnly) { 2472 const modulesAtTypes = combinePaths(nodeModulesFolder, "@types"); 2473 let nodeModulesAtTypesExists = nodeModulesFolderExists; 2474 if (nodeModulesFolderExists && !directoryProbablyExists(modulesAtTypes, state.host)) { 2475 if (state.traceEnabled) { 2476 trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, modulesAtTypes); 2477 } 2478 nodeModulesAtTypesExists = false; 2479 } 2480 return loadModuleFromSpecificNodeModulesDirectory(Extensions.DtsOnly, mangleScopedPackageNameWithTrace(moduleName, state), modulesAtTypes, nodeModulesAtTypesExists, state, cache, redirectedReference); 2481 } 2482} 2483 2484function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, moduleName: string, nodeModulesDirectory: string, nodeModulesDirectoryExists: boolean, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): Resolved | undefined { 2485 const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName)); 2486 2487 // If oh_modules exist, look for a nested oh-package.json5, as in `oh_modules/foo/bar/oh-package.json5`. 2488 // Otherwise, look for a nested package.json, as in `node_modules/foo/bar/package.json`. 2489 let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state); 2490 // But only if we're not respecting export maps (if we are, we might redirect around this location) 2491 if (!(state.features & NodeResolutionFeatures.Exports)) { 2492 if (packageInfo) { 2493 const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state); 2494 if (fromFile) { 2495 return noPackageId(fromFile); 2496 } 2497 2498 const fromDirectory = loadNodeModuleFromDirectoryWorker( 2499 extensions, 2500 candidate, 2501 !nodeModulesDirectoryExists, 2502 state, 2503 packageInfo.contents.packageJsonContent, 2504 packageInfo.contents.versionPaths 2505 ); 2506 return withPackageId(packageInfo, fromDirectory); 2507 } 2508 } 2509 2510 const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => { 2511 let pathAndExtension = 2512 loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) || 2513 loadNodeModuleFromDirectoryWorker( 2514 extensions, 2515 candidate, 2516 onlyRecordFailures, 2517 state, 2518 packageInfo && packageInfo.contents.packageJsonContent, 2519 packageInfo && packageInfo.contents.versionPaths 2520 ); 2521 if ( 2522 !pathAndExtension && packageInfo 2523 // eslint-disable-next-line no-null/no-null 2524 && (packageInfo.contents.packageJsonContent.exports === undefined || packageInfo.contents.packageJsonContent.exports === null) 2525 && state.features & NodeResolutionFeatures.EsmMode 2526 ) { 2527 // EsmMode disables index lookup in `loadNodeModuleFromDirectoryWorker` generally, however non-relative package resolutions still assume 2528 // a default `index.js` entrypoint if no `main` or `exports` are present 2529 pathAndExtension = loadModuleFromFile(extensions, combinePaths(candidate, "index.js"), onlyRecordFailures, state); 2530 } 2531 return withPackageId(packageInfo, pathAndExtension); 2532 }; 2533 2534 const { packageName, rest } = parsePackageName(moduleName); 2535 const packageDirectory = combinePaths(nodeModulesDirectory, packageName); 2536 if (rest !== "") { 2537 // Previous `packageInfo` may have been from a nested package.json; ensure we have the one from the package root now. 2538 packageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state); 2539 } 2540 // package exports are higher priority than file/directory/typesVersions lookups and (and, if there's exports present, blocks them) 2541 if (packageInfo && packageInfo.contents.packageJsonContent.exports && state.features & NodeResolutionFeatures.Exports) { 2542 return loadModuleFromExports(packageInfo, extensions, combinePaths(".", rest), state, cache, redirectedReference)?.value; 2543 } 2544 if (rest !== "" && packageInfo && packageInfo.contents.versionPaths) { 2545 if (state.traceEnabled) { 2546 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.contents.versionPaths.version, version, rest); 2547 } 2548 const packageDirectoryExists = nodeModulesDirectoryExists && directoryProbablyExists(packageDirectory, state.host); 2549 const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.contents.versionPaths.paths, /*pathPatterns*/ undefined, loader, !packageDirectoryExists, state); 2550 if (fromPaths) { 2551 return fromPaths.value; 2552 } 2553 } 2554 2555 return loader(extensions, candidate, !nodeModulesDirectoryExists, state); 2556} 2557 2558function tryLoadModuleUsingPaths(extensions: Extensions, moduleName: string, baseDirectory: string, paths: MapLike<string[]>, pathPatterns: readonly (string | Pattern)[] | undefined, loader: ResolutionKindSpecificLoader, onlyRecordFailures: boolean, state: ModuleResolutionState): SearchResult<Resolved> { 2559 pathPatterns ||= tryParsePatterns(paths); 2560 const matchedPattern = matchPatternOrExact(pathPatterns, moduleName); 2561 if (matchedPattern) { 2562 const matchedStar = isString(matchedPattern) ? undefined : matchedText(matchedPattern, moduleName); 2563 const matchedPatternText = isString(matchedPattern) ? matchedPattern : patternText(matchedPattern); 2564 if (state.traceEnabled) { 2565 trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); 2566 } 2567 const resolved = forEach(paths[matchedPatternText], subst => { 2568 const path = matchedStar ? subst.replace("*", matchedStar) : subst; 2569 // When baseUrl is not specified, the command line parser resolves relative paths to the config file location. 2570 const candidate = normalizePath(combinePaths(baseDirectory, path)); 2571 if (state.traceEnabled) { 2572 trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path); 2573 } 2574 // A path mapping may have an extension, in contrast to an import, which should omit it. 2575 const extension = tryGetExtensionFromPath(subst); 2576 if (extension !== undefined) { 2577 const path = tryFile(candidate, onlyRecordFailures, state); 2578 if (path !== undefined) { 2579 return noPackageId({ path, ext: extension }); 2580 } 2581 } 2582 return loader(extensions, candidate, onlyRecordFailures || !directoryProbablyExists(getDirectoryPath(candidate), state.host), state); 2583 }); 2584 return { value: resolved }; 2585 } 2586} 2587 2588/** Double underscores are used in DefinitelyTyped to delimit scoped packages. */ 2589const mangledScopedPackageSeparator = "__"; 2590 2591/** For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. */ 2592function mangleScopedPackageNameWithTrace(packageName: string, state: ModuleResolutionState): string { 2593 const mangled = mangleScopedPackageName(packageName); 2594 if (state.traceEnabled && mangled !== packageName) { 2595 trace(state.host, Diagnostics.Scoped_package_detected_looking_in_0, mangled); 2596 } 2597 return mangled; 2598} 2599 2600/** @internal */ 2601export function getTypesPackageName(packageName: string): string { 2602 return `@types/${mangleScopedPackageName(packageName)}`; 2603} 2604 2605/** @internal */ 2606export function mangleScopedPackageName(packageName: string): string { 2607 if (startsWith(packageName, "@")) { 2608 const replaceSlash = packageName.replace(directorySeparator, mangledScopedPackageSeparator); 2609 if (replaceSlash !== packageName) { 2610 return replaceSlash.slice(1); // Take off the "@" 2611 } 2612 } 2613 return packageName; 2614} 2615 2616/** @internal */ 2617export function getPackageNameFromTypesPackageName(mangledName: string): string { 2618 const withoutAtTypePrefix = removePrefix(mangledName, "@types/"); 2619 if (withoutAtTypePrefix !== mangledName) { 2620 return unmangleScopedPackageName(withoutAtTypePrefix); 2621 } 2622 return mangledName; 2623} 2624 2625/** @internal */ 2626export function unmangleScopedPackageName(typesPackageName: string): string { 2627 return stringContains(typesPackageName, mangledScopedPackageSeparator) ? 2628 "@" + typesPackageName.replace(mangledScopedPackageSeparator, directorySeparator) : 2629 typesPackageName; 2630} 2631 2632function tryFindNonRelativeModuleNameInCache(cache: PerModuleNameCache | undefined, moduleName: string, containingDirectory: string, state: ModuleResolutionState): SearchResult<Resolved> { 2633 const result = cache && cache.get(containingDirectory); 2634 if (result) { 2635 if (state.traceEnabled) { 2636 trace(state.host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory); 2637 } 2638 state.resultFromCache = result; 2639 return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, originalPath: result.resolvedModule.originalPath || true, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } }; 2640 } 2641} 2642 2643export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations { 2644 const traceEnabled = isTraceEnabled(compilerOptions, host); 2645 const failedLookupLocations: string[] = []; 2646 const affectingLocations: string[] = []; 2647 const containingDirectory = getDirectoryPath(containingFile); 2648 const diagnostics: Diagnostic[] = []; 2649 const state: ModuleResolutionState = { 2650 compilerOptions, 2651 host, 2652 traceEnabled, 2653 failedLookupLocations, 2654 affectingLocations, packageJsonInfoCache: cache, 2655 features: NodeResolutionFeatures.None, 2656 conditions: [], 2657 requestContainingDirectory: containingDirectory, 2658 reportDiagnostic: diag => void diagnostics.push(diag), 2659 }; 2660 2661 const resolved = tryResolve(Extensions.TypeScript) || tryResolve(Extensions.JavaScript); 2662 // No originalPath because classic resolution doesn't resolve realPath 2663 return createResolvedModuleWithFailedLookupLocations( 2664 resolved && resolved.value, 2665 /*isExternalLibraryImport*/ false, 2666 failedLookupLocations, 2667 affectingLocations, 2668 diagnostics, 2669 state.resultFromCache 2670 ); 2671 2672 function tryResolve(extensions: Extensions): SearchResult<Resolved> { 2673 const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFileNoPackageId, state); 2674 if (resolvedUsingSettings) { 2675 return { value: resolvedUsingSettings }; 2676 } 2677 2678 if (!isExternalModuleNameRelative(moduleName)) { 2679 const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, /*mode*/ undefined, redirectedReference); 2680 // Climb up parent directories looking for a module. 2681 const resolved = forEachAncestorDirectory(containingDirectory, directory => { 2682 const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, directory, state); 2683 if (resolutionFromCache) { 2684 return resolutionFromCache; 2685 } 2686 const searchName = normalizePath(combinePaths(directory, moduleName)); 2687 return toSearchResult(loadModuleFromFileNoPackageId(extensions, searchName, /*onlyRecordFailures*/ false, state)); 2688 }); 2689 if (resolved) { 2690 return resolved; 2691 } 2692 if (extensions === Extensions.TypeScript) { 2693 // If we didn't find the file normally, look it up in @types. 2694 return loadModuleFromNearestModulesDirectoryTypesScope(moduleName, containingDirectory, state); 2695 } 2696 } 2697 else { 2698 const candidate = normalizePath(combinePaths(containingDirectory, moduleName)); 2699 return toSearchResult(loadModuleFromFileNoPackageId(extensions, candidate, /*onlyRecordFailures*/ false, state)); 2700 } 2701 } 2702} 2703 2704/** 2705 * A host may load a module from a global cache of typings. 2706 * This is the minumum code needed to expose that functionality; the rest is in the host. 2707 * 2708 * @internal 2709 */ 2710export function loadModuleFromGlobalCache(moduleName: string, projectName: string | undefined, compilerOptions: CompilerOptions, host: ModuleResolutionHost, globalCache: string, packageJsonInfoCache: PackageJsonInfoCache): ResolvedModuleWithFailedLookupLocations { 2711 const traceEnabled = isTraceEnabled(compilerOptions, host); 2712 if (traceEnabled) { 2713 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); 2714 } 2715 const failedLookupLocations: string[] = []; 2716 const affectingLocations: string[] = []; 2717 const diagnostics: Diagnostic[] = []; 2718 const state: ModuleResolutionState = { 2719 compilerOptions, 2720 host, 2721 traceEnabled, 2722 failedLookupLocations, 2723 affectingLocations, 2724 packageJsonInfoCache, 2725 features: NodeResolutionFeatures.None, 2726 conditions: [], 2727 requestContainingDirectory: undefined, 2728 reportDiagnostic: diag => void diagnostics.push(diag), 2729 }; 2730 const resolved = loadModuleFromImmediateNodeModulesDirectory(Extensions.DtsOnly, moduleName, globalCache, state, /*typesScopeOnly*/ false, /*cache*/ undefined, /*redirectedReference*/ undefined); 2731 return createResolvedModuleWithFailedLookupLocations( 2732 resolved, 2733 /*isExternalLibraryImport*/ true, 2734 failedLookupLocations, 2735 affectingLocations, 2736 diagnostics, 2737 state.resultFromCache 2738 ); 2739} 2740 2741/** 2742 * Represents result of search. Normally when searching among several alternatives we treat value `undefined` as indicator 2743 * that search fails and we should try another option. 2744 * 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). 2745 * SearchResult is used to deal with this issue, its values represents following outcomes: 2746 * - undefined - not found, continue searching 2747 * - { value: undefined } - not found - stop searching 2748 * - { value: <some-value> } - found - stop searching 2749 */ 2750type SearchResult<T> = { value: T | undefined } | undefined; 2751 2752/** 2753 * Wraps value to SearchResult. 2754 * @returns undefined if value is undefined or { value } otherwise 2755 */ 2756function toSearchResult<T>(value: T | undefined): SearchResult<T> { 2757 return value !== undefined ? { value } : undefined; 2758} 2759 2760function traceIfEnabled(state: ModuleResolutionState, diagnostic: DiagnosticMessage, ...args: string[]) { 2761 if (state.traceEnabled) { 2762 trace(state.host, diagnostic, ...args); 2763 } 2764} 2765