Skip to content

Commit

Permalink
Support finding references at module in module.exports = or `expo…
Browse files Browse the repository at this point in the history
…rt` in `export =` (#28221)

* Support finding references at `module` in `module.exports =` or `export` in `export =`

* Add json test
  • Loading branch information
Andy authored Nov 9, 2018
1 parent 69c61cd commit aaf1d80
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 19 deletions.
3 changes: 3 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28100,6 +28100,9 @@ namespace ts {
case SyntaxKind.ImportType:
return isLiteralImportTypeNode(node) ? getSymbolAtLocation(node.argument.literal) : undefined;

case SyntaxKind.ExportKeyword:
return isExportAssignment(node.parent) ? Debug.assertDefined(node.parent.symbol) : undefined;

default:
return undefined;
}
Expand Down
20 changes: 20 additions & 0 deletions src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,10 @@ namespace ts.FindAllReferences.Core {
return !options.implementations && isStringLiteral(node) ? getReferencesForStringLiteral(node, sourceFiles, cancellationToken) : undefined;
}

if (symbol.escapedName === InternalSymbolName.ExportEquals) {
return getReferencedSymbolsForModule(program, symbol.parent!, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet);
}

let moduleReferences: SymbolAndEntries[] = emptyArray;
const moduleSourceFile = isModuleSymbol(symbol);
let referencedNode: Node | undefined = node;
Expand Down Expand Up @@ -427,6 +431,22 @@ namespace ts.FindAllReferences.Core {
}
}

const exported = symbol.exports!.get(InternalSymbolName.ExportEquals);
if (exported) {
for (const decl of exported.declarations) {
const sourceFile = decl.getSourceFile();
if (sourceFilesSet.has(sourceFile.fileName)) {
// At `module.exports = ...`, reference node is `module`
const node = isBinaryExpression(decl) && isPropertyAccessExpression(decl.left)
? decl.left.expression
: isExportAssignment(decl)
? Debug.assertDefined(findChildOfKind(decl, SyntaxKind.ExportKeyword, sourceFile))
: getNameOfDeclaration(decl) || decl;
references.push(nodeEntry(node));
}
}
}

return references.length ? [{ definition: { type: DefinitionKind.Symbol, symbol }, references }] : emptyArray;
}

Expand Down
17 changes: 9 additions & 8 deletions tests/cases/fourslash/findAllRefsExportEquals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

// @Filename: /a.ts
////type [|{| "isWriteAccess": true, "isDefinition": true |}T|] = number;
////export = [|T|];
////[|export|] = [|T|];

// @Filename: /b.ts
////import [|{| "isWriteAccess": true, "isDefinition": true |}T|] = require("[|./a|]");

const [r0, r1, r2, r3] = test.ranges();
const mod = { definition: 'module "/a"', ranges: [r3] };
const a = { definition: "type T = number", ranges: [r0, r1] };
const b = { definition: '(alias) type T = number\nimport T = require("./a")', ranges: [r2] };
verify.referenceGroups([r0, r1], [a, b]);
verify.referenceGroups(r2, [b, a]);
verify.referenceGroups(r3, [mod, a, b]);
const [r0, r1, r2, r3, r4] = test.ranges();
const mod = { definition: 'module "/a"', ranges: [r4, r1] };
const a = { definition: "type T = number", ranges: [r0, r2] };
const b = { definition: '(alias) type T = number\nimport T = require("./a")', ranges: [r3] };
verify.referenceGroups([r0, r2], [a, b]);
verify.referenceGroups(r3, [b, a]);
verify.referenceGroups(r4, [mod, a, b]);
verify.referenceGroups(r1, [mod]);
24 changes: 24 additions & 0 deletions tests/cases/fourslash/findAllRefsImportEqualsJsonFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// <reference path='fourslash.ts' />

// @allowJs: true
// @checkJs: true
// @resolveJsonModule: true

// @Filename: /a.ts
////import [|{| "isWriteAccess": true, "isDefinition": true |}j|] = require("[|./j.json|]");
////[|j|];

// @Filename: /b.js
////const [|{| "isWriteAccess": true, "isDefinition": true |}j|] = require("[|./j.json|]");
////[|j|];

// @Filename: /j.json
////[|{ "x": 0 }|]

verify.noErrors();

const [r0, r1, r2, r3, r4, r5, r6] = test.ranges();
verify.singleReferenceGroup('import j = require("./j.json")', [r0, r2]);
verify.referenceGroups([r1, r4], [{ definition: 'module "/j"', ranges: [r1, r4, r6] }]);
verify.singleReferenceGroup('const j: {\n "x": number;\n}', [r3, r5]);
verify.referenceGroups(r6, undefined);
11 changes: 11 additions & 0 deletions tests/cases/fourslash/findAllRefsModuleDotExports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// <reference path='fourslash.ts' />

// @allowJs: true

// @Filename: /a.js
////const b = require("[|./b|]");

// @Filename: /b.js
////[|module|].exports = 0;

verify.singleReferenceGroup('module "/b"')
13 changes: 6 additions & 7 deletions tests/cases/fourslash/findAllRefs_importType_exportEquals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,21 @@
////namespace [|{| "isWriteAccess": true, "isDefinition": true |}T|] {
//// export type U = string;
////}
////export = [|T|];
////[|export|] = [|T|];

// @Filename: /b.ts
////const x: import("[|./[|a|]|]") = 0;
////const y: import("[|./[|a|]|]").U = "";

verify.noErrors();

const [r0, r1, r2, r3, r3b, r4, r4b] = test.ranges();
const [r0, r1, rExport, r2, r3, r3b, r4, r4b] = test.ranges();
verify.referenceGroups(r0, [{ definition: "type T = number\nnamespace T", ranges: [r0, r2, r3] }]);
verify.referenceGroups(r1, [{ definition: "namespace T", ranges: [r1, r2] }]);
verify.referenceGroups(r2, [{ definition: "type T = number\nnamespace T", ranges: [r0, r1, r2, r3] }]);
verify.referenceGroups([r3, r4], [
{ definition: 'module "/a"', ranges: [r4] },
{ definition: "type T = number\nnamespace T", ranges: [r0, r1, r2, r3] },
]);
const t: FourSlashInterface.ReferenceGroup = { definition: "type T = number\nnamespace T", ranges: [r0, r1, r2, r3] };
verify.referenceGroups(r2, [t]);
verify.referenceGroups([r3, r4], [{ definition: 'module "/a"', ranges: [r4, rExport] }, t]);
verify.referenceGroups(rExport, [{ definition: 'module "/a"', ranges: [r3, r4, rExport] }]);

verify.renameLocations(r0, [r0, r2]);
verify.renameLocations(r1, [r1, r2]);
Expand Down
7 changes: 4 additions & 3 deletions tests/cases/fourslash/findAllRefs_importType_js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// @checkJs: true

// @Filename: /a.js
////module.exports = class [|{| "isWriteAccess": true, "isDefinition": true |}C|] {};
////[|module|].exports = class [|{| "isWriteAccess": true, "isDefinition": true |}C|] {};
////module.exports.[|{| "isWriteAccess": true, "isDefinition": true |}D|] = class [|{| "isWriteAccess": true, "isDefinition": true |}D|] {};

// @Filename: /b.js
Expand All @@ -17,7 +17,8 @@ verify.noErrors();

// TODO: GH#24025

const [r0, r1, r2, r3, r4, r5] = test.ranges();
const [rModule, r0, r1, r2, r3, r4, r5] = test.ranges();
verify.referenceGroups(rModule, [{ definition: 'module "/a"', ranges: [r3, r4, rModule] }]);
verify.referenceGroups(r0, [
{ definition: "(local class) C", ranges: [r0] },
// TODO: This definition is really ugly
Expand All @@ -31,7 +32,7 @@ verify.referenceGroups(r2, [
{ definition: "class D\n(property) D: typeof D", ranges: [r5] },
]);
verify.referenceGroups([r3, r4], [
{ definition: 'module "/a"', ranges: [r4] },
{ definition: 'module "/a"', ranges: [r4, rModule] },
{ definition: "(local class) C", ranges: [r0] },
{ definition: "(alias) (local class) export=\nimport export=", ranges: [r3] },
]);
6 changes: 5 additions & 1 deletion tests/cases/fourslash/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ declare namespace FourSlashInterface {
* For each of starts, asserts the ranges that are referenced from there.
* This uses the 'findReferences' command instead of 'getReferencesAtPosition', so references are grouped by their definition.
*/
referenceGroups(starts: ArrayOrSingle<string> | ArrayOrSingle<Range>, parts: Array<{ definition: ReferencesDefinition, ranges: Range[] }>): void;
referenceGroups(starts: ArrayOrSingle<string> | ArrayOrSingle<Range>, parts: ReadonlyArray<ReferenceGroup>): void;
singleReferenceGroup(definition: ReferencesDefinition, ranges?: Range[]): void;
rangesAreOccurrences(isWriteAccess?: boolean): void;
rangesWithSameTextAreRenameLocations(): void;
Expand Down Expand Up @@ -487,6 +487,10 @@ declare namespace FourSlashInterface {
};
}

interface ReferenceGroup {
readonly definition: ReferencesDefinition;
readonly ranges: ReadonlyArray<Range>;
}
type ReferencesDefinition = string | {
text: string;
range: Range;
Expand Down

0 comments on commit aaf1d80

Please sign in to comment.