From 62bf4aefe23aeb67513af1b4961288244e9716a1 Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Thu, 25 Feb 2016 12:37:55 -0800 Subject: [PATCH 1/4] Updated binding for module.exports --- src/compiler/binder.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 933381afa92..1e24227a876 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -278,9 +278,11 @@ namespace ts { function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); + const isJsModuleExport = node.kind === SyntaxKind.BinaryExpression ? getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports : false; const isDefaultExport = node.flags & NodeFlags.Default; + // The exported symbol for an export default function/class node is always named "default" - const name = isDefaultExport && parent ? "default" : getDeclarationName(node); + const name = isJsModuleExport ? "export=" : isDefaultExport && parent ? "default" : getDeclarationName(node); let symbol: Symbol; if (name !== undefined) { @@ -1428,7 +1430,7 @@ namespace ts { function bindModuleExportsAssignment(node: BinaryExpression) { // 'module.exports = expr' assignment setCommonJsModuleIndicator(node); - bindExportAssignment(node); + declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.Export | SymbolFlags.ValueModule, SymbolFlags.None); } function bindThisPropertyAssignment(node: BinaryExpression) { From 9e46c180b4063e56eb947928615a3ebd46ecbe5e Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Thu, 25 Feb 2016 17:06:31 -0800 Subject: [PATCH 2/4] Fixed es2015 imports from export= --- src/compiler/checker.ts | 10 +++++++++- tests/cases/fourslash/javascriptModules20.ts | 13 ++++++++++++ tests/cases/fourslash/javascriptModules21.ts | 14 +++++++++++++ tests/cases/fourslash/javascriptModules22.ts | 13 ++++++++++++ tests/cases/fourslash/javascriptModules23.ts | 12 +++++++++++ tests/cases/fourslash/javascriptModules24.ts | 21 ++++++++++++++++++++ 6 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/javascriptModules20.ts create mode 100644 tests/cases/fourslash/javascriptModules21.ts create mode 100644 tests/cases/fourslash/javascriptModules22.ts create mode 100644 tests/cases/fourslash/javascriptModules23.ts create mode 100644 tests/cases/fourslash/javascriptModules24.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 603f5dae821..703777337af 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -960,8 +960,16 @@ namespace ts { if (targetSymbol) { const name = specifier.propertyName || specifier.name; if (name.text) { + let symbolFromVariable: Symbol; + // First check if module was specified with "export=". If so, get the member from the resolved type + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { + const members = (getTypeOfSymbol(targetSymbol) as ResolvedType).members; + symbolFromVariable = members && members[name.text]; + } + else { + symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text); + } const symbolFromModule = getExportOfModule(targetSymbol, name.text); - const symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text); const symbol = symbolFromModule && symbolFromVariable ? combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; diff --git a/tests/cases/fourslash/javascriptModules20.ts b/tests/cases/fourslash/javascriptModules20.ts new file mode 100644 index 00000000000..7ef5c73e1c3 --- /dev/null +++ b/tests/cases/fourslash/javascriptModules20.ts @@ -0,0 +1,13 @@ +/// +// @allowJs: true + +// @Filename: mod.js +//// function foo() { return {a: true}; } +//// module.exports = foo(); + +// @Filename: app.js +//// import * as mod from "./mod" +//// mod./**/ + +goTo.marker(); +verify.completionListContains('a'); diff --git a/tests/cases/fourslash/javascriptModules21.ts b/tests/cases/fourslash/javascriptModules21.ts new file mode 100644 index 00000000000..3a046515924 --- /dev/null +++ b/tests/cases/fourslash/javascriptModules21.ts @@ -0,0 +1,14 @@ +/// +// @allowJs: true +// @module: system + +// @Filename: mod.js +//// function foo() { return {a: true}; } +//// module.exports = foo(); + +// @Filename: app.js +//// import mod from "./mod" +//// mod./**/ + +goTo.marker(); +verify.completionListContains('a'); diff --git a/tests/cases/fourslash/javascriptModules22.ts b/tests/cases/fourslash/javascriptModules22.ts new file mode 100644 index 00000000000..67e3423ec40 --- /dev/null +++ b/tests/cases/fourslash/javascriptModules22.ts @@ -0,0 +1,13 @@ +/// +// @allowJs: true + +// @Filename: mod.js +//// function foo() { return {a: "hello, world"}; } +//// module.exports = foo(); + +// @Filename: app.js +//// import {a} from "./mod" +//// a./**/ + +goTo.marker(); +verify.completionListContains('toString'); diff --git a/tests/cases/fourslash/javascriptModules23.ts b/tests/cases/fourslash/javascriptModules23.ts new file mode 100644 index 00000000000..eafbea87baa --- /dev/null +++ b/tests/cases/fourslash/javascriptModules23.ts @@ -0,0 +1,12 @@ +/// + +// @Filename: mod.ts +//// var foo = {a: "test"}; +//// export = foo; + +// @Filename: app.ts +//// import {a} from "./mod" +//// a./**/ + +goTo.marker(); +verify.completionListContains('toString'); diff --git a/tests/cases/fourslash/javascriptModules24.ts b/tests/cases/fourslash/javascriptModules24.ts new file mode 100644 index 00000000000..5a0dd892db7 --- /dev/null +++ b/tests/cases/fourslash/javascriptModules24.ts @@ -0,0 +1,21 @@ +/// + +// @Filename: mod.ts +//// function foo() { return 42; } +//// namespace foo { +//// export function bar (a: string) { return a; } +//// } +//// export = foo; + +// @Filename: app.ts +//// import * as foo from "./mod" +//// foo/*1*/(); +//// foo.bar(/*2*/"test"); + +goTo.marker('1'); + +/**** BUG: Should be an error to invoke a call signature on a namespace import ****/ +//verify.errorExistsBeforeMarker('1'); +verify.quickInfoIs("(alias) foo(): number\nimport foo"); +goTo.marker('2'); +verify.signatureHelpArgumentCountIs(1); From c4a10cfcdd51f831c3039e305c1c465a85c93b0b Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Sun, 28 Feb 2016 18:25:04 -0800 Subject: [PATCH 3/4] Fixed default import from export equals --- src/compiler/binder.ts | 2 +- src/compiler/checker.ts | 9 ++++-- src/compiler/utilities.ts | 3 ++ .../reference/exportEqualsDefaultProperty.js | 27 ++++++++++++++++++ .../exportEqualsDefaultProperty.symbols | 21 ++++++++++++++ .../exportEqualsDefaultProperty.types | 28 +++++++++++++++++++ .../compiler/exportEqualsDefaultProperty.ts | 12 ++++++++ tests/cases/fourslash/javascriptModules22.ts | 19 +++++++++++++ 8 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/exportEqualsDefaultProperty.js create mode 100644 tests/baselines/reference/exportEqualsDefaultProperty.symbols create mode 100644 tests/baselines/reference/exportEqualsDefaultProperty.types create mode 100644 tests/cases/compiler/exportEqualsDefaultProperty.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1e24227a876..e0297145805 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -278,7 +278,7 @@ namespace ts { function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); - const isJsModuleExport = node.kind === SyntaxKind.BinaryExpression ? getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports : false; + const isJsModuleExport = getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports; const isDefaultExport = node.flags & NodeFlags.Default; // The exported symbol for an export default function/class node is always named "default" diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 703777337af..ce01b5c8013 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -888,8 +888,12 @@ namespace ts { function getTargetOfImportClause(node: ImportClause): Symbol { const moduleSymbol = resolveExternalModuleName(node, (node.parent).moduleSpecifier); + if (moduleSymbol) { - const exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]); + const exportDefaultSymbol = moduleSymbol.exports["export="] ? + getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : + resolveSymbol(moduleSymbol.exports["default"]); + if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); } @@ -963,8 +967,7 @@ namespace ts { let symbolFromVariable: Symbol; // First check if module was specified with "export=". If so, get the member from the resolved type if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { - const members = (getTypeOfSymbol(targetSymbol) as ResolvedType).members; - symbolFromVariable = members && members[name.text]; + symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text); } else { symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fa9305ae722..42f439e0ded 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1107,6 +1107,9 @@ namespace ts { /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property /// assignments we treat as special in the binder export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind { + if (!isInJavaScriptFile(expression)) { + return SpecialPropertyAssignmentKind.None; + } if (expression.kind !== SyntaxKind.BinaryExpression) { return SpecialPropertyAssignmentKind.None; } diff --git a/tests/baselines/reference/exportEqualsDefaultProperty.js b/tests/baselines/reference/exportEqualsDefaultProperty.js new file mode 100644 index 00000000000..6d75ef5d95d --- /dev/null +++ b/tests/baselines/reference/exportEqualsDefaultProperty.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/exportEqualsDefaultProperty.ts] //// + +//// [exp.ts] + +var x = { + "greeting": "hello, world", + "default": 42 +}; + +export = x + +//// [imp.ts] +import foo from "./exp"; +foo.toExponential(2); + + +//// [exp.js] +"use strict"; +var x = { + "greeting": "hello, world", + "default": 42 +}; +module.exports = x; +//// [imp.js] +"use strict"; +var exp_1 = require("./exp"); +exp_1["default"].toExponential(2); diff --git a/tests/baselines/reference/exportEqualsDefaultProperty.symbols b/tests/baselines/reference/exportEqualsDefaultProperty.symbols new file mode 100644 index 00000000000..54bbbde6956 --- /dev/null +++ b/tests/baselines/reference/exportEqualsDefaultProperty.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/exp.ts === + +var x = { +>x : Symbol(x, Decl(exp.ts, 1, 3)) + + "greeting": "hello, world", + "default": 42 +}; + +export = x +>x : Symbol(x, Decl(exp.ts, 1, 3)) + +=== tests/cases/compiler/imp.ts === +import foo from "./exp"; +>foo : Symbol(foo, Decl(imp.ts, 0, 6)) + +foo.toExponential(2); +>foo.toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --)) +>foo : Symbol(foo, Decl(imp.ts, 0, 6)) +>toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/exportEqualsDefaultProperty.types b/tests/baselines/reference/exportEqualsDefaultProperty.types new file mode 100644 index 00000000000..31a6c8052f9 --- /dev/null +++ b/tests/baselines/reference/exportEqualsDefaultProperty.types @@ -0,0 +1,28 @@ +=== tests/cases/compiler/exp.ts === + +var x = { +>x : { "greeting": string; "default": number; } +>{ "greeting": "hello, world", "default": 42} : { "greeting": string; "default": number; } + + "greeting": "hello, world", +>"hello, world" : string + + "default": 42 +>42 : number + +}; + +export = x +>x : { "greeting": string; "default": number; } + +=== tests/cases/compiler/imp.ts === +import foo from "./exp"; +>foo : number + +foo.toExponential(2); +>foo.toExponential(2) : string +>foo.toExponential : (fractionDigits?: number) => string +>foo : number +>toExponential : (fractionDigits?: number) => string +>2 : number + diff --git a/tests/cases/compiler/exportEqualsDefaultProperty.ts b/tests/cases/compiler/exportEqualsDefaultProperty.ts new file mode 100644 index 00000000000..1adce3cabe1 --- /dev/null +++ b/tests/cases/compiler/exportEqualsDefaultProperty.ts @@ -0,0 +1,12 @@ + +// @Filename: exp.ts +var x = { + "greeting": "hello, world", + "default": 42 +}; + +export = x + +// @Filename: imp.ts +import foo from "./exp"; +foo.toExponential(2); diff --git a/tests/cases/fourslash/javascriptModules22.ts b/tests/cases/fourslash/javascriptModules22.ts index 67e3423ec40..89fa99b5ea2 100644 --- a/tests/cases/fourslash/javascriptModules22.ts +++ b/tests/cases/fourslash/javascriptModules22.ts @@ -5,9 +5,28 @@ //// function foo() { return {a: "hello, world"}; } //// module.exports = foo(); +// @Filename: mod2.js +//// var x = {name: 'test'}; +//// (function createExport(obj){ +//// module.exports = { +//// "default": x, +//// "sausages": {eggs: 2} +//// }; +//// })(); + // @Filename: app.js //// import {a} from "./mod" +//// import def, {sausages} from "./mod2" //// a./**/ goTo.marker(); verify.completionListContains('toString'); + +edit.backspace(2); +edit.insert("def."); +verify.completionListContains("name"); + +edit.insert("name;\nsausages."); +verify.completionListContains("eggs"); +edit.insert("eggs;"); +verify.numberOfErrorsInCurrentFile(0); From fa6db1b8c95e28209a4147838dc7fd2ca433b9df Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Wed, 9 Mar 2016 14:57:55 -0800 Subject: [PATCH 4/4] Addressed feedback --- src/compiler/binder.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e0297145805..dc1c908fbe2 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -278,11 +278,10 @@ namespace ts { function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); - const isJsModuleExport = getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports; const isDefaultExport = node.flags & NodeFlags.Default; // The exported symbol for an export default function/class node is always named "default" - const name = isJsModuleExport ? "export=" : isDefaultExport && parent ? "default" : getDeclarationName(node); + const name = isDefaultExport && parent ? "default" : getDeclarationName(node); let symbol: Symbol; if (name !== undefined) {