[DO NOT MERGE UNTIL 5.6] Fix re-exported defaults in ExportInfoMap (#58837)
This commit is contained in:
Родитель
46fe067a06
Коммит
1948e92e3a
|
@ -1187,7 +1187,7 @@ function getNewImportFixes(
|
|||
const exportEquals = checker.resolveExternalModuleSymbol(exportInfo.moduleSymbol);
|
||||
let namespacePrefix;
|
||||
if (exportEquals !== exportInfo.moduleSymbol) {
|
||||
namespacePrefix = forEachNameOfDefaultExport(exportEquals, checker, compilerOptions, /*preferCapitalizedNames*/ false, identity)!;
|
||||
namespacePrefix = forEachNameOfDefaultExport(exportEquals, checker, getEmitScriptTarget(compilerOptions), identity)!;
|
||||
}
|
||||
namespacePrefix ||= moduleSymbolToValidIdentifier(
|
||||
exportInfo.moduleSymbol,
|
||||
|
@ -1544,7 +1544,7 @@ function getExportInfos(
|
|||
if (
|
||||
defaultInfo
|
||||
&& symbolFlagsHaveMeaning(checker.getSymbolFlags(defaultInfo.symbol), currentTokenMeaning)
|
||||
&& forEachNameOfDefaultExport(defaultInfo.symbol, checker, compilerOptions, isJsxTagName, name => name === symbolName)
|
||||
&& forEachNameOfDefaultExport(defaultInfo.symbol, checker, getEmitScriptTarget(compilerOptions), (name, capitalizedName) => (isJsxTagName ? capitalizedName ?? name : name) === symbolName)
|
||||
) {
|
||||
addSymbol(moduleSymbol, sourceFile, defaultInfo.symbol, defaultInfo.exportKind, program, isFromPackageJson);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
append,
|
||||
arrayIsEqualTo,
|
||||
CancellationToken,
|
||||
CompilerOptions,
|
||||
consumesNodeCoreModules,
|
||||
createMultiMap,
|
||||
Debug,
|
||||
|
@ -18,9 +17,7 @@ import {
|
|||
GetCanonicalFileName,
|
||||
getDefaultLikeExportNameFromDeclaration,
|
||||
getDirectoryPath,
|
||||
getEmitScriptTarget,
|
||||
getLocalSymbolForExportDefault,
|
||||
getNamesForExportedSymbol,
|
||||
getNodeModulePathParts,
|
||||
getPackageNameFromTypesPackageName,
|
||||
getRegexFromPattern,
|
||||
|
@ -46,6 +43,7 @@ import {
|
|||
Path,
|
||||
pathContainsNodeModules,
|
||||
Program,
|
||||
ScriptTarget,
|
||||
skipAlias,
|
||||
SourceFile,
|
||||
startsWith,
|
||||
|
@ -198,7 +196,7 @@ export function createCacheableExportInfoMap(host: CacheableExportInfoMapHost):
|
|||
// get a better name.
|
||||
const names = exportKind === ExportKind.Named || isExternalModuleSymbol(namedSymbol)
|
||||
? unescapeLeadingUnderscores(symbolTableKey)
|
||||
: getNamesForExportedSymbol(namedSymbol, /*scriptTarget*/ undefined);
|
||||
: getNamesForExportedSymbol(namedSymbol, checker, /*scriptTarget*/ undefined);
|
||||
|
||||
const symbolName = typeof names === "string" ? names : names[0];
|
||||
const capitalizedSymbolName = typeof names === "string" ? undefined : names[1];
|
||||
|
@ -558,12 +556,21 @@ function isImportableSymbol(symbol: Symbol, checker: TypeChecker) {
|
|||
return !checker.isUndefinedSymbol(symbol) && !checker.isUnknownSymbol(symbol) && !isKnownSymbol(symbol) && !isPrivateIdentifierSymbol(symbol);
|
||||
}
|
||||
|
||||
function getNamesForExportedSymbol(defaultExport: Symbol, checker: TypeChecker, scriptTarget: ScriptTarget | undefined) {
|
||||
let names: string | string[] | undefined;
|
||||
forEachNameOfDefaultExport(defaultExport, checker, scriptTarget, (name, capitalizedName) => {
|
||||
names = capitalizedName ? [name, capitalizedName] : name;
|
||||
return true;
|
||||
});
|
||||
return Debug.checkDefined(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* May call `cb` multiple times with the same name.
|
||||
* Terminates when `cb` returns a truthy value.
|
||||
*/
|
||||
export function forEachNameOfDefaultExport<T>(defaultExport: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions, preferCapitalizedNames: boolean, cb: (name: string) => T | undefined): T | undefined {
|
||||
export function forEachNameOfDefaultExport<T>(defaultExport: Symbol, checker: TypeChecker, scriptTarget: ScriptTarget | undefined, cb: (name: string, capitalizedName?: string) => T | undefined): T | undefined {
|
||||
let chain: Symbol[] | undefined;
|
||||
let current: Symbol | undefined = defaultExport;
|
||||
|
||||
|
@ -588,7 +595,10 @@ export function forEachNameOfDefaultExport<T>(defaultExport: Symbol, checker: Ty
|
|||
|
||||
for (const symbol of chain ?? emptyArray) {
|
||||
if (symbol.parent && isExternalModuleSymbol(symbol.parent)) {
|
||||
const final = cb(moduleSymbolToValidIdentifier(symbol.parent, getEmitScriptTarget(compilerOptions), preferCapitalizedNames));
|
||||
const final = cb(
|
||||
moduleSymbolToValidIdentifier(symbol.parent, scriptTarget, /*forceCapitalize*/ false),
|
||||
moduleSymbolToValidIdentifier(symbol.parent, scriptTarget, /*forceCapitalize*/ true),
|
||||
);
|
||||
if (final) return final;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4024,22 +4024,13 @@ export function firstOrOnly<T>(valueOrArray: T | readonly T[]): T {
|
|||
return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getNamesForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined): string | [lowercase: string, capitalized: string] {
|
||||
if (needsNameFromDeclaration(symbol)) {
|
||||
const fromDeclaration = getDefaultLikeExportNameFromDeclaration(symbol);
|
||||
if (fromDeclaration) return fromDeclaration;
|
||||
const fileNameCase = moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ false);
|
||||
const capitalized = moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ true);
|
||||
if (fileNameCase === capitalized) return fileNameCase;
|
||||
return [fileNameCase, capitalized];
|
||||
}
|
||||
return symbol.name;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
/**
|
||||
* If a type checker and multiple files are available, consider using `forEachNameOfDefaultExport`
|
||||
* instead, which searches for names of re-exported defaults/namespaces in target files.
|
||||
* @internal
|
||||
*/
|
||||
export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined, preferCapitalized?: boolean) {
|
||||
if (needsNameFromDeclaration(symbol)) {
|
||||
if (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default) {
|
||||
// Names for default exports:
|
||||
// - export default foo => foo
|
||||
// - export { foo as default } => foo
|
||||
|
@ -4050,11 +4041,11 @@ export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTar
|
|||
return symbol.name;
|
||||
}
|
||||
|
||||
function needsNameFromDeclaration(symbol: Symbol) {
|
||||
return !(symbol.flags & SymbolFlags.Transient) && (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
/**
|
||||
* If a type checker and multiple files are available, consider using `forEachNameOfDefaultExport`
|
||||
* instead, which searches for names of re-exported defaults/namespaces in target files.
|
||||
* @internal
|
||||
*/
|
||||
export function getDefaultLikeExportNameFromDeclaration(symbol: Symbol): string | undefined {
|
||||
return firstDefined(symbol.declarations, d => {
|
||||
// "export default" in this case. See `ExportAssignment`for more details.
|
||||
|
|
|
@ -380,7 +380,7 @@ Info seq [hh:mm:ss:mss] getCompletionData: Is inside comment: *
|
|||
Info seq [hh:mm:ss:mss] getCompletionData: Get previous token: *
|
||||
Info seq [hh:mm:ss:mss] getExportInfoMap: cache miss or empty; calculating new results
|
||||
Info seq [hh:mm:ss:mss] getExportInfoMap: done in * ms
|
||||
Info seq [hh:mm:ss:mss] collectAutoImports: resolved 0 module specifiers, plus 0 ambient and 3 from cache
|
||||
Info seq [hh:mm:ss:mss] collectAutoImports: resolved 0 module specifiers, plus 0 ambient and 4 from cache
|
||||
Info seq [hh:mm:ss:mss] collectAutoImports: response is incomplete
|
||||
Info seq [hh:mm:ss:mss] collectAutoImports: *
|
||||
Info seq [hh:mm:ss:mss] getCompletionData: Semantic work: *
|
||||
|
@ -1053,6 +1053,19 @@ Info seq [hh:mm:ss:mss] response:
|
|||
"fileName": "/third_party/marked/src/defaults.js"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "defaults",
|
||||
"kind": "property",
|
||||
"kindModifiers": "",
|
||||
"sortText": "16",
|
||||
"hasAction": true,
|
||||
"source": "/third_party/marked/src/defaults",
|
||||
"data": {
|
||||
"exportName": "export=",
|
||||
"exportMapKey": "8 * defaults ",
|
||||
"fileName": "/third_party/marked/src/defaults.js"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "defaults",
|
||||
"kind": "alias",
|
||||
|
@ -1257,7 +1270,7 @@ Info seq [hh:mm:ss:mss] getCompletionData: Get current token: *
|
|||
Info seq [hh:mm:ss:mss] getCompletionData: Is inside comment: *
|
||||
Info seq [hh:mm:ss:mss] getCompletionData: Get previous token: *
|
||||
Info seq [hh:mm:ss:mss] getExportInfoMap: cache hit
|
||||
Info seq [hh:mm:ss:mss] collectAutoImports: resolved 0 module specifiers, plus 0 ambient and 3 from cache
|
||||
Info seq [hh:mm:ss:mss] collectAutoImports: resolved 0 module specifiers, plus 0 ambient and 4 from cache
|
||||
Info seq [hh:mm:ss:mss] collectAutoImports: response is incomplete
|
||||
Info seq [hh:mm:ss:mss] collectAutoImports: *
|
||||
Info seq [hh:mm:ss:mss] getCompletionData: Semantic work: *
|
||||
|
@ -1943,6 +1956,19 @@ Info seq [hh:mm:ss:mss] response:
|
|||
"fileName": "/third_party/marked/src/defaults.js"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "defaults",
|
||||
"kind": "property",
|
||||
"kindModifiers": "",
|
||||
"sortText": "16",
|
||||
"hasAction": true,
|
||||
"source": "/third_party/marked/src/defaults",
|
||||
"data": {
|
||||
"exportName": "export=",
|
||||
"exportMapKey": "8 * defaults ",
|
||||
"fileName": "/third_party/marked/src/defaults.js"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "defaults",
|
||||
"kind": "alias",
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @module: preserve
|
||||
// @checkJs: true
|
||||
|
||||
// @Filename: /node_modules/example/package.json
|
||||
//// { "name": "example", "version": "1.0.0", "main": "dist/index.js" }
|
||||
|
||||
// @Filename: /node_modules/example/dist/nested/module.d.ts
|
||||
//// declare const defaultExport: () => void;
|
||||
//// declare const namedExport: () => void;
|
||||
////
|
||||
//// export default defaultExport;
|
||||
//// export { namedExport };
|
||||
|
||||
// @Filename: /node_modules/example/dist/index.d.ts
|
||||
//// export { default, namedExport } from "./nested/module";
|
||||
|
||||
// @Filename: /index.mjs
|
||||
//// import { namedExport } from "example";
|
||||
//// defaultExp/**/
|
||||
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: completion.globalsInJsPlus([
|
||||
"namedExport",
|
||||
{
|
||||
name: "defaultExport",
|
||||
source: "example",
|
||||
sourceDisplay: "example",
|
||||
hasAction: true,
|
||||
sortText: completion.SortText.AutoImportSuggestions
|
||||
},
|
||||
]),
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true,
|
||||
allowIncompleteCompletions: true,
|
||||
},
|
||||
});
|
Загрузка…
Ссылка в новой задаче