mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 12:51:30 -05:00
Support find-all-references for default keyword (#17992)
* Support find-all-references for anonymous default exports * Also handle re-exported default exports * Add test for using `export =` with `--allowSyntheticDefaultExports`
This commit is contained in:
@@ -22987,6 +22987,9 @@ namespace ts {
|
||||
: undefined;
|
||||
return objectType && getPropertyOfType(objectType, escapeLeadingUnderscores((node as StringLiteral | NumericLiteral).text));
|
||||
|
||||
case SyntaxKind.DefaultKeyword:
|
||||
return getSymbolOfNode(node.parent);
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -176,7 +176,9 @@ namespace ts.FindAllReferences {
|
||||
fileName: node.getSourceFile().fileName,
|
||||
textSpan: getTextSpan(node),
|
||||
isWriteAccess: isWriteAccess(node),
|
||||
isDefinition: isAnyDeclarationName(node) || isLiteralComputedPropertyDeclarationName(node),
|
||||
isDefinition: node.kind === SyntaxKind.DefaultKeyword
|
||||
|| isAnyDeclarationName(node)
|
||||
|| isLiteralComputedPropertyDeclarationName(node),
|
||||
isInString
|
||||
};
|
||||
}
|
||||
@@ -243,7 +245,7 @@ namespace ts.FindAllReferences {
|
||||
|
||||
/** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */
|
||||
function isWriteAccess(node: Node): boolean {
|
||||
if (isAnyDeclarationName(node)) {
|
||||
if (node.kind === SyntaxKind.DefaultKeyword || isAnyDeclarationName(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -743,7 +745,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
function isValidReferencePosition(node: Node, searchSymbolName: string): boolean {
|
||||
// Compare the length so we filter out strict superstrings of the symbol we are looking for
|
||||
switch (node && node.kind) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return (node as Identifier).text.length === searchSymbolName.length;
|
||||
|
||||
@@ -754,6 +756,9 @@ namespace ts.FindAllReferences.Core {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return isLiteralNameOfPropertyDeclarationOrIndexAccess(node as NumericLiteral) && (node as NumericLiteral).text.length === searchSymbolName.length;
|
||||
|
||||
case SyntaxKind.DefaultKeyword:
|
||||
return "default".length === searchSymbolName.length;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -180,7 +180,6 @@ namespace ts.FindAllReferences {
|
||||
* But re-exports will be placed in 'singleReferences' since they cannot be locally referenced.
|
||||
*/
|
||||
function getSearchesFromDirectImports(directImports: Importer[], exportSymbol: Symbol, exportKind: ExportKind, checker: TypeChecker, isForRename: boolean): Pick<ImportsResult, "importSearches" | "singleReferences"> {
|
||||
const exportName = exportSymbol.escapedName;
|
||||
const importSearches: Array<[Identifier, Symbol]> = [];
|
||||
const singleReferences: Identifier[] = [];
|
||||
function addSearch(location: Identifier, symbol: Symbol): void {
|
||||
@@ -218,12 +217,11 @@ namespace ts.FindAllReferences {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decl.importClause) {
|
||||
const { importClause } = decl;
|
||||
if (!importClause) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { importClause } = decl;
|
||||
|
||||
const { namedBindings } = importClause;
|
||||
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
|
||||
handleNamespaceImportLike(namedBindings.name);
|
||||
@@ -245,7 +243,6 @@ namespace ts.FindAllReferences {
|
||||
|
||||
// 'default' might be accessed as a named import `{ default as foo }`.
|
||||
if (!isForRename && exportKind === ExportKind.Default) {
|
||||
Debug.assert(exportName === "default");
|
||||
searchForNamedImport(namedBindings as NamedImports | undefined);
|
||||
}
|
||||
}
|
||||
@@ -258,36 +255,43 @@ namespace ts.FindAllReferences {
|
||||
*/
|
||||
function handleNamespaceImportLike(importName: Identifier): void {
|
||||
// Don't rename an import that already has a different name than the export.
|
||||
if (exportKind === ExportKind.ExportEquals && (!isForRename || importName.escapedText === exportName)) {
|
||||
if (exportKind === ExportKind.ExportEquals && (!isForRename || isNameMatch(importName.escapedText))) {
|
||||
addSearch(importName, checker.getSymbolAtLocation(importName));
|
||||
}
|
||||
}
|
||||
|
||||
function searchForNamedImport(namedBindings: NamedImportsOrExports | undefined): void {
|
||||
if (namedBindings) {
|
||||
for (const element of namedBindings.elements) {
|
||||
const { name, propertyName } = element;
|
||||
if ((propertyName || name).escapedText !== exportName) {
|
||||
continue;
|
||||
}
|
||||
if (!namedBindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyName) {
|
||||
// This is `import { foo as bar } from "./a"` or `export { foo as bar } from "./a"`. `foo` isn't a local in the file, so just add it as a single reference.
|
||||
singleReferences.push(propertyName);
|
||||
if (!isForRename) { // If renaming `foo`, don't touch `bar`, just `foo`.
|
||||
// Search locally for `bar`.
|
||||
addSearch(name, checker.getSymbolAtLocation(name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const localSymbol = element.kind === SyntaxKind.ExportSpecifier && element.propertyName
|
||||
? checker.getExportSpecifierLocalTargetSymbol(element) // For re-exporting under a different name, we want to get the re-exported symbol.
|
||||
: checker.getSymbolAtLocation(name);
|
||||
addSearch(name, localSymbol);
|
||||
for (const element of namedBindings.elements) {
|
||||
const { name, propertyName } = element;
|
||||
if (!isNameMatch((propertyName || name).escapedText)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertyName) {
|
||||
// This is `import { foo as bar } from "./a"` or `export { foo as bar } from "./a"`. `foo` isn't a local in the file, so just add it as a single reference.
|
||||
singleReferences.push(propertyName);
|
||||
if (!isForRename) { // If renaming `foo`, don't touch `bar`, just `foo`.
|
||||
// Search locally for `bar`.
|
||||
addSearch(name, checker.getSymbolAtLocation(name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const localSymbol = element.kind === SyntaxKind.ExportSpecifier && element.propertyName
|
||||
? checker.getExportSpecifierLocalTargetSymbol(element) // For re-exporting under a different name, we want to get the re-exported symbol.
|
||||
: checker.getSymbolAtLocation(name);
|
||||
addSearch(name, localSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isNameMatch(name: __String): boolean {
|
||||
// Use name of "default" even in `export =` case because we may have allowSyntheticDefaultImports
|
||||
return name === exportSymbol.escapedName || exportKind !== ExportKind.Named && name === "default";
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns 'true' is the namespace 'name' is re-exported from this module, and 'false' if it is only used locally. */
|
||||
@@ -413,7 +417,7 @@ namespace ts.FindAllReferences {
|
||||
case SyntaxKind.ExternalModuleReference:
|
||||
return (decl as ExternalModuleReference).parent;
|
||||
default:
|
||||
Debug.fail(`Unexpected module specifier parent: ${decl.kind}`);
|
||||
Debug.fail("Unexpected module specifier parent: " + decl.kind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,11 +472,11 @@ namespace ts.FindAllReferences {
|
||||
return exportInfo(symbol, getExportKindForDeclaration(exportNode));
|
||||
}
|
||||
}
|
||||
// If we are in `export = a;`, `parent` is the export assignment.
|
||||
// If we are in `export = a;` or `export default a;`, `parent` is the export assignment.
|
||||
else if (isExportAssignment(parent)) {
|
||||
return getExportAssignmentExport(parent);
|
||||
}
|
||||
// If we are in `export = class A {};` at `A`, `parent.parent` is the export assignment.
|
||||
// If we are in `export = class A {};` (or `export = class A {};`) at `A`, `parent.parent` is the export assignment.
|
||||
else if (isExportAssignment(parent.parent)) {
|
||||
return getExportAssignmentExport(parent.parent);
|
||||
}
|
||||
@@ -489,7 +493,8 @@ namespace ts.FindAllReferences {
|
||||
// Get the symbol for the `export =` node; its parent is the module it's the export of.
|
||||
const exportingModuleSymbol = ex.symbol.parent;
|
||||
Debug.assert(!!exportingModuleSymbol);
|
||||
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol, exportKind: ExportKind.ExportEquals } };
|
||||
const exportKind = ex.isExportEquals ? ExportKind.ExportEquals : ExportKind.Default;
|
||||
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol, exportKind } };
|
||||
}
|
||||
|
||||
function getSpecialPropertyExport(node: ts.BinaryExpression, useLhsSymbol: boolean): ExportedSymbol | undefined {
|
||||
@@ -525,7 +530,11 @@ namespace ts.FindAllReferences {
|
||||
importedSymbol = getExportEqualsLocalSymbol(importedSymbol, checker);
|
||||
}
|
||||
|
||||
if (symbolName(importedSymbol) === symbol.escapedName) { // If this is a rename import, do not continue searching.
|
||||
// If the import has a different name than the export, do not continue searching.
|
||||
// If `importedName` is undefined, do continue searching as the export is anonymous.
|
||||
// (All imports returned from this function will be ignored anyway if we are in rename and this is a not a named export.)
|
||||
const importedName = symbolName(importedSymbol);
|
||||
if (importedName === undefined || importedName === "default" || importedName === symbol.escapedName) {
|
||||
return { kind: ImportExport.Import, symbol: importedSymbol, ...isImport };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user