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