• 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                            } else {
434                                isNotNeededPackage = host.fileExists(packageJsonPath) && (readJson(packageJsonPath, host) as PackageJson).typings === null;
435                            }
436                            if (!isNotNeededPackage) {
437                                const baseFileName = getBaseFileName(normalized);
438
439                                // At this stage, skip results with leading dot.
440                                if (baseFileName.charCodeAt(0) !== CharacterCodes.dot) {
441                                    // Return just the type directive names
442                                    result.push(baseFileName);
443                                }
444                            }
445                        }
446                    }
447                }
448            }
449        }
450        return result;
451    }
452
453    /**
454     * Cached module resolutions per containing directory.
455     * This assumes that any module id will have the same resolution for sibling files located in the same folder.
456     */
457    export interface ModuleResolutionCache extends NonRelativeModuleNameResolutionCache {
458        getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map<ResolvedModuleWithFailedLookupLocations>;
459        /*@internal*/ directoryToModuleNameMap: CacheWithRedirects<ESMap<string, ResolvedModuleWithFailedLookupLocations>>;
460    }
461
462    /**
463     * Stored map from non-relative module name to a table: directory -> result of module lookup in this directory
464     * We support only non-relative module names because resolution of relative module names is usually more deterministic and thus less expensive.
465     */
466    export interface NonRelativeModuleNameResolutionCache {
467        getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache;
468        /*@internal*/ moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>;
469    }
470
471    export interface PerModuleNameCache {
472        get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined;
473        set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void;
474    }
475
476    export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string, options?: CompilerOptions): ModuleResolutionCache {
477        return createModuleResolutionCacheWithMaps(
478            createCacheWithRedirects(options),
479            createCacheWithRedirects(options),
480            currentDirectory,
481            getCanonicalFileName
482        );
483    }
484
485
486    /*@internal*/
487    export interface CacheWithRedirects<T> {
488        ownMap: ESMap<string, T>;
489        redirectsMap: ESMap<Path, ESMap<string, T>>;
490        getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): ESMap<string, T>;
491        clear(): void;
492        setOwnOptions(newOptions: CompilerOptions): void;
493        setOwnMap(newOwnMap: ESMap<string, T>): void;
494    }
495
496    /*@internal*/
497    export function createCacheWithRedirects<T>(options?: CompilerOptions): CacheWithRedirects<T> {
498        let ownMap: ESMap<string, T> = new Map();
499        const redirectsMap = new Map<Path, ESMap<string, T>>();
500        return {
501            ownMap,
502            redirectsMap,
503            getOrCreateMapOfCacheRedirects,
504            clear,
505            setOwnOptions,
506            setOwnMap
507        };
508
509        function setOwnOptions(newOptions: CompilerOptions) {
510            options = newOptions;
511        }
512
513        function setOwnMap(newOwnMap: ESMap<string, T>) {
514            ownMap = newOwnMap;
515        }
516
517        function getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined) {
518            if (!redirectedReference) {
519                return ownMap;
520            }
521            const path = redirectedReference.sourceFile.path;
522            let redirects = redirectsMap.get(path);
523            if (!redirects) {
524                // Reuse map if redirected reference map uses same resolution
525                redirects = !options || optionsHaveModuleResolutionChanges(options, redirectedReference.commandLine.options) ? new Map() : ownMap;
526                redirectsMap.set(path, redirects);
527            }
528            return redirects;
529        }
530
531        function clear() {
532            ownMap.clear();
533            redirectsMap.clear();
534        }
535    }
536
537    /*@internal*/
538    export function createModuleResolutionCacheWithMaps(
539        directoryToModuleNameMap: CacheWithRedirects<ESMap<string, ResolvedModuleWithFailedLookupLocations>>,
540        moduleNameToDirectoryMap: CacheWithRedirects<PerModuleNameCache>,
541        currentDirectory: string,
542        getCanonicalFileName: GetCanonicalFileName): ModuleResolutionCache {
543
544        return { getOrCreateCacheForDirectory, getOrCreateCacheForModuleName, directoryToModuleNameMap, moduleNameToDirectoryMap };
545
546        function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) {
547            const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
548            return getOrCreateCache<ESMap<string, ResolvedModuleWithFailedLookupLocations>>(directoryToModuleNameMap, redirectedReference, path, () => new Map());
549        }
550
551        function getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache {
552            Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName));
553            return getOrCreateCache(moduleNameToDirectoryMap, redirectedReference, nonRelativeModuleName, createPerModuleNameCache);
554        }
555
556        function getOrCreateCache<T>(cacheWithRedirects: CacheWithRedirects<T>, redirectedReference: ResolvedProjectReference | undefined, key: string, create: () => T): T {
557            const cache = cacheWithRedirects.getOrCreateMapOfCacheRedirects(redirectedReference);
558            let result = cache.get(key);
559            if (!result) {
560                result = create();
561                cache.set(key, result);
562            }
563            return result;
564        }
565
566        function createPerModuleNameCache(): PerModuleNameCache {
567            const directoryPathMap = new Map<string, ResolvedModuleWithFailedLookupLocations>();
568
569            return { get, set };
570
571            function get(directory: string): ResolvedModuleWithFailedLookupLocations | undefined {
572                return directoryPathMap.get(toPath(directory, currentDirectory, getCanonicalFileName));
573            }
574
575            /**
576             * At first this function add entry directory -> module resolution result to the table.
577             * Then it computes the set of parent folders for 'directory' that should have the same module resolution result
578             * and for every parent folder in set it adds entry: parent -> module resolution. .
579             * Lets say we first directory name: /a/b/c/d/e and resolution result is: /a/b/bar.ts.
580             * Set of parent folders that should have the same result will be:
581             * [
582             *     /a/b/c/d, /a/b/c, /a/b
583             * ]
584             * this means that request for module resolution from file in any of these folder will be immediately found in cache.
585             */
586            function set(directory: string, result: ResolvedModuleWithFailedLookupLocations): void {
587                const path = toPath(directory, currentDirectory, getCanonicalFileName);
588                // if entry is already in cache do nothing
589                if (directoryPathMap.has(path)) {
590                    return;
591                }
592                directoryPathMap.set(path, result);
593
594                const resolvedFileName = result.resolvedModule &&
595                    (result.resolvedModule.originalPath || result.resolvedModule.resolvedFileName);
596                // find common prefix between directory and resolved file name
597                // this common prefix should be the shortest path that has the same resolution
598                // directory: /a/b/c/d/e
599                // resolvedFileName: /a/b/foo.d.ts
600                // commonPrefix: /a/b
601                // for failed lookups cache the result for every directory up to root
602                const commonPrefix = resolvedFileName && getCommonPrefix(path, resolvedFileName);
603                let current = path;
604                while (current !== commonPrefix) {
605                    const parent = getDirectoryPath(current);
606                    if (parent === current || directoryPathMap.has(parent)) {
607                        break;
608                    }
609                    directoryPathMap.set(parent, result);
610                    current = parent;
611                }
612            }
613
614            function getCommonPrefix(directory: Path, resolution: string) {
615                const resolutionDirectory = toPath(getDirectoryPath(resolution), currentDirectory, getCanonicalFileName);
616
617                // find first position where directory and resolution differs
618                let i = 0;
619                const limit = Math.min(directory.length, resolutionDirectory.length);
620                while (i < limit && directory.charCodeAt(i) === resolutionDirectory.charCodeAt(i)) {
621                    i++;
622                }
623                if (i === directory.length && (resolutionDirectory.length === i || resolutionDirectory[i] === directorySeparator)) {
624                    return directory;
625                }
626                const rootLength = getRootLength(directory);
627                if (i < rootLength) {
628                    return undefined;
629                }
630                const sep = directory.lastIndexOf(directorySeparator, i - 1);
631                if (sep === -1) {
632                    return undefined;
633                }
634                return directory.substr(0, Math.max(sep, rootLength));
635            }
636        }
637    }
638
639    export function resolveModuleNameFromCache(moduleName: string, containingFile: string, cache: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations | undefined {
640        const containingDirectory = getDirectoryPath(containingFile);
641        const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory);
642        return perFolderCache && perFolderCache.get(moduleName);
643    }
644
645    export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
646        const traceEnabled = isTraceEnabled(compilerOptions, host);
647        if (redirectedReference) {
648            compilerOptions = redirectedReference.commandLine.options;
649        }
650        if (traceEnabled) {
651            trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
652            if (redirectedReference) {
653                trace(host, Diagnostics.Using_compiler_options_of_project_reference_redirect_0, redirectedReference.sourceFile.fileName);
654            }
655        }
656        const containingDirectory = getDirectoryPath(containingFile);
657        const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory, redirectedReference);
658        let result = perFolderCache && perFolderCache.get(moduleName);
659
660        if (result) {
661            if (traceEnabled) {
662                trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory);
663            }
664        }
665        else {
666            let moduleResolution = compilerOptions.moduleResolution;
667            if (moduleResolution === undefined) {
668                moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
669                if (traceEnabled) {
670                    trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
671                }
672            }
673            else {
674                if (traceEnabled) {
675                    trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
676                }
677            }
678
679            perfLogger.logStartResolveModule(moduleName /* , containingFile, ModuleResolutionKind[moduleResolution]*/);
680            switch (moduleResolution) {
681                case ModuleResolutionKind.NodeJs:
682                    result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference);
683                    break;
684                case ModuleResolutionKind.Classic:
685                    result = classicNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference);
686                    break;
687                default:
688                    return Debug.fail(`Unexpected moduleResolution: ${moduleResolution}`);
689            }
690            if (result && result.resolvedModule) perfLogger.logInfoEvent(`Module "${moduleName}" resolved to "${result.resolvedModule.resolvedFileName}"`);
691            perfLogger.logStopResolveModule((result && result.resolvedModule) ? "" + result.resolvedModule.resolvedFileName : "null");
692
693            if (perFolderCache) {
694                perFolderCache.set(moduleName, result);
695                if (!isExternalModuleNameRelative(moduleName)) {
696                    // put result in per-module name cache
697                    cache!.getOrCreateCacheForModuleName(moduleName, redirectedReference).set(containingDirectory, result);
698                }
699            }
700        }
701
702        if (traceEnabled) {
703            if (result.resolvedModule) {
704                if (result.resolvedModule.packageId) {
705                    trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1_with_Package_ID_2, moduleName, result.resolvedModule.resolvedFileName, packageIdToString(result.resolvedModule.packageId));
706                }
707                else {
708                    trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
709                }
710            }
711            else {
712                trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
713            }
714        }
715
716        return result;
717    }
718
719    /*
720     * Every module resolution kind can has its specific understanding how to load module from a specific path on disk
721     * I.e. for path '/a/b/c':
722     * - Node loader will first to try to check if '/a/b/c' points to a file with some supported extension and if this fails
723     * it will try to load module from directory: directory '/a/b/c' should exist and it should have either 'package.json' with
724     * 'typings' entry or file 'index' with some supported extension
725     * - Classic loader will only try to interpret '/a/b/c' as file.
726     */
727    type ResolutionKindSpecificLoader = (extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState) => Resolved | undefined;
728
729    /**
730     * Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to
731     * mitigate differences between design time structure of the project and its runtime counterpart so the same import name
732     * can be resolved successfully by TypeScript compiler and runtime module loader.
733     * If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will
734     * fallback to standard resolution routine.
735     *
736     * - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative
737     * names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will
738     * be '/a/b/c/d'
739     * - paths - this setting can only be used when baseUrl is specified. allows to tune how non-relative module names
740     * will be resolved based on the content of the module name.
741     * Structure of 'paths' compiler options
742     * 'paths': {
743     *    pattern-1: [...substitutions],
744     *    pattern-2: [...substitutions],
745     *    ...
746     *    pattern-n: [...substitutions]
747     * }
748     * Pattern here is a string that can contain zero or one '*' character. During module resolution module name will be matched against
749     * all patterns in the list. Matching for patterns that don't contain '*' means that module name must be equal to pattern respecting the case.
750     * If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>.
751     * <MatchedStar> denotes part of the module name between <prefix> and <suffix>.
752     * If module name can be matches with multiple patterns then pattern with the longest prefix will be picked.
753     * After selecting pattern we'll use list of substitutions to get candidate locations of the module and the try to load module
754     * from the candidate location.
755     * Substitution is a string that can contain zero or one '*'. To get candidate location from substitution we'll pick every
756     * substitution in the list and replace '*' with <MatchedStar> string. If candidate location is not rooted it
757     * will be converted to absolute using baseUrl.
758     * For example:
759     * baseUrl: /a/b/c
760     * "paths": {
761     *     // match all module names
762     *     "*": [
763     *         "*",        // use matched name as is,
764     *                     // <matched name> will be looked as /a/b/c/<matched name>
765     *
766     *         "folder1/*" // substitution will convert matched name to 'folder1/<matched name>',
767     *                     // since it is not rooted then final candidate location will be /a/b/c/folder1/<matched name>
768     *     ],
769     *     // match module names that start with 'components/'
770     *     "components/*": [ "/root/components/*" ] // substitution will convert /components/folder1/<matched name> to '/root/components/folder1/<matched name>',
771     *                                              // it is rooted so it will be final candidate location
772     * }
773     *
774     * 'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if
775     * they were in the same location. For example lets say there are two files
776     * '/local/src/content/file1.ts'
777     * '/shared/components/contracts/src/content/protocols/file2.ts'
778     * After bundling content of '/shared/components/contracts/src' will be merged with '/local/src' so
779     * if file1 has the following import 'import {x} from "./protocols/file2"' it will be resolved successfully in runtime.
780     * 'rootDirs' provides the way to tell compiler that in order to get the whole project it should behave as if content of all
781     * root dirs were merged together.
782     * I.e. for the example above 'rootDirs' will have two entries: [ '/local/src', '/shared/components/contracts/src' ].
783     * Compiler will first convert './protocols/file2' into absolute path relative to the location of containing file:
784     * '/local/src/content/protocols/file2' and try to load it - failure.
785     * Then it will search 'rootDirs' looking for a longest matching prefix of this absolute path and if such prefix is found - absolute path will
786     * be converted to a path relative to found rootDir entry './content/protocols/file2' (*). As a last step compiler will check all remaining
787     * entries in 'rootDirs', use them to build absolute path out of (*) and try to resolve module from this location.
788     */
789    function tryLoadModuleUsingOptionalResolutionSettings(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
790        state: ModuleResolutionState): Resolved | undefined {
791
792        const resolved = tryLoadModuleUsingPathsIfEligible(extensions, moduleName, loader, state);
793        if (resolved) return resolved.value;
794
795        if (!isExternalModuleNameRelative(moduleName)) {
796            return tryLoadModuleUsingBaseUrl(extensions, moduleName, loader, state);
797        }
798        else {
799            return tryLoadModuleUsingRootDirs(extensions, moduleName, containingDirectory, loader, state);
800        }
801    }
802
803    function tryLoadModuleUsingPathsIfEligible(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState) {
804        const { baseUrl, paths } = state.compilerOptions;
805        if (paths && !pathIsRelative(moduleName)) {
806            if (state.traceEnabled) {
807                if (baseUrl) {
808                    trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, baseUrl, moduleName);
809                }
810                trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
811            }
812            const baseDirectory = getPathsBasePath(state.compilerOptions, state.host)!; // Always defined when 'paths' is defined
813            return tryLoadModuleUsingPaths(extensions, moduleName, baseDirectory, paths, loader, /*onlyRecordFailures*/ false, state);
814        }
815    }
816
817    function tryLoadModuleUsingRootDirs(extensions: Extensions, moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
818        state: ModuleResolutionState): Resolved | undefined {
819
820        if (!state.compilerOptions.rootDirs) {
821            return undefined;
822        }
823
824        if (state.traceEnabled) {
825            trace(state.host, Diagnostics.rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName);
826        }
827
828        const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
829
830        let matchedRootDir: string | undefined;
831        let matchedNormalizedPrefix: string | undefined;
832        for (const rootDir of state.compilerOptions.rootDirs) {
833            // rootDirs are expected to be absolute
834            // in case of tsconfig.json this will happen automatically - compiler will expand relative names
835            // using location of tsconfig.json as base location
836            let normalizedRoot = normalizePath(rootDir);
837            if (!endsWith(normalizedRoot, directorySeparator)) {
838                normalizedRoot += directorySeparator;
839            }
840            const isLongestMatchingPrefix =
841                startsWith(candidate, normalizedRoot) &&
842                (matchedNormalizedPrefix === undefined || matchedNormalizedPrefix.length < normalizedRoot.length);
843
844            if (state.traceEnabled) {
845                trace(state.host, Diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2, normalizedRoot, candidate, isLongestMatchingPrefix);
846            }
847
848            if (isLongestMatchingPrefix) {
849                matchedNormalizedPrefix = normalizedRoot;
850                matchedRootDir = rootDir;
851            }
852        }
853        if (matchedNormalizedPrefix) {
854            if (state.traceEnabled) {
855                trace(state.host, Diagnostics.Longest_matching_prefix_for_0_is_1, candidate, matchedNormalizedPrefix);
856            }
857            const suffix = candidate.substr(matchedNormalizedPrefix.length);
858
859            // first - try to load from a initial location
860            if (state.traceEnabled) {
861                trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, matchedNormalizedPrefix, candidate);
862            }
863            const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(containingDirectory, state.host), state);
864            if (resolvedFileName) {
865                return resolvedFileName;
866            }
867
868            if (state.traceEnabled) {
869                trace(state.host, Diagnostics.Trying_other_entries_in_rootDirs);
870            }
871            // then try to resolve using remaining entries in rootDirs
872            for (const rootDir of state.compilerOptions.rootDirs) {
873                if (rootDir === matchedRootDir) {
874                    // skip the initially matched entry
875                    continue;
876                }
877                const candidate = combinePaths(normalizePath(rootDir), suffix);
878                if (state.traceEnabled) {
879                    trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, rootDir, candidate);
880                }
881                const baseDirectory = getDirectoryPath(candidate);
882                const resolvedFileName = loader(extensions, candidate, !directoryProbablyExists(baseDirectory, state.host), state);
883                if (resolvedFileName) {
884                    return resolvedFileName;
885                }
886            }
887            if (state.traceEnabled) {
888                trace(state.host, Diagnostics.Module_resolution_using_rootDirs_has_failed);
889            }
890        }
891        return undefined;
892    }
893
894    function tryLoadModuleUsingBaseUrl(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState): Resolved | undefined {
895        const { baseUrl } = state.compilerOptions;
896        if (!baseUrl) {
897            return undefined;
898        }
899        if (state.traceEnabled) {
900            trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, baseUrl, moduleName);
901        }
902        const candidate = normalizePath(combinePaths(baseUrl, moduleName));
903        if (state.traceEnabled) {
904            trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, baseUrl, candidate);
905        }
906        return loader(extensions, candidate, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
907    }
908
909    /**
910     * Expose resolution logic to allow us to use Node module resolution logic from arbitrary locations.
911     * No way to do this with `require()`: https://github.com/nodejs/node/issues/5963
912     * Throws an error if the module can't be resolved.
913     */
914    /* @internal */
915    export function resolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string {
916        const { resolvedModule, failedLookupLocations } = tryResolveJSModuleWorker(moduleName, initialDir, host);
917        if (!resolvedModule) {
918            throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`);
919        }
920        return resolvedModule.resolvedFileName;
921    }
922
923    /* @internal */
924    export function tryResolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string | undefined {
925        const { resolvedModule } = tryResolveJSModuleWorker(moduleName, initialDir, host);
926        return resolvedModule && resolvedModule.resolvedFileName;
927    }
928
929    const jsOnlyExtensions = [Extensions.JavaScript];
930    const tsExtensions = [Extensions.TypeScript, Extensions.JavaScript];
931    const tsPlusJsonExtensions = [...tsExtensions, Extensions.Json];
932    const tsconfigExtensions = [Extensions.TSConfig];
933    function tryResolveJSModuleWorker(moduleName: string, initialDir: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
934        return nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, jsOnlyExtensions, /*redirectedReferences*/ undefined);
935    }
936
937    export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations;
938    /* @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
939    export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, lookupConfig?: boolean): ResolvedModuleWithFailedLookupLocations {
940        return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, lookupConfig ? tsconfigExtensions : (compilerOptions.resolveJsonModule ? tsPlusJsonExtensions : tsExtensions), redirectedReference);
941    }
942
943    function nodeModuleNameResolverWorker(moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, extensions: Extensions[], redirectedReference: ResolvedProjectReference | undefined): ResolvedModuleWithFailedLookupLocations {
944        const traceEnabled = isTraceEnabled(compilerOptions, host);
945
946        const failedLookupLocations: string[] = [];
947        const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations };
948
949        const result = forEach(extensions, ext => tryResolve(ext));
950        return createResolvedModuleWithFailedLookupLocations(result?.value?.resolved, result?.value?.isExternalLibraryImport, failedLookupLocations, state.resultFromCache);
951
952        function tryResolve(extensions: Extensions): SearchResult<{ resolved: Resolved, isExternalLibraryImport: boolean }> {
953            const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => nodeLoadModuleByRelativeName(extensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ true);
954            const isOHModules: boolean = isOhpm(compilerOptions.packageManagerType);
955            const resolved = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loader, state);
956            if (resolved) {
957                return toSearchResult({ resolved, isExternalLibraryImport: isOHModules ? pathContainsOHModules(resolved.path) :
958                  pathContainsNodeModules(resolved.path) });
959            }
960
961            if (!isExternalModuleNameRelative(moduleName)) {
962                if (traceEnabled) {
963                    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;
964                    trace(host, message, moduleName, Extensions[extensions]);
965                }
966                const resolved = loadModuleFromNearestModulesDirectory(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
967                if (!resolved) return undefined;
968
969                let resolvedValue = resolved.value;
970                if (!compilerOptions.preserveSymlinks && resolvedValue && !resolvedValue.originalPath) {
971                    const path = realPath(resolvedValue.path, host, traceEnabled);
972                    const originalPath = path === resolvedValue.path ? undefined : resolvedValue.path;
973                    resolvedValue = { ...resolvedValue, path, originalPath };
974                }
975                // 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.
976                return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } };
977            }
978            else {
979                const { path: candidate, parts } = normalizePathAndParts(combinePaths(containingDirectory, moduleName));
980                const resolved = nodeLoadModuleByRelativeName(extensions, candidate, /*onlyRecordFailures*/ false, state, /*considerPackageJson*/ true);
981                // Treat explicit "node_modules" or "oh_modules" import as an external library import.
982                return resolved && toSearchResult({ resolved, isExternalLibraryImport: contains(parts, getModuleByPMType(compilerOptions.packageManagerType)) });
983            }
984        }
985    }
986
987    function realPath(path: string, host: ModuleResolutionHost, traceEnabled: boolean): string {
988        if (!host.realpath) {
989            return path;
990        }
991
992        const real = normalizePath(host.realpath(path));
993        if (traceEnabled) {
994            trace(host, Diagnostics.Resolving_real_path_for_0_result_1, path, real);
995        }
996        Debug.assert(host.fileExists(real), `${path} linked to nonexistent file ${real}`);
997        return real;
998    }
999
1000    function nodeLoadModuleByRelativeName(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson: boolean): Resolved | undefined {
1001        if (state.traceEnabled) {
1002            trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0_target_file_type_1, candidate, Extensions[extensions]);
1003        }
1004        if (!hasTrailingDirectorySeparator(candidate)) {
1005            if (!onlyRecordFailures) {
1006                const parentOfCandidate = getDirectoryPath(candidate);
1007                if (!directoryProbablyExists(parentOfCandidate, state.host)) {
1008                    if (state.traceEnabled) {
1009                        trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, parentOfCandidate);
1010                    }
1011                    onlyRecordFailures = true;
1012                }
1013            }
1014            const resolvedFromFile = loadModuleFromFile(extensions, candidate, onlyRecordFailures, state);
1015            if (resolvedFromFile) {
1016                const packageDirectory = considerPackageJson ? parseModuleFromPath(resolvedFromFile, state.compilerOptions.packageManagerType) : undefined;
1017                const packageInfo = packageDirectory ? getPackageJsonInfo(packageDirectory, /*onlyRecordFailures*/ false, state) : undefined;
1018                return withPackageId(packageInfo, resolvedFromFile);
1019            }
1020        }
1021        if (!onlyRecordFailures) {
1022            const candidateExists = directoryProbablyExists(candidate, state.host);
1023            if (!candidateExists) {
1024                if (state.traceEnabled) {
1025                    trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, candidate);
1026                }
1027                onlyRecordFailures = true;
1028            }
1029        }
1030        return loadNodeModuleFromDirectory(extensions, candidate, onlyRecordFailures, state, considerPackageJson);
1031    }
1032
1033    /*@internal*/
1034    export const nodeModulesPathPart = "/node_modules/";
1035    export const ohModulesPathPart = "/oh_modules/";
1036
1037    /*@internal*/
1038    export function pathContainsNodeModules(path: string): boolean {
1039        return stringContains(path, nodeModulesPathPart);
1040    }
1041
1042    /*@internal*/
1043    export function pathContainsOHModules(path: string): boolean {
1044        return stringContains(path, ohModulesPathPart);
1045    }
1046
1047    /**
1048     * This will be called on the successfully resolved path from `loadModuleFromFile`.
1049     * (Not needed for `loadModuleFromNodeModules` as that looks up the `package.json` or `oh-package.json5` as part of resolution.)
1050     *
1051     * packageDirectory is the directory of the package itself.
1052     *   For `blah/node_modules/foo/index.d.ts` this is packageDirectory: "foo"
1053     *   For `/node_modules/foo/bar.d.ts` this is packageDirectory: "foo"
1054     *   For `/node_modules/@types/foo/bar/index.d.ts` this is packageDirectory: "@types/foo"
1055     *   For `/node_modules/foo/bar/index.d.ts` this is packageDirectory: "foo"
1056     */
1057    function parseModuleFromPath(resolved: PathAndExtension, packageManagerType?: string): string | undefined {
1058        const modulesPathPart = isOhpm(packageManagerType) ? ohModulesPathPart : nodeModulesPathPart;
1059        const path = normalizePath(resolved.path);
1060        const idx = path.lastIndexOf(modulesPathPart);
1061        if (idx === -1) {
1062            return undefined;
1063        }
1064
1065        const indexAfterModules = idx + modulesPathPart.length;
1066        let indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterModules);
1067        if (path.charCodeAt(indexAfterModules) === CharacterCodes.at) {
1068            indexAfterPackageName = moveToNextDirectorySeparatorIfAvailable(path, indexAfterPackageName);
1069        }
1070        return path.slice(0, indexAfterPackageName);
1071    }
1072
1073    function moveToNextDirectorySeparatorIfAvailable(path: string, prevSeparatorIndex: number): number {
1074        const nextSeparatorIndex = path.indexOf(directorySeparator, prevSeparatorIndex + 1);
1075        return nextSeparatorIndex === -1 ? prevSeparatorIndex : nextSeparatorIndex;
1076    }
1077
1078    function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
1079        return noPackageId(loadModuleFromFile(extensions, candidate, onlyRecordFailures, state));
1080    }
1081
1082    /**
1083     * @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
1084     * 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.
1085     */
1086    function loadModuleFromFile(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
1087        if (extensions === Extensions.Json || extensions === Extensions.TSConfig) {
1088            const extensionLess = tryRemoveExtension(candidate, Extension.Json);
1089            return (extensionLess === undefined && extensions === Extensions.Json) ? undefined : tryAddingExtensions(extensionLess || candidate, extensions, onlyRecordFailures, state);
1090        }
1091
1092        // First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
1093        const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, onlyRecordFailures, state);
1094        if (resolvedByAddingExtension) {
1095            return resolvedByAddingExtension;
1096        }
1097
1098        // If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one;
1099        // e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
1100        if (hasJSFileExtension(candidate)) {
1101            const extensionless = removeFileExtension(candidate);
1102            if (state.traceEnabled) {
1103                const extension = candidate.substring(extensionless.length);
1104                trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
1105            }
1106            return tryAddingExtensions(extensionless, extensions, onlyRecordFailures, state);
1107        }
1108    }
1109
1110    /** Try to return an existing file that adds one of the `extensions` to `candidate`. */
1111    function tryAddingExtensions(candidate: string, extensions: Extensions, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
1112        if (!onlyRecordFailures) {
1113            // check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
1114            const directory = getDirectoryPath(candidate);
1115            if (directory) {
1116                onlyRecordFailures = !directoryProbablyExists(directory, state.host);
1117            }
1118        }
1119
1120        switch (extensions) {
1121            case Extensions.DtsOnly:
1122                if (state.compilerOptions.ets) {
1123                    return tryExtension(Extension.Dets) || tryExtension(Extension.Dts);
1124                }
1125                else {
1126                    return tryExtension(Extension.Dts);
1127                }
1128            case Extensions.TypeScript:
1129                if (state.compilerOptions.ets) {
1130                    return tryExtension(Extension.Ets) || tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dets) || tryExtension(Extension.Dts);
1131                }
1132                else {
1133                    return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts) || tryExtension(Extension.Ets);
1134                }
1135            case Extensions.JavaScript:
1136                return tryExtension(Extension.Js) || tryExtension(Extension.Jsx);
1137            case Extensions.TSConfig:
1138            case Extensions.Json:
1139                return tryExtension(Extension.Json);
1140        }
1141
1142        function tryExtension(ext: Extension): PathAndExtension | undefined {
1143            const path = tryFile(candidate + ext, onlyRecordFailures, state);
1144            return path === undefined ? undefined : { path, ext };
1145        }
1146    }
1147
1148    /** Return the file if it exists. */
1149    function tryFile(fileName: string, onlyRecordFailures: boolean, state: ModuleResolutionState): string | undefined {
1150        if (!onlyRecordFailures) {
1151            if (state.host.fileExists(fileName)) {
1152                if (state.traceEnabled) {
1153                    trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
1154                }
1155                return fileName;
1156            }
1157            else {
1158                if (state.traceEnabled) {
1159                    trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
1160                }
1161            }
1162        }
1163        state.failedLookupLocations.push(fileName);
1164        return undefined;
1165    }
1166
1167    function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
1168        const packageInfo = considerPackageJson ? getPackageJsonInfo(candidate, onlyRecordFailures, state) : undefined;
1169        const packageJsonContent = packageInfo && packageInfo.packageJsonContent;
1170        const versionPaths = packageInfo && packageInfo.versionPaths;
1171        return withPackageId(packageInfo, loadModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
1172    }
1173
1174    interface PackageJsonInfo {
1175        packageDirectory: string;
1176        packageJsonContent: PackageJsonPathFields;
1177        versionPaths: VersionPaths | undefined;
1178    }
1179
1180    function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
1181        const { host, traceEnabled } = state;
1182        const directoryExists = !onlyRecordFailures && directoryProbablyExists(packageDirectory, host);
1183        const packageJsonPath = combinePaths(packageDirectory, getPackageJsonByPMType(state.compilerOptions.packageManagerType));
1184        if (directoryExists && host.fileExists(packageJsonPath)) {
1185            const isOHModules: boolean = isOhpm(state.compilerOptions.packageManagerType);
1186            const packageJsonContent = isOHModules ? JSON5.parse(host.readFile!(packageJsonPath)!) : readJson(packageJsonPath, host) as PackageJson;
1187            if (traceEnabled) {
1188                const message = isOHModules ? Diagnostics.Found_oh_package_json5_at_0 : Diagnostics.Found_package_json_at_0;
1189                trace(host, message, packageJsonPath);
1190            }
1191            const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
1192            return { packageDirectory, packageJsonContent, versionPaths };
1193        }
1194        else {
1195            if (directoryExists && traceEnabled) {
1196                trace(host, Diagnostics.File_0_does_not_exist, packageJsonPath);
1197            }
1198
1199            // record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
1200            state.failedLookupLocations.push(packageJsonPath);
1201        }
1202    }
1203
1204    function loadModuleFromDirectoryWorker(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState, jsonContent: PackageJsonPathFields | undefined, versionPaths: VersionPaths | undefined): PathAndExtension | undefined {
1205        let packageFile: string | undefined;
1206        if (jsonContent) {
1207            switch (extensions) {
1208                case Extensions.JavaScript:
1209                case Extensions.Json:
1210                    packageFile = readPackageJsonMainField(jsonContent, candidate, state);
1211                    break;
1212                case Extensions.TypeScript:
1213                    // When resolving typescript modules, try resolving using main field as well
1214                    packageFile = readPackageJsonTypesFields(jsonContent, candidate, state) || readPackageJsonMainField(jsonContent, candidate, state);
1215                    break;
1216                case Extensions.DtsOnly:
1217                    packageFile = readPackageJsonTypesFields(jsonContent, candidate, state);
1218                    break;
1219                case Extensions.TSConfig:
1220                    packageFile = readPackageJsonTSConfigField(jsonContent, candidate, state);
1221                    break;
1222                default:
1223                    return Debug.assertNever(extensions);
1224            }
1225        }
1226
1227        const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
1228            const fromFile = tryFile(candidate, onlyRecordFailures, state);
1229            if (fromFile) {
1230                const resolved = resolvedIfExtensionMatches(extensions, fromFile);
1231                if (resolved) {
1232                    return noPackageId(resolved);
1233                }
1234                if (state.traceEnabled) {
1235                    trace(state.host, Diagnostics.File_0_has_an_unsupported_extension_so_skipping_it, fromFile);
1236                }
1237            }
1238
1239            // Even if extensions is DtsOnly, we can still look up a .ts file as a result of package.json "types"
1240            const nextExtensions = extensions === Extensions.DtsOnly ? Extensions.TypeScript : extensions;
1241            // Don't do package.json lookup recursively, because Node.js' package lookup doesn't.
1242            return nodeLoadModuleByRelativeName(nextExtensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ false);
1243        };
1244
1245        const onlyRecordFailuresForPackageFile = packageFile ? !directoryProbablyExists(getDirectoryPath(packageFile), state.host) : undefined;
1246        const onlyRecordFailuresForIndex = onlyRecordFailures || !directoryProbablyExists(candidate, state.host);
1247        const indexPath = combinePaths(candidate, extensions === Extensions.TSConfig ? "tsconfig" : "index");
1248
1249        if (versionPaths && (!packageFile || containsPath(candidate, packageFile))) {
1250            const moduleName = getRelativePathFromDirectory(candidate, packageFile || indexPath, /*ignoreCase*/ false);
1251            if (state.traceEnabled) {
1252                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);
1253            }
1254            const result = tryLoadModuleUsingPaths(extensions, moduleName, candidate, versionPaths.paths, loader, onlyRecordFailuresForPackageFile || onlyRecordFailuresForIndex, state);
1255            if (result) {
1256                return removeIgnoredPackageId(result.value);
1257            }
1258        }
1259
1260        // It won't have a `packageId` set, because we disabled `considerPackageJson`.
1261        const packageFileResult = packageFile && removeIgnoredPackageId(loader(extensions, packageFile, onlyRecordFailuresForPackageFile!, state));
1262        if (packageFileResult) return packageFileResult;
1263
1264        return loadModuleFromFile(extensions, indexPath, onlyRecordFailuresForIndex, state);
1265    }
1266
1267    /** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */
1268    function resolvedIfExtensionMatches(extensions: Extensions, path: string): PathAndExtension | undefined {
1269        const ext = tryGetExtensionFromPath(path);
1270        return ext !== undefined && extensionIsOk(extensions, ext) ? { path, ext } : undefined;
1271    }
1272
1273    /** True if `extension` is one of the supported `extensions`. */
1274    function extensionIsOk(extensions: Extensions, extension: Extension): boolean {
1275        switch (extensions) {
1276            case Extensions.JavaScript:
1277                return extension === Extension.Js || extension === Extension.Jsx;
1278            case Extensions.TSConfig:
1279            case Extensions.Json:
1280                return extension === Extension.Json;
1281            case Extensions.TypeScript:
1282                return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Dts || extension === Extension.Ets || extension === Extension.Dets;
1283            case Extensions.DtsOnly:
1284                return extension === Extension.Dts || extension === Extension.Dets;
1285        }
1286    }
1287
1288    /* @internal */
1289    export function parsePackageName(moduleName: string): { packageName: string, rest: string } {
1290        let idx = moduleName.indexOf(directorySeparator);
1291        if (moduleName[0] === "@") {
1292            idx = moduleName.indexOf(directorySeparator, idx + 1);
1293        }
1294        return idx === -1 ? { packageName: moduleName, rest: "" } : { packageName: moduleName.slice(0, idx), rest: moduleName.slice(idx + 1) };
1295    }
1296
1297    function loadModuleFromNearestModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
1298        return loadModuleFromNearestModulesDirectoryWorker(extensions, moduleName, directory, state, /*typesScopeOnly*/ false, cache, redirectedReference);
1299    }
1300
1301    function loadModuleFromNearestModulesDirectoryTypesScope(moduleName: string, directory: string, state: ModuleResolutionState): SearchResult<Resolved> {
1302        // Extensions parameter here doesn't actually matter, because typesOnly ensures we're just doing @types lookup, which is always DtsOnly.
1303        return loadModuleFromNearestModulesDirectoryWorker(Extensions.DtsOnly, moduleName, directory, state, /*typesScopeOnly*/ true, /*cache*/ undefined, /*redirectedReference*/ undefined);
1304    }
1305
1306    function loadModuleFromNearestModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
1307        const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference);
1308        const packageManagerType = state.compilerOptions.packageManagerType;
1309        const modulePathPart = getModuleByPMType(packageManagerType);
1310        return forEachAncestorDirectory(normalizeSlashes(directory), ancestorDirectory => {
1311            if (getBaseFileName(ancestorDirectory) !== modulePathPart) {
1312                const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, ancestorDirectory, state);
1313                if (resolutionFromCache) {
1314                    return resolutionFromCache;
1315                }
1316                return toSearchResult(loadModuleFromImmediateModulesDirectory(extensions, moduleName, ancestorDirectory, state, typesScopeOnly));
1317            }
1318        });
1319    }
1320
1321    function loadModuleFromImmediateModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean): Resolved | undefined {
1322        const modulesFolder = combinePaths(directory, getModuleByPMType(state.compilerOptions.packageManagerType));
1323        const modulesFolderExists = directoryProbablyExists(modulesFolder, state.host);
1324        if (!modulesFolderExists && state.traceEnabled) {
1325            trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, modulesFolder);
1326        }
1327
1328        const packageResult = typesScopeOnly ? undefined : loadModuleFromSpecificModulesDirectory(extensions, moduleName, modulesFolder, modulesFolderExists, state);
1329        if (packageResult) {
1330            return packageResult;
1331        }
1332        if (extensions === Extensions.TypeScript || extensions === Extensions.DtsOnly) {
1333            const modulesAtTypes = combinePaths(modulesFolder, "@types");
1334            let modulesAtTypesExists = modulesFolderExists;
1335            if (modulesFolderExists && !directoryProbablyExists(modulesAtTypes, state.host)) {
1336                if (state.traceEnabled) {
1337                    trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, modulesAtTypes);
1338                }
1339                modulesAtTypesExists = false;
1340            }
1341            return loadModuleFromSpecificModulesDirectory(Extensions.DtsOnly, mangleScopedPackageNameWithTrace(moduleName, state), modulesAtTypes, modulesAtTypesExists, state);
1342        }
1343    }
1344
1345    function loadModuleFromSpecificModulesDirectory(extensions: Extensions, moduleName: string, nodeModulesDirectory: string, nodeModulesDirectoryExists: boolean, state: ModuleResolutionState): Resolved | undefined {
1346        const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName));
1347
1348        // If oh_modules exist, look for a nested oh-package.json5, as in `oh_modules/foo/bar/oh-package.json5`.
1349        // Otherwise, look for a nested package.json, as in `node_modules/foo/bar/package.json`.
1350        let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state);
1351        if (packageInfo) {
1352            const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
1353            if (fromFile) {
1354                return noPackageId(fromFile);
1355            }
1356
1357            const fromDirectory = loadModuleFromDirectoryWorker(
1358                extensions,
1359                candidate,
1360                !nodeModulesDirectoryExists,
1361                state,
1362                packageInfo.packageJsonContent,
1363                packageInfo.versionPaths
1364            );
1365            return withPackageId(packageInfo, fromDirectory);
1366        }
1367
1368        const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
1369            const pathAndExtension =
1370                loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
1371                loadModuleFromDirectoryWorker(
1372                    extensions,
1373                    candidate,
1374                    onlyRecordFailures,
1375                    state,
1376                    packageInfo && packageInfo.packageJsonContent,
1377                    packageInfo && packageInfo.versionPaths
1378                );
1379            return withPackageId(packageInfo, pathAndExtension);
1380        };
1381
1382        const { packageName, rest } = parsePackageName(moduleName);
1383        if (rest !== "") { // If "rest" is empty, we just did this search above.
1384            const packageDirectory = combinePaths(nodeModulesDirectory, packageName);
1385
1386            // 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.
1387            packageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state);
1388            if (packageInfo && packageInfo.versionPaths) {
1389                if (state.traceEnabled) {
1390                    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);
1391                }
1392                const packageDirectoryExists = nodeModulesDirectoryExists && directoryProbablyExists(packageDirectory, state.host);
1393                const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.versionPaths.paths, loader, !packageDirectoryExists, state);
1394                if (fromPaths) {
1395                    return fromPaths.value;
1396                }
1397            }
1398        }
1399
1400        return loader(extensions, candidate, !nodeModulesDirectoryExists, state);
1401    }
1402
1403    function tryLoadModuleUsingPaths(extensions: Extensions, moduleName: string, baseDirectory: string, paths: MapLike<string[]>, loader: ResolutionKindSpecificLoader, onlyRecordFailures: boolean, state: ModuleResolutionState): SearchResult<Resolved> {
1404        const matchedPattern = matchPatternOrExact(getOwnKeys(paths), moduleName);
1405        if (matchedPattern) {
1406            const matchedStar = isString(matchedPattern) ? undefined : matchedText(matchedPattern, moduleName);
1407            const matchedPatternText = isString(matchedPattern) ? matchedPattern : patternText(matchedPattern);
1408            if (state.traceEnabled) {
1409                trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText);
1410            }
1411            const resolved = forEach(paths[matchedPatternText], subst => {
1412                const path = matchedStar ? subst.replace("*", matchedStar) : subst;
1413                // When baseUrl is not specified, the command line parser resolves relative paths to the config file location.
1414                const candidate = normalizePath(combinePaths(baseDirectory, path));
1415                if (state.traceEnabled) {
1416                    trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
1417                }
1418                // A path mapping may have an extension, in contrast to an import, which should omit it.
1419                const extension = tryGetExtensionFromPath(subst);
1420                if (extension !== undefined) {
1421                    const path = tryFile(candidate, onlyRecordFailures, state);
1422                    if (path !== undefined) {
1423                        return noPackageId({ path, ext: extension });
1424                    }
1425                }
1426                return loader(extensions, candidate, onlyRecordFailures || !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
1427            });
1428            return { value: resolved };
1429        }
1430    }
1431
1432    /** Double underscores are used in DefinitelyTyped to delimit scoped packages. */
1433    const mangledScopedPackageSeparator = "__";
1434
1435    /** For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. */
1436    function mangleScopedPackageNameWithTrace(packageName: string, state: ModuleResolutionState): string {
1437        const mangled = mangleScopedPackageName(packageName);
1438        if (state.traceEnabled && mangled !== packageName) {
1439            trace(state.host, Diagnostics.Scoped_package_detected_looking_in_0, mangled);
1440        }
1441        return mangled;
1442    }
1443
1444    /* @internal */
1445    export function getTypesPackageName(packageName: string): string {
1446        return `@types/${mangleScopedPackageName(packageName)}`;
1447    }
1448
1449    /* @internal */
1450    export function mangleScopedPackageName(packageName: string): string {
1451        if (startsWith(packageName, "@")) {
1452            const replaceSlash = packageName.replace(directorySeparator, mangledScopedPackageSeparator);
1453            if (replaceSlash !== packageName) {
1454                return replaceSlash.slice(1); // Take off the "@"
1455            }
1456        }
1457        return packageName;
1458    }
1459
1460    /* @internal */
1461    export function getPackageNameFromTypesPackageName(mangledName: string): string {
1462        const withoutAtTypePrefix = removePrefix(mangledName, "@types/");
1463        if (withoutAtTypePrefix !== mangledName) {
1464            return unmangleScopedPackageName(withoutAtTypePrefix);
1465        }
1466        return mangledName;
1467    }
1468
1469    /* @internal */
1470    export function unmangleScopedPackageName(typesPackageName: string): string {
1471        return stringContains(typesPackageName, mangledScopedPackageSeparator) ?
1472            "@" + typesPackageName.replace(mangledScopedPackageSeparator, directorySeparator) :
1473            typesPackageName;
1474    }
1475
1476    function tryFindNonRelativeModuleNameInCache(cache: PerModuleNameCache | undefined, moduleName: string, containingDirectory: string, state: ModuleResolutionState): SearchResult<Resolved> {
1477        const result = cache && cache.get(containingDirectory);
1478        if (result) {
1479            if (state.traceEnabled) {
1480                trace(state.host, Diagnostics.Resolution_for_module_0_was_found_in_cache_from_location_1, moduleName, containingDirectory);
1481            }
1482            state.resultFromCache = result;
1483            return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, originalPath: result.resolvedModule.originalPath || true, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } };
1484        }
1485    }
1486
1487    export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
1488        const traceEnabled = isTraceEnabled(compilerOptions, host);
1489        const failedLookupLocations: string[] = [];
1490        const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations };
1491        const containingDirectory = getDirectoryPath(containingFile);
1492
1493        const resolved = tryResolve(Extensions.TypeScript) || tryResolve(Extensions.JavaScript);
1494        // No originalPath because classic resolution doesn't resolve realPath
1495        return createResolvedModuleWithFailedLookupLocations(resolved && resolved.value, /*isExternalLibraryImport*/ false, failedLookupLocations, state.resultFromCache);
1496
1497        function tryResolve(extensions: Extensions): SearchResult<Resolved> {
1498            const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFileNoPackageId, state);
1499            if (resolvedUsingSettings) {
1500                return { value: resolvedUsingSettings };
1501            }
1502
1503            if (!isExternalModuleNameRelative(moduleName)) {
1504                const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference);
1505                // Climb up parent directories looking for a module.
1506                const resolved = forEachAncestorDirectory(containingDirectory, directory => {
1507                    const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, directory, state);
1508                    if (resolutionFromCache) {
1509                        return resolutionFromCache;
1510                    }
1511                    const searchName = normalizePath(combinePaths(directory, moduleName));
1512                    return toSearchResult(loadModuleFromFileNoPackageId(extensions, searchName, /*onlyRecordFailures*/ false, state));
1513                });
1514                if (resolved) {
1515                    return resolved;
1516                }
1517                if (extensions === Extensions.TypeScript) {
1518                    // If we didn't find the file normally, look it up in @types.
1519                    return loadModuleFromNearestModulesDirectoryTypesScope(moduleName, containingDirectory, state);
1520                }
1521            }
1522            else {
1523                const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
1524                return toSearchResult(loadModuleFromFileNoPackageId(extensions, candidate, /*onlyRecordFailures*/ false, state));
1525            }
1526        }
1527    }
1528
1529    /**
1530     * A host may load a module from a global cache of typings.
1531     * This is the minumum code needed to expose that functionality; the rest is in the host.
1532     */
1533    /* @internal */
1534    export function loadModuleFromGlobalCache(moduleName: string, projectName: string | undefined, compilerOptions: CompilerOptions, host: ModuleResolutionHost, globalCache: string): ResolvedModuleWithFailedLookupLocations {
1535        const traceEnabled = isTraceEnabled(compilerOptions, host);
1536        if (traceEnabled) {
1537            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);
1538        }
1539        const failedLookupLocations: string[] = [];
1540        const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations };
1541        const resolved = loadModuleFromImmediateModulesDirectory(Extensions.DtsOnly, moduleName, globalCache, state, /*typesScopeOnly*/ false);
1542        return createResolvedModuleWithFailedLookupLocations(resolved, /*isExternalLibraryImport*/ true, failedLookupLocations, state.resultFromCache);
1543    }
1544
1545    /**
1546     * Represents result of search. Normally when searching among several alternatives we treat value `undefined` as indicator
1547     * that search fails and we should try another option.
1548     * 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).
1549     * SearchResult is used to deal with this issue, its values represents following outcomes:
1550     * - undefined - not found, continue searching
1551     * - { value: undefined } - not found - stop searching
1552     * - { value: <some-value> } - found - stop searching
1553     */
1554    type SearchResult<T> = { value: T | undefined } | undefined;
1555
1556    /**
1557     * Wraps value to SearchResult.
1558     * @returns undefined if value is undefined or { value } otherwise
1559     */
1560    function toSearchResult<T>(value: T | undefined): SearchResult<T> {
1561        return value !== undefined ? { value } : undefined;
1562    }
1563}
1564