diff --git a/src/parser/statement.js b/src/parser/statement.js index 730cf0c2a4..b8822c3d77 100644 --- a/src/parser/statement.js +++ b/src/parser/statement.js @@ -627,9 +627,9 @@ pp.parseExport = function (node) { specifier.exported = this.parseIdent(); node.specifiers = [this.finishNode(specifier, "ExportNamespaceSpecifier")]; this.parseExportSpecifiersMaybe(node); - this.parseExportFrom(node); + this.parseExportFrom(node, true); } else { - this.parseExportFrom(node); + this.parseExportFrom(node, true); return this.finishNode(node, "ExportAllDeclaration"); } } else if (this.options.features["es7.exportExtensions"] && this.isExportDefaultSpecifier()) { @@ -646,7 +646,7 @@ pp.parseExport = function (node) { } else { this.parseExportSpecifiersMaybe(node); } - this.parseExportFrom(node); + this.parseExportFrom(node, true); } else if (this.eat(tt._default)) { // export default ... let expr = this.parseMaybeAssign(); let needsSemi = true; @@ -661,23 +661,22 @@ pp.parseExport = function (node) { this.checkExport(node); return this.finishNode(node, "ExportDefaultDeclaration"); } else if (this.state.type.keyword || this.shouldParseExportDeclaration()) { - node.declaration = this.parseStatement(true); node.specifiers = []; node.source = null; + node.declaration = this.parseExportDeclaration(node); } else { // export { x, y as z } [from '...'] node.declaration = null; node.specifiers = this.parseExportSpecifiers(); - if (this.eatContextual("from")) { - node.source = this.match(tt.string) ? this.parseExprAtom() : this.unexpected(); - } else { - node.source = null; - } - this.semicolon(); + this.parseExportFrom(node); } this.checkExport(node); return this.finishNode(node, "ExportNamedDeclaration"); }; +pp.parseExportDeclaration = function () { + return this.parseStatement(true); +}; + pp.isExportDefaultSpecifier = function () { if (this.match(tt.name)) { return this.state.value !== "type" && this.state.value !== "async"; @@ -697,11 +696,19 @@ pp.parseExportSpecifiersMaybe = function (node) { } }; -pp.parseExportFrom = function (node) { - this.expectContextual("from"); - node.source = this.match(tt.string) ? this.parseExprAtom() : this.unexpected(); +pp.parseExportFrom = function (node, expect?) { + if (this.eatContextual("from")) { + node.source = this.match(tt.string) ? this.parseExprAtom() : this.unexpected(); + this.checkExport(node); + } else { + if (expect) { + this.unexpected(); + } else { + node.source = null; + } + } + this.semicolon(); - this.checkExport(node); }; pp.shouldParseExportDeclaration = function () { diff --git a/src/plugins/flow.js b/src/plugins/flow.js index 550446ad10..b95ceeab60 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -669,6 +669,39 @@ export default function (instance) { }; }); + instance.extend("parseExport", function (inner) { + return function (node) { + node = inner.call(this, node); + if (node.type === "ExportNamedDeclaration") { + node.exportKind = node.exportKind || "value"; + } + return node; + }; + }); + + instance.extend("parseExportDeclaration", function (inner) { + return function (node) { + if (this.isContextual("type")) { + node.exportKind = "type"; + + var declarationNode = this.startNode(); + this.next(); + + if (this.match(tt.braceL)) { + // export type { foo, bar }; + node.specifiers = this.parseExportSpecifiers(); + this.parseExportFrom(node); + return null; + } else { + // export type Foo = Bar; + return this.flowParseTypeAlias(declarationNode); + } + } else { + return inner.call(this, node); + } + }; + }); + instance.extend("parseClassId", function (inner) { return function (node, isStatement) { inner.call(this, node, isStatement); diff --git a/test/fixtures/flow/type-exports/alias/actual.js b/test/fixtures/flow/type-exports/alias/actual.js new file mode 100644 index 0000000000..ed20a4e018 --- /dev/null +++ b/test/fixtures/flow/type-exports/alias/actual.js @@ -0,0 +1 @@ +export type a = number; diff --git a/test/fixtures/flow/type-exports/alias/expected.json b/test/fixtures/flow/type-exports/alias/expected.json new file mode 100644 index 0000000000..7387d671c4 --- /dev/null +++ b/test/fixtures/flow/type-exports/alias/expected.json @@ -0,0 +1,98 @@ +{ + "type": "File", + "start": 0, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExportNamedDeclaration", + "start": 0, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "exportKind": "type", + "specifiers": [], + "source": null, + "declaration": { + "type": "TypeAlias", + "start": 7, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "id": { + "type": "Identifier", + "start": 12, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "name": "a" + }, + "typeParameters": null, + "right": { + "type": "NumberTypeAnnotation", + "start": 16, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 22 + } + } + } + } + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/flow/type-exports/specifier-from/actual.js b/test/fixtures/flow/type-exports/specifier-from/actual.js new file mode 100644 index 0000000000..520a0786b8 --- /dev/null +++ b/test/fixtures/flow/type-exports/specifier-from/actual.js @@ -0,0 +1 @@ +export type { foo } from "foobar"; diff --git a/test/fixtures/flow/type-exports/specifier-from/expected.json b/test/fixtures/flow/type-exports/specifier-from/expected.json new file mode 100644 index 0000000000..b0ed4559fe --- /dev/null +++ b/test/fixtures/flow/type-exports/specifier-from/expected.json @@ -0,0 +1,117 @@ +{ + "type": "File", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 34 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 34 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExportNamedDeclaration", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 34 + } + }, + "exportKind": "type", + "specifiers": [ + { + "type": "ExportSpecifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "local": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "name": "foo" + }, + "exported": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "name": "foo" + } + } + ], + "source": { + "type": "Literal", + "start": 25, + "end": 33, + "loc": { + "start": { + "line": 1, + "column": 25 + }, + "end": { + "line": 1, + "column": 33 + } + }, + "value": "foobar", + "rawValue": "foobar", + "raw": "\"foobar\"" + }, + "declaration": null + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/flow/type-exports/specifier/actual.js b/test/fixtures/flow/type-exports/specifier/actual.js new file mode 100644 index 0000000000..e34ea63759 --- /dev/null +++ b/test/fixtures/flow/type-exports/specifier/actual.js @@ -0,0 +1 @@ +export type { foo }; diff --git a/test/fixtures/flow/type-exports/specifier/expected.json b/test/fixtures/flow/type-exports/specifier/expected.json new file mode 100644 index 0000000000..37900069e8 --- /dev/null +++ b/test/fixtures/flow/type-exports/specifier/expected.json @@ -0,0 +1,100 @@ +{ + "type": "File", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExportNamedDeclaration", + "start": 0, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "exportKind": "type", + "specifiers": [ + { + "type": "ExportSpecifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "local": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "name": "foo" + }, + "exported": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "name": "foo" + } + } + ], + "source": null, + "declaration": null + } + ] + } +} \ No newline at end of file