From fb9a752262c512195e1d86c0326b0a3ad05d462c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Tue, 25 Jul 2017 17:07:01 +0200 Subject: [PATCH] Function sent (#5920) * Create "babel-helper-wrap-function" It contains the logic to wrap a function inside a call expression. It was part of the "babel-helper-remap-async-to-generator" package, but it is needed to transpile "function.sent" * Create "babel-transform-function-sent" It transforms the "function.sent" meta property by replacing it with "yield" and making the generator ignore the first ".next()" call. * "function.sent" is the last value passed to .next(), not the first one * Disable exec tests on old node * Fix flow error * Add "transform-function-sent" to "stage-2" preset * Do every trasformation in one traversal * Test for "yield function.sent" * [skip ci] * Fix some typos [skip ci] --- .../package.json | 2 +- .../src/index.js | 148 +----------------- .../babel-helper-wrap-function/.npmignore | 3 + packages/babel-helper-wrap-function/README.md | 27 ++++ .../babel-helper-wrap-function/package.json | 14 ++ .../babel-helper-wrap-function/src/index.js | 146 +++++++++++++++++ packages/babel-helpers/src/helpers.js | 10 ++ .../.npmignore | 3 + .../README.md | 66 ++++++++ .../package.json | 18 +++ .../src/index.js | 56 +++++++ .../fixtures/function-sent/basic/actual.js | 3 + .../test/fixtures/function-sent/basic/exec.js | 13 ++ .../fixtures/function-sent/basic/expected.js | 13 ++ .../fixtures/function-sent/basic/options.json | 3 + .../fixtures/function-sent/multiple/actual.js | 10 ++ .../fixtures/function-sent/multiple/exec.js | 23 +++ .../function-sent/multiple/expected.js | 13 ++ .../function-sent/multiple/options.json | 3 + .../test/fixtures/function-sent/options.json | 3 + .../without-function-sent/actual.js | 4 + .../without-function-sent/expected.js | 4 + .../yield-function-sent/actual.js | 3 + .../yield-function-sent/expected.js | 7 + .../generator-kinds/async-generator/actual.js | 3 + .../async-generator/expected.js | 15 ++ .../async-generator/options.json | 3 + .../generator-kinds/class-method/actual.js | 5 + .../generator-kinds/class-method/expected.js | 12 ++ .../export-default-anonymous/actual.js | 3 + .../export-default-anonymous/expected.js | 7 + .../export-default-named/actual.js | 3 + .../export-default-named/expected.js | 15 ++ .../fixtures/generator-kinds/export/actual.js | 3 + .../generator-kinds/export/expected.js | 13 ++ .../expression-anonymous/actual.js | 3 + .../expression-anonymous/expected.js | 7 + .../expression-named/actual.js | 3 + .../expression-named/expected.js | 15 ++ .../generator-kinds/object-method/actual.js | 5 + .../generator-kinds/object-method/expected.js | 12 ++ .../fixtures/generator-kinds/options.json | 3 + .../generator-kinds/statement/actual.js | 3 + .../generator-kinds/statement/expected.js | 13 ++ .../test/index.js | 3 + packages/babel-preset-stage-2/package.json | 1 + packages/babel-preset-stage-2/src/index.js | 3 +- 47 files changed, 603 insertions(+), 145 deletions(-) create mode 100644 packages/babel-helper-wrap-function/.npmignore create mode 100644 packages/babel-helper-wrap-function/README.md create mode 100644 packages/babel-helper-wrap-function/package.json create mode 100644 packages/babel-helper-wrap-function/src/index.js create mode 100644 packages/babel-plugin-transform-function-sent/.npmignore create mode 100644 packages/babel-plugin-transform-function-sent/README.md create mode 100644 packages/babel-plugin-transform-function-sent/package.json create mode 100644 packages/babel-plugin-transform-function-sent/src/index.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/exec.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/options.json create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/exec.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/options.json create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/options.json create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/without-function-sent/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/without-function-sent/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/yield-function-sent/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/yield-function-sent/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/options.json create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/class-method/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/class-method/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-anonymous/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-anonymous/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-named/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-named/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-anonymous/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-anonymous/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-named/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-named/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/object-method/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/object-method/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/options.json create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/statement/actual.js create mode 100644 packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/statement/expected.js create mode 100644 packages/babel-plugin-transform-function-sent/test/index.js diff --git a/packages/babel-helper-remap-async-to-generator/package.json b/packages/babel-helper-remap-async-to-generator/package.json index bb3c82a488..76cab3933f 100644 --- a/packages/babel-helper-remap-async-to-generator/package.json +++ b/packages/babel-helper-remap-async-to-generator/package.json @@ -6,7 +6,7 @@ "license": "MIT", "main": "lib/index.js", "dependencies": { - "babel-helper-function-name": "7.0.0-alpha.15", + "babel-helper-wrap-function": "7.0.0-alpha.15", "babel-template": "7.0.0-alpha.15", "babel-traverse": "7.0.0-alpha.15", "babel-types": "7.0.0-alpha.15" 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 c34d0e5f8e..92f48dc585 100644 --- a/packages/babel-helper-remap-async-to-generator/src/index.js +++ b/packages/babel-helper-remap-async-to-generator/src/index.js @@ -1,30 +1,10 @@ /* @noflow */ import type { NodePath } from "babel-traverse"; -import nameFunction from "babel-helper-function-name"; -import template from "babel-template"; +import wrapFunction from "babel-helper-wrap-function"; import * as t from "babel-types"; import rewriteForAwait from "./for-await"; -const buildWrapper = template(` - (() => { - var REF = FUNCTION; - return function NAME(PARAMS) { - return REF.apply(this, arguments); - }; - }) -`); - -const namedBuildWrapper = template(` - (() => { - var REF = FUNCTION; - function NAME(PARAMS) { - return REF.apply(this, arguments); - } - return NAME; - }) -`); - const awaitVisitor = { Function(path) { path.skip(); @@ -71,123 +51,6 @@ const awaitVisitor = { }, }; -function classOrObjectMethod(path: NodePath, callId: Object) { - const node = path.node; - const body = node.body; - - node.async = false; - - const container = t.functionExpression( - null, - [], - t.blockStatement(body.body), - true, - ); - body.body = [ - t.returnStatement( - t.callExpression(t.callExpression(callId, [container]), []), - ), - ]; - - // Regardless of whether or not the wrapped function is a an async method - // or generator the outer function should not be - node.generator = false; - - // Unwrap the wrapper IIFE's environment so super and this and such still work. - path - .get("body.body.0.argument.callee.arguments.0") - .unwrapFunctionEnvironment(); -} - -function plainFunction(path: NodePath, callId: Object) { - const node = path.node; - const isDeclaration = path.isFunctionDeclaration(); - const asyncFnId = node.id; - let wrapper = buildWrapper; - - if (path.isArrowFunctionExpression()) { - path.arrowFunctionToExpression(); - } else if (!isDeclaration && asyncFnId) { - wrapper = namedBuildWrapper; - } - - node.async = false; - node.generator = true; - - node.id = null; - - if (isDeclaration) { - node.type = "FunctionExpression"; - } - - const built = t.callExpression(callId, [node]); - const container = wrapper({ - NAME: asyncFnId || null, - REF: path.scope.generateUidIdentifier("ref"), - FUNCTION: built, - PARAMS: node.params.reduce( - (acc, param) => { - acc.done = - acc.done || t.isAssignmentPattern(param) || t.isRestElement(param); - - if (!acc.done) { - acc.params.push(path.scope.generateUidIdentifier("x")); - } - - return acc; - }, - { - params: [], - done: false, - }, - ).params, - }).expression; - - if (isDeclaration) { - const declar = t.variableDeclaration("let", [ - t.variableDeclarator( - t.identifier(asyncFnId.name), - t.callExpression(container, []), - ), - ]); - declar._blockHoist = true; - - if (path.parentPath.isExportDefaultDeclaration()) { - // change the path type so that replaceWith() does not wrap - // the identifier into an expressionStatement - path.parentPath.insertBefore(declar); - path.parentPath.replaceWith( - t.exportNamedDeclaration(null, [ - t.exportSpecifier( - t.identifier(asyncFnId.name), - t.identifier("default"), - ), - ]), - ); - return; - } - - path.replaceWith(declar); - } else { - const retFunction = container.body.body[1].argument; - if (!asyncFnId) { - nameFunction({ - node: retFunction, - parent: path.parent, - scope: path.scope, - }); - } - - if (!retFunction || retFunction.id || node.params.length) { - // we have an inferred function id or params so we need this wrapper - path.replaceWith(t.callExpression(container, [])); - } else { - // we can omit this wrapper as the conditions it protects for do not apply - path.replaceWith(built); - } - } -} - export default function(path: NodePath, file: Object, helpers: Object) { if (!helpers) { // bc for 6.15 and earlier @@ -199,9 +62,8 @@ export default function(path: NodePath, file: Object, helpers: Object) { wrapAwait: helpers.wrapAwait, }); - if (path.isClassMethod() || path.isObjectMethod()) { - classOrObjectMethod(path, helpers.wrapAsync); - } else { - plainFunction(path, helpers.wrapAsync); - } + path.node.async = false; + path.node.generator = true; + + wrapFunction(path, helpers.wrapAsync); } diff --git a/packages/babel-helper-wrap-function/.npmignore b/packages/babel-helper-wrap-function/.npmignore new file mode 100644 index 0000000000..f980694583 --- /dev/null +++ b/packages/babel-helper-wrap-function/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-helper-wrap-function/README.md b/packages/babel-helper-wrap-function/README.md new file mode 100644 index 0000000000..cca5b95f11 --- /dev/null +++ b/packages/babel-helper-wrap-function/README.md @@ -0,0 +1,27 @@ +# babel-helper-wrap-function + +This helper wraps a function within a call expression. It works with any function: statements, expressions and methods; both named and anonymous. + +## Example + +**In** + +```js +(function () { +}()); +``` + +**Out** + +```js +_wrapper(function () { +})(); +``` + +## Usage + +```js +import wrapFunction from "babel-helper-wrap-function"; + +wrapFunction(nodePathOfTheFunction, nodeWhichReferencesToTheWrapper); +``` diff --git a/packages/babel-helper-wrap-function/package.json b/packages/babel-helper-wrap-function/package.json new file mode 100644 index 0000000000..971da6d1e2 --- /dev/null +++ b/packages/babel-helper-wrap-function/package.json @@ -0,0 +1,14 @@ +{ + "name": "babel-helper-wrap-function", + "version": "7.0.0-alpha.15", + "description": "Helper to wrap functions inside a function call.", + "repository": "https://github.com/babel/babel/tree/master/packages/babel-helper-wrap-function", + "license": "MIT", + "main": "lib/index.js", + "dependencies": { + "babel-helper-function-name": "7.0.0-alpha.15", + "babel-template": "7.0.0-alpha.15", + "babel-traverse": "7.0.0-alpha.15", + "babel-types": "7.0.0-alpha.15" + } +} diff --git a/packages/babel-helper-wrap-function/src/index.js b/packages/babel-helper-wrap-function/src/index.js new file mode 100644 index 0000000000..e89ac21eb4 --- /dev/null +++ b/packages/babel-helper-wrap-function/src/index.js @@ -0,0 +1,146 @@ +/* @flow */ + +import type { NodePath } from "babel-traverse"; +import nameFunction from "babel-helper-function-name"; +import template from "babel-template"; +import * as t from "babel-types"; + +const buildWrapper = template(` + (() => { + var REF = FUNCTION; + return function NAME(PARAMS) { + return REF.apply(this, arguments); + }; + }) +`); + +const namedBuildWrapper = template(` + (() => { + var REF = FUNCTION; + function NAME(PARAMS) { + return REF.apply(this, arguments); + } + return NAME; + }) +`); + +function classOrObjectMethod(path: NodePath, callId: Object) { + const node = path.node; + const body = node.body; + + const container = t.functionExpression( + null, + [], + t.blockStatement(body.body), + true, + ); + body.body = [ + t.returnStatement( + t.callExpression(t.callExpression(callId, [container]), []), + ), + ]; + + // Regardless of whether or not the wrapped function is a an async method + // or generator the outer function should not be + node.async = false; + node.generator = false; + + // Unwrap the wrapper IIFE's environment so super and this and such still work. + path + .get("body.body.0.argument.callee.arguments.0") + .unwrapFunctionEnvironment(); +} + +function plainFunction(path: NodePath, callId: Object) { + const node = path.node; + const isDeclaration = path.isFunctionDeclaration(); + const functionId = node.id; + let wrapper = buildWrapper; + + if (path.isArrowFunctionExpression()) { + path.arrowFunctionToExpression(); + } else if (!isDeclaration && functionId) { + wrapper = namedBuildWrapper; + } + + node.id = null; + + if (isDeclaration) { + node.type = "FunctionExpression"; + } + + const built = t.callExpression(callId, [node]); + const container = wrapper({ + NAME: functionId || null, + REF: path.scope.generateUidIdentifier("ref"), + FUNCTION: built, + PARAMS: node.params.reduce( + (acc, param) => { + acc.done = + acc.done || t.isAssignmentPattern(param) || t.isRestElement(param); + + if (!acc.done) { + acc.params.push(path.scope.generateUidIdentifier("x")); + } + + return acc; + }, + { + params: [], + done: false, + }, + ).params, + }).expression; + + if (isDeclaration && functionId) { + const declar = t.variableDeclaration("let", [ + t.variableDeclarator( + t.identifier(functionId.name), + t.callExpression(container, []), + ), + ]); + (declar: any)._blockHoist = true; + + if (path.parentPath.isExportDefaultDeclaration()) { + // change the path type so that replaceWith() does not wrap + // the identifier into an expressionStatement + path.parentPath.insertBefore(declar); + path.parentPath.replaceWith( + t.exportNamedDeclaration(null, [ + t.exportSpecifier( + t.identifier(functionId.name), + t.identifier("default"), + ), + ]), + ); + return; + } + + path.replaceWith(declar); + } else { + const retFunction = container.body.body[1].argument; + if (!functionId) { + nameFunction({ + node: retFunction, + parent: path.parent, + scope: path.scope, + }); + } + + if (!retFunction || retFunction.id || node.params.length) { + // we have an inferred function id or params so we need this wrapper + path.replaceWith(t.callExpression(container, [])); + } else { + // we can omit this wrapper as the conditions it protects for do not apply + path.replaceWith(built); + } + } +} + +export default function wrapFunction(path: NodePath, callId: Object) { + if (path.isClassMethod() || path.isObjectMethod()) { + classOrObjectMethod(path, callId); + } else { + plainFunction(path, callId); + } +} diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index f7ad50c838..b4b8c203a2 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -611,6 +611,16 @@ helpers.toConsumableArray = template(` }); `); +helpers.skipFirstGeneratorNext = template(` + (function (fn) { + return function () { + var it = fn.apply(this, arguments); + it.next(); + return it; + } + }); +`); + helpers.toPropertyKey = template(` (function (key) { if (typeof key === "symbol") { diff --git a/packages/babel-plugin-transform-function-sent/.npmignore b/packages/babel-plugin-transform-function-sent/.npmignore new file mode 100644 index 0000000000..f980694583 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-plugin-transform-function-sent/README.md b/packages/babel-plugin-transform-function-sent/README.md new file mode 100644 index 0000000000..119aebcd35 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/README.md @@ -0,0 +1,66 @@ +# babel-plugin-transform-function-sent + +> Compile the `function.sent` meta property, used inside generator functions, to valid ES2015 code. + +## Example + +```js +function* generator() { + console.log("Sent", function.sent); + console.log("Yield", yield); +} + +const iterator = generator(); +iterator.next(1); // Logs "Sent 1" +iterator.next(2); // Logs "Yield 2" +``` + +Is compiled roughly to + +```js +let generator = _skipFirstGeneratorNext(function* () { + const _functionSent = yield; + console.log("Sent", _functionSent); + console.log("Yield", yield); +}); + +const iterator = generator(); +iterator.next(1); // Logs "Sent 1" +iterator.next(2); // Logs "Yield 1" +``` + +## Installation + +```sh +npm install --save-dev babel-plugin-transform-function-sent +``` + +## Usage + +### Via `.babelrc` (Recommended) + +**.babelrc** + +```json +{ + "plugins": ["transform-function-sent"] +} +``` + +### Via CLI + +```sh +babel --plugins transform-function-sent script.js +``` + +### Via Node API + +```javascript +require("babel-core").transform("code", { + plugins: ["transform-function-sent"] +}); +``` + +## References + +* [Proposal](https://github.com/allenwb/ESideas/blob/master/Generator%20metaproperty.md) diff --git a/packages/babel-plugin-transform-function-sent/package.json b/packages/babel-plugin-transform-function-sent/package.json new file mode 100644 index 0000000000..130c7bcb9e --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/package.json @@ -0,0 +1,18 @@ +{ + "name": "babel-plugin-transform-function-sent", + "version": "7.0.0-alpha.15", + "description": "Compile the function.sent meta propety to valid ES2015 code", + "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-function-sent", + "license": "MIT", + "main": "lib/index.js", + "keywords": [ + "babel-plugin" + ], + "dependencies": { + "babel-plugin-syntax-function-sent": "7.0.0-alpha.15", + "babel-helper-wrap-function": "7.0.0-alpha.15" + }, + "devDependencies": { + "babel-helper-plugin-test-runner": "7.0.0-alpha.15" + } +} diff --git a/packages/babel-plugin-transform-function-sent/src/index.js b/packages/babel-plugin-transform-function-sent/src/index.js new file mode 100644 index 0000000000..bc5f8d9261 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/src/index.js @@ -0,0 +1,56 @@ +import syntaxFunctionSent from "babel-plugin-syntax-function-sent"; +import wrapFunction from "babel-helper-wrap-function"; + +export default function({ types: t }) { + const isFunctionSent = node => + t.isIdentifier(node.meta, { name: "function" }) && + t.isIdentifier(node.property, { name: "sent" }); + + const yieldVisitor = { + Function(path) { + path.skip(); + }, + + YieldExpression(path) { + const replaced = t.isAssignmentExpression(path.parent, { + left: this.sentId, + }); + if (!replaced) { + path.replaceWith(t.assignmentExpression("=", this.sentId, path.node)); + } + }, + + MetaProperty(path) { + if (isFunctionSent(path.node)) { + path.replaceWith(this.sentId); + } + }, + }; + + return { + inherits: syntaxFunctionSent, + + visitor: { + MetaProperty(path, state) { + if (!isFunctionSent(path.node)) return; + + const fnPath = path.getFunctionParent(); + + if (!fnPath.node.generator) { + throw new Error("Parent generator function not found"); + } + + const sentId = path.scope.generateUidIdentifier("function.sent"); + + fnPath.traverse(yieldVisitor, { sentId }); + fnPath.node.body.body.unshift( + t.variableDeclaration("let", [ + t.variableDeclarator(sentId, t.yieldExpression()), + ]), + ); + + wrapFunction(fnPath, state.addHelper("skipFirstGeneratorNext")); + }, + }, + }; +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/actual.js new file mode 100644 index 0000000000..47d70b8659 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/actual.js @@ -0,0 +1,3 @@ +function* gen() { + let sent = function.sent; +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/exec.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/exec.js new file mode 100644 index 0000000000..d916bf7a92 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/exec.js @@ -0,0 +1,13 @@ +let sent, yielded; + +function* gen() { + sent = function.sent; + yielded = yield; +} + +const it = gen(); +it.next(1); +it.next(2); + +assert.equal(sent, 1); +assert.equal(yielded, 2); diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/expected.js new file mode 100644 index 0000000000..e4c498a00b --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/expected.js @@ -0,0 +1,13 @@ +let gen = (() => { + var _ref = _skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + let sent = _functionSent; + }); + + return function gen() { + return _ref.apply(this, arguments); + }; +})(); + +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/options.json b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/options.json new file mode 100644 index 0000000000..7d8c3c204c --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/basic/options.json @@ -0,0 +1,3 @@ +{ + "minNodeVersion": "6.0.0" +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/actual.js new file mode 100644 index 0000000000..3a21660dc0 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/actual.js @@ -0,0 +1,10 @@ +(function* () { + const a = function.sent; + const b = function.sent; + yield 4; + const c = function.sent; + const d = yield; + const e = function.sent; + + return [ a, b, c, d, e ]; +}()); diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/exec.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/exec.js new file mode 100644 index 0000000000..4f8e23fd9c --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/exec.js @@ -0,0 +1,23 @@ +const values = []; + +function* gen() { + values.push(function.sent); + values.push(function.sent); + values.push(yield "foo"); + values.push(function.sent); + values.push(yield); + values.push(function.sent); + values.push(function.sent); +} + +const it = gen(); +assert.deepEqual(values, []); + +assert.equal(it.next(1).value, "foo"); +assert.deepEqual(values, [ 1, 1 ]); + +assert.equal(it.next(2).value, undefined); +assert.deepEqual(values, [ 1, 1, 2, 2 ]); + +assert.equal(it.next(3).done, true); +assert.deepEqual(values, [ 1, 1, 2, 2, 3, 3, 3 ]); diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/expected.js new file mode 100644 index 0000000000..8d62b2b382 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/expected.js @@ -0,0 +1,13 @@ +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +_skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + const a = _functionSent; + const b = _functionSent; + _functionSent = yield 4; + const c = _functionSent; + const d = _functionSent = yield; + const e = _functionSent; + return [a, b, c, d, e]; +})(); diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/options.json b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/options.json new file mode 100644 index 0000000000..7d8c3c204c --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/multiple/options.json @@ -0,0 +1,3 @@ +{ + "minNodeVersion": "6.0.0" +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/options.json b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/options.json new file mode 100644 index 0000000000..120c931571 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-function-sent"] +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/without-function-sent/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/without-function-sent/actual.js new file mode 100644 index 0000000000..979caa1acf --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/without-function-sent/actual.js @@ -0,0 +1,4 @@ +function* foo() { + let a = yield; + return yield; +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/without-function-sent/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/without-function-sent/expected.js new file mode 100644 index 0000000000..3b88122c1f --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/without-function-sent/expected.js @@ -0,0 +1,4 @@ +function* foo() { + let a = yield; + return yield; +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/yield-function-sent/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/yield-function-sent/actual.js new file mode 100644 index 0000000000..3e03871cda --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/yield-function-sent/actual.js @@ -0,0 +1,3 @@ +(function* () { + yield function.sent; +})(); diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/yield-function-sent/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/yield-function-sent/expected.js new file mode 100644 index 0000000000..f2e8d68f1c --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/function-sent/yield-function-sent/expected.js @@ -0,0 +1,7 @@ +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +_skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + _functionSent = yield _functionSent; +})(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/actual.js new file mode 100644 index 0000000000..c9c8634567 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/actual.js @@ -0,0 +1,3 @@ +async function* foo() { + await function.sent; +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/expected.js new file mode 100644 index 0000000000..a750d83151 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/expected.js @@ -0,0 +1,15 @@ +var _asyncGenerator = 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; 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); } }; }(); + +let foo = (() => { + var _ref = _asyncGenerator.wrap(_skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + _functionSent = yield _asyncGenerator.await(_functionSent); + })); + + return function foo() { + return _ref.apply(this, arguments); + }; +})(); + +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/options.json b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/options.json new file mode 100644 index 0000000000..cb751cbafe --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/async-generator/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [ "transform-function-sent", "transform-async-generator-functions" ] +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/class-method/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/class-method/actual.js new file mode 100644 index 0000000000..33a2a4d781 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/class-method/actual.js @@ -0,0 +1,5 @@ +class Foo { + *gen() { + return function.sent; + } +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/class-method/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/class-method/expected.js new file mode 100644 index 0000000000..de7cc1a2cd --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/class-method/expected.js @@ -0,0 +1,12 @@ +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +class Foo { + gen() { + return _skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + return _functionSent; + })(); + } + +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-anonymous/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-anonymous/actual.js new file mode 100644 index 0000000000..cf258e3904 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-anonymous/actual.js @@ -0,0 +1,3 @@ +export default function* () { + return function.sent; +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-anonymous/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-anonymous/expected.js new file mode 100644 index 0000000000..ad75cfc899 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-anonymous/expected.js @@ -0,0 +1,7 @@ +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +export default _skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + return _functionSent; +}); \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-named/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-named/actual.js new file mode 100644 index 0000000000..8d712b5bf4 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-named/actual.js @@ -0,0 +1,3 @@ +export default function* gen() { + return function.sent; +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-named/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-named/expected.js new file mode 100644 index 0000000000..f5289601cb --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export-default-named/expected.js @@ -0,0 +1,15 @@ +let gen = (() => { + var _ref = _skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + return _functionSent; + }); + + return function gen() { + return _ref.apply(this, arguments); + }; +})(); + +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +export { gen as default }; \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export/actual.js new file mode 100644 index 0000000000..ca45d14cf1 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export/actual.js @@ -0,0 +1,3 @@ +export function* gen() { + return function.sent; +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export/expected.js new file mode 100644 index 0000000000..c71bef4799 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/export/expected.js @@ -0,0 +1,13 @@ +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +export let gen = (() => { + var _ref = _skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + return _functionSent; + }); + + return function gen() { + return _ref.apply(this, arguments); + }; +})(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-anonymous/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-anonymous/actual.js new file mode 100644 index 0000000000..225f6c9511 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-anonymous/actual.js @@ -0,0 +1,3 @@ +(function* () { + return function.sent; +}()); diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-anonymous/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-anonymous/expected.js new file mode 100644 index 0000000000..4a61077dc9 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-anonymous/expected.js @@ -0,0 +1,7 @@ +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +_skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + return _functionSent; +})(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-named/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-named/actual.js new file mode 100644 index 0000000000..a6ccacf314 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-named/actual.js @@ -0,0 +1,3 @@ +const foo = function* gen() { + return function.sent; +}; diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-named/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-named/expected.js new file mode 100644 index 0000000000..4850e3344a --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/expression-named/expected.js @@ -0,0 +1,15 @@ +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +const foo = (() => { + var _ref = _skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + return _functionSent; + }); + + function gen() { + return _ref.apply(this, arguments); + } + + return gen; +})(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/object-method/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/object-method/actual.js new file mode 100644 index 0000000000..c908f3f8f5 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/object-method/actual.js @@ -0,0 +1,5 @@ +const obj = { + *gen() { + return function.sent; + }, +}; diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/object-method/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/object-method/expected.js new file mode 100644 index 0000000000..5a244aab57 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/object-method/expected.js @@ -0,0 +1,12 @@ +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } + +const obj = { + gen() { + return _skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + return _functionSent; + })(); + } + +}; \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/options.json b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/options.json new file mode 100644 index 0000000000..120c931571 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-function-sent"] +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/statement/actual.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/statement/actual.js new file mode 100644 index 0000000000..e785175745 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/statement/actual.js @@ -0,0 +1,3 @@ +function* gen() { + return function.sent; +} diff --git a/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/statement/expected.js b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/statement/expected.js new file mode 100644 index 0000000000..94096fe3e3 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/fixtures/generator-kinds/statement/expected.js @@ -0,0 +1,13 @@ +let gen = (() => { + var _ref = _skipFirstGeneratorNext(function* () { + let _functionSent = yield; + + return _functionSent; + }); + + return function gen() { + return _ref.apply(this, arguments); + }; +})(); + +function _skipFirstGeneratorNext(fn) { return function () { var it = fn.apply(this, arguments); it.next(); return it; }; } \ No newline at end of file diff --git a/packages/babel-plugin-transform-function-sent/test/index.js b/packages/babel-plugin-transform-function-sent/test/index.js new file mode 100644 index 0000000000..09cfbc31f5 --- /dev/null +++ b/packages/babel-plugin-transform-function-sent/test/index.js @@ -0,0 +1,3 @@ +import runner from "babel-helper-plugin-test-runner"; + +runner(__dirname); diff --git a/packages/babel-preset-stage-2/package.json b/packages/babel-preset-stage-2/package.json index 61039e71cf..94c23c88b5 100644 --- a/packages/babel-preset-stage-2/package.json +++ b/packages/babel-preset-stage-2/package.json @@ -9,6 +9,7 @@ "main": "lib/index.js", "dependencies": { "babel-plugin-transform-class-properties": "7.0.0-alpha.15", + "babel-plugin-transform-function-sent": "7.0.0-alpha.15", "babel-preset-stage-3": "7.0.0-alpha.15" } } diff --git a/packages/babel-preset-stage-2/src/index.js b/packages/babel-preset-stage-2/src/index.js index 26e0db391c..f388f9a2f3 100644 --- a/packages/babel-preset-stage-2/src/index.js +++ b/packages/babel-preset-stage-2/src/index.js @@ -1,10 +1,11 @@ import presetStage3 from "babel-preset-stage-3"; import transformClassProperties from "babel-plugin-transform-class-properties"; +import transformFunctionSent from "babel-plugin-transform-function-sent"; export default function() { return { presets: [presetStage3], - plugins: [transformClassProperties], + plugins: [transformClassProperties, transformFunctionSent], }; }