From 1c56b98d5cc372e9f879e3413e0d7c5fdb0cab60 Mon Sep 17 00:00:00 2001 From: mag123c Date: Mon, 8 Sep 2025 17:09:47 +0900 Subject: [PATCH 1/3] Fix findAllReferences for export= namespace with ES6 imports --- src/services/findAllReferences.ts | 14 +++++++++++++- .../findAllReferencesExportEqualsNamespace.ts | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/findAllReferencesExportEqualsNamespace.ts diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index a2ed0feb795a7..54518677ef931 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -135,6 +135,7 @@ import { isIdentifier, isIdentifierPart, isImportMeta, + isImportDeclaration, isImportOrExportSpecifier, isImportSpecifier, isImportTypeNode, @@ -1012,7 +1013,18 @@ export namespace Core { const checker = program.getTypeChecker(); // constructors should use the class symbol, detected by name, if present - const symbol = checker.getSymbolAtLocation(isConstructorDeclaration(node) && node.parent.name || node); + let symbol = checker.getSymbolAtLocation(isConstructorDeclaration(node) && node.parent.name || node); + + // Handle export = namespace case where ES6 import cannot resolve the symbol + if (!symbol && isIdentifier(node) && isImportSpecifier(node.parent)) { + const importDeclaration = findAncestor(node, isImportDeclaration); + if (importDeclaration && isImportDeclaration(importDeclaration) && importDeclaration.moduleSpecifier) { + const moduleSymbol = checker.getSymbolAtLocation(importDeclaration.moduleSpecifier); + if (moduleSymbol && moduleSymbol.exports) { + symbol = moduleSymbol.exports.get(node.escapedText); + } + } + } // Could not find a symbol e.g. unknown identifier if (!symbol) { diff --git a/tests/cases/fourslash/findAllReferencesExportEqualsNamespace.ts b/tests/cases/fourslash/findAllReferencesExportEqualsNamespace.ts new file mode 100644 index 0000000000000..d57ccbd7dc1d2 --- /dev/null +++ b/tests/cases/fourslash/findAllReferencesExportEqualsNamespace.ts @@ -0,0 +1,16 @@ +/// + +// @module: commonjs + +// @Filename: /mod.d.ts +////export = React; +//// +////declare namespace React { +//// function /*1*/lazy(): void; +////} + +// @Filename: /index.ts +////import { /*2*/lazy } from "./mod" +/////*3*/lazy(); + +verify.baselineFindAllReferences("1", "2", "3"); \ No newline at end of file From e97a7e9d98703244e75a2a56352a773a457839b3 Mon Sep 17 00:00:00 2001 From: mag123c Date: Mon, 8 Sep 2025 17:19:55 +0900 Subject: [PATCH 2/3] Fix findAllReferences for export= namespace with ES6 imports --- src/services/findAllReferences.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 54518677ef931..db3bbae0e498c 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -134,8 +134,8 @@ import { isFunctionLikeDeclaration, isIdentifier, isIdentifierPart, - isImportMeta, isImportDeclaration, + isImportMeta, isImportOrExportSpecifier, isImportSpecifier, isImportTypeNode, From 48971b1be50670637e8ad2511c3a06df0adafe38 Mon Sep 17 00:00:00 2001 From: mag123c Date: Tue, 9 Sep 2025 14:00:23 +0900 Subject: [PATCH 3/3] refactor: use getExportsOfModule for export= namespace symbol resolution Replace direct exports Map access with TypeScript's official API for more robust export= namespace + ES6 import support. --- src/services/findAllReferences.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index db3bbae0e498c..94d5e990121ab 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1017,11 +1017,20 @@ export namespace Core { // Handle export = namespace case where ES6 import cannot resolve the symbol if (!symbol && isIdentifier(node) && isImportSpecifier(node.parent)) { - const importDeclaration = findAncestor(node, isImportDeclaration); - if (importDeclaration && isImportDeclaration(importDeclaration) && importDeclaration.moduleSpecifier) { - const moduleSymbol = checker.getSymbolAtLocation(importDeclaration.moduleSpecifier); - if (moduleSymbol && moduleSymbol.exports) { - symbol = moduleSymbol.exports.get(node.escapedText); + const spec = node.parent; + // Get the imported name: for 'import { foo as bar }', use 'foo'; for 'import { foo }', use 'foo' + const importedName = spec.propertyName && isIdentifier(spec.propertyName) + ? spec.propertyName.escapedText + : spec.name.escapedText; + const importDecl = findAncestor(spec, isImportDeclaration); + const moduleSymbol = importDecl?.moduleSpecifier ? checker.getSymbolAtLocation(importDecl.moduleSpecifier) : undefined; + if (moduleSymbol) { + // Use TypeScript's official getExportsOfModule API for robust symbol resolution + // This handles complex export= namespace cases and internal resolution rules + const moduleExports = checker.getExportsOfModule(moduleSymbol); + const exportedSymbol = find(moduleExports, s => s.escapedName === importedName); + if (exportedSymbol) { + symbol = exportedSymbol; } } }