diff --git a/packages/babel-parser/src/index.js b/packages/babel-parser/src/index.js index d9f812695545..bb9b26dfd1d5 100755 --- a/packages/babel-parser/src/index.js +++ b/packages/babel-parser/src/index.js @@ -25,15 +25,31 @@ export function parse(input: string, options?: Options): File { const parser = getParser(options, input); const ast = parser.parse(); - // Rather than try to parse as a script first, we opt to parse as a module and convert back - // to a script where possible to avoid having to do a full re-parse of the input content. - if (!parser.sawUnambiguousESM) ast.program.sourceType = "script"; - return ast; + if (parser.sawUnambiguousESM) { + return ast; + } + + if (!parser.ambiguousScriptDifferentAst) { + ast.program.sourceType = "script"; + return ast; + } + + // Top level await introduces code which can be both a valid script and + // a valid module, but which produces different ASTs: + // await + // 0 + // can be parser either as an AwaitExpression, or as two ExpressionStatements. + try { + options.sourceType = "script"; + return getParser(options, input).parse(); + } catch { + return ast; + } } catch (moduleError) { try { options.sourceType = "script"; return getParser(options, input).parse(); - } catch (scriptError) {} + } catch {} throw moduleError; } diff --git a/packages/babel-parser/src/parser/base.js b/packages/babel-parser/src/parser/base.js index 1e451794158c..f4fb9f3b66e0 100644 --- a/packages/babel-parser/src/parser/base.js +++ b/packages/babel-parser/src/parser/base.js @@ -13,6 +13,7 @@ export default class BaseParser { plugins: PluginsMap; filename: ?string; sawUnambiguousESM: boolean = false; + ambiguousScriptDifferentAst: boolean = false; // Initialized by Tokenizer state: State; diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 991582c89374..2ba501884386 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -141,6 +141,7 @@ export default class ExpressionParser extends LValParser { this.toReferencedList(node.expressions); return this.finishNode(node, "SequenceExpression"); } + return expr; } @@ -2235,9 +2236,18 @@ export default class ExpressionParser extends LValParser { ); } + if (!this.scope.inFunction && !this.options.allowAwaitOutsideFunction) { + if (this.hasPrecedingLineBreak()) { + this.ambiguousScriptDifferentAst = true; + } else { + this.sawUnambiguousESM = true; + } + } + if (!this.state.soloAwait) { node.argument = this.parseMaybeUnary(); } + return this.finishNode(node, "AwaitExpression"); } diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/input.js b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/input.js new file mode 100644 index 000000000000..34723bac4ae7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/input.js @@ -0,0 +1 @@ +await 0 \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/options.json b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/options.json new file mode 100644 index 000000000000..aceca10c3d74 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/options.json @@ -0,0 +1,5 @@ +{ + "plugins": ["topLevelAwait"], + "sourceType": "unambiguous", + "allowAwaitOutsideFunction": true +} diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/output.json b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/output.json new file mode 100644 index 000000000000..1dab4defe2b8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-allowAwaitOutsideFunction/output.json @@ -0,0 +1,85 @@ +{ + "type": "File", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "expression": { + "type": "AwaitExpression", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "argument": { + "type": "NumericLiteral", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/input.js b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/input.js new file mode 100644 index 000000000000..9a32f9264db2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/input.js @@ -0,0 +1,2 @@ +await +0 \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/options.json b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/options.json new file mode 100644 index 000000000000..2503b82adf74 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["topLevelAwait"], + "sourceType": "unambiguous" +} diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/output.json b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/output.json new file mode 100644 index 000000000000..f22c73c95d00 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-ambiguous-script/output.json @@ -0,0 +1,102 @@ +{ + "type": "File", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 1 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 5 + } + }, + "expression": { + "type": "Identifier", + "start": 0, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "await" + }, + "name": "await" + } + }, + { + "type": "ExpressionStatement", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 1 + } + }, + "expression": { + "type": "NumericLiteral", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 1 + } + }, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/input.js b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/input.js new file mode 100644 index 000000000000..34723bac4ae7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/input.js @@ -0,0 +1 @@ +await 0 \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/options.json b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/options.json new file mode 100644 index 000000000000..2503b82adf74 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["topLevelAwait"], + "sourceType": "unambiguous" +} diff --git a/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/output.json b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/output.json new file mode 100644 index 000000000000..f0d3c6ce6ac7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/top-level-await/unambiguous-module/output.json @@ -0,0 +1,85 @@ +{ + "type": "File", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "expression": { + "type": "AwaitExpression", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "argument": { + "type": "NumericLiteral", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + ], + "directives": [] + } +} \ No newline at end of file