Skip to content

Commit 89974bd

Browse files
authored
Merge pull request #14172 from Microsoft/moduleExportsAlias
Fix #14171: Recognize property assignements to `module.export` aliases as exports
2 parents 4610dc7 + 1d339de commit 89974bd

File tree

6 files changed

+770
-11
lines changed

6 files changed

+770
-11
lines changed

src/compiler/binder.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2289,7 +2289,42 @@ namespace ts {
22892289
declareSymbol(file.symbol.exports, file.symbol, <PropertyAccessExpression>node.left, SymbolFlags.Property | SymbolFlags.Export, SymbolFlags.None);
22902290
}
22912291

2292+
function isExportsOrModuleExportsOrAlias(node: Node): boolean {
2293+
return isExportsIdentifier(node) ||
2294+
isModuleExportsPropertyAccessExpression(node) ||
2295+
isNameOfExportsOrModuleExportsAliasDeclaration(node);
2296+
}
2297+
2298+
function isNameOfExportsOrModuleExportsAliasDeclaration(node: Node) {
2299+
if (node.kind === SyntaxKind.Identifier) {
2300+
const symbol = container.locals.get((<Identifier>node).text);
2301+
if (symbol && symbol.valueDeclaration && symbol.valueDeclaration.kind === SyntaxKind.VariableDeclaration) {
2302+
const declaration = symbol.valueDeclaration as VariableDeclaration;
2303+
if (declaration.initializer) {
2304+
return isExportsOrModuleExportsOrAliasOrAssignemnt(declaration.initializer);
2305+
}
2306+
}
2307+
}
2308+
return false;
2309+
}
2310+
2311+
function isExportsOrModuleExportsOrAliasOrAssignemnt(node: Node): boolean {
2312+
return isExportsOrModuleExportsOrAlias(node) ||
2313+
(isAssignmentExpression(node, /*excludeCompoundAssignements*/ true) && (isExportsOrModuleExportsOrAliasOrAssignemnt(node.left) || isExportsOrModuleExportsOrAliasOrAssignemnt(node.right)));
2314+
}
2315+
22922316
function bindModuleExportsAssignment(node: BinaryExpression) {
2317+
// A common practice in node modules is to set 'export = module.exports = {}', this ensures that 'exports'
2318+
// is still pointing to 'module.exports'.
2319+
// We do not want to consider this as 'export=' since a module can have only one of these.
2320+
// Similarlly we do not want to treat 'module.exports = exports' as an 'export='.
2321+
const assignedExpression = getRightMostAssignedExpression(node.right);
2322+
if (isEmptyObjectLiteral(assignedExpression) || isExportsOrModuleExportsOrAlias(assignedExpression)) {
2323+
// Mark it as a module in case there are no other exports in the file
2324+
setCommonJsModuleIndicator(node);
2325+
return;
2326+
}
2327+
22932328
// 'module.exports = expr' assignment
22942329
setCommonJsModuleIndicator(node);
22952330
declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.Export | SymbolFlags.ValueModule, SymbolFlags.None);
@@ -2351,11 +2386,20 @@ namespace ts {
23512386
leftSideOfAssignment.parent = node;
23522387
target.parent = leftSideOfAssignment;
23532388

2354-
bindPropertyAssignment(target.text, leftSideOfAssignment, /*isPrototypeProperty*/ false);
2389+
if (isNameOfExportsOrModuleExportsAliasDeclaration(target)) {
2390+
// This can be an alias for the 'exports' or 'module.exports' names, e.g.
2391+
// var util = module.exports;
2392+
// util.property = function ...
2393+
bindExportsPropertyAssignment(node);
2394+
}
2395+
else {
2396+
bindPropertyAssignment(target.text, leftSideOfAssignment, /*isPrototypeProperty*/ false);
2397+
}
23552398
}
23562399

23572400
function bindPropertyAssignment(functionName: string, propertyAccessExpression: PropertyAccessExpression, isPrototypeProperty: boolean) {
23582401
let targetSymbol = container.locals.get(functionName);
2402+
23592403
if (targetSymbol && isDeclarationOfFunctionOrClassExpression(targetSymbol)) {
23602404
targetSymbol = (targetSymbol.valueDeclaration as VariableDeclaration).initializer.symbol;
23612405
}

src/compiler/transformers/destructuring.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ namespace ts {
4343
let value: Expression;
4444
if (isDestructuringAssignment(node)) {
4545
value = node.right;
46-
while (isEmptyObjectLiteralOrArrayLiteral(node.left)) {
46+
while (isEmptyArrayLiteral(node.left) || isEmptyObjectLiteral(node.left)) {
4747
if (isDestructuringAssignment(value)) {
4848
location = node = value;
4949
value = node.right;

src/compiler/utilities.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,21 @@ namespace ts {
14251425
return false;
14261426
}
14271427

1428+
export function getRightMostAssignedExpression(node: Node) {
1429+
while (isAssignmentExpression(node, /*excludeCompoundAssignements*/ true)) {
1430+
node = node.right;
1431+
}
1432+
return node;
1433+
}
1434+
1435+
export function isExportsIdentifier(node: Node) {
1436+
return isIdentifier(node) && node.text === "exports";
1437+
}
1438+
1439+
export function isModuleExportsPropertyAccessExpression(node: Node) {
1440+
return isPropertyAccessExpression(node) && isIdentifier(node.expression) && node.expression.text === "module" && node.name.text === "exports";
1441+
}
1442+
14281443
/// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property
14291444
/// assignments we treat as special in the binder
14301445
export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind {
@@ -3148,15 +3163,14 @@ namespace ts {
31483163
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node);
31493164
}
31503165

3151-
export function isEmptyObjectLiteralOrArrayLiteral(expression: Node): boolean {
3152-
const kind = expression.kind;
3153-
if (kind === SyntaxKind.ObjectLiteralExpression) {
3154-
return (<ObjectLiteralExpression>expression).properties.length === 0;
3155-
}
3156-
if (kind === SyntaxKind.ArrayLiteralExpression) {
3157-
return (<ArrayLiteralExpression>expression).elements.length === 0;
3158-
}
3159-
return false;
3166+
export function isEmptyObjectLiteral(expression: Node): boolean {
3167+
return expression.kind === SyntaxKind.ObjectLiteralExpression &&
3168+
(<ObjectLiteralExpression>expression).properties.length === 0;
3169+
}
3170+
3171+
export function isEmptyArrayLiteral(expression: Node): boolean {
3172+
return expression.kind === SyntaxKind.ArrayLiteralExpression &&
3173+
(<ArrayLiteralExpression>expression).elements.length === 0;
31603174
}
31613175

31623176
export function getLocalSymbolForExportDefault(symbol: Symbol) {
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
=== tests/cases/conformance/salsa/a.ts ===
2+
3+
import b = require("./b.js");
4+
>b : Symbol(b, Decl(a.ts, 0, 0))
5+
6+
b.func1;
7+
>b.func1 : Symbol(b.func1, Decl(b.js, 0, 27))
8+
>b : Symbol(b, Decl(a.ts, 0, 0))
9+
>func1 : Symbol(b.func1, Decl(b.js, 0, 27))
10+
11+
b.func2;
12+
>b.func2 : Symbol(b.func2, Decl(b.js, 1, 37))
13+
>b : Symbol(b, Decl(a.ts, 0, 0))
14+
>func2 : Symbol(b.func2, Decl(b.js, 1, 37))
15+
16+
b.func3;
17+
>b.func3 : Symbol(b.func3, Decl(b.js, 4, 40))
18+
>b : Symbol(b, Decl(a.ts, 0, 0))
19+
>func3 : Symbol(b.func3, Decl(b.js, 4, 40))
20+
21+
b.func4;
22+
>b.func4 : Symbol(b.func4, Decl(b.js, 5, 43))
23+
>b : Symbol(b, Decl(a.ts, 0, 0))
24+
>func4 : Symbol(b.func4, Decl(b.js, 5, 43))
25+
26+
b.func5;
27+
>b.func5 : Symbol(b.func5, Decl(b.js, 8, 57))
28+
>b : Symbol(b, Decl(a.ts, 0, 0))
29+
>func5 : Symbol(b.func5, Decl(b.js, 8, 57))
30+
31+
b.func6;
32+
>b.func6 : Symbol(b.func6, Decl(b.js, 11, 57))
33+
>b : Symbol(b, Decl(a.ts, 0, 0))
34+
>func6 : Symbol(b.func6, Decl(b.js, 11, 57))
35+
36+
b.func7;
37+
>b.func7 : Symbol(b.func7, Decl(b.js, 15, 60))
38+
>b : Symbol(b, Decl(a.ts, 0, 0))
39+
>func7 : Symbol(b.func7, Decl(b.js, 15, 60))
40+
41+
b.func8;
42+
>b.func8 : Symbol(b.func8, Decl(b.js, 18, 67))
43+
>b : Symbol(b, Decl(a.ts, 0, 0))
44+
>func8 : Symbol(b.func8, Decl(b.js, 18, 67))
45+
46+
b.func9;
47+
>b.func9 : Symbol(b.func9, Decl(b.js, 21, 62))
48+
>b : Symbol(b, Decl(a.ts, 0, 0))
49+
>func9 : Symbol(b.func9, Decl(b.js, 21, 62))
50+
51+
b.func10;
52+
>b.func10 : Symbol(b.func10, Decl(b.js, 24, 62))
53+
>b : Symbol(b, Decl(a.ts, 0, 0))
54+
>func10 : Symbol(b.func10, Decl(b.js, 24, 62))
55+
56+
b.func11;
57+
>b.func11 : Symbol(b.func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50))
58+
>b : Symbol(b, Decl(a.ts, 0, 0))
59+
>func11 : Symbol(b.func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50))
60+
61+
b.func12;
62+
>b.func12 : Symbol(b.func12, Decl(b.js, 28, 33), Decl(b.js, 32, 33))
63+
>b : Symbol(b, Decl(a.ts, 0, 0))
64+
>func12 : Symbol(b.func12, Decl(b.js, 28, 33), Decl(b.js, 32, 33))
65+
66+
b.func13;
67+
>b.func13 : Symbol(b.func13, Decl(b.js, 35, 30))
68+
>b : Symbol(b, Decl(a.ts, 0, 0))
69+
>func13 : Symbol(b.func13, Decl(b.js, 35, 30))
70+
71+
b.func14;
72+
>b.func14 : Symbol(b.func14, Decl(b.js, 36, 33))
73+
>b : Symbol(b, Decl(a.ts, 0, 0))
74+
>func14 : Symbol(b.func14, Decl(b.js, 36, 33))
75+
76+
b.func15;
77+
>b.func15 : Symbol(b.func15, Decl(b.js, 39, 30))
78+
>b : Symbol(b, Decl(a.ts, 0, 0))
79+
>func15 : Symbol(b.func15, Decl(b.js, 39, 30))
80+
81+
b.func16;
82+
>b.func16 : Symbol(b.func16, Decl(b.js, 40, 33))
83+
>b : Symbol(b, Decl(a.ts, 0, 0))
84+
>func16 : Symbol(b.func16, Decl(b.js, 40, 33))
85+
86+
b.func17;
87+
>b.func17 : Symbol(b.func17, Decl(b.js, 43, 30))
88+
>b : Symbol(b, Decl(a.ts, 0, 0))
89+
>func17 : Symbol(b.func17, Decl(b.js, 43, 30))
90+
91+
b.func18;
92+
>b.func18 : Symbol(b.func18, Decl(b.js, 44, 33))
93+
>b : Symbol(b, Decl(a.ts, 0, 0))
94+
>func18 : Symbol(b.func18, Decl(b.js, 44, 33))
95+
96+
b.func19;
97+
>b.func19 : Symbol(b.func19, Decl(b.js, 47, 20))
98+
>b : Symbol(b, Decl(a.ts, 0, 0))
99+
>func19 : Symbol(b.func19, Decl(b.js, 47, 20))
100+
101+
b.func20;
102+
>b.func20 : Symbol(b.func20, Decl(b.js, 48, 33))
103+
>b : Symbol(b, Decl(a.ts, 0, 0))
104+
>func20 : Symbol(b.func20, Decl(b.js, 48, 33))
105+
106+
107+
=== tests/cases/conformance/salsa/b.js ===
108+
var exportsAlias = exports;
109+
>exportsAlias : Symbol(exportsAlias, Decl(b.js, 0, 3))
110+
111+
exportsAlias.func1 = function () { };
112+
>exportsAlias : Symbol(exportsAlias, Decl(b.js, 0, 3))
113+
114+
exports.func2 = function () { };
115+
>exports : Symbol(func2, Decl(b.js, 1, 37))
116+
>func2 : Symbol(func2, Decl(b.js, 1, 37))
117+
118+
var moduleExportsAlias = module.exports;
119+
>moduleExportsAlias : Symbol(moduleExportsAlias, Decl(b.js, 4, 3))
120+
121+
moduleExportsAlias.func3 = function () { };
122+
>moduleExportsAlias : Symbol(moduleExportsAlias, Decl(b.js, 4, 3))
123+
124+
module.exports.func4 = function () { };
125+
>module.exports : Symbol(func4, Decl(b.js, 5, 43))
126+
>func4 : Symbol(func4, Decl(b.js, 5, 43))
127+
128+
var multipleDeclarationAlias1 = exports = module.exports;
129+
>multipleDeclarationAlias1 : Symbol(multipleDeclarationAlias1, Decl(b.js, 8, 3))
130+
131+
multipleDeclarationAlias1.func5 = function () { };
132+
>multipleDeclarationAlias1 : Symbol(multipleDeclarationAlias1, Decl(b.js, 8, 3))
133+
134+
var multipleDeclarationAlias2 = module.exports = exports;
135+
>multipleDeclarationAlias2 : Symbol(multipleDeclarationAlias2, Decl(b.js, 11, 3))
136+
137+
multipleDeclarationAlias2.func6 = function () { };
138+
>multipleDeclarationAlias2 : Symbol(multipleDeclarationAlias2, Decl(b.js, 11, 3))
139+
140+
var someOtherVariable;
141+
>someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3))
142+
143+
var multipleDeclarationAlias3 = someOtherVariable = exports;
144+
>multipleDeclarationAlias3 : Symbol(multipleDeclarationAlias3, Decl(b.js, 15, 3))
145+
>someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3))
146+
147+
multipleDeclarationAlias3.func7 = function () { };
148+
>multipleDeclarationAlias3 : Symbol(multipleDeclarationAlias3, Decl(b.js, 15, 3))
149+
150+
var multipleDeclarationAlias4 = someOtherVariable = module.exports;
151+
>multipleDeclarationAlias4 : Symbol(multipleDeclarationAlias4, Decl(b.js, 18, 3))
152+
>someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3))
153+
154+
multipleDeclarationAlias4.func8 = function () { };
155+
>multipleDeclarationAlias4 : Symbol(multipleDeclarationAlias4, Decl(b.js, 18, 3))
156+
157+
var multipleDeclarationAlias5 = module.exports = exports = {};
158+
>multipleDeclarationAlias5 : Symbol(multipleDeclarationAlias5, Decl(b.js, 21, 3))
159+
160+
multipleDeclarationAlias5.func9 = function () { };
161+
>multipleDeclarationAlias5 : Symbol(multipleDeclarationAlias5, Decl(b.js, 21, 3))
162+
163+
var multipleDeclarationAlias6 = exports = module.exports = {};
164+
>multipleDeclarationAlias6 : Symbol(multipleDeclarationAlias6, Decl(b.js, 24, 3))
165+
166+
multipleDeclarationAlias6.func10 = function () { };
167+
>multipleDeclarationAlias6 : Symbol(multipleDeclarationAlias6, Decl(b.js, 24, 3))
168+
169+
exports = module.exports = someOtherVariable = {};
170+
>someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3))
171+
172+
exports.func11 = function () { };
173+
>exports : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50))
174+
>func11 : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50))
175+
176+
module.exports.func12 = function () { };
177+
>module.exports : Symbol(func12, Decl(b.js, 28, 33), Decl(b.js, 32, 33))
178+
>func12 : Symbol(func12, Decl(b.js, 28, 33), Decl(b.js, 32, 33))
179+
180+
exports = module.exports = someOtherVariable = {};
181+
>someOtherVariable : Symbol(someOtherVariable, Decl(b.js, 14, 3))
182+
183+
exports.func11 = function () { };
184+
>exports : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50))
185+
>func11 : Symbol(func11, Decl(b.js, 27, 50), Decl(b.js, 31, 50))
186+
187+
module.exports.func12 = function () { };
188+
>module.exports : Symbol(func12, Decl(b.js, 28, 33), Decl(b.js, 32, 33))
189+
>func12 : Symbol(func12, Decl(b.js, 28, 33), Decl(b.js, 32, 33))
190+
191+
exports = module.exports = {};
192+
exports.func13 = function () { };
193+
>exports : Symbol(func13, Decl(b.js, 35, 30))
194+
>func13 : Symbol(func13, Decl(b.js, 35, 30))
195+
196+
module.exports.func14 = function () { };
197+
>module.exports : Symbol(func14, Decl(b.js, 36, 33))
198+
>func14 : Symbol(func14, Decl(b.js, 36, 33))
199+
200+
exports = module.exports = {};
201+
exports.func15 = function () { };
202+
>exports : Symbol(func15, Decl(b.js, 39, 30))
203+
>func15 : Symbol(func15, Decl(b.js, 39, 30))
204+
205+
module.exports.func16 = function () { };
206+
>module.exports : Symbol(func16, Decl(b.js, 40, 33))
207+
>func16 : Symbol(func16, Decl(b.js, 40, 33))
208+
209+
module.exports = exports = {};
210+
exports.func17 = function () { };
211+
>exports : Symbol(func17, Decl(b.js, 43, 30))
212+
>func17 : Symbol(func17, Decl(b.js, 43, 30))
213+
214+
module.exports.func18 = function () { };
215+
>module.exports : Symbol(func18, Decl(b.js, 44, 33))
216+
>func18 : Symbol(func18, Decl(b.js, 44, 33))
217+
218+
module.exports = {};
219+
exports.func19 = function () { };
220+
>exports : Symbol(func19, Decl(b.js, 47, 20))
221+
>func19 : Symbol(func19, Decl(b.js, 47, 20))
222+
223+
module.exports.func20 = function () { };
224+
>module.exports : Symbol(func20, Decl(b.js, 48, 33))
225+
>func20 : Symbol(func20, Decl(b.js, 48, 33))
226+
227+

0 commit comments

Comments
 (0)