diff --git a/src/parser/statement.js b/src/parser/statement.js index 0426edda69..566942c7a8 100644 --- a/src/parser/statement.js +++ b/src/parser/statement.js @@ -1208,20 +1208,9 @@ export default class StatementParser extends ExpressionParser { // TODO: better type. Node is an N.AnyExport. parseExport(node: N.Node): N.Node { // export * from '...' - if (this.match(tt.star)) { - const specifier = this.startNode(); - this.next(); - if (this.hasPlugin("exportExtensions") && this.eatContextual("as")) { - specifier.exported = this.parseIdentifier(true); - node.specifiers = [ - this.finishNode(specifier, "ExportNamespaceSpecifier"), - ]; - this.parseExportSpecifiersMaybe(node); - this.parseExportFrom(node, true); - } else { - this.parseExportFrom(node, true); - return this.finishNode(node, "ExportAllDeclaration"); - } + if (this.shouldParseExportStar()) { + this.parseExportStar(node, this.hasPlugin("exportExtensions")); + if (node.type === "ExportAllDeclaration") return node; } else if ( this.hasPlugin("exportExtensions") && this.isExportDefaultSpecifier() @@ -1323,6 +1312,31 @@ export default class StatementParser extends ExpressionParser { this.semicolon(); } + shouldParseExportStar(): boolean { + return this.match(tt.star); + } + + parseExportStar(node: N.ExportNamedDeclaration, allowNamed: boolean): void { + this.expect(tt.star); + + if (allowNamed && this.isContextual("as")) { + const specifier = this.startNodeAt( + this.state.lastTokStart, + this.state.lastTokStartLoc, + ); + this.next(); + specifier.exported = this.parseIdentifier(true); + node.specifiers = [ + this.finishNode(specifier, "ExportNamespaceSpecifier"), + ]; + this.parseExportSpecifiersMaybe(node); + this.parseExportFrom(node, true); + } else { + this.parseExportFrom(node, true); + this.finishNode(node, "ExportAllDeclaration"); + } + } + shouldParseExportDeclaration(): boolean { return ( this.state.type.keyword === "var" || diff --git a/src/plugins/flow.js b/src/plugins/flow.js index b3faa7c350..4aef3fc723 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1283,7 +1283,10 @@ export default (superClass: Class): Class => parseExport(node: N.ExportNamedDeclaration): N.ExportNamedDeclaration { node = super.parseExport(node); - if (node.type === "ExportNamedDeclaration") { + if ( + node.type === "ExportNamedDeclaration" || + node.type === "ExportAllDeclaration" + ) { node.exportKind = node.exportKind || "value"; } return node; @@ -1315,6 +1318,22 @@ export default (superClass: Class): Class => } } + shouldParseExportStar(): boolean { + return ( + super.shouldParseExportStar() || + (this.isContextual("type") && this.lookahead().type === tt.star) + ); + } + + parseExportStar(node: N.ExportNamedDeclaration, allowNamed: boolean): void { + if (this.eatContextual("type")) { + node.exportKind = "type"; + allowNamed = false; + } + + return super.parseExportStar(node, allowNamed); + } + parseClassId(node: N.Class, isStatement: boolean, optionalId: ?boolean) { super.parseClassId(node, isStatement, optionalId); if (this.isRelational("<")) { diff --git a/src/types.js b/src/types.js index 7e85d4c7f0..8a30a73f22 100644 --- a/src/types.js +++ b/src/types.js @@ -773,6 +773,7 @@ export type ExportDefaultDeclaration = NodeBase & { export type ExportAllDeclaration = NodeBase & { type: "ExportAllDeclaration", source: Literal, + exportKind?: "type" | "value", // TODO: Not in spec }; // JSX (TODO: Not in spec) diff --git a/test/fixtures/flow/declare-export/export-star/expected.json b/test/fixtures/flow/declare-export/export-star/expected.json index 16df6ff122..acf7bb46da 100644 --- a/test/fixtures/flow/declare-export/export-star/expected.json +++ b/test/fixtures/flow/declare-export/export-star/expected.json @@ -110,7 +110,8 @@ "raw": "\"bar\"" }, "value": "bar" - } + }, + "exportKind": "value" } ] }, diff --git a/test/fixtures/flow/declare-export/export-type-star-from/actual.js b/test/fixtures/flow/declare-export/export-type-star-from/actual.js new file mode 100644 index 0000000000..ba8d8f1f1a --- /dev/null +++ b/test/fixtures/flow/declare-export/export-type-star-from/actual.js @@ -0,0 +1 @@ +declare module "foo" { declare export type * from "bar"; } \ No newline at end of file diff --git a/test/fixtures/flow/declare-export/export-type-star-from/expected.json b/test/fixtures/flow/declare-export/export-type-star-from/expected.json new file mode 100644 index 0000000000..976a865247 --- /dev/null +++ b/test/fixtures/flow/declare-export/export-type-star-from/expected.json @@ -0,0 +1,123 @@ +{ + "type": "File", + "start": 0, + "end": 58, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 58 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 58, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 58 + } + }, + "sourceType": "module", + "body": [ + { + "type": "DeclareModule", + "start": 0, + "end": 58, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 58 + } + }, + "id": { + "type": "StringLiteral", + "start": 15, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + }, + "body": { + "type": "BlockStatement", + "start": 21, + "end": 58, + "loc": { + "start": { + "line": 1, + "column": 21 + }, + "end": { + "line": 1, + "column": 58 + } + }, + "body": [ + { + "type": "DeclareExportAllDeclaration", + "start": 23, + "end": 56, + "loc": { + "start": { + "line": 1, + "column": 23 + }, + "end": { + "line": 1, + "column": 56 + } + }, + "exportKind": "type", + "source": { + "type": "StringLiteral", + "start": 50, + "end": 55, + "loc": { + "start": { + "line": 1, + "column": 50 + }, + "end": { + "line": 1, + "column": 55 + } + }, + "extra": { + "rawValue": "bar", + "raw": "\"bar\"" + }, + "value": "bar" + } + } + ] + }, + "kind": "ES" + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/flow/type-exports/star-as-from/actual.js b/test/fixtures/flow/type-exports/star-as-from/actual.js new file mode 100644 index 0000000000..0c64058352 --- /dev/null +++ b/test/fixtures/flow/type-exports/star-as-from/actual.js @@ -0,0 +1 @@ +export type * as fooTypes from "foo"; diff --git a/test/fixtures/flow/type-exports/star-as-from/options.json b/test/fixtures/flow/type-exports/star-as-from/options.json new file mode 100644 index 0000000000..fbdcfac11d --- /dev/null +++ b/test/fixtures/flow/type-exports/star-as-from/options.json @@ -0,0 +1,5 @@ +{ + "sourceType": "module", + "plugins": ["flow","exportExtensions"], + "throws": "Unexpected token (1:14)" +} \ No newline at end of file diff --git a/test/fixtures/flow/type-exports/star-from/actual.js b/test/fixtures/flow/type-exports/star-from/actual.js new file mode 100644 index 0000000000..2cbf3033e3 --- /dev/null +++ b/test/fixtures/flow/type-exports/star-from/actual.js @@ -0,0 +1 @@ +export type * from "foo"; diff --git a/test/fixtures/flow/type-exports/star-from/expected.json b/test/fixtures/flow/type-exports/star-from/expected.json new file mode 100644 index 0000000000..455d3ecd1d --- /dev/null +++ b/test/fixtures/flow/type-exports/star-from/expected.json @@ -0,0 +1,70 @@ +{ + "type": "File", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExportAllDeclaration", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "exportKind": "type", + "source": { + "type": "StringLiteral", + "start": 19, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} \ No newline at end of file