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