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