• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2namespace ts {
3    export const enum ImportKind {
4        Named,
5        Default,
6        Namespace,
7        CommonJS,
8    }
9
10    export const enum ExportKind {
11        Named,
12        Default,
13        ExportEquals,
14        UMD,
15    }
16
17    export interface SymbolExportInfo {
18        readonly symbol: Symbol;
19        readonly moduleSymbol: Symbol;
20        /** Set if `moduleSymbol` is an external module, not an ambient module */
21        moduleFileName: string | undefined;
22        exportKind: ExportKind;
23        targetFlags: SymbolFlags;
24        /** True if export was only found via the package.json AutoImportProvider (for telemetry). */
25        isFromPackageJson: boolean;
26    }
27
28    interface CachedSymbolExportInfo {
29        // Used to rehydrate `symbol` and `moduleSymbol` when transient
30        id: number;
31        symbolName: string;
32        capitalizedSymbolName: string | undefined;
33        symbolTableKey: __String;
34        moduleName: string;
35        moduleFile: SourceFile | undefined;
36        packageName: string | undefined;
37
38        // SymbolExportInfo, but optional symbols
39        readonly symbol: Symbol | undefined;
40        readonly moduleSymbol: Symbol | undefined;
41        moduleFileName: string | undefined;
42        exportKind: ExportKind;
43        targetFlags: SymbolFlags;
44        isFromPackageJson: boolean;
45    }
46
47    export interface ExportInfoMap {
48        isUsableByFile(importingFile: Path): boolean;
49        clear(): void;
50        add(importingFile: Path, symbol: Symbol, key: __String, moduleSymbol: Symbol, moduleFile: SourceFile | undefined, exportKind: ExportKind, isFromPackageJson: boolean, checker: TypeChecker): void;
51        get(importingFile: Path, key: string): readonly SymbolExportInfo[] | undefined;
52        search<T>(importingFile: Path, preferCapitalized: boolean, matches: (name: string, targetFlags: SymbolFlags) => boolean, action: (info: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean, key: string) => T | undefined): T | undefined;
53        releaseSymbols(): void;
54        isEmpty(): boolean;
55        /** @returns Whether the change resulted in the cache being cleared */
56        onFileChanged(oldSourceFile: SourceFile, newSourceFile: SourceFile, typeAcquisitionEnabled: boolean): boolean;
57    }
58
59    export interface CacheableExportInfoMapHost {
60        getCurrentProgram(): Program | undefined;
61        getPackageJsonAutoImportProvider(): Program | undefined;
62        getGlobalTypingsCacheLocation(): string | undefined;
63    }
64
65    export function createCacheableExportInfoMap(host: CacheableExportInfoMapHost): ExportInfoMap {
66        let exportInfoId = 1;
67        const exportInfo = createMultiMap<string, CachedSymbolExportInfo>();
68        const symbols = new Map<number, [symbol: Symbol, moduleSymbol: Symbol]>();
69        /**
70         * Key: node_modules package name (no @types).
71         * Value: path to deepest node_modules folder seen that is
72         * both visible to `usableByFileName` and contains the package.
73         *
74         * Later, we can see if a given SymbolExportInfo is shadowed by
75         * a another installation of the same package in a deeper
76         * node_modules folder by seeing if its path starts with the
77         * value stored here.
78         */
79        const packages = new Map<string, string>();
80        let usableByFileName: Path | undefined;
81        const cache: ExportInfoMap = {
82            isUsableByFile: importingFile => importingFile === usableByFileName,
83            isEmpty: () => !exportInfo.size,
84            clear: () => {
85                exportInfo.clear();
86                symbols.clear();
87                usableByFileName = undefined;
88            },
89            add: (importingFile, symbol, symbolTableKey, moduleSymbol, moduleFile, exportKind, isFromPackageJson, checker) => {
90                if (importingFile !== usableByFileName) {
91                    cache.clear();
92                    usableByFileName = importingFile;
93                }
94
95                let packageName;
96                if (moduleFile) {
97                    const nodeModulesPathParts = getNodeModulePathParts(moduleFile.fileName, isOhpm(host.getCurrentProgram()?.getCompilerOptions().packageManagerType) ? ohModulesPathPart : nodeModulesPathPart);
98                    if (nodeModulesPathParts) {
99                        const { topLevelNodeModulesIndex, topLevelPackageNameIndex, packageRootIndex } = nodeModulesPathParts;
100                        packageName = unmangleScopedPackageName(getPackageNameFromTypesPackageName(moduleFile.fileName.substring(topLevelPackageNameIndex + 1, packageRootIndex)));
101                        if (startsWith(importingFile, moduleFile.path.substring(0, topLevelNodeModulesIndex))) {
102                            const prevDeepestNodeModulesPath = packages.get(packageName);
103                            const nodeModulesPath = moduleFile.fileName.substring(0, topLevelPackageNameIndex + 1);
104                            if (prevDeepestNodeModulesPath) {
105                                const prevDeepestNodeModulesIndex = prevDeepestNodeModulesPath.indexOf(nodeModulesPathPart);
106                                if (topLevelNodeModulesIndex > prevDeepestNodeModulesIndex) {
107                                    packages.set(packageName, nodeModulesPath);
108                                }
109                            }
110                            else {
111                                packages.set(packageName, nodeModulesPath);
112                            }
113                        }
114                    }
115                }
116
117                const isDefault = exportKind === ExportKind.Default;
118                const namedSymbol = isDefault && getLocalSymbolForExportDefault(symbol) || symbol;
119                // 1. A named export must be imported by its key in `moduleSymbol.exports` or `moduleSymbol.members`.
120                // 2. A re-export merged with an export from a module augmentation can result in `symbol`
121                //    being an external module symbol; the name it is re-exported by will be `symbolTableKey`
122                //    (which comes from the keys of `moduleSymbol.exports`.)
123                // 3. Otherwise, we have a default/namespace import that can be imported by any name, and
124                //    `symbolTableKey` will be something undesirable like `export=` or `default`, so we try to
125                //    get a better name.
126                const names = exportKind === ExportKind.Named || isExternalModuleSymbol(namedSymbol)
127                    ? unescapeLeadingUnderscores(symbolTableKey)
128                    : getNamesForExportedSymbol(namedSymbol, /*scriptTarget*/ undefined);
129
130                const symbolName = typeof names === "string" ? names : names[0];
131                const capitalizedSymbolName = typeof names === "string" ? undefined : names[1];
132
133                const moduleName = stripQuotes(moduleSymbol.name);
134                const id = exportInfoId++;
135                const target = skipAlias(symbol, checker);
136                const storedSymbol = symbol.flags & SymbolFlags.Transient ? undefined : symbol;
137                const storedModuleSymbol = moduleSymbol.flags & SymbolFlags.Transient ? undefined : moduleSymbol;
138                if (!storedSymbol || !storedModuleSymbol) symbols.set(id, [symbol, moduleSymbol]);
139
140                exportInfo.add(key(symbolName, symbol, isExternalModuleNameRelative(moduleName) ? undefined : moduleName, checker), {
141                    id,
142                    symbolTableKey,
143                    symbolName,
144                    capitalizedSymbolName,
145                    moduleName,
146                    moduleFile,
147                    moduleFileName: moduleFile?.fileName,
148                    packageName,
149                    exportKind,
150                    targetFlags: target.flags,
151                    isFromPackageJson,
152                    symbol: storedSymbol,
153                    moduleSymbol: storedModuleSymbol,
154                });
155            },
156            get: (importingFile, key) => {
157                if (importingFile !== usableByFileName) return;
158                const result = exportInfo.get(key);
159                return result?.map(rehydrateCachedInfo);
160            },
161            search: (importingFile, preferCapitalized, matches, action) => {
162                if (importingFile !== usableByFileName) return;
163                return forEachEntry(exportInfo, (info, key) => {
164                    const { symbolName, ambientModuleName } = parseKey(key);
165                    const name = preferCapitalized && info[0].capitalizedSymbolName || symbolName;
166                    if (matches(name, info[0].targetFlags)) {
167                        const rehydrated = info.map(rehydrateCachedInfo);
168                        const filtered = rehydrated.filter((r, i) => isNotShadowedByDeeperNodeModulesPackage(r, info[i].packageName));
169                        if (filtered.length) {
170                            const res = action(filtered, name, !!ambientModuleName, key);
171                            if (res !== undefined) return res;
172                        }
173                    }
174                });
175            },
176            releaseSymbols: () => {
177                symbols.clear();
178            },
179            onFileChanged: (oldSourceFile: SourceFile, newSourceFile: SourceFile, typeAcquisitionEnabled: boolean) => {
180                if (fileIsGlobalOnly(oldSourceFile) && fileIsGlobalOnly(newSourceFile)) {
181                    // File is purely global; doesn't affect export map
182                    return false;
183                }
184                if (
185                    usableByFileName && usableByFileName !== newSourceFile.path ||
186                    // If ATA is enabled, auto-imports uses existing imports to guess whether you want auto-imports from node.
187                    // Adding or removing imports from node could change the outcome of that guess, so could change the suggestions list.
188                    typeAcquisitionEnabled && consumesNodeCoreModules(oldSourceFile) !== consumesNodeCoreModules(newSourceFile) ||
189                    // Module agumentation and ambient module changes can add or remove exports available to be auto-imported.
190                    // Changes elsewhere in the file can change the *type* of an export in a module augmentation,
191                    // but type info is gathered in getCompletionEntryDetails, which doesn’t use the cache.
192                    !arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations) ||
193                    !ambientModuleDeclarationsAreEqual(oldSourceFile, newSourceFile)
194                ) {
195                    cache.clear();
196                    return true;
197                }
198                usableByFileName = newSourceFile.path;
199                return false;
200            },
201        };
202        if (Debug.isDebugging) {
203            Object.defineProperty(cache, "__cache", { get: () => exportInfo });
204        }
205        return cache;
206
207        function rehydrateCachedInfo(info: CachedSymbolExportInfo): SymbolExportInfo {
208            if (info.symbol && info.moduleSymbol) return info as SymbolExportInfo;
209            const { id, exportKind, targetFlags, isFromPackageJson, moduleFileName } = info;
210            const [cachedSymbol, cachedModuleSymbol] = symbols.get(id) || emptyArray;
211            if (cachedSymbol && cachedModuleSymbol) {
212                return {
213                    symbol: cachedSymbol,
214                    moduleSymbol: cachedModuleSymbol,
215                    moduleFileName,
216                    exportKind,
217                    targetFlags,
218                    isFromPackageJson,
219                };
220            }
221            const checker = (isFromPackageJson
222                ? host.getPackageJsonAutoImportProvider()!
223                : host.getCurrentProgram()!).getTypeChecker();
224            const moduleSymbol = info.moduleSymbol || cachedModuleSymbol || Debug.checkDefined(info.moduleFile
225                ? checker.getMergedSymbol(info.moduleFile.symbol)
226                : checker.tryFindAmbientModule(info.moduleName));
227            const symbol = info.symbol || cachedSymbol || Debug.checkDefined(exportKind === ExportKind.ExportEquals
228                ? checker.resolveExternalModuleSymbol(moduleSymbol)
229                : checker.tryGetMemberInModuleExportsAndProperties(unescapeLeadingUnderscores(info.symbolTableKey), moduleSymbol),
230                `Could not find symbol '${info.symbolName}' by key '${info.symbolTableKey}' in module ${moduleSymbol.name}`);
231            symbols.set(id, [symbol, moduleSymbol]);
232            return {
233                symbol,
234                moduleSymbol,
235                moduleFileName,
236                exportKind,
237                targetFlags,
238                isFromPackageJson,
239            };
240        }
241
242        function key(importedName: string, symbol: Symbol, ambientModuleName: string | undefined, checker: TypeChecker): string {
243            const moduleKey = ambientModuleName || "";
244            return `${importedName}|${getSymbolId(skipAlias(symbol, checker))}|${moduleKey}`;
245        }
246
247        function parseKey(key: string) {
248            const symbolName = key.substring(0, key.indexOf("|"));
249            const moduleKey = key.substring(key.lastIndexOf("|") + 1);
250            const ambientModuleName = moduleKey === "" ? undefined : moduleKey;
251            return { symbolName, ambientModuleName };
252        }
253
254        function fileIsGlobalOnly(file: SourceFile) {
255            return !file.commonJsModuleIndicator && !file.externalModuleIndicator && !file.moduleAugmentations && !file.ambientModuleNames;
256        }
257
258        function ambientModuleDeclarationsAreEqual(oldSourceFile: SourceFile, newSourceFile: SourceFile) {
259            if (!arrayIsEqualTo(oldSourceFile.ambientModuleNames, newSourceFile.ambientModuleNames)) {
260                return false;
261            }
262            let oldFileStatementIndex = -1;
263            let newFileStatementIndex = -1;
264            for (const ambientModuleName of newSourceFile.ambientModuleNames) {
265                const isMatchingModuleDeclaration = (node: Statement) => isNonGlobalAmbientModule(node) && node.name.text === ambientModuleName;
266                oldFileStatementIndex = findIndex(oldSourceFile.statements, isMatchingModuleDeclaration, oldFileStatementIndex + 1);
267                newFileStatementIndex = findIndex(newSourceFile.statements, isMatchingModuleDeclaration, newFileStatementIndex + 1);
268                if (oldSourceFile.statements[oldFileStatementIndex] !== newSourceFile.statements[newFileStatementIndex]) {
269                    return false;
270                }
271            }
272            return true;
273        }
274
275        function isNotShadowedByDeeperNodeModulesPackage(info: SymbolExportInfo, packageName: string | undefined) {
276            if (!packageName || !info.moduleFileName) return true;
277            const typingsCacheLocation = host.getGlobalTypingsCacheLocation();
278            if (typingsCacheLocation && startsWith(info.moduleFileName, typingsCacheLocation)) return true;
279            const packageDeepestNodeModulesPath = packages.get(packageName);
280            return !packageDeepestNodeModulesPath || startsWith(info.moduleFileName, packageDeepestNodeModulesPath);
281        }
282    }
283
284    export function isImportableFile(
285        program: Program,
286        from: SourceFile,
287        to: SourceFile,
288        preferences: UserPreferences,
289        packageJsonFilter: PackageJsonImportFilter | undefined,
290        moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost,
291        moduleSpecifierCache: ModuleSpecifierCache | undefined,
292    ): boolean {
293        if (from === to) return false;
294        const cachedResult = moduleSpecifierCache?.get(from.path, to.path, preferences, {});
295        if (cachedResult?.isBlockedByPackageJsonDependencies !== undefined) {
296            return !cachedResult.isBlockedByPackageJsonDependencies;
297        }
298
299        const getCanonicalFileName = hostGetCanonicalFileName(moduleSpecifierResolutionHost);
300        const globalTypingsCache = moduleSpecifierResolutionHost.getGlobalTypingsCacheLocation?.();
301        const hasImportablePath = !!moduleSpecifiers.forEachFileNameOfModule(
302            from.fileName,
303            to.fileName,
304            moduleSpecifierResolutionHost,
305            /*preferSymlinks*/ false,
306            toPath => {
307                const toFile = program.getSourceFile(toPath);
308                // Determine to import using toPath only if toPath is what we were looking at
309                // or there doesnt exist the file in the program by the symlink
310                return (toFile === to || !toFile) &&
311                    isImportablePath(from.fileName, toPath, getCanonicalFileName, globalTypingsCache, program.getCompilerOptions().packageManagerType);
312            }
313        );
314
315        if (packageJsonFilter) {
316            const isAutoImportable = hasImportablePath && packageJsonFilter.allowsImportingSourceFile(to, moduleSpecifierResolutionHost);
317            moduleSpecifierCache?.setBlockedByPackageJsonDependencies(from.path, to.path, preferences, {}, !isAutoImportable);
318            return isAutoImportable;
319        }
320
321        return hasImportablePath;
322    }
323
324    /**
325     * Don't include something from a `node_modules` that isn't actually reachable by a global import.
326     * A relative import to node_modules is usually a bad idea.
327     */
328    function isImportablePath(fromPath: string, toPath: string, getCanonicalFileName: GetCanonicalFileName, globalCachePath?: string, packageManagerType?: string): boolean {
329        // If it's in a `node_modules` but is not reachable from here via a global import, don't bother.
330        const toNodeModules = forEachAncestorDirectory(toPath, ancestor => (getBaseFileName(ancestor) === getModuleByPMType(packageManagerType)) ? ancestor : undefined);
331        const toNodeModulesParent = toNodeModules && getDirectoryPath(getCanonicalFileName(toNodeModules));
332        return toNodeModulesParent === undefined
333            || startsWith(getCanonicalFileName(fromPath), toNodeModulesParent)
334            || (!!globalCachePath && startsWith(getCanonicalFileName(globalCachePath), toNodeModulesParent));
335    }
336
337    export function forEachExternalModuleToImportFrom(
338        program: Program,
339        host: LanguageServiceHost,
340        preferences: UserPreferences,
341        useAutoImportProvider: boolean,
342        cb: (module: Symbol, moduleFile: SourceFile | undefined, program: Program, isFromPackageJson: boolean) => void,
343    ) {
344        const useCaseSensitiveFileNames = hostUsesCaseSensitiveFileNames(host);
345        const excludePatterns = preferences.autoImportFileExcludePatterns && mapDefined(preferences.autoImportFileExcludePatterns, spec => {
346            // The client is expected to send rooted path specs since we don't know
347            // what directory a relative path is relative to.
348            const pattern = getPatternFromSpec(spec, "", "exclude");
349            return pattern ? getRegexFromPattern(pattern, useCaseSensitiveFileNames) : undefined;
350        });
351
352        forEachExternalModule(program.getTypeChecker(), program.getSourceFiles(), excludePatterns, (module, file) => cb(module, file, program, /*isFromPackageJson*/ false));
353        const autoImportProvider = useAutoImportProvider && host.getPackageJsonAutoImportProvider?.();
354        if (autoImportProvider) {
355            const start = timestamp();
356            forEachExternalModule(autoImportProvider.getTypeChecker(), autoImportProvider.getSourceFiles(), excludePatterns, (module, file) => cb(module, file, autoImportProvider, /*isFromPackageJson*/ true));
357            host.log?.(`forEachExternalModuleToImportFrom autoImportProvider: ${timestamp() - start}`);
358        }
359    }
360
361    function forEachExternalModule(checker: TypeChecker, allSourceFiles: readonly SourceFile[], excludePatterns: readonly RegExp[] | undefined, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) {
362        const isExcluded = (fileName: string) => excludePatterns?.some(p => p.test(fileName));
363        for (const ambient of checker.getAmbientModules()) {
364            if (!stringContains(ambient.name, "*") && !(excludePatterns && ambient.declarations?.every(d => isExcluded(d.getSourceFile().fileName)))) {
365                cb(ambient, /*sourceFile*/ undefined);
366            }
367        }
368        for (const sourceFile of allSourceFiles) {
369            if (isExternalOrCommonJsModule(sourceFile) && !isExcluded(sourceFile.fileName)) {
370                cb(checker.getMergedSymbol(sourceFile.symbol), sourceFile);
371            }
372        }
373    }
374
375    export function getExportInfoMap(importingFile: SourceFile, host: LanguageServiceHost, program: Program, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): ExportInfoMap {
376        const start = timestamp();
377        // Pulling the AutoImportProvider project will trigger its updateGraph if pending,
378        // which will invalidate the export map cache if things change, so pull it before
379        // checking the cache.
380        host.getPackageJsonAutoImportProvider?.();
381        const cache = host.getCachedExportInfoMap?.() || createCacheableExportInfoMap({
382            getCurrentProgram: () => program,
383            getPackageJsonAutoImportProvider: () => host.getPackageJsonAutoImportProvider?.(),
384            getGlobalTypingsCacheLocation: () => host.getGlobalTypingsCacheLocation?.(),
385        });
386
387        if (cache.isUsableByFile(importingFile.path)) {
388            host.log?.("getExportInfoMap: cache hit");
389            return cache;
390        }
391
392        host.log?.("getExportInfoMap: cache miss or empty; calculating new results");
393        const compilerOptions = program.getCompilerOptions();
394        let moduleCount = 0;
395        try {
396            forEachExternalModuleToImportFrom(program, host, preferences, /*useAutoImportProvider*/ true, (moduleSymbol, moduleFile, program, isFromPackageJson) => {
397                if (++moduleCount % 100 === 0) cancellationToken?.throwIfCancellationRequested();
398                const seenExports = new Map<__String, true>();
399                const checker = program.getTypeChecker();
400                const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
401                // Note: I think we shouldn't actually see resolved module symbols here, but weird merges
402                // can cause it to happen: see 'completionsImport_mergedReExport.ts'
403                if (defaultInfo && isImportableSymbol(defaultInfo.symbol, checker)) {
404                    cache.add(
405                        importingFile.path,
406                        defaultInfo.symbol,
407                        defaultInfo.exportKind === ExportKind.Default ? InternalSymbolName.Default : InternalSymbolName.ExportEquals,
408                        moduleSymbol,
409                        moduleFile,
410                        defaultInfo.exportKind,
411                        isFromPackageJson,
412                        checker);
413                }
414                checker.forEachExportAndPropertyOfModule(moduleSymbol, (exported, key) => {
415                    if (exported !== defaultInfo?.symbol && isImportableSymbol(exported, checker) && addToSeen(seenExports, key)) {
416                        cache.add(
417                            importingFile.path,
418                            exported,
419                            key,
420                            moduleSymbol,
421                            moduleFile,
422                            ExportKind.Named,
423                            isFromPackageJson,
424                            checker);
425                    }
426                });
427            });
428        }
429        catch (err) {
430            // Ensure cache is reset if operation is cancelled
431            cache.clear();
432            throw err;
433        }
434
435        host.log?.(`getExportInfoMap: done in ${timestamp() - start} ms`);
436        return cache;
437    }
438
439    export function getDefaultLikeExportInfo(moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions) {
440        const exported = getDefaultLikeExportWorker(moduleSymbol, checker);
441        if (!exported) return undefined;
442        const { symbol, exportKind } = exported;
443        const info = getDefaultExportInfoWorker(symbol, checker, compilerOptions);
444        return info && { symbol, exportKind, ...info };
445    }
446
447    function isImportableSymbol(symbol: Symbol, checker: TypeChecker) {
448        return !checker.isUndefinedSymbol(symbol) && !checker.isUnknownSymbol(symbol) && !isKnownSymbol(symbol) && !isPrivateIdentifierSymbol(symbol);
449    }
450
451    function getDefaultLikeExportWorker(moduleSymbol: Symbol, checker: TypeChecker): { readonly symbol: Symbol, readonly exportKind: ExportKind } | undefined {
452        const exportEquals = checker.resolveExternalModuleSymbol(moduleSymbol);
453        if (exportEquals !== moduleSymbol) return { symbol: exportEquals, exportKind: ExportKind.ExportEquals };
454        const defaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol);
455        if (defaultExport) return { symbol: defaultExport, exportKind: ExportKind.Default };
456    }
457
458    function getDefaultExportInfoWorker(defaultExport: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
459        const localSymbol = getLocalSymbolForExportDefault(defaultExport);
460        if (localSymbol) return { symbolForMeaning: localSymbol, name: localSymbol.name };
461
462        const name = getNameForExportDefault(defaultExport);
463        if (name !== undefined) return { symbolForMeaning: defaultExport, name };
464
465        if (defaultExport.flags & SymbolFlags.Alias) {
466            const aliased = checker.getImmediateAliasedSymbol(defaultExport);
467            if (aliased && aliased.parent) {
468                // - `aliased` will be undefined if the module is exporting an unresolvable name,
469                //    but we can still offer completions for it.
470                // - `aliased.parent` will be undefined if the module is exporting `globalThis.something`,
471                //    or another expression that resolves to a global.
472                return getDefaultExportInfoWorker(aliased, checker, compilerOptions);
473            }
474        }
475
476        if (defaultExport.escapedName !== InternalSymbolName.Default &&
477            defaultExport.escapedName !== InternalSymbolName.ExportEquals) {
478            return { symbolForMeaning: defaultExport, name: defaultExport.getName() };
479        }
480        return { symbolForMeaning: defaultExport, name: getNameForExportedSymbol(defaultExport, compilerOptions.target) };
481    }
482
483    function getNameForExportDefault(symbol: Symbol): string | undefined {
484        return symbol.declarations && firstDefined(symbol.declarations, declaration => {
485            if (isExportAssignment(declaration)) {
486                return tryCast(skipOuterExpressions(declaration.expression), isIdentifier)?.text;
487            }
488            else if (isExportSpecifier(declaration)) {
489                Debug.assert(declaration.name.text === InternalSymbolName.Default, "Expected the specifier to be a default export");
490                return declaration.propertyName && declaration.propertyName.text;
491            }
492        });
493    }
494}
495