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