Skip to content

Commit 50059b6

Browse files
committed
Merge pull request #7249 from Microsoft/FixCommonJSModules
Fix CommonJs modules
2 parents 3e43344 + bc8ac66 commit 50059b6

12 files changed

+198
-3
lines changed

src/compiler/binder.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ namespace ts {
279279
Debug.assert(!hasDynamicName(node));
280280

281281
const isDefaultExport = node.flags & NodeFlags.Default;
282+
282283
// The exported symbol for an export default function/class node is always named "default"
283284
const name = isDefaultExport && parent ? "default" : getDeclarationName(node);
284285

@@ -1429,7 +1430,7 @@ namespace ts {
14291430
function bindModuleExportsAssignment(node: BinaryExpression) {
14301431
// 'module.exports = expr' assignment
14311432
setCommonJsModuleIndicator(node);
1432-
bindExportAssignment(node);
1433+
declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.Export | SymbolFlags.ValueModule, SymbolFlags.None);
14331434
}
14341435

14351436
function bindThisPropertyAssignment(node: BinaryExpression) {

src/compiler/checker.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -888,8 +888,12 @@ namespace ts {
888888

889889
function getTargetOfImportClause(node: ImportClause): Symbol {
890890
const moduleSymbol = resolveExternalModuleName(node, (<ImportDeclaration>node.parent).moduleSpecifier);
891+
891892
if (moduleSymbol) {
892-
const exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]);
893+
const exportDefaultSymbol = moduleSymbol.exports["export="] ?
894+
getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") :
895+
resolveSymbol(moduleSymbol.exports["default"]);
896+
893897
if (!exportDefaultSymbol && !allowSyntheticDefaultImports) {
894898
error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol));
895899
}
@@ -960,8 +964,15 @@ namespace ts {
960964
if (targetSymbol) {
961965
const name = specifier.propertyName || specifier.name;
962966
if (name.text) {
967+
let symbolFromVariable: Symbol;
968+
// First check if module was specified with "export=". If so, get the member from the resolved type
969+
if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) {
970+
symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text);
971+
}
972+
else {
973+
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
974+
}
963975
const symbolFromModule = getExportOfModule(targetSymbol, name.text);
964-
const symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
965976
const symbol = symbolFromModule && symbolFromVariable ?
966977
combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) :
967978
symbolFromModule || symbolFromVariable;

src/compiler/utilities.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,9 @@ namespace ts {
11071107
/// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property
11081108
/// assignments we treat as special in the binder
11091109
export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind {
1110+
if (!isInJavaScriptFile(expression)) {
1111+
return SpecialPropertyAssignmentKind.None;
1112+
}
11101113
if (expression.kind !== SyntaxKind.BinaryExpression) {
11111114
return SpecialPropertyAssignmentKind.None;
11121115
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//// [tests/cases/compiler/exportEqualsDefaultProperty.ts] ////
2+
3+
//// [exp.ts]
4+
5+
var x = {
6+
"greeting": "hello, world",
7+
"default": 42
8+
};
9+
10+
export = x
11+
12+
//// [imp.ts]
13+
import foo from "./exp";
14+
foo.toExponential(2);
15+
16+
17+
//// [exp.js]
18+
"use strict";
19+
var x = {
20+
"greeting": "hello, world",
21+
"default": 42
22+
};
23+
module.exports = x;
24+
//// [imp.js]
25+
"use strict";
26+
var exp_1 = require("./exp");
27+
exp_1["default"].toExponential(2);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/compiler/exp.ts ===
2+
3+
var x = {
4+
>x : Symbol(x, Decl(exp.ts, 1, 3))
5+
6+
"greeting": "hello, world",
7+
"default": 42
8+
};
9+
10+
export = x
11+
>x : Symbol(x, Decl(exp.ts, 1, 3))
12+
13+
=== tests/cases/compiler/imp.ts ===
14+
import foo from "./exp";
15+
>foo : Symbol(foo, Decl(imp.ts, 0, 6))
16+
17+
foo.toExponential(2);
18+
>foo.toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --))
19+
>foo : Symbol(foo, Decl(imp.ts, 0, 6))
20+
>toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --))
21+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/compiler/exp.ts ===
2+
3+
var x = {
4+
>x : { "greeting": string; "default": number; }
5+
>{ "greeting": "hello, world", "default": 42} : { "greeting": string; "default": number; }
6+
7+
"greeting": "hello, world",
8+
>"hello, world" : string
9+
10+
"default": 42
11+
>42 : number
12+
13+
};
14+
15+
export = x
16+
>x : { "greeting": string; "default": number; }
17+
18+
=== tests/cases/compiler/imp.ts ===
19+
import foo from "./exp";
20+
>foo : number
21+
22+
foo.toExponential(2);
23+
>foo.toExponential(2) : string
24+
>foo.toExponential : (fractionDigits?: number) => string
25+
>foo : number
26+
>toExponential : (fractionDigits?: number) => string
27+
>2 : number
28+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
// @Filename: exp.ts
3+
var x = {
4+
"greeting": "hello, world",
5+
"default": 42
6+
};
7+
8+
export = x
9+
10+
// @Filename: imp.ts
11+
import foo from "./exp";
12+
foo.toExponential(2);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts'/>
2+
// @allowJs: true
3+
4+
// @Filename: mod.js
5+
//// function foo() { return {a: true}; }
6+
//// module.exports = foo();
7+
8+
// @Filename: app.js
9+
//// import * as mod from "./mod"
10+
//// mod./**/
11+
12+
goTo.marker();
13+
verify.completionListContains('a');
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path='fourslash.ts'/>
2+
// @allowJs: true
3+
// @module: system
4+
5+
// @Filename: mod.js
6+
//// function foo() { return {a: true}; }
7+
//// module.exports = foo();
8+
9+
// @Filename: app.js
10+
//// import mod from "./mod"
11+
//// mod./**/
12+
13+
goTo.marker();
14+
verify.completionListContains('a');
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference path='fourslash.ts'/>
2+
// @allowJs: true
3+
4+
// @Filename: mod.js
5+
//// function foo() { return {a: "hello, world"}; }
6+
//// module.exports = foo();
7+
8+
// @Filename: mod2.js
9+
//// var x = {name: 'test'};
10+
//// (function createExport(obj){
11+
//// module.exports = {
12+
//// "default": x,
13+
//// "sausages": {eggs: 2}
14+
//// };
15+
//// })();
16+
17+
// @Filename: app.js
18+
//// import {a} from "./mod"
19+
//// import def, {sausages} from "./mod2"
20+
//// a./**/
21+
22+
goTo.marker();
23+
verify.completionListContains('toString');
24+
25+
edit.backspace(2);
26+
edit.insert("def.");
27+
verify.completionListContains("name");
28+
29+
edit.insert("name;\nsausages.");
30+
verify.completionListContains("eggs");
31+
edit.insert("eggs;");
32+
verify.numberOfErrorsInCurrentFile(0);

0 commit comments

Comments
 (0)