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