From e4bcd1d0fff6c29390b17b0030005b9181e84b8a Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Fri, 27 Oct 2017 11:55:24 -0700 Subject: [PATCH] Allow Flowtype's imports and exports when sourceType:script is set. (#771) --- src/parser/statement.js | 40 ++-- src/plugins/flow.js | 16 ++ .../sourcetype-script/export-named/actual.js | 2 + .../export-named/expected.json | 167 +++++++++++++++ .../sourcetype-script/export-star/actual.js | 1 + .../export-star/expected.json | 70 +++++++ .../flow/sourcetype-script/import/actual.js | 2 + .../sourcetype-script/import/expected.json | 192 ++++++++++++++++++ .../flow/sourcetype-script/options.json | 4 + 9 files changed, 477 insertions(+), 17 deletions(-) create mode 100644 test/fixtures/flow/sourcetype-script/export-named/actual.js create mode 100644 test/fixtures/flow/sourcetype-script/export-named/expected.json create mode 100644 test/fixtures/flow/sourcetype-script/export-star/actual.js create mode 100644 test/fixtures/flow/sourcetype-script/export-star/expected.json create mode 100644 test/fixtures/flow/sourcetype-script/import/actual.js create mode 100644 test/fixtures/flow/sourcetype-script/import/expected.json create mode 100644 test/fixtures/flow/sourcetype-script/options.json diff --git a/src/parser/statement.js b/src/parser/statement.js index 4145d5e61f..8ab5f8d31c 100644 --- a/src/parser/statement.js +++ b/src/parser/statement.js @@ -129,7 +129,7 @@ export default class StatementParser extends ExpressionParser { case tt.semi: return this.parseEmptyStatement(node); case tt._export: - case tt._import: + case tt._import: { if ( (this.hasPlugin("dynamicImport") && this.lookahead().type === tt.parenL) || @@ -137,29 +137,26 @@ export default class StatementParser extends ExpressionParser { ) break; - if (!this.options.allowImportExportEverywhere) { - if (!topLevel) { - this.raise( - this.state.start, - "'import' and 'export' may only appear at the top level", - ); - } - - if (!this.inModule) { - this.raise( - this.state.start, - `'import' and 'export' may appear only with 'sourceType: "module"'`, - ); - } + if (!this.options.allowImportExportEverywhere && !topLevel) { + this.raise( + this.state.start, + "'import' and 'export' may only appear at the top level", + ); } this.next(); + + let result; if (starttype == tt._import) { - return this.parseImport(node); + result = this.parseImport(node); } else { - return this.parseExport(node); + result = this.parseExport(node); } + this.assertModuleNodeAllowed(node); + + return result; + } case tt.name: if (this.state.value === "async") { // peek ahead and see if next token is a function @@ -193,6 +190,15 @@ export default class StatementParser extends ExpressionParser { } } + assertModuleNodeAllowed(node: N.Node): void { + if (!this.options.allowImportExportEverywhere && !this.inModule) { + this.raise( + node.start, + `'import' and 'export' may appear only with 'sourceType: "module"'`, + ); + } + } + takeDecorators(node: N.HasDecorators): void { const decorators = this.state.decoratorStack[ this.state.decoratorStack.length - 1 diff --git a/src/plugins/flow.js b/src/plugins/flow.js index 8cc8461198..3351461786 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -1498,6 +1498,22 @@ export default (superClass: Class): Class => return node; } + assertModuleNodeAllowed(node: N.Node) { + if ( + (node.type === "ImportDeclaration" && + (node.importKind === "type" || node.importKind === "typeof")) || + (node.type === "ExportNamedDeclaration" && + node.exportKind === "type") || + (node.type === "ExportAllDeclaration" && node.exportKind === "type") + ) { + // Allow Flowtype imports and exports in all conditions because + // Flow itself does not care about 'sourceType'. + return; + } + + super.assertModuleNodeAllowed(node); + } + parseExport(node: N.ExportNamedDeclaration): N.ExportNamedDeclaration { node = super.parseExport(node); if ( diff --git a/test/fixtures/flow/sourcetype-script/export-named/actual.js b/test/fixtures/flow/sourcetype-script/export-named/actual.js new file mode 100644 index 0000000000..f0afdd16f8 --- /dev/null +++ b/test/fixtures/flow/sourcetype-script/export-named/actual.js @@ -0,0 +1,2 @@ +export type Foo = number; +export opaque type Foo = number; diff --git a/test/fixtures/flow/sourcetype-script/export-named/expected.json b/test/fixtures/flow/sourcetype-script/export-named/expected.json new file mode 100644 index 0000000000..ea31b11733 --- /dev/null +++ b/test/fixtures/flow/sourcetype-script/export-named/expected.json @@ -0,0 +1,167 @@ +{ + "type": "File", + "start": 0, + "end": 58, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 32 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 58, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 32 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExportNamedDeclaration", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "specifiers": [], + "source": null, + "exportKind": "type", + "declaration": { + "type": "TypeAlias", + "start": 7, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 25 + } + }, + "id": { + "type": "Identifier", + "start": 12, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "Foo" + }, + "name": "Foo" + }, + "typeParameters": null, + "right": { + "type": "NumberTypeAnnotation", + "start": 18, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 24 + } + } + } + } + }, + { + "type": "ExportNamedDeclaration", + "start": 26, + "end": 58, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 32 + } + }, + "specifiers": [], + "source": null, + "exportKind": "type", + "declaration": { + "type": "OpaqueType", + "start": 33, + "end": 58, + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 32 + } + }, + "id": { + "type": "Identifier", + "start": 45, + "end": 48, + "loc": { + "start": { + "line": 2, + "column": 19 + }, + "end": { + "line": 2, + "column": 22 + }, + "identifierName": "Foo" + }, + "name": "Foo" + }, + "typeParameters": null, + "supertype": null, + "impltype": { + "type": "NumberTypeAnnotation", + "start": 51, + "end": 57, + "loc": { + "start": { + "line": 2, + "column": 25 + }, + "end": { + "line": 2, + "column": 31 + } + } + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/flow/sourcetype-script/export-star/actual.js b/test/fixtures/flow/sourcetype-script/export-star/actual.js new file mode 100644 index 0000000000..2cbf3033e3 --- /dev/null +++ b/test/fixtures/flow/sourcetype-script/export-star/actual.js @@ -0,0 +1 @@ +export type * from "foo"; diff --git a/test/fixtures/flow/sourcetype-script/export-star/expected.json b/test/fixtures/flow/sourcetype-script/export-star/expected.json new file mode 100644 index 0000000000..2ec0d6c1b1 --- /dev/null +++ b/test/fixtures/flow/sourcetype-script/export-star/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": "script", + "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 diff --git a/test/fixtures/flow/sourcetype-script/import/actual.js b/test/fixtures/flow/sourcetype-script/import/actual.js new file mode 100644 index 0000000000..f61b89613b --- /dev/null +++ b/test/fixtures/flow/sourcetype-script/import/actual.js @@ -0,0 +1,2 @@ +import type { Foo } from ""; +import typeof Foo2 from ""; diff --git a/test/fixtures/flow/sourcetype-script/import/expected.json b/test/fixtures/flow/sourcetype-script/import/expected.json new file mode 100644 index 0000000000..98f93f216a --- /dev/null +++ b/test/fixtures/flow/sourcetype-script/import/expected.json @@ -0,0 +1,192 @@ +{ + "type": "File", + "start": 0, + "end": 56, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 56, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ImportDeclaration", + "start": 0, + "end": 28, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 28 + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "imported": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + }, + "identifierName": "Foo" + }, + "name": "Foo" + }, + "importKind": null, + "local": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 17 + }, + "identifierName": "Foo" + }, + "name": "Foo" + } + } + ], + "importKind": "type", + "source": { + "type": "StringLiteral", + "start": 25, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 25 + }, + "end": { + "line": 1, + "column": 27 + } + }, + "extra": { + "rawValue": "", + "raw": "\"\"" + }, + "value": "" + } + }, + { + "type": "ImportDeclaration", + "start": 29, + "end": 56, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start": 43, + "end": 47, + "loc": { + "start": { + "line": 2, + "column": 14 + }, + "end": { + "line": 2, + "column": 18 + } + }, + "local": { + "type": "Identifier", + "start": 43, + "end": 47, + "loc": { + "start": { + "line": 2, + "column": 14 + }, + "end": { + "line": 2, + "column": 18 + }, + "identifierName": "Foo2" + }, + "name": "Foo2" + } + } + ], + "importKind": "typeof", + "source": { + "type": "StringLiteral", + "start": 53, + "end": 55, + "loc": { + "start": { + "line": 2, + "column": 24 + }, + "end": { + "line": 2, + "column": 26 + } + }, + "extra": { + "rawValue": "", + "raw": "\"\"" + }, + "value": "" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/flow/sourcetype-script/options.json b/test/fixtures/flow/sourcetype-script/options.json new file mode 100644 index 0000000000..2a1feba000 --- /dev/null +++ b/test/fixtures/flow/sourcetype-script/options.json @@ -0,0 +1,4 @@ +{ + "sourceType": "script", + "plugins": ["flow"] +}