diff --git a/packages/babel-core/src/transformation/file/index.js b/packages/babel-core/src/transformation/file/index.js index 793d850adf76..617fde6b1de4 100644 --- a/packages/babel-core/src/transformation/file/index.js +++ b/packages/babel-core/src/transformation/file/index.js @@ -414,7 +414,7 @@ export default class File extends Store { parse(code: string) { let parseCode = parse; - let parserOpts = this.opts.parserOpts || this.parserOpts; + let parserOpts = this.opts.parserOpts; if (parserOpts) { parserOpts = Object.assign({}, this.parserOpts, parserOpts); @@ -441,7 +441,7 @@ export default class File extends Store { } this.log.debug("Parse start"); - let ast = parseCode(code, parserOpts); + let ast = parseCode(code, parserOpts || this.parserOpts); this.log.debug("Parse stop"); return ast; } diff --git a/packages/babel-generator/src/generators/statements.js b/packages/babel-generator/src/generators/statements.js index 3a31ccc84d15..a5ec4818820f 100644 --- a/packages/babel-generator/src/generators/statements.js +++ b/packages/babel-generator/src/generators/statements.js @@ -84,7 +84,13 @@ let buildForXStatement = function (op) { return function (node: Object) { this.word("for"); this.space(); + if (op === "await") { + this.word("await"); + this.space(); + op = "of"; + } this.token("("); + this.print(node.left, node); this.space(); this.word(op); @@ -97,6 +103,7 @@ let buildForXStatement = function (op) { export let ForInStatement = buildForXStatement("in"); export let ForOfStatement = buildForXStatement("of"); +export let ForAwaitStatement = buildForXStatement("await"); export function DoWhileStatement(node: Object) { this.word("do"); diff --git a/packages/babel-generator/src/index.js b/packages/babel-generator/src/index.js index 098a343256b6..ad6fee5724ea 100644 --- a/packages/babel-generator/src/index.js +++ b/packages/babel-generator/src/index.js @@ -122,7 +122,7 @@ function findCommonStringDelimiter(code, tokens) { if (occurences.single > occurences.double) { return "single"; } else { - return DEFAULT_STRING_DELIMITER; + return "double"; } } diff --git a/packages/babel-helper-remap-async-to-generator/src/for-await.js b/packages/babel-helper-remap-async-to-generator/src/for-await.js new file mode 100644 index 000000000000..5e3fe1f0bc24 --- /dev/null +++ b/packages/babel-helper-remap-async-to-generator/src/for-await.js @@ -0,0 +1,106 @@ +import * as t from "babel-types"; +import template from "babel-template"; +import traverse from "babel-traverse"; + +let buildForAwait = template(` + function* wrapper() { + var ITERATOR_COMPLETION = true; + var ITERATOR_HAD_ERROR_KEY = false; + var ITERATOR_ERROR_KEY = undefined; + try { + for ( + var ITERATOR_KEY = GET_ITERATOR(OBJECT), STEP_KEY, STEP_VALUE; + ( + STEP_KEY = yield AWAIT(ITERATOR_KEY.next()), + ITERATOR_COMPLETION = STEP_KEY.done, + STEP_VALUE = yield AWAIT(STEP_KEY.value), + !ITERATOR_COMPLETION + ); + ITERATOR_COMPLETION = true) { + } + } catch (err) { + ITERATOR_HAD_ERROR_KEY = true; + ITERATOR_ERROR_KEY = err; + } finally { + try { + if (!ITERATOR_COMPLETION && ITERATOR_KEY.return) { + yield AWAIT(ITERATOR_KEY.return()); + } + } finally { + if (ITERATOR_HAD_ERROR_KEY) { + throw ITERATOR_ERROR_KEY; + } + } + } + } +`); + +let forAwaitVisitor = { + noScope: true, + + Identifier(path, replacements) { + if (path.node.name in replacements) { + path.replaceInline(replacements[path.node.name]); + } + }, + + CallExpression(path, replacements) { + let callee = path.node.callee; + + // if no await wrapping is being applied, unwrap the call expression + if (t.isIdentifier(callee) && callee.name === "AWAIT" && !replacements.AWAIT) { + path.replaceWith(path.node.arguments[0]); + } + } +}; + +export default function (path, helpers) { + let { node, scope, parent } = path; + + let stepKey = scope.generateUidIdentifier("step"); + let stepValue = scope.generateUidIdentifier("value"); + let left = node.left; + let declar; + + if (t.isIdentifier(left) || t.isPattern(left) || t.isMemberExpression(left)) { + // for await (i of test), for await ({ i } of test) + declar = t.expressionStatement(t.assignmentExpression("=", left, stepValue)); + } else if (t.isVariableDeclaration(left)) { + // for await (let i of test) + declar = t.variableDeclaration(left.kind, [ + t.variableDeclarator(left.declarations[0].id, stepValue) + ]); + } + + let template = buildForAwait(); + + traverse(template, forAwaitVisitor, null, { + ITERATOR_HAD_ERROR_KEY: scope.generateUidIdentifier("didIteratorError"), + ITERATOR_COMPLETION: scope.generateUidIdentifier("iteratorNormalCompletion"), + ITERATOR_ERROR_KEY: scope.generateUidIdentifier("iteratorError"), + ITERATOR_KEY: scope.generateUidIdentifier("iterator"), + GET_ITERATOR: helpers.getAsyncIterator, + OBJECT: node.right, + STEP_VALUE: stepValue, + STEP_KEY: stepKey, + AWAIT: helpers.wrapAwait + }); + + // remove generator function wrapper + template = template.body.body; + + let isLabeledParent = t.isLabeledStatement(parent); + let tryBody = template[3].block.body; + let loop = tryBody[0]; + + if (isLabeledParent) { + tryBody[0] = t.labeledStatement(parent.label, loop); + } + + return { + replaceParent: isLabeledParent, + node: template, + declar, + loop + }; +} diff --git a/packages/babel-helper-remap-async-to-generator/src/index.js b/packages/babel-helper-remap-async-to-generator/src/index.js index 2e28dc3be11c..4f53d5bb5dac 100644 --- a/packages/babel-helper-remap-async-to-generator/src/index.js +++ b/packages/babel-helper-remap-async-to-generator/src/index.js @@ -4,6 +4,7 @@ import type { NodePath } from "babel-traverse"; import nameFunction from "babel-helper-function-name"; import template from "babel-template"; import * as t from "babel-types"; +import rewriteForAwait from "./for-await"; let buildWrapper = template(` (() => { @@ -25,15 +26,54 @@ let namedBuildWrapper = template(` `); let awaitVisitor = { - ArrowFunctionExpression(path) { - if (!path.node.async) { + Function(path) { + if (path.isArrowFunctionExpression() && !path.node.async) { path.arrowFunctionToShadowed(); + return; } + path.skip(); }, - AwaitExpression({ node }) { + AwaitExpression({ node }, { wrapAwait }) { node.type = "YieldExpression"; + if (wrapAwait) { + node.argument = t.callExpression(wrapAwait, [node.argument]); + } + }, + + ForAwaitStatement(path, { file, wrapAwait }) { + let { node } = path; + + let build = rewriteForAwait(path, { + getAsyncIterator: file.addHelper("asyncIterator"), + wrapAwait + }); + + let { declar, loop } = build; + let block = loop.body; + + // ensure that it's a block so we can take all its statements + path.ensureBlock(); + + // add the value declaration to the new loop body + if (declar) { + block.body.push(declar); + } + + // push the rest of the original loop body onto our new body + block.body = block.body.concat(node.body.body); + + t.inherits(loop, node); + t.inherits(loop.body, node.body); + + if (build.replaceParent) { + path.parentPath.replaceWithMultiple(build.node); + path.remove(); + } else { + path.replaceWithMultiple(build.node); + } } + }; function classOrObjectMethod(path: NodePath, callId: Object) { @@ -111,15 +151,15 @@ function plainFunction(path: NodePath, callId: Object) { } } -export default function (path: NodePath, callId: Object) { - let node = path.node; - if (node.generator) return; - - path.traverse(awaitVisitor); +export default function (path: NodePath, file: Object, helpers: Object) { + path.get("body").traverse(awaitVisitor, { + file, + wrapAwait: helpers.wrapAwait + }); if (path.isClassMethod() || path.isObjectMethod()) { - return classOrObjectMethod(path, callId); + classOrObjectMethod(path, helpers.wrapAsync); } else { - return plainFunction(path, callId); + plainFunction(path, helpers.wrapAsync); } } diff --git a/packages/babel-helper-transform-fixture-test-runner/src/index.js b/packages/babel-helper-transform-fixture-test-runner/src/index.js index 766d3424d37a..f1361482ff65 100644 --- a/packages/babel-helper-transform-fixture-test-runner/src/index.js +++ b/packages/babel-helper-transform-fixture-test-runner/src/index.js @@ -47,6 +47,7 @@ function run(task) { let execCode = exec.code; let result; + let resultExec; if (execCode) { let execOpts = getOpts(exec); @@ -54,7 +55,7 @@ function run(task) { execCode = result.code; try { - runExec(execOpts, execCode); + resultExec = runExec(execOpts, execCode); } catch (err) { err.message = exec.loc + ": " + err.message; err.message += codeFrame(execCode); @@ -90,6 +91,10 @@ function run(task) { chai.expect({ line: expect.line, column: expect.column }).to.deep.equal(actual); }); } + + if (execCode && resultExec) { + return resultExec; + } } function runExec(opts, execCode) { @@ -151,7 +156,14 @@ export default function ( return throwMsg === true || err.message.indexOf(throwMsg) >= 0; }); } else { - runTask(); + if (task.exec.code) { + let result = run(task); + if (result && typeof result.then === "function") { + return result; + } + } else { + runTask(); + } } }); } diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index d70abc2f6a36..87a58c3dc957 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -61,6 +61,158 @@ helpers.jsx = template(` })() `); +helpers.asyncIterator = template(` + (function (iterable) { + if (typeof Symbol === "function") { + if (Symbol.asyncIterator) { + var method = iterable[Symbol.asyncIterator]; + if (method != null) return method.call(iterable); + } + if (Symbol.iterator) { + return iterable[Symbol.iterator](); + } + } + throw new TypeError("Object is not async iterable"); + }) +`); + +helpers.asyncGenerator = template(` + (function () { + function AwaitValue(value) { + this.value = value; + } + + function AsyncGenerator(gen) { + var front, back; + + function send(key, arg) { + return new Promise(function (resolve, reject) { + var request = { + key: key, + arg: arg, + resolve: resolve, + reject: reject, + next: null + }; + + if (back) { + back = back.next = request; + } else { + front = back = request; + resume(key, arg); + } + }); + } + + function resume(key, arg) { + try { + var result = gen[key](arg) + var value = result.value; + if (value instanceof AwaitValue) { + Promise.resolve(value.value).then( + function (arg) { resume("next", arg); }, + function (arg) { resume("throw", arg); }); + } else { + settle(result.done ? "return" : "normal", result.value); + } + } catch (err) { + settle("throw", err); + } + } + + function settle(type, value) { + switch (type) { + case "return": + front.resolve({ value: value, done: true }); + break; + case "throw": + front.reject(value); + break; + default: + front.resolve({ value: value, done: false }); + break; + } + + front = front.next; + if (front) { + resume(front.key, front.arg); + } else { + back = null; + } + } + + this._invoke = send; + + // Hide "return" method if generator return is not supported + if (typeof gen.return !== "function") { + this.return = undefined; + } + } + + if (typeof Symbol === "function" && Symbol.asyncIterator) { + AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; + } + + AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); }; + AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); }; + AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); }; + + return { + wrap: function (fn) { + return function () { + return new AsyncGenerator(fn.apply(this, arguments)); + }; + }, + await: function (value) { + return new AwaitValue(value); + } + }; + + })() +`); + +helpers.asyncGeneratorDelegate = template(` + (function (inner, awaitWrap) { + var iter = {}, waiting = false; + + function pump(key, value) { + waiting = true; + value = new Promise(function (resolve) { resolve(inner[key](value)); }); + return { done: false, value: awaitWrap(value) }; + }; + + if (typeof Symbol === "function" && Symbol.iterator) { + iter[Symbol.iterator] = function () { return this; }; + } + + iter.next = function (value) { + if (waiting) { + waiting = false; + return value; + } + return pump("next", value); + }; + + if (typeof inner.throw === "function") { + iter.throw = function (value) { + if (waiting) { + waiting = false; + throw value; + } + return pump("throw", value); + }; + } + + if (typeof inner.return === "function") { + iter.return = function (value) { + return pump("return", value); + }; + } + + return iter; + }) +`); + helpers.asyncToGenerator = template(` (function (fn) { return function () { @@ -92,7 +244,6 @@ helpers.asyncToGenerator = template(` }) `); - helpers.classCallCheck = template(` (function (instance, Constructor) { if (!(instance instanceof Constructor)) { diff --git a/packages/babel-plugin-transform-async-generator-functions/.npmignore b/packages/babel-plugin-transform-async-generator-functions/.npmignore new file mode 100644 index 000000000000..31852902b187 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/.npmignore @@ -0,0 +1,4 @@ +node_modules +*.log +src +test diff --git a/packages/babel-plugin-transform-async-generator-functions/README.md b/packages/babel-plugin-transform-async-generator-functions/README.md new file mode 100644 index 000000000000..d0d16279a963 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/README.md @@ -0,0 +1,35 @@ +# babel-plugin-transform-async-functions + +Turn async generator functions and for-await statements to ES2015 generators + +## Installation + +```sh +$ npm install babel-plugin-transform-async-generator-functions +``` + +## Usage + +### Via `.babelrc` (Recommended) + +**.babelrc** + +```json +{ + "plugins": ["transform-async-generator-functions"] +} +``` + +### Via CLI + +```sh +$ babel --plugins transform-async-generator-functions script.js +``` + +### Via Node API + +```javascript +require("babel-core").transform("code", { + plugins: ["transform-async-generator-functions"] +}); +``` diff --git a/packages/babel-plugin-transform-async-generator-functions/package.json b/packages/babel-plugin-transform-async-generator-functions/package.json new file mode 100644 index 000000000000..c1461c35a5e5 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/package.json @@ -0,0 +1,19 @@ +{ + "name": "babel-plugin-transform-async-generator-functions", + "version": "6.7.4", + "description": "Turn async generator functions into ES2015 generators", + "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-async-generator-functions", + "license": "MIT", + "main": "lib/index.js", + "keywords": [ + "babel-plugin" + ], + "dependencies": { + "babel-helper-remap-async-to-generator": "^6.7.0", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^5.0.0" + }, + "devDependencies": { + "babel-helper-plugin-test-runner": "^6.3.13" + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/src/index.js b/packages/babel-plugin-transform-async-generator-functions/src/index.js new file mode 100644 index 000000000000..eb9ec868be9a --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/src/index.js @@ -0,0 +1,36 @@ +import remapAsyncToGenerator from "babel-helper-remap-async-to-generator"; + +export default function ({ types: t }) { + let yieldStarVisitor = { + Function(path) { + path.skip(); + }, + + YieldExpression({ node }, state) { + if (!node.delegate) return; + let callee = state.addHelper("asyncGeneratorDelegate"); + node.argument = t.callExpression(callee, [ + t.callExpression(state.addHelper("asyncIterator"), [node.argument]), + t.memberExpression(state.addHelper("asyncGenerator"), t.identifier("await")) + ]); + } + }; + + return { + inherits: require("babel-plugin-syntax-async-generators"), + visitor: { + Function(path, state) { + if (!path.node.async || !path.node.generator) return; + + path.get("body").traverse(yieldStarVisitor, state); + + remapAsyncToGenerator(path, state.file, { + wrapAsync: t.memberExpression( + state.addHelper("asyncGenerator"), t.identifier("wrap")), + wrapAwait: t.memberExpression( + state.addHelper("asyncGenerator"), t.identifier("await")) + }); + } + } + }; +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/class-method/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/class-method/actual.js new file mode 100644 index 000000000000..e424c364d6a1 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/class-method/actual.js @@ -0,0 +1,8 @@ +class C { + async *g() { + this; + await 1; + yield 2; + return 3; + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/class-method/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/class-method/expected.js new file mode 100644 index 000000000000..a774b790eb38 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/class-method/expected.js @@ -0,0 +1,12 @@ +class C { + *g() { + var _this = this; + + return babelHelpers.asyncGenerator.wrap(function* () { + _this; + yield babelHelpers.asyncGenerator.await(1); + yield 2; + return 3; + })(); + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/declaration/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/declaration/actual.js new file mode 100644 index 000000000000..d99c759fd18f --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/declaration/actual.js @@ -0,0 +1,6 @@ +async function* agf() { + this; + await 1; + yield 2; + return 3; +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/declaration/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/declaration/expected.js new file mode 100644 index 000000000000..20293ae9a055 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/declaration/expected.js @@ -0,0 +1,12 @@ +let agf = (() => { + var _ref = babelHelpers.asyncGenerator.wrap(function* () { + this; + yield babelHelpers.asyncGenerator.await(1); + yield 2; + return 3; + }); + + return function agf() { + return _ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/expression/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/expression/actual.js new file mode 100644 index 000000000000..7256cc084e58 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/expression/actual.js @@ -0,0 +1,6 @@ +(async function* agf() { + this; + await 1; + yield 2; + return 3; +}); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/expression/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/expression/expected.js new file mode 100644 index 000000000000..551b495d2092 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/expression/expected.js @@ -0,0 +1,14 @@ +(() => { + var _ref = babelHelpers.asyncGenerator.wrap(function* () { + this; + yield babelHelpers.asyncGenerator.await(1); + yield 2; + return 3; + }); + + function agf() { + return _ref.apply(this, arguments); + } + + return agf; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/object-method/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/object-method/actual.js new file mode 100644 index 000000000000..162d728c618c --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/object-method/actual.js @@ -0,0 +1,8 @@ +({ + async *g() { + this; + await 1; + yield 2; + return 3; + } +}); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/object-method/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/object-method/expected.js new file mode 100644 index 000000000000..e4fefacc6c50 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/object-method/expected.js @@ -0,0 +1,12 @@ +({ + *g() { + var _this = this; + + return babelHelpers.asyncGenerator.wrap(function* () { + _this; + yield babelHelpers.asyncGenerator.await(1); + yield 2; + return 3; + })(); + } +}); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/options.json b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/options.json new file mode 100644 index 000000000000..aef31a16820b --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["external-helpers", "transform-async-generator-functions"] +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/static-method/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/static-method/actual.js new file mode 100644 index 000000000000..6e5ee476529e --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/static-method/actual.js @@ -0,0 +1,8 @@ +class C { + static async *g() { + this; + await 1; + yield 2; + return 3; + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/static-method/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/static-method/expected.js new file mode 100644 index 000000000000..01bbe9574235 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/static-method/expected.js @@ -0,0 +1,12 @@ +class C { + static *g() { + var _this = this; + + return babelHelpers.asyncGenerator.wrap(function* () { + _this; + yield babelHelpers.asyncGenerator.await(1); + yield 2; + return 3; + })(); + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/yield-star/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/yield-star/actual.js new file mode 100644 index 000000000000..04ff990adacb --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/yield-star/actual.js @@ -0,0 +1,4 @@ +async function* g() { + yield* [1, 2, 3]; + yield* iterable; +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/yield-star/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/yield-star/expected.js new file mode 100644 index 000000000000..153b55cf298b --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/async-generators/yield-star/expected.js @@ -0,0 +1,10 @@ +let g = (() => { + var _ref = babelHelpers.asyncGenerator.wrap(function* () { + yield* babelHelpers.asyncGeneratorDelegate(babelHelpers.asyncIterator([1, 2, 3]), babelHelpers.asyncGenerator.await); + yield* babelHelpers.asyncGeneratorDelegate(babelHelpers.asyncIterator(iterable), babelHelpers.asyncGenerator.await); + }); + + return function g() { + return _ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-arrow/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-arrow/actual.js new file mode 100644 index 000000000000..58977d06d133 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-arrow/actual.js @@ -0,0 +1,5 @@ +async () => { + for await (let x of y) { + f(x); + } +}; diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-arrow/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-arrow/expected.js new file mode 100644 index 000000000000..592442cb6c3f --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-arrow/expected.js @@ -0,0 +1,26 @@ +babelHelpers.asyncToGenerator(function* () { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = babelHelpers.asyncIterator(y), _step, _value; _step = yield _iterator.next(), _iteratorNormalCompletion = _step.done, _value = yield _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) { + let x = _value; + + f(x); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + yield _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } +}); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-function/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-function/actual.js new file mode 100644 index 000000000000..5c5c665303a2 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-function/actual.js @@ -0,0 +1,5 @@ +async function f() { + for await (let x of y) { + g(x); + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-function/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-function/expected.js new file mode 100644 index 000000000000..432fe03b12bd --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-function/expected.js @@ -0,0 +1,32 @@ +let f = (() => { + var _ref = babelHelpers.asyncToGenerator(function* () { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = babelHelpers.asyncIterator(y), _step, _value; _step = yield _iterator.next(), _iteratorNormalCompletion = _step.done, _value = yield _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) { + let x = _value; + + g(x); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + yield _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + }); + + return function f() { + return _ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator-exec/exec.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator-exec/exec.js new file mode 100644 index 000000000000..3cf76d12ef5e --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator-exec/exec.js @@ -0,0 +1,26 @@ +"use strict"; + +async function* genAnswers() { + var stream = [ Promise.resolve(4), Promise.resolve(9), Promise.resolve(12) ]; + var total = 0; + for await (let val of stream) { + total += await val; + yield total; + } +} + +function forEach(ai, fn) { + return ai.next().then(function (r) { + if (!r.done) { + fn(r); + return forEach(ai, fn); + } + }); +} + +var output = 0; +return forEach(genAnswers(), function(val) { output += val.value }) +.then(function () { + assert.equal(output, 42); +}); + diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator-exec/options.json b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator-exec/options.json new file mode 100644 index 000000000000..1130726a1c29 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator-exec/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + "external-helpers", + "transform-async-to-generator", + "transform-async-generator-functions" + ], + "presets": ["es2015"], + "parserOpts": { + "allowReturnOutsideFunction": true + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator/actual.js new file mode 100644 index 000000000000..e384d58b64e0 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator/actual.js @@ -0,0 +1,5 @@ +async function* g() { + for await (let x of y) { + f(x); + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator/expected.js new file mode 100644 index 000000000000..cb749b1f405b --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/async-generator/expected.js @@ -0,0 +1,32 @@ +let g = (() => { + var _ref = babelHelpers.asyncGenerator.wrap(function* () { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = babelHelpers.asyncIterator(y), _step, _value; _step = yield babelHelpers.asyncGenerator.await(_iterator.next()), _iteratorNormalCompletion = _step.done, _value = yield babelHelpers.asyncGenerator.await(_step.value), !_iteratorNormalCompletion; _iteratorNormalCompletion = true) { + let x = _value; + + f(x); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + yield babelHelpers.asyncGenerator.await(_iterator.return()); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + }); + + return function g() { + return _ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/destructuring/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/destructuring/actual.js new file mode 100644 index 000000000000..4feeb6f5421d --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/destructuring/actual.js @@ -0,0 +1,5 @@ +async function f() { + for await (let { x, y: [z] } of a) { + g(x, z); + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/destructuring/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/destructuring/expected.js new file mode 100644 index 000000000000..d65b4e8c713e --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/destructuring/expected.js @@ -0,0 +1,32 @@ +let f = (() => { + var _ref = babelHelpers.asyncToGenerator(function* () { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = babelHelpers.asyncIterator(a), _step, _value; _step = yield _iterator.next(), _iteratorNormalCompletion = _step.done, _value = yield _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) { + let { x, y: [z] } = _value; + + g(x, z); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + yield _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + }); + + return function f() { + return _ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/options.json b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/options.json new file mode 100644 index 000000000000..3fa0c2907898 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/for-await/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + "external-helpers", + "transform-async-to-generator", + "transform-async-generator-functions" + ] +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/arrows-in-declaration/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/arrows-in-declaration/actual.js new file mode 100644 index 000000000000..f3b855d8f48f --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/arrows-in-declaration/actual.js @@ -0,0 +1,11 @@ +async function* g() { + () => this; + function f() { + () => this; + } + async () => { + this; + await 1; + } + await 1; +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/arrows-in-declaration/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/arrows-in-declaration/expected.js new file mode 100644 index 000000000000..23d3264481f7 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/arrows-in-declaration/expected.js @@ -0,0 +1,21 @@ +let g = (() => { + var _ref = babelHelpers.asyncGenerator.wrap(function* () { + var _this = this; + + (function () { + return _this; + }); + function f() { + () => this; + } + babelHelpers.asyncToGenerator(function* () { + _this; + yield 1; + }); + yield babelHelpers.asyncGenerator.await(1); + }); + + return function g() { + return _ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/async-in-params/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/async-in-params/actual.js new file mode 100644 index 000000000000..4689f9d05830 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/async-in-params/actual.js @@ -0,0 +1,4 @@ +async function* g(x = async function() { await 1 }) { + await 2; + yield 3; +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/async-in-params/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/async-in-params/expected.js new file mode 100644 index 000000000000..4ad05ddcfacf --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/async-in-params/expected.js @@ -0,0 +1,12 @@ +let g = (() => { + var _ref = babelHelpers.asyncGenerator.wrap(function* (x = babelHelpers.asyncToGenerator(function* () { + yield 1; + })) { + yield babelHelpers.asyncGenerator.await(2); + yield 3; + }); + + return function g(_x) { + return _ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/generator-in-async/actual.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/generator-in-async/actual.js new file mode 100644 index 000000000000..47f4d695c432 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/generator-in-async/actual.js @@ -0,0 +1,7 @@ +async function f() { + await 1; + async function* g() { + await 2; + yield 3; + } +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/generator-in-async/expected.js b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/generator-in-async/expected.js new file mode 100644 index 000000000000..77d601e92076 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/generator-in-async/expected.js @@ -0,0 +1,20 @@ +let f = (() => { + var _ref = babelHelpers.asyncToGenerator(function* () { + let g = (() => { + var _ref2 = babelHelpers.asyncGenerator.wrap(function* () { + yield babelHelpers.asyncGenerator.await(2); + yield 3; + }); + + return function g() { + return _ref2.apply(this, arguments); + }; + })(); + + yield 1; + }); + + return function f() { + return _ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/options.json b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/options.json new file mode 100644 index 000000000000..fb37d724a88d --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/fixtures/nested/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + "external-helpers", + "transform-async-to-generator", + "transform-async-generator-functions" + ] +} diff --git a/packages/babel-plugin-transform-async-generator-functions/test/index.js b/packages/babel-plugin-transform-async-generator-functions/test/index.js new file mode 100644 index 000000000000..1f6634aabde0 --- /dev/null +++ b/packages/babel-plugin-transform-async-generator-functions/test/index.js @@ -0,0 +1 @@ +require("babel-helper-plugin-test-runner")(__dirname); diff --git a/packages/babel-plugin-transform-async-to-generator/src/index.js b/packages/babel-plugin-transform-async-to-generator/src/index.js index ac28f6aae40a..beabecb253c8 100644 --- a/packages/babel-plugin-transform-async-to-generator/src/index.js +++ b/packages/babel-plugin-transform-async-to-generator/src/index.js @@ -8,7 +8,9 @@ export default function () { Function(path, state) { if (!path.node.async || path.node.generator) return; - remapAsyncToGenerator(path, state.addHelper("asyncToGenerator")); + remapAsyncToGenerator(path, state.file, { + wrapAsync: state.addHelper("asyncToGenerator") + }); } } }; diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/object-method-with-arrows/actual.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/object-method-with-arrows/actual.js index dbda981a1fdc..f3e32193aa2c 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/object-method-with-arrows/actual.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/object-method-with-arrows/actual.js @@ -1,6 +1,6 @@ class Class { async method() { - this; + this; () => this; () => { this; diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/object-method-with-arrows/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/object-method-with-arrows/expected.js index dfb676bf2fc1..d95ecfaa9450 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/object-method-with-arrows/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/object-method-with-arrows/expected.js @@ -16,9 +16,9 @@ class Class { var _this2 = this; this; - (function () { - _this2; - }); + () => { + this; + }; babelHelpers.asyncToGenerator(function* () { _this2; }); @@ -27,10 +27,10 @@ class Class { function x() { var _this3 = this; - this; - (function () { - _this3; - }); + this; + () => { + this; + }; babelHelpers.asyncToGenerator(function* () { _this3; }); diff --git a/packages/babel-plugin-transform-async-to-module-method/src/index.js b/packages/babel-plugin-transform-async-to-module-method/src/index.js index 3b8039bffea8..f9a7706ed1e9 100644 --- a/packages/babel-plugin-transform-async-to-module-method/src/index.js +++ b/packages/babel-plugin-transform-async-to-module-method/src/index.js @@ -8,10 +8,9 @@ export default function () { Function(path, state) { if (!path.node.async || path.node.generator) return; - remapAsyncToGenerator( - path, - state.addImport(state.opts.module, state.opts.method) - ); + remapAsyncToGenerator(path, state.file, { + wrapAsync: state.addImport(state.opts.module, state.opts.method) + }); } } }; diff --git a/packages/babel-preset-stage-2/package.json b/packages/babel-preset-stage-2/package.json index 22e8e0dc36be..ca6572cb19b5 100644 --- a/packages/babel-preset-stage-2/package.json +++ b/packages/babel-preset-stage-2/package.json @@ -8,6 +8,7 @@ "repository": "https://github.com/babel/babel/tree/master/packages/babel-preset-stage-2", "main": "lib/index.js", "dependencies": { + "babel-plugin-transform-async-generator-functions": "^6.7.4", "babel-plugin-transform-class-properties": "^6.3.13", "babel-plugin-transform-decorators": "^6.13.0", "babel-plugin-transform-object-rest-spread": "^6.3.13", diff --git a/packages/babel-types/src/definitions/experimental.js b/packages/babel-types/src/definitions/experimental.js index cf4667d8d553..afe11aa07c45 100644 --- a/packages/babel-types/src/definitions/experimental.js +++ b/packages/babel-types/src/definitions/experimental.js @@ -11,6 +11,22 @@ defineType("AwaitExpression", { } }); +defineType("ForAwaitStatement", { + visitor: ["left", "right", "body"], + aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop", "ForXStatement"], + fields: { + left: { + validate: assertNodeType("VariableDeclaration", "LVal") + }, + right: { + validate: assertNodeType("Expression") + }, + body: { + validate: assertNodeType("Statement") + } + } +}); + defineType("BindExpression", { visitor: ["object", "callee"], aliases: ["Expression"],