Skip to content

Commit 2567d83

Browse files
Handle CommonJS exports
When we export a class or function expression (as opposed to class or function definition), the exported symbol should be used as the canonical definition.
1 parent 3c8e55e commit 2567d83

4 files changed

Lines changed: 89 additions & 32 deletions

File tree

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,28 @@
11
// < definition pure-js 1.0.0 src/`exports.js`/
22

33
exports.SomeExportedClass = class LocalClassName {
4-
//^^^^^ reference local 1
5-
// ^^^^^^^^^^^^^^^^^ definition local 2
6-
// ^^^^^^^^^^^^^^ reference local 1
4+
// ^^^^^^^^^^^^^^^^^ definition pure-js 1.0.0 src/`exports.js`/exports.SomeExportedClass#
5+
// ^^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.SomeExportedClass#
76
method() {}
8-
//^^^^^^ definition local 4
7+
//^^^^^^ definition pure-js 1.0.0 src/`exports.js`/exports.SomeExportedClass#method().
98
}
109

1110
module.exports.SomeAnonymousClass = class /*anonymous*/ {
12-
//^^^^ reference local 6
13-
// ^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/
14-
// ^^^^^^^^^^^^^^^^^^ definition local 8
11+
//^^^^ reference local 1
12+
// ^^^^^^^^^^^^^^^^^^ definition pure-js 1.0.0 src/`exports.js`/exports.SomeAnonymousClass#
1513
method() {}
16-
//^^^^^^ definition local 11
14+
//^^^^^^ definition pure-js 1.0.0 src/`exports.js`/exports.SomeAnonymousClass#method().
1715
}
1816

1917
exports.someFunc = function localFuncName() {}
20-
//^^^^^ reference local 14
21-
// ^^^^^^^^ definition local 15
22-
// ^^^^^^^^^^^^^ reference local 16
18+
// ^^^^^^^^ definition pure-js 1.0.0 src/`exports.js`/exports.someFunc.
19+
// ^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someFunc.
2320
exports.someAnonymousFunc = function () /*anonymous*/ {}
24-
//^^^^^ reference local 19
25-
// ^^^^^^^^^^^^^^^^^ definition local 20
21+
// ^^^^^^^^^^^^^^^^^ definition pure-js 1.0.0 src/`exports.js`/exports.someAnonymousFunc.
2622

2723
exports.someArrowFunc = () => {}
28-
//^^^^^ reference local 23
29-
// ^^^^^^^^^^^^^ definition local 24
24+
// ^^^^^^^^^^^^^ definition pure-js 1.0.0 src/`exports.js`/exports.someArrowFunc.
3025

3126
exports.someValue = 4
32-
//^^^^^ reference local 27
33-
// ^^^^^^^^^ definition local 28
27+
// ^^^^^^^^^ definition pure-js 1.0.0 src/`exports.js`/exports.someValue.
3428

snapshots/output/pure-js/src/main.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -129,33 +129,33 @@ new SomeClass().someMethod2()
129129

130130
import {
131131
SomeExportedClass,
132-
//^^^^^^^^^^^^^^^^^ reference local 8
132+
//^^^^^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.SomeExportedClass#
133133
SomeAnonymousClass,
134-
//^^^^^^^^^^^^^^^^^^ reference local 10
134+
//^^^^^^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.SomeAnonymousClass#
135135
someFunc,
136-
//^^^^^^^^ reference local 13
136+
//^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someFunc.
137137
someAnonymousFunc,
138-
//^^^^^^^^^^^^^^^^^ reference local 16
138+
//^^^^^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someAnonymousFunc.
139139
someArrowFunc,
140-
//^^^^^^^^^^^^^ reference local 19
140+
//^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someArrowFunc.
141141
someValue,
142-
//^^^^^^^^^ reference local 22
142+
//^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someValue.
143143
} from './exports'
144144
// ^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/
145145

146146
new SomeExportedClass().method()
147-
// ^^^^^^^^^^^^^^^^^ reference local 8
148-
// ^^^^^^ reference local 24
147+
// ^^^^^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.SomeExportedClass#
148+
// ^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.SomeExportedClass#method().
149149
new SomeAnonymousClass().method()
150-
// ^^^^^^^^^^^^^^^^^^ reference local 10
151-
// ^^^^^^ reference local 28
150+
// ^^^^^^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.SomeAnonymousClass#
151+
// ^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.SomeAnonymousClass#method().
152152
someFunc()
153-
//^^^^^^ reference local 29
153+
//^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someFunc.
154154
someAnonymousFunc()
155-
//^^^^^^^^^^^^^^^ reference local 30
155+
//^^^^^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someAnonymousFunc.
156156
someArrowFunc()
157-
//^^^^^^^^^^^ reference local 31
157+
//^^^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someArrowFunc.
158158
const i = someValue
159159
// ^ definition pure-js 1.0.0 src/`main.js`/i.
160-
// ^^^^^^^^^ reference local 32
160+
// ^^^^^^^^^ reference pure-js 1.0.0 src/`exports.js`/exports.someValue.
161161

snapshots/output/syntax/src/decorators.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function MyDecorator(value: Configuration) {
99
// ^^^^^ definition syntax 1.0.0 src/`decorators.ts`/MyDecorator().(value)
1010
// ^^^^^^^^^^^^^ reference syntax 1.0.0 src/`reusable-types.ts`/Configuration#
1111
return function (target: Function) {
12-
// ^^^^^^ definition local 2
12+
// ^^^^^^ definition local 1
1313
// ^^^^^^^^ reference typescript 5.9.3 lib/`lib.es5.d.ts`/Function#
1414
// ^^^^^^^^ reference typescript 5.9.3 lib/`lib.es5.d.ts`/Function.
1515
// ^^^^^^^^ reference typescript 5.9.3 lib/`lib.es2015.core.d.ts`/Function#

src/FileIndexer.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ export class FileIndexer {
115115
//
116116
// This code is directly based off src/services/goToDefinition.ts.
117117
private getTSSymbolAtLocation(node: ts.Node): ts.Symbol | undefined {
118+
// Surprisingly, in `exports.exported = …`, `getSymbolAtLocation` resolves `export` to the assigned symbol.
119+
// More surprisingly, in `moduile.exports.exported`, `getSymbolAtLocation` resolves `export` to the file symbol.
120+
// Let's just ignore that case.
121+
if (isCommonJsExports(node)) {
122+
return undefined
123+
}
124+
118125
const symbol = this.checker.getSymbolAtLocation(node)
119126

120127
// If this is an alias, and the request came at the declaration location
@@ -445,6 +452,35 @@ export class FileIndexer {
445452
return fromCache
446453
}
447454

455+
if (isCommonJsExports(node)) {
456+
return this.cached(
457+
node,
458+
ScipSymbol.global(
459+
this.scipSymbol(node.getSourceFile()),
460+
termDescriptor('exports')
461+
)
462+
)
463+
}
464+
465+
// When we have one of:
466+
// ```
467+
// obj.prop = class LocalClassName {}
468+
// obj.prop = class {}
469+
// obj.prop = function localFunctionName() {}
470+
// obj.prop = function () {}
471+
// ```
472+
// use the SCIP symbol from prop for the class or symbol expression.
473+
if (ts.isClassExpression(node) || ts.isFunctionExpression(node)) {
474+
if (
475+
ts.isBinaryExpression(node.parent) &&
476+
node.parent.operatorToken.kind === ts.SyntaxKind.FirstAssignment
477+
) {
478+
return this.scipSymbol(node.parent.left)
479+
}
480+
481+
return this.newLocalSymbol(node)
482+
}
483+
448484
const tsSymbol = this.getTSSymbolAtLocation(node)
449485
if (
450486
tsSymbol?.valueDeclaration &&
@@ -971,3 +1007,30 @@ function declarationName(node: ts.Node): ts.Node | undefined {
9711007
function isDefinition(node: ts.Node): boolean {
9721008
return declarationName(node.parent) === node
9731009
}
1010+
1011+
function isLeftMostOfAPropertyAccess(node: ts.Node): boolean {
1012+
let parent: ts.Node = node
1013+
while (ts.isPropertyAccessExpression(parent.parent)) {
1014+
if (parent.parent.expression !== parent) {
1015+
return false
1016+
}
1017+
parent = parent.parent
1018+
}
1019+
1020+
return true
1021+
}
1022+
1023+
function isCommonJsExports(node: ts.Node): boolean {
1024+
if (ts.isPropertyAccessExpression(node)) {
1025+
return node.getText() === 'module.exports'
1026+
}
1027+
1028+
if (ts.isIdentifier(node)) {
1029+
return (
1030+
node.getText() === 'exports' &&
1031+
(isLeftMostOfAPropertyAccess(node) || isCommonJsExports(node.parent))
1032+
)
1033+
}
1034+
1035+
return false
1036+
}

0 commit comments

Comments
 (0)