diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts
index 581774ae21..d909419def 100644
--- a/src/services/findAllReferences.ts
+++ b/src/services/findAllReferences.ts
@@ -935,8 +935,6 @@ namespace ts.FindAllReferences.Core {
addReference(referenceLocation, search.symbol, search.location, state);
}
- const classSymbol = skipAliases(search.symbol, state.checker);
- Debug.assert(isClassLike(classSymbol.valueDeclaration));
const pusher = state.referenceAdder(search.symbol, search.location);
if (isClassLike(referenceLocation.parent)) {
@@ -1687,10 +1685,6 @@ namespace ts.FindAllReferences.Core {
return false;
}
- function skipAliases(symbol: Symbol, checker: TypeChecker): Symbol {
- return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
- }
-
/**
* If we are just looking for implementations and this is a property access expression, we need to get the
* symbol of the local type of the symbol the property is being accessed on. This is because our search
diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts
index 5bbcdb220f..b9b0d31984 100644
--- a/src/services/importTracker.ts
+++ b/src/services/importTracker.ts
@@ -32,7 +32,8 @@ namespace ts.FindAllReferences {
interface AmbientModuleDeclaration extends ModuleDeclaration { body?: ModuleBlock; }
type SourceFileLike = SourceFile | AmbientModuleDeclaration;
- type Importer = AnyImportSyntax | ExportDeclaration;
+ // Identifier for the case of `const x = require("y")`.
+ type Importer = AnyImportSyntax | Identifier | ExportDeclaration;
type ImporterOrCallExpression = Importer | CallExpression;
/** Returns import statements that directly reference the exporting module, and a list of files that may access the module through a namespace. */
@@ -55,7 +56,7 @@ namespace ts.FindAllReferences {
// Module augmentations may use this module's exports without importing it.
for (const decl of exportingModuleSymbol.declarations) {
- if (ts.isExternalModuleAugmentation(decl)) {
+ if (isExternalModuleAugmentation(decl)) {
addIndirectUser(decl as SourceFileLike);
}
}
@@ -74,6 +75,15 @@ namespace ts.FindAllReferences {
switch (direct.kind) {
case SyntaxKind.CallExpression:
if (!isAvailableThroughGlobal) {
+ const parent = direct.parent!;
+ if (exportKind === ExportKind.ExportEquals && parent.kind === SyntaxKind.VariableDeclaration) {
+ const { name } = parent as ts.VariableDeclaration;
+ if (name.kind === SyntaxKind.Identifier) {
+ directImports.push(name);
+ break;
+ }
+ }
+
// Don't support re-exporting 'require()' calls, so just add a single indirect user.
addIndirectUser(direct.getSourceFile());
}
@@ -179,6 +189,11 @@ namespace ts.FindAllReferences {
return;
}
+ if (decl.kind === ts.SyntaxKind.Identifier) {
+ handleNamespaceImportLike(decl);
+ return;
+ }
+
// Ignore if there's a grammar error
if (decl.moduleSpecifier.kind !== SyntaxKind.StringLiteral) {
return;
@@ -192,7 +207,7 @@ namespace ts.FindAllReferences {
const { importClause } = decl;
const { namedBindings } = importClause;
- if (namedBindings && namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
+ if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
handleNamespaceImportLike(namedBindings.name);
return;
}
@@ -333,13 +348,13 @@ namespace ts.FindAllReferences {
if (sourceFile.flags & NodeFlags.JavaScriptFile) {
// Find all 'require()' calls.
- recur(sourceFile);
- function recur(node: Node): void {
+ sourceFile.forEachChild(function recur(node: Node): void {
if (isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) {
action(node, node.arguments[0] as StringLiteral);
+ } else {
+ node.forEachChild(recur);
}
- forEachChild(node, recur);
- }
+ });
}
}
}
@@ -379,18 +394,9 @@ namespace ts.FindAllReferences {
if (parent.kind === SyntaxKind.PropertyAccessExpression) {
// When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use.
// So check that we are at the declaration.
- if (!symbol.declarations.some(d => d === parent)) {
- return undefined;
- }
-
- switch (getSpecialPropertyAssignmentKind(parent.parent)) {
- case SpecialPropertyAssignmentKind.ExportsProperty:
- return exportInfo(symbol, ExportKind.Named);
- case SpecialPropertyAssignmentKind.ModuleExports:
- return exportInfo(symbol, ExportKind.ExportEquals);
- default:
- return undefined;
- }
+ return symbol.declarations.some(d => d === parent) && parent.parent.kind === ts.SyntaxKind.BinaryExpression
+ ? getSpecialPropertyExport(parent.parent as ts.BinaryExpression, /*useLhsSymbol*/ false)
+ : undefined;
}
else {
const { exportSymbol } = symbol;
@@ -420,6 +426,29 @@ namespace ts.FindAllReferences {
Debug.assert(!!exportingModuleSymbol);
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol, exportKind: ExportKind.ExportEquals } };
}
+ else if (parent.kind === ts.SyntaxKind.BinaryExpression) {
+ return getSpecialPropertyExport(parent as ts.BinaryExpression, /*useLhsSymbol*/ true);
+ }
+ else if (parent.parent.kind === SyntaxKind.BinaryExpression) {
+ return getSpecialPropertyExport(parent.parent as ts.BinaryExpression, /*useLhsSymbol*/ true);
+ }
+ }
+
+ function getSpecialPropertyExport(node: ts.BinaryExpression, useLhsSymbol: boolean): ExportedSymbol | undefined {
+ let kind: ExportKind;
+ switch (getSpecialPropertyAssignmentKind(node)) {
+ case SpecialPropertyAssignmentKind.ExportsProperty:
+ kind = ExportKind.Named;
+ break;
+ case SpecialPropertyAssignmentKind.ModuleExports:
+ kind = ExportKind.ExportEquals;
+ break;
+ default:
+ return undefined;
+ }
+
+ const sym = useLhsSymbol ? checker.getSymbolAtLocation((node.left as ts.PropertyAccessExpression).name) : symbol;
+ return sym && exportInfo(sym, kind);
}
}
diff --git a/tests/cases/fourslash/renameJsExports02.ts b/tests/cases/fourslash/renameJsExports02.ts
new file mode 100644
index 0000000000..94c8498078
--- /dev/null
+++ b/tests/cases/fourslash/renameJsExports02.ts
@@ -0,0 +1,16 @@
+///
+
+// @allowJs: true
+// @Filename: a.js
+////module.exports = class [|{| "isWriteAccess": true, "isDefinition": true |}A|] {}
+
+// @Filename: b.js
+////const [|{| "isWriteAccess": true, "isDefinition": true |}A|] = require("./a");
+
+const [r0, r1] = test.ranges();
+verify.referenceGroups(r0, [
+ { definition: "(local class) A", ranges: [r0] },
+ { definition: "const A: typeof A", ranges: [r1] }
+]);
+
+verify.singleReferenceGroup("const A: typeof A", [r1]);
diff --git a/tests/cases/fourslash/renameJsExports03.ts b/tests/cases/fourslash/renameJsExports03.ts
new file mode 100644
index 0000000000..0ffd4d0469
--- /dev/null
+++ b/tests/cases/fourslash/renameJsExports03.ts
@@ -0,0 +1,31 @@
+///
+
+// @allowJs: true
+// @Filename: a.js
+////class [|{| "isWriteAccess": true, "isDefinition": true |}A|] {
+//// [|constructor|]() { }
+////}
+////module.exports = [|A|];
+
+// @Filename: b.js
+////const [|{| "isWriteAccess": true, "isDefinition": true |}A|] = require("./a");
+////new [|A|];
+
+const [r0, r1, r2, r3, r4] = test.ranges();
+verify.referenceGroups([r0, r2], [
+ { definition: "class A", ranges: [r0, r2] },
+ { definition: "const A: typeof A", ranges: [r3, r4] }
+]);
+
+verify.referenceGroups(r1, [
+ { definition: "constructor A(): A", ranges: [r1] },
+ { definition: "const A: typeof A", ranges: [r4] }
+]);
+
+verify.referenceGroups(r3, [
+ { definition: "const A: typeof A", ranges: [r3, r4] }
+]);
+verify.referenceGroups(r4, [
+ { definition: "const A: new () => A", ranges: [r3, r4] }
+]);
+