Skip to content

Commit

Permalink
feat: it just works
Browse files Browse the repository at this point in the history
TODO: PR to babel repo
  • Loading branch information
JounQin committed Aug 27, 2020
1 parent e8efedd commit 4cc44f8
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 12 deletions.
1 change: 1 addition & 0 deletions babel-parser/parser/error-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ export const ErrorMessages = Object.freeze({
UnexpectedToken: "Unexpected token '%0'",
UnexpectedTokenUnaryExponentiation:
"Illegal expression. Wrap left hand side or entire exponentiation in parentheses.",
UnexpectedAwaitOperation: "Unexpected await operation '%0', only `all`, `race`, `allSettled` and `any` are supported",
UnsupportedBind: "Binding should be performed on object property.",
UnsupportedDecoratorExport:
"A decorated export must export a class declaration",
Expand Down
12 changes: 12 additions & 0 deletions babel-parser/parser/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ import {
} from "../util/production-parameter";
import { Errors } from "./error";

const AWAIT_EXPRESSIONS = ['all', 'race', 'allSettled', 'any'];

export default class ExpressionParser extends LValParser {
// Forward-declaration: defined in statement.js
/*::
Expand Down Expand Up @@ -2418,10 +2420,20 @@ export default class ExpressionParser extends LValParser {
} else if (this.state.awaitPos === -1) {
this.state.awaitPos = node.start;
}

if (this.eat(tt.star)) {
this.raise(node.start, Errors.ObsoleteAwaitStar);
}

if(this.hasPlugin('await-ops') && this.eat(tt.dot)) {
const id = this.parseIdentifier().name;
if (AWAIT_EXPRESSIONS.includes(id)) {
node.operation = id;
} else {
this.raise(node.start, Errors.UnexpectedAwaitOperation, id);
}
}

if (!this.scope.inFunction && !this.options.allowAwaitOutsideFunction) {
if (
this.hasPrecedingLineBreak() ||
Expand Down
1 change: 1 addition & 0 deletions babel-parser/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ export type YieldExpression = NodeBase & {
export type AwaitExpression = NodeBase & {
type: "AwaitExpression",
argument: ?Expression,
operation?: "all" | "race" | "allSettled" | "any";
};

export type ArrayExpression = NodeBase & {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"@babel/preset-flow": "^7.10.4",
"babel-plugin-transform-charcodes": "^0.2.0",
"charcodes": "^0.2.0",
"jest": "^26.4.2"
"jest": "^26.4.2",
"prettier": "^2.1.1"
},
"jest": {
"roots": [
Expand Down
32 changes: 28 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
import { declare } from "@babel/helper-plugin-utils";

import { parse } from "../babel-parser";

export default function babelPluginProposalAwaitOps(api, options) {
import syntax from "./syntax";

export default declare((api) => {
api.assertVersion(7);

const t = api.types;

return {
parserOverride(code, options) {
return parse(code, options);
name: "transform-await-ops",
inherits: syntax,
parserOverride: parse,
visitor: {
AwaitExpression({ node }) {
if (!node.operation) {
return;
}

node.argument = t.callExpression(
t.memberExpression(
t.identifier("Promise"),
t.identifier(node.operation)
),
[node.argument]
);

delete node.operation;
},
},
};
}
});
11 changes: 11 additions & 0 deletions src/syntax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { declare } from "@babel/helper-plugin-utils";

export default declare(api => {
api.assertVersion(7);
return {
name: "syntax-await-ops",
manipulateOptions(opts, parserOpts) {
parserOpts.plugins.push("await-ops");
},
};
});
111 changes: 104 additions & 7 deletions tests/parser.spec.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,110 @@
import { transform } from "@babel/core";
import { parseExpression } from "../babel-parser";
import { Errors } from "../babel-parser/parser/error";

import babelPluginProposalAwaitOps from "../src";
const COMMON_OPTIONS = {
allowAwaitOutsideFunction: true,
plugins: ["await-ops"],
};

describe("parser", () => {
it("should just work", () => {
expect(
transform("const foo = 1", {
plugins: [babelPluginProposalAwaitOps],
}).code
).toBe("const foo = 1;");
expect(parseExpression("await.all foo", COMMON_OPTIONS))
.toMatchInlineSnapshot(`
Node {
"argument": Node {
"end": 13,
"loc": SourceLocation {
"end": Position {
"column": 13,
"line": 1,
},
"identifierName": "foo",
"start": Position {
"column": 10,
"line": 1,
},
},
"name": "foo",
"start": 10,
"type": "Identifier",
},
"comments": Array [],
"end": 13,
"errors": Array [],
"loc": SourceLocation {
"end": Position {
"column": 13,
"line": 1,
},
"start": Position {
"column": 0,
"line": 1,
},
},
"operation": "all",
"start": 0,
"type": "AwaitExpression",
}
`);
});

it("should be able to parse literal array", () => {
expect(parseExpression("await.race []", COMMON_OPTIONS))
.toMatchInlineSnapshot(`
Node {
"argument": Node {
"elements": Array [],
"end": 13,
"loc": SourceLocation {
"end": Position {
"column": 13,
"line": 1,
},
"start": Position {
"column": 11,
"line": 1,
},
},
"start": 11,
"type": "ArrayExpression",
},
"comments": Array [],
"end": 13,
"errors": Array [],
"loc": SourceLocation {
"end": Position {
"column": 13,
"line": 1,
},
"start": Position {
"column": 0,
"line": 1,
},
},
"operation": "race",
"start": 0,
"type": "AwaitExpression",
}
`);
});

it("should throw syntax error if plugin not enabled", () => {
expect(() =>
parseExpression("await.ops foo", {
allowAwaitOutsideFunction: true,
})
).toThrow("Unexpected token");
});

it("should throw on unsupported operation", () => {
expect(() => parseExpression("await.ops foo", COMMON_OPTIONS)).toThrow(
Errors.UnexpectedAwaitOperation.replace(/%0/g, "ops")
);
});

it("should throw on invalid syntax", () => {
expect(() => parseExpression("await. []", COMMON_OPTIONS)).toThrow(
"Unexpected token"
);
});
});
26 changes: 26 additions & 0 deletions tests/plugin.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { transform } from "@babel/core";

import babelPluginProposalAwaitOps from "../src";

const COMMON_OPTIONS = {
babelrc: false,
plugins: ["@babel/syntax-top-level-await", babelPluginProposalAwaitOps],
};

describe("plugin", () => {
it("should just work", () => {
expect(transform(`await.all foo`, COMMON_OPTIONS).code).toBe(
"await Promise.all(foo);"
);
});

it("should work with literal array", () => {
expect(transform(`await.all [Promise.resolve()]`, COMMON_OPTIONS).code).toBe(
"await Promise.all([Promise.resolve()]);"
);
});

it("should work with regular await expression", () => {
expect(transform(`await foo`, COMMON_OPTIONS).code).toBe("await foo;");
});
});
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3406,6 +3406,11 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=

prettier@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.1.tgz#d9485dd5e499daa6cb547023b87a6cf51bee37d6"
integrity sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw==

pretty-format@^26.4.2:
version "26.4.2"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237"
Expand Down

0 comments on commit 4cc44f8

Please sign in to comment.