diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4e9cd66b807..27803b43526 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -355,11 +355,24 @@ namespace ts { ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; - forEach(symbol.declarations, declaration => { - if (hasModifier(declaration, ModifierFlags.Default)) { + if (symbol.declarations && symbol.declarations.length) { + // If the current node is a default export of some sort, then check if + // there are any other default exports that we need to error on. + // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. + if (isDefaultExport) { message = Diagnostics.A_module_cannot_have_multiple_default_exports; } - }); + else { + // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. + // Error on multiple export default in the following case: + // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default + // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) + if (symbol.declarations && symbol.declarations.length && + (isDefaultExport || (node.kind === SyntaxKind.ExportAssignment && !(node).isExportEquals))) { + message = Diagnostics.A_module_cannot_have_multiple_default_exports; + } + } + } forEach(symbol.declarations, declaration => { file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration))); @@ -1944,12 +1957,15 @@ namespace ts { bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node)); } else { + // An export default clause with an expression exports a value + // We want to exclude both class and function here, this is necessary to issue an error when there are both + // default export-assignment and default export function and class declaration. const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node) // An export default clause with an EntityNameExpression exports all meanings of that identifier ? SymbolFlags.Alias // An export default clause with any other expression exports a value : SymbolFlags.Property; - declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); + declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.Property | SymbolFlags.AliasExcludes | SymbolFlags.Class | SymbolFlags.Function); } } diff --git a/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt b/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt index 4c1d237aa47..05fecf03d30 100644 --- a/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt +++ b/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt @@ -1,9 +1,15 @@ +tests/cases/compiler/a.js(1,22): error TS2528: A module cannot have multiple default exports. +tests/cases/compiler/a.js(3,1): error TS2528: A module cannot have multiple default exports. tests/cases/compiler/a.js(3,16): error TS1109: Expression expected. -==== tests/cases/compiler/a.js (1 errors) ==== +==== tests/cases/compiler/a.js (3 errors) ==== export default class a { + ~ +!!! error TS2528: A module cannot have multiple default exports. } export default var a = 10; + ~~~~~~~~~~~~~~ +!!! error TS2528: A module cannot have multiple default exports. ~~~ !!! error TS1109: Expression expected. \ No newline at end of file diff --git a/tests/baselines/reference/multipleDefaultExports01.errors.txt b/tests/baselines/reference/multipleDefaultExports01.errors.txt index b3e7d757289..16aa3b2f7b1 100644 --- a/tests/baselines/reference/multipleDefaultExports01.errors.txt +++ b/tests/baselines/reference/multipleDefaultExports01.errors.txt @@ -1,16 +1,13 @@ -tests/cases/conformance/es6/modules/m1.ts(2,22): error TS2323: Cannot redeclare exported variable 'default'. tests/cases/conformance/es6/modules/m1.ts(2,22): error TS2528: A module cannot have multiple default exports. tests/cases/conformance/es6/modules/m1.ts(6,25): error TS2528: A module cannot have multiple default exports. -tests/cases/conformance/es6/modules/m1.ts(11,1): error TS2323: Cannot redeclare exported variable 'default'. +tests/cases/conformance/es6/modules/m1.ts(11,1): error TS2528: A module cannot have multiple default exports. tests/cases/conformance/es6/modules/m2.ts(3,1): error TS2348: Value of type 'typeof foo' is not callable. Did you mean to include 'new'? -==== tests/cases/conformance/es6/modules/m1.ts (4 errors) ==== +==== tests/cases/conformance/es6/modules/m1.ts (3 errors) ==== export default class foo { ~~~ -!!! error TS2323: Cannot redeclare exported variable 'default'. - ~~~ !!! error TS2528: A module cannot have multiple default exports. } @@ -24,7 +21,7 @@ tests/cases/conformance/es6/modules/m2.ts(3,1): error TS2348: Value of type 'typ var x = 10; export default x; ~~~~~~~~~~~~~~~~~ -!!! error TS2323: Cannot redeclare exported variable 'default'. +!!! error TS2528: A module cannot have multiple default exports. ==== tests/cases/conformance/es6/modules/m2.ts (1 errors) ==== import Entity from "./m1" diff --git a/tests/baselines/reference/multipleExportDefault1.errors.txt b/tests/baselines/reference/multipleExportDefault1.errors.txt new file mode 100644 index 00000000000..3da6afc41e0 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault1.errors.txt @@ -0,0 +1,19 @@ +tests/cases/conformance/externalModules/multipleExportDefault1.ts(1,25): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault1.ts(5,1): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault1.ts (2 errors) ==== + export default function Foo (){ + ~~~ +!!! error TS2528: A module cannot have multiple default exports. + + } + + export default { + ~~~~~~~~~~~~~~~~ + uhoh: "another default", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~ +!!! error TS2528: A module cannot have multiple default exports. + \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault1.js b/tests/baselines/reference/multipleExportDefault1.js new file mode 100644 index 00000000000..bec4fb48969 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault1.js @@ -0,0 +1,20 @@ +//// [multipleExportDefault1.ts] +export default function Foo (){ + +} + +export default { + uhoh: "another default", +}; + + +//// [multipleExportDefault1.js] +"use strict"; +function Foo() { +} +exports.__esModule = true; +exports["default"] = Foo; +exports.__esModule = true; +exports["default"] = { + uhoh: "another default" +}; diff --git a/tests/baselines/reference/multipleExportDefault2.errors.txt b/tests/baselines/reference/multipleExportDefault2.errors.txt new file mode 100644 index 00000000000..0543e25a163 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault2.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/externalModules/multipleExportDefault2.ts(1,1): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault2.ts(5,25): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault2.ts (2 errors) ==== + export default { + ~~~~~~~~~~~~~~~~ + uhoh: "another default", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~ +!!! error TS2528: A module cannot have multiple default exports. + + export default function Foo() { } + ~~~ +!!! error TS2528: A module cannot have multiple default exports. + + \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault2.js b/tests/baselines/reference/multipleExportDefault2.js new file mode 100644 index 00000000000..dc82500a1d9 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault2.js @@ -0,0 +1,18 @@ +//// [multipleExportDefault2.ts] +export default { + uhoh: "another default", +}; + +export default function Foo() { } + + + +//// [multipleExportDefault2.js] +"use strict"; +exports.__esModule = true; +exports["default"] = { + uhoh: "another default" +}; +function Foo() { } +exports.__esModule = true; +exports["default"] = Foo; diff --git a/tests/baselines/reference/multipleExportDefault3.errors.txt b/tests/baselines/reference/multipleExportDefault3.errors.txt new file mode 100644 index 00000000000..f1e55da9112 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault3.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/externalModules/multipleExportDefault3.ts(1,1): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault3.ts(5,22): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault3.ts (2 errors) ==== + export default { + ~~~~~~~~~~~~~~~~ + uhoh: "another default", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~ +!!! error TS2528: A module cannot have multiple default exports. + + export default class C { } + ~ +!!! error TS2528: A module cannot have multiple default exports. + + \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault3.js b/tests/baselines/reference/multipleExportDefault3.js new file mode 100644 index 00000000000..5d7ad48e0ca --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault3.js @@ -0,0 +1,22 @@ +//// [multipleExportDefault3.ts] +export default { + uhoh: "another default", +}; + +export default class C { } + + + +//// [multipleExportDefault3.js] +"use strict"; +exports.__esModule = true; +exports["default"] = { + uhoh: "another default" +}; +var C = (function () { + function C() { + } + return C; +}()); +exports.__esModule = true; +exports["default"] = C; diff --git a/tests/baselines/reference/multipleExportDefault4.errors.txt b/tests/baselines/reference/multipleExportDefault4.errors.txt new file mode 100644 index 00000000000..bba15527d00 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault4.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/externalModules/multipleExportDefault4.ts(1,22): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault4.ts(3,1): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault4.ts (2 errors) ==== + export default class C { } + ~ +!!! error TS2528: A module cannot have multiple default exports. + + export default { + ~~~~~~~~~~~~~~~~ + uhoh: "another default", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }; + ~~ +!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault4.js b/tests/baselines/reference/multipleExportDefault4.js new file mode 100644 index 00000000000..1199fa91c9c --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault4.js @@ -0,0 +1,20 @@ +//// [multipleExportDefault4.ts] +export default class C { } + +export default { + uhoh: "another default", +}; + +//// [multipleExportDefault4.js] +"use strict"; +var C = (function () { + function C() { + } + return C; +}()); +exports.__esModule = true; +exports["default"] = C; +exports.__esModule = true; +exports["default"] = { + uhoh: "another default" +}; diff --git a/tests/baselines/reference/multipleExportDefault5.errors.txt b/tests/baselines/reference/multipleExportDefault5.errors.txt new file mode 100644 index 00000000000..30c87339ff2 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault5.errors.txt @@ -0,0 +1,11 @@ +tests/cases/conformance/externalModules/multipleExportDefault5.ts(1,25): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault5.ts(2,22): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault5.ts (2 errors) ==== + export default function bar() { } + ~~~ +!!! error TS2528: A module cannot have multiple default exports. + export default class C {} + ~ +!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault5.js b/tests/baselines/reference/multipleExportDefault5.js new file mode 100644 index 00000000000..7ae33aee9f4 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault5.js @@ -0,0 +1,16 @@ +//// [multipleExportDefault5.ts] +export default function bar() { } +export default class C {} + +//// [multipleExportDefault5.js] +"use strict"; +function bar() { } +exports.__esModule = true; +exports["default"] = bar; +var C = (function () { + function C() { + } + return C; +}()); +exports.__esModule = true; +exports["default"] = C; diff --git a/tests/baselines/reference/multipleExportDefault6.errors.txt b/tests/baselines/reference/multipleExportDefault6.errors.txt new file mode 100644 index 00000000000..a8bf80e8df0 --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault6.errors.txt @@ -0,0 +1,20 @@ +tests/cases/conformance/externalModules/multipleExportDefault6.ts(1,1): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/externalModules/multipleExportDefault6.ts(5,1): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/externalModules/multipleExportDefault6.ts (2 errors) ==== + export default { + ~~~~~~~~~~~~~~~~ + lol: 1 + ~~~~~~~~~~ + } + ~ +!!! error TS2528: A module cannot have multiple default exports. + + export default { + ~~~~~~~~~~~~~~~~ + lol: 2 + ~~~~~~~~~~ + } + ~ +!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault6.js b/tests/baselines/reference/multipleExportDefault6.js new file mode 100644 index 00000000000..a8b93459e4e --- /dev/null +++ b/tests/baselines/reference/multipleExportDefault6.js @@ -0,0 +1,19 @@ +//// [multipleExportDefault6.ts] +export default { + lol: 1 +} + +export default { + lol: 2 +} + +//// [multipleExportDefault6.js] +"use strict"; +exports.__esModule = true; +exports["default"] = { + lol: 1 +}; +exports.__esModule = true; +exports["default"] = { + lol: 2 +}; diff --git a/tests/cases/conformance/externalModules/multipleExportDefault1.ts b/tests/cases/conformance/externalModules/multipleExportDefault1.ts new file mode 100644 index 00000000000..623cc2acc20 --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault1.ts @@ -0,0 +1,7 @@ +export default function Foo (){ + +} + +export default { + uhoh: "another default", +}; diff --git a/tests/cases/conformance/externalModules/multipleExportDefault2.ts b/tests/cases/conformance/externalModules/multipleExportDefault2.ts new file mode 100644 index 00000000000..0e14b570aa1 --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault2.ts @@ -0,0 +1,6 @@ +export default { + uhoh: "another default", +}; + +export default function Foo() { } + diff --git a/tests/cases/conformance/externalModules/multipleExportDefault3.ts b/tests/cases/conformance/externalModules/multipleExportDefault3.ts new file mode 100644 index 00000000000..3b5460e8141 --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault3.ts @@ -0,0 +1,6 @@ +export default { + uhoh: "another default", +}; + +export default class C { } + diff --git a/tests/cases/conformance/externalModules/multipleExportDefault4.ts b/tests/cases/conformance/externalModules/multipleExportDefault4.ts new file mode 100644 index 00000000000..6181c360798 --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault4.ts @@ -0,0 +1,5 @@ +export default class C { } + +export default { + uhoh: "another default", +}; \ No newline at end of file diff --git a/tests/cases/conformance/externalModules/multipleExportDefault5.ts b/tests/cases/conformance/externalModules/multipleExportDefault5.ts new file mode 100644 index 00000000000..d2b67fce46f --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault5.ts @@ -0,0 +1,2 @@ +export default function bar() { } +export default class C {} \ No newline at end of file diff --git a/tests/cases/conformance/externalModules/multipleExportDefault6.ts b/tests/cases/conformance/externalModules/multipleExportDefault6.ts new file mode 100644 index 00000000000..889ae64376b --- /dev/null +++ b/tests/cases/conformance/externalModules/multipleExportDefault6.ts @@ -0,0 +1,7 @@ +export default { + lol: 1 +} + +export default { + lol: 2 +} \ No newline at end of file