diff --git a/packages/babylon/README.md b/packages/babylon/README.md index c1762058be..c710218b6b 100644 --- a/packages/babylon/README.md +++ b/packages/babylon/README.md @@ -32,6 +32,10 @@ mind. When in doubt, use `.parse()`. declarations can only appear at a program's top level. Setting this option to `true` allows them anywhere where a statement is allowed. +- **allowAwaitOutsideFunction**: By default, `await` use is not allowed + outside of an async function. Set this to `true` to accept such + code. + - **allowReturnOutsideFunction**: By default, a return statement at the top level raises an error. Set this to `true` to accept such code. diff --git a/packages/babylon/src/options.js b/packages/babylon/src/options.js index e78a7fe98d..a8d5be16ba 100755 --- a/packages/babylon/src/options.js +++ b/packages/babylon/src/options.js @@ -7,6 +7,7 @@ export type Options = { sourceType: "script" | "module", sourceFilename?: string, startLine: number, + allowAwaitOutsideFunction: boolean, allowReturnOutsideFunction: boolean, allowImportExportEverywhere: boolean, allowSuperOutsideMethod: boolean, @@ -24,6 +25,9 @@ export const defaultOptions: Options = { // Line from which to start counting source. Useful for // integration with other tools. startLine: 1, + // When enabled, await at the top level is not considered an + // error. + allowAwaitOutsideFunction: false, // When enabled, a return at the top level is not considered an // error. allowReturnOutsideFunction: false, diff --git a/packages/babylon/src/parser/expression.js b/packages/babylon/src/parser/expression.js index 1abe3905a3..d7d4429dcc 100644 --- a/packages/babylon/src/parser/expression.js +++ b/packages/babylon/src/parser/expression.js @@ -753,13 +753,21 @@ export default class ExpressionParser extends LValParser { case tt.name: { node = this.startNode(); - const allowAwait = this.state.value === "await" && this.state.inAsync; + const allowAwait = + this.state.value === "await" && + (this.state.inAsync || + (!this.state.inFunction && this.options.allowAwaitOutsideFunction)); + const containsEsc = this.state.containsEsc; const allowYield = this.shouldAllowYieldIdentifier(); const id = this.parseIdentifier(allowAwait || allowYield); if (id.name === "await") { - if (this.state.inAsync || this.inModule) { + if ( + this.state.inAsync || + this.inModule || + (!this.state.inFunction && this.options.allowAwaitOutsideFunction) + ) { return this.parseAwait(node); } } else if ( @@ -1862,7 +1870,10 @@ export default class ExpressionParser extends LValParser { parseAwait(node: N.AwaitExpression): N.AwaitExpression { // istanbul ignore next: this condition is checked at the call site so won't be hit here - if (!this.state.inAsync) { + if ( + !this.state.inAsync && + (this.state.inFunction || !this.options.allowAwaitOutsideFunction) + ) { this.unexpected(); } if (this.match(tt.star)) { diff --git a/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function-throw/input.js b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function-throw/input.js new file mode 100644 index 0000000000..62d44c99a0 --- /dev/null +++ b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function-throw/input.js @@ -0,0 +1,3 @@ +function a() { + return await 1 +} diff --git a/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function-throw/options.json b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function-throw/options.json new file mode 100644 index 0000000000..3c9820fa55 --- /dev/null +++ b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function-throw/options.json @@ -0,0 +1,4 @@ +{ + "allowAwaitOutsideFunction": true, + "throws": "Unexpected token, expected \";\" (2:15)" +} diff --git a/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/input.js b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/input.js new file mode 100644 index 0000000000..ed1d1f1152 --- /dev/null +++ b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/input.js @@ -0,0 +1 @@ +await 1 diff --git a/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/options.json b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/options.json new file mode 100644 index 0000000000..40360d582b --- /dev/null +++ b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/options.json @@ -0,0 +1,3 @@ +{ + "allowAwaitOutsideFunction": true +} diff --git a/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/output.json b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/output.json new file mode 100644 index 0000000000..4b56863b51 --- /dev/null +++ b/packages/babylon/test/fixtures/es2017/async-functions/allow-await-outside-function/output.json @@ -0,0 +1,84 @@ +{ + "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", + "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": 1, + "raw": "1" + }, + "value": 1 + } + } + } + ], + "directives": [] + } +}