clean up babel-plugin-transform-regenerator

This commit is contained in:
Sebastian McKenzie 2015-11-09 04:15:51 -08:00
parent f047a593df
commit 6fa85e6f5b
28 changed files with 2575 additions and 7448 deletions

1
.gitignore vendored
View File

@ -15,6 +15,5 @@ dist
/packages/babel-runtime/helpers/*.js /packages/babel-runtime/helpers/*.js
/packages/babel-runtime/regenerator/*.js /packages/babel-runtime/regenerator/*.js
/packages/*/lib /packages/*/lib
!/packages/babel-plugin-transform-regenerator/lib
_babel.github.io _babel.github.io
/tests/.browser-build.js /tests/.browser-build.js

View File

@ -1,12 +0,0 @@
var vm = require("vm");
var fs = require("fs");
var loc = __dirname + "/../dist/browser.js";
suite("browser", function () {
test("sanity check", function () {
if (fs.existsSync(loc)) {
require(loc);
}
});
});

View File

@ -1,11 +0,0 @@
npm-debug.log
node_modules
test/mocha.js
test/mocha.css
test/tests.es5.js
test/async.es5.js
test/tests.browser.js
.idea

View File

@ -1,805 +0,0 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
var assert = require("assert");
describe("async functions and await expressions", function () {
Promise = require("promise");
describe("regeneratorRuntime", function () {
it("should be defined globally", function () {
var global = Function("return this")();
assert.ok("regeneratorRuntime" in global);
assert.strictEqual(global.regeneratorRuntime, regeneratorRuntime);
});
it("should have a .wrap method", function () {
assert.strictEqual(typeof regeneratorRuntime.wrap, "function");
});
});
describe("Promise", function () {
it("should be defined globally", function () {
var global = Function("return this")();
assert.ok("Promise" in global);
assert.strictEqual(global.Promise, Promise);
});
it("should be a function", function () {
assert.strictEqual(typeof Promise, "function");
});
});
describe("no-await async function", function () {
it("should return a Promise", function (done) {
var called = false;
function noAwait(value) {
return regeneratorRuntime.async(function noAwait$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
called = true;
return _context.abrupt("return", value);
case 2:
case "end":
return _context.stop();
}
}, null, this);
}
var promise = noAwait("asdf");
assert.strictEqual(called, true);
promise.then(function (value) {
assert.strictEqual(called, true);
assert.strictEqual(value, "asdf");
done();
}).catch(done);
});
});
describe("one-await async function", function () {
it("should finish asynchronously", function (done) {
var flag1 = false;
var flag2 = false;
function oneAwait(value) {
var result;
return regeneratorRuntime.async(function oneAwait$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
flag1 = true;
_context2.next = 3;
return regeneratorRuntime.awrap(value);
case 3:
result = _context2.sent;
flag2 = true;
return _context2.abrupt("return", result);
case 6:
case "end":
return _context2.stop();
}
}, null, this);
}
var promise = oneAwait("asdf");
assert.strictEqual(flag1, true);
assert.strictEqual(flag2, false);
promise.then(function (value) {
assert.strictEqual(flag2, true);
assert.strictEqual(value, "asdf");
done();
}).catch(done);
});
});
describe("nested async function calls", function () {
it("should evaluate in the right order", function (done) {
var markers = [];
function innerMost(marker) {
return regeneratorRuntime.async(function innerMost$(_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
markers.push(marker);
_context3.next = 3;
return regeneratorRuntime.awrap(marker);
case 3:
return _context3.abrupt("return", _context3.sent);
case 4:
case "end":
return _context3.stop();
}
}, null, this);
}
function inner(marker) {
return regeneratorRuntime.async(function inner$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
markers.push(marker);
_context4.t0 = assert;
_context4.next = 4;
return regeneratorRuntime.awrap(innerMost(marker + 1));
case 4:
_context4.t1 = _context4.sent;
_context4.t2 = marker + 1;
_context4.t0.strictEqual.call(_context4.t0, _context4.t1, _context4.t2);
markers.push(marker + 2);
_context4.t3 = assert;
_context4.next = 11;
return regeneratorRuntime.awrap(innerMost(marker + 3));
case 11:
_context4.t4 = _context4.sent;
_context4.t5 = marker + 3;
_context4.t3.strictEqual.call(_context4.t3, _context4.t4, _context4.t5);
markers.push(marker + 4);
case 15:
case "end":
return _context4.stop();
}
}, null, this);
}
function outer() {
return regeneratorRuntime.async(function outer$(_context5) {
while (1) switch (_context5.prev = _context5.next) {
case 0:
markers.push(0);
_context5.next = 3;
return regeneratorRuntime.awrap(inner(1));
case 3:
markers.push(6);
_context5.next = 6;
return regeneratorRuntime.awrap(inner(7));
case 6:
markers.push(12);
case 7:
case "end":
return _context5.stop();
}
}, null, this);
}
outer().then(function () {
var expected = [];
for (var i = 0; i <= 12; ++i) expected.push(i);
assert.deepEqual(markers, expected);
done();
}).catch(done);
});
});
describe("dependent promises", function () {
it("should be awaitable out of order", function (done) {
function outer(value) {
var resolved, p1, v2, v1;
return regeneratorRuntime.async(function outer$(_context6) {
while (1) switch (_context6.prev = _context6.next) {
case 0:
resolved = false;
p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve(value + 1);
resolved = true;
}, 0);
});
assert.strictEqual(resolved, false);
_context6.next = 5;
return regeneratorRuntime.awrap(p1.then(function (value) {
return value + 1;
}));
case 5:
v2 = _context6.sent;
assert.strictEqual(resolved, true);
_context6.next = 9;
return regeneratorRuntime.awrap(p1);
case 9:
v1 = _context6.sent;
return _context6.abrupt("return", [v1, v2]);
case 11:
case "end":
return _context6.stop();
}
}, null, this);
}
outer(1).then(function (pair) {
assert.deepEqual(pair, [2, 3]);
done();
}).catch(done);
});
});
describe("rejected promises", function () {
it("should cause await expressions to throw", function (done) {
var error = new Error("rejected");
function f(arg) {
return regeneratorRuntime.async(function f$(_context7) {
while (1) switch (_context7.prev = _context7.next) {
case 0:
_context7.prev = 0;
_context7.next = 3;
return regeneratorRuntime.awrap(arg);
case 3:
return _context7.abrupt("return", _context7.sent);
case 6:
_context7.prev = 6;
_context7.t0 = _context7["catch"](0);
assert.strictEqual(_context7.t0, error);
return _context7.abrupt("return", "did throw");
case 10:
case "end":
return _context7.stop();
}
}, null, this, [[0, 6]]);
}
Promise.all([f(Promise.reject(error)), f(Promise.resolve("did not throw"))]).then(function (results) {
assert.deepEqual(results, ["did throw", "did not throw"]);
done();
}).catch(done);
});
it("should be returned by exceptional async functions", function (done) {
var error = new Error("rejected");
function e(arg) {
return regeneratorRuntime.async(function e$(_context8) {
while (1) switch (_context8.prev = _context8.next) {
case 0:
if (!arg) {
_context8.next = 2;
break;
}
throw arg;
case 2:
return _context8.abrupt("return", "did not throw");
case 3:
case "end":
return _context8.stop();
}
}, null, this);
}
function f(arg) {
return regeneratorRuntime.async(function f$(_context9) {
while (1) switch (_context9.prev = _context9.next) {
case 0:
_context9.next = 2;
return regeneratorRuntime.awrap(e(arg));
case 2:
return _context9.abrupt("return", _context9.sent);
case 3:
case "end":
return _context9.stop();
}
}, null, this);
}
function g(arg) {
return regeneratorRuntime.async(function g$(_context10) {
while (1) switch (_context10.prev = _context10.next) {
case 0:
_context10.next = 2;
return regeneratorRuntime.awrap(f(arg));
case 2:
return _context10.abrupt("return", _context10.sent);
case 3:
case "end":
return _context10.stop();
}
}, null, this);
}
function h(arg) {
return regeneratorRuntime.async(function h$(_context11) {
while (1) switch (_context11.prev = _context11.next) {
case 0:
_context11.next = 2;
return regeneratorRuntime.awrap(Promise.all([g(arg), Promise.resolve("dummy")]));
case 2:
return _context11.abrupt("return", _context11.sent);
case 3:
case "end":
return _context11.stop();
}
}, null, this);
}
Promise.all([h(error).then(function () {
done(new Error("should not have resolved"));
}, function (e) {
assert.strictEqual(e, error);
return "ok1";
}), h(null).then(function (result) {
assert.deepEqual(result, ["did not throw", "dummy"]);
return "ok2";
})]).then(function (results) {
assert.deepEqual(results, ["ok1", "ok2"]);
done();
}).catch(done);
});
it("should propagate failure when returned", function () {
var rejection = new Error("rejection");
function f() {
return regeneratorRuntime.async(function f$(_context12) {
while (1) switch (_context12.prev = _context12.next) {
case 0:
return _context12.abrupt("return", new Promise(function (resolve, reject) {
reject(rejection);
}));
case 1:
case "end":
return _context12.stop();
}
}, null, this);
}
return f().then(function (result) {
assert.ok(false, "should have been rejected");
}, function (error) {
assert.strictEqual(error, rejection);
});
});
});
describe("async function expressions", function () {
it("should be allowed", function (done) {
(function _callee(arg) {
return regeneratorRuntime.async(function _callee$(_context13) {
while (1) switch (_context13.prev = _context13.next) {
case 0:
_context13.next = 2;
return regeneratorRuntime.awrap(arg);
case 2:
return _context13.abrupt("return", _context13.sent);
case 3:
case "end":
return _context13.stop();
}
}, null, this);
})(Promise.resolve(1234)).then(function (value) {
assert.strictEqual(value, 1234);
done();
}).catch(done);
});
});
});
describe("async generator functions", function () {
it("should return a working AsyncIterator", function () {
var _marked = [gen].map(regeneratorRuntime.mark);
var markers = [];
function gen(arg) {
var sent, result;
return regeneratorRuntime.async(function gen$(_context14) {
while (1) switch (_context14.prev = _context14.next) {
case 0:
markers.push(0);
_context14.next = 3;
return arg;
case 3:
sent = _context14.sent;
markers.push(1);
_context14.next = 7;
return regeneratorRuntime.awrap(sent);
case 7:
result = _context14.sent;
markers.push(2);
_context14.t0 = assert;
_context14.t1 = regeneratorRuntime;
_context14.next = 13;
return "second";
case 13:
_context14.t2 = _context14.sent;
_context14.next = 16;
return _context14.t1.awrap.call(_context14.t1, _context14.t2);
case 16:
_context14.t3 = _context14.sent;
_context14.t0.strictEqual.call(_context14.t0, _context14.t3, "sent after second");
markers.push(3);
return _context14.abrupt("return", result);
case 20:
case "end":
return _context14.stop();
}
}, _marked[0], this);
}
var iter = gen("initial argument");
assert.deepEqual(markers, []);
var firstPromise = iter.next();
assert.deepEqual(markers, [0]);
return firstPromise.then(function (firstResult) {
assert.deepEqual(firstResult, {
value: "initial argument",
done: false
});
assert.deepEqual(markers, [0]);
return iter.next(new Promise(function (resolve) {
setTimeout(resolve, 100);
}).then(function () {
assert.deepEqual(markers, [0, 1]);
return "will become final result";
}));
}).then(function (secondResult) {
assert.deepEqual(secondResult, {
value: "second",
done: false
});
assert.deepEqual(markers, [0, 1, 2]);
return iter.next("sent after second");
}).then(function (finalResult) {
assert.deepEqual(markers, [0, 1, 2, 3]);
assert.deepEqual(finalResult, {
value: "will become final result",
done: true
});
});
});
it("should keep results in order", function () {
var _marked2 = [range].map(regeneratorRuntime.mark);
function range(limit) {
var before, after, i;
return regeneratorRuntime.async(function range$(_context15) {
while (1) switch (_context15.prev = _context15.next) {
case 0:
before = [];
after = [];
i = 0;
case 3:
if (!(i < limit)) {
_context15.next = 11;
break;
}
before.push(i);
_context15.next = 7;
return i;
case 7:
after.push(i);
case 8:
++i;
_context15.next = 3;
break;
case 11:
assert.deepEqual(before, after);
return _context15.abrupt("return", before);
case 13:
case "end":
return _context15.stop();
}
}, _marked2[0], this);
}
var limit = 10;
var iter = range(limit);
var promises = [];
var results = [];
for (var i = 0; i < limit; ++i) {
var promise = iter.next();
promises.push(promise);
promise.then(function (result) {
assert.strictEqual(result.done, false);
results.push(result);
});
}
assert.deepEqual(results, []);
return Promise.all(promises).then(function (promiseResults) {
assert.deepEqual(results, promiseResults);
return iter.next();
}).then(function (finalResult) {
assert.deepEqual(results.map(function (result) {
return result.value;
}), finalResult.value);
assert.strictEqual(finalResult.done, true);
});
});
it("should be able to handle many awaits", function () {
var _marked3 = [gen].map(regeneratorRuntime.mark);
var awaitCount = 0;
function countAwait(i) {
return Promise.resolve(i).then(function () {
++awaitCount;
});
}
function gen(limit) {
var i;
return regeneratorRuntime.async(function gen$(_context16) {
while (1) switch (_context16.prev = _context16.next) {
case 0:
_context16.next = 2;
return regeneratorRuntime.awrap(countAwait(0));
case 2:
_context16.next = 4;
return 1;
case 4:
_context16.next = 6;
return regeneratorRuntime.awrap(countAwait(2));
case 6:
_context16.next = 8;
return regeneratorRuntime.awrap(countAwait(3));
case 8:
_context16.next = 10;
return 4;
case 10:
_context16.next = 12;
return regeneratorRuntime.awrap(countAwait(5));
case 12:
_context16.next = 14;
return regeneratorRuntime.awrap(countAwait(6));
case 14:
_context16.next = 16;
return regeneratorRuntime.awrap(countAwait(7));
case 16:
_context16.next = 18;
return 8;
case 18:
i = 0;
case 19:
if (!(i < limit)) {
_context16.next = 25;
break;
}
_context16.next = 22;
return regeneratorRuntime.awrap(countAwait(i));
case 22:
++i;
_context16.next = 19;
break;
case 25:
return _context16.abrupt("return", "done");
case 26:
case "end":
return _context16.stop();
}
}, _marked3[0], this);
}
var iter = gen(100);
return iter.next().then(function (result) {
assert.strictEqual(awaitCount, 1);
assert.deepEqual(result, {
value: 1,
done: false
});
return iter.next();
}).then(function (result) {
assert.strictEqual(awaitCount, 3);
assert.deepEqual(result, {
value: 4,
done: false
});
return iter.next();
}).then(function (result) {
assert.strictEqual(awaitCount, 6);
assert.deepEqual(result, {
value: 8,
done: false
});
return iter.next();
}).then(function (result) {
assert.strictEqual(awaitCount, 6 + 100);
assert.deepEqual(result, {
value: "done",
done: true
});
return iter.next();
}).then(function (result) {
assert.deepEqual(result, {
value: void 0,
done: true
});
});
});
it("should not propagate exceptions between iterations", function () {
var _marked4 = [gen].map(regeneratorRuntime.mark);
function gen() {
return regeneratorRuntime.async(function gen$(_context17) {
while (1) switch (_context17.prev = _context17.next) {
case 0:
_context17.next = 2;
return 1;
case 2:
_context17.next = 4;
return 2;
case 4:
case "end":
return _context17.stop();
}
}, _marked4[0], this);
}
var iter = gen();
return iter.next().then(function (result) {
assert.deepEqual(result, {
value: 1,
done: false
});
return iter.throw(new Error("thrown from first yield"));
}).then(function () {
throw new Error("should have thrown");
}, function (error) {
assert.strictEqual(error.message, "thrown from first yield");
return iter.next();
}).then(function (result) {
assert.deepEqual(result, {
value: void 0,
done: true
});
});
});
it("should allow yielding a rejected Promise", function () {
var _marked5 = [gen].map(regeneratorRuntime.mark);
var yielded = new Error("yielded rejection");
var returned = new Error("returned rejection");
function gen() {
return regeneratorRuntime.async(function gen$(_context18) {
while (1) switch (_context18.prev = _context18.next) {
case 0:
_context18.t0 = assert;
_context18.next = 3;
return Promise.reject(yielded);
case 3:
_context18.t1 = _context18.sent;
_context18.t0.strictEqual.call(_context18.t0, _context18.t1, "first sent");
_context18.t2 = assert;
_context18.next = 8;
return "middle";
case 8:
_context18.t3 = _context18.sent;
_context18.t2.strictEqual.call(_context18.t2, _context18.t3, "second sent");
return _context18.abrupt("return", Promise.reject(returned));
case 11:
case "end":
return _context18.stop();
}
}, _marked5[0], this);
}
var iter = gen();
return iter.next().then(function (result) {
assert.ok(false, "should have yielded a rejected Promise");
}, function (error) {
assert.strictEqual(error, yielded);
return iter.next("first sent");
}).then(function (result) {
assert.deepEqual(result, {
value: "middle",
done: false
});
return iter.next("second sent");
}).then(function (result) {
assert.ok(false, "should have returned a rejected Promise");
}, function (error) {
assert.strictEqual(error, returned);
});
});
});

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Mocha</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="tests.browser.js"></script>
<script>
mocha.run();
</script>
</body>
</html>

View File

@ -1,3 +0,0 @@
function asdf() {
return "no generators or a-s-y-n-c functions to see here";
}

View File

@ -1,154 +0,0 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
var fs = require("fs");
var path = require("path");
var semver = require("semver");
var spawn = require("child_process").spawn;
var regenerator = require("../main");
var mochaDir = path.dirname(require.resolve("mocha"));
function convert(es6File, es5File, callback) {
fs.readFile(es6File, "utf-8", function(err, es6) {
if (err) {
return callback(err);
}
fs.writeFile(es5File, regenerator.compile(es6).code, callback);
});
}
function bundle(es5Files, browserFile, callback) {
var bundle = require("browserify")();
es5Files.forEach(bundle.add, bundle);
bundle.bundle(function(err, src) {
if (err) {
return callback(err);
}
fs.writeFile(browserFile, src, callback);
});
}
var queue = [];
function enqueue(cmd, args, quiet) {
queue.push({
cmd: cmd,
args: args || [],
quiet: !!quiet
});
}
function flush() {
var entry = queue.shift();
if (entry) {
var cmd = entry.cmd;
if (typeof cmd === "function") {
cmd.apply(null, entry.args.concat(asyncCallback));
} else {
spawn(cmd, entry.args, {
stdio: [
process.stdin,
entry.quiet ? "ignore" : process.stdout,
process.stderr
]
}).on("exit", asyncCallback);
}
}
}
function asyncCallback(err) {
if (err) {
console.error("process exited abnormally:", err);
process.exit(typeof err === "number" ? err : -1);
} else {
process.nextTick(flush);
}
}
function makeMochaCopyFunction(fileName) {
return function copy(callback) {
var src = path.join(mochaDir, fileName);
var dst = path.join(__dirname, fileName);
fs.unlink(dst, function() {
fs.symlink(src, dst, callback);
});
};
}
if (semver.gte(process.version, "0.11.2")) {
enqueue("mocha", [
"--harmony",
"--reporter", "spec",
"--require", "./runtime",
"./test/tests.es6.js"
]);
}
enqueue(convert, [
"./test/tests.es6.js",
"./test/tests.es5.js"
]);
enqueue(convert, [
"./test/async.es6.js",
"./test/async.es5.js"
]);
enqueue(makeMochaCopyFunction("mocha.js"));
enqueue(makeMochaCopyFunction("mocha.css"));
// uglify-js does not work properly due to Node 0.11.7 bug.
// (https://github.com/joyent/node/issues/6235)
if (!semver.eq(process.version, "0.11.7")) {
try {
require.resolve("browserify"); // Throws if missing.
enqueue(bundle, [
["./runtime.js",
"./test/tests.es5.js",
"./test/async.es5.js"],
"./test/tests.browser.js"
]);
} catch (ignored) {
console.error("browserify not installed; skipping bundle step");
}
}
enqueue("mocha", [
"--reporter", "spec",
"--require", "./runtime",
"./test/tests.es5.js",
"./test/async.es5.js",
"./test/tests.transform.js"
]);
// Run command-line tool with available options to make sure it works.
enqueue("./bin/regenerator", [
"./test/async.es5.js"
], true);
enqueue("./bin/regenerator", [
"--include-runtime",
"./test/async.es5.js"
], true);
// Make sure we run the command-line tool on a file that does not need any
// transformation, too.
enqueue("./bin/regenerator", [
"./test/nothing-to-transform.js"
], true);
enqueue("./bin/regenerator", [
"--include-runtime",
"./test/nothing-to-transform.js"
], true);
flush();

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
var regenerator = require("..");
var babylon = require("babylon");
var assert = require("assert");
var babel = require("babel-core");
var t = require("babel-types");
describe("_blockHoist nodes", function() {
it("should be hoisted to the outer body", function() {
var foo;
var names = [];
var ast = babylon.parse([
"function *foo(doNotHoistMe, hoistMe) {",
" var sent = yield doNotHoistMe();",
" hoistMe();",
" names.push(sent);",
" return 123;",
"}"
].join("\n"));
var hoistMeStmt = ast.program.body[0].body.body[1];
t.assertExpressionStatement(hoistMeStmt);
t.assertCallExpression(hoistMeStmt.expression);
t.assertIdentifier(hoistMeStmt.expression.callee);
assert.strictEqual(hoistMeStmt.expression.callee.name, "hoistMe");
hoistMeStmt._blockHoist = 1;
eval(babel.transformFromAst(ast, null, { plugins: [regenerator] }).code);
assert.strictEqual(typeof foo, "function");
assert.ok(regeneratorRuntime.isGeneratorFunction(foo));
assert.strictEqual(names.length, 0);
var g = foo(function doNotHoistMe() {
names.push("doNotHoistMe");
return "yielded";
}, function hoistMe() {
names.push("hoistMe");
});
assert.deepEqual(names, ["hoistMe"]);
assert.deepEqual(g.next(), { value: "yielded", done: false });
assert.deepEqual(names, ["hoistMe", "doNotHoistMe"]);
assert.deepEqual(g.next("oyez"), { value: 123, done: true });
assert.deepEqual(names, ["hoistMe", "doNotHoistMe", "oyez"]);
});
});

View File

@ -1,11 +0,0 @@
language: node_js
node_js:
- "4.0"
- "iojs"
- "0.12"
- "0.11"
- "0.10"
- "0.8"
before_install:
- npm install -g npm@1.4.28
sudo: false

View File

@ -1,80 +0,0 @@
# Contributing to Regenerator
Regenerator uses GitHub as its sole source of truth. Everything happens
here. Facebook employees who contribute to Regenerator are expected to do
so in the same way as everyone else. In other words, this document applies
equally to all contributors.
### `master` is unsafe
We will do our best to keep `master` in good shape, with tests passing at
all times. But in order to move fast, we will make API changes that your
application might not be compatible with. We will do our best to
communicate these changes and always version appropriately so you can lock
into a specific version if need be.
### Pull Requests
In case you've never submitted a pull request (PR) via GitHub before,
please read [this short
tutorial](https://help.github.com/articles/creating-a-pull-request). If
you've submitted a PR before, there should be nothing surprising about our
procedures for Regenerator.
*Before* submitting a pull request, please make sure the following is done…
1. Fork the repo and create your branch from `master`.
2. If you've added code that should be tested, add tests!
3. Ensure the test suite passes (`npm test`).
4. If you haven't already, complete the CLA.
5. Submit a pull request via GitHub.
6. Check that Travis CI tests pass (pull request turns green).
### Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You
only need to do this once, so if you've done this for another Facebook
open source project, you're good to go. If you are submitting a pull
request for the first time, just let us know that you have completed the
CLA and we can cross-check with your GitHub username.
Complete your CLA here: <https://code.facebook.com/cla>
## Bugs
### Where to Find Known Issues
We will be using GitHub Issues for all bugs. Before filing a new issue,
please try to make sure your problem doesn't already exist. If you think
your issue is more general than one that already exists, our preference is
still to modify the original issue to reflect the underlying problem more
faithfully.
### Reporting New Issues
The best way to get a bug fixed is to provide a reduced test case, and the
easiest way to reduce a testcase is to edit it in [the
sandbox](http://facebook.github.io/regenerator/) until you're satisfied
and then click the "report a bug" link (the new issue will be populated
automatically with your code).
### Security Bugs
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for
the safe disclosure of security bugs. With that in mind, please do not
file public issues and go through the process outlined on that page.
## Coding Style
* Use semicolons;
* Commas last,
* 2 spaces for indentation (no tabs).
* Prefer `"` over `'`
* 80 character line length
* Match surrounding coding style.
* Less code is better code.
## License
By contributing to Regenerator, you agree that your contributions will be
licensed under the [BSD License](LICENSE).

View File

@ -1,74 +1 @@
regenerator [![Build Status](https://travis-ci.org/facebook/regenerator.png?branch=master)](https://travis-ci.org/facebook/regenerator) # babel-plugin-transform-regenerator
===
This package implements a fully-functional source transformation that
takes the proposed syntax for generators/`yield` from future versions of
JS ([ECMAScript6 or ES6](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts), experimentally implemented in Node.js v0.11) and
spits out efficient JS-of-today (ES5) that behaves the same way.
A small runtime library (less than 1KB compressed) is required to provide the
`wrapGenerator` function. You can install it either as a CommonJS module
or as a standalone .js file, whichever you prefer.
Installation
---
From NPM:
```sh
npm install -g regenerator
```
From GitHub:
```sh
cd path/to/node_modules
git clone git://github.com/facebook/regenerator.git
cd regenerator
npm install .
npm test
```
Usage
---
You have several options for using this module.
Simplest usage:
```sh
regenerator es6.js > es5.js # Just the transform.
regenerator --include-runtime es6.js > es5.js # Add the runtime too.
regenerator src lib # Transform every .js file in src and output to lib.
```
Programmatic usage:
```js
var es5Source = require("regenerator").compile(es6Source).code;
var es5SourceWithRuntime = require("regenerator").compile(es6Source, {
includeRuntime: true
}).code;
```
Babel plugin:
```js
var babel = require("babel-core");
var code = babel.transform(es6Source, {
plugins: [require("generator")]
}).code;
```
How can you get involved?
---
The easiest way to get involved is to look for buggy examples using [the
sandbox](http://facebook.github.io/regenerator/), and when you find
something strange just click the "report a bug" link (the new issue form
will be populated automatically with the problematic code).
Alternatively, you can
[fork](https://github.com/facebook/regenerator/fork) the repository,
create some failing tests cases in [test/tests.es6.js](test/tests.es6.js),
and send pull requests for me to fix.
If you're feeling especially brave, you are more than welcome to dive into
the transformer code and fix the bug(s) yourself, but I must warn you that
the code could really benefit from [better implementation
comments](https://github.com/facebook/regenerator/issues/7).

View File

@ -1,17 +0,0 @@
#!/usr/bin/env node
// -*- mode: js -*-
var compile = require("../main").compile;
require("commoner").version(
require("../package.json").version
).resolve(function(id) {
return this.readModuleP(id);
}).option(
"-r, --include-runtime",
"Prepend the runtime to the output."
).process(function(id, source) {
return compile(source, {
includeRuntime: this.options.includeRuntime
}).code;
});

File diff suppressed because it is too large Load Diff

View File

@ -8,16 +8,23 @@
* the same directory. * the same directory.
*/ */
var traverse = require("babel-traverse"); "use strict";
var assert = require("assert");
var t = require("babel-types"); var _Object$keys = require("babel-runtime/core-js/object/keys")["default"];
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var hasOwn = Object.prototype.hasOwnProperty; var hasOwn = Object.prototype.hasOwnProperty;
// The hoist function takes a FunctionExpression or FunctionDeclaration // The hoist function takes a FunctionExpression or FunctionDeclaration
// and replaces any Declaration nodes in its body with assignments, then // and replaces any Declaration nodes in its body with assignments, then
// returns a VariableDeclaration containing just the names of the removed // returns a VariableDeclaration containing just the names of the removed
// declarations. // declarations.
exports.hoist = function(funPath) { exports.hoist = function (funPath) {
t.assertFunction(funPath.node); t.assertFunction(funPath.node);
var vars = {}; var vars = {};
@ -27,30 +34,26 @@ exports.hoist = function(funPath) {
// TODO assert.equal(vdec.kind, "var"); // TODO assert.equal(vdec.kind, "var");
var exprs = []; var exprs = [];
vdec.declarations.forEach(function(dec) { vdec.declarations.forEach(function (dec) {
vars[dec.id.name] = dec.id; vars[dec.id.name] = dec.id;
if (dec.init) { if (dec.init) {
exprs.push(t.assignmentExpression( exprs.push(t.assignmentExpression("=", dec.id, dec.init));
"=", dec.id, dec.init
));
} else if (includeIdentifiers) { } else if (includeIdentifiers) {
exprs.push(dec.id); exprs.push(dec.id);
} }
}); });
if (exprs.length === 0) if (exprs.length === 0) return null;
return null;
if (exprs.length === 1) if (exprs.length === 1) return exprs[0];
return exprs[0];
return t.sequenceExpression(exprs); return t.sequenceExpression(exprs);
} }
funPath.get("body").traverse({ funPath.get("body").traverse({
VariableDeclaration: { VariableDeclaration: {
exit: function(path) { exit: function exit(path) {
var expr = varDeclToExpr(path.node, false); var expr = varDeclToExpr(path.node, false);
if (expr === null) { if (expr === null) {
path.remove(); path.remove();
@ -66,38 +69,25 @@ exports.hoist = function(funPath) {
} }
}, },
ForStatement: function(path) { ForStatement: function ForStatement(path) {
var init = path.node.init; var init = path.node.init;
if (t.isVariableDeclaration(init)) { if (t.isVariableDeclaration(init)) {
path.get("init").replaceWith(varDeclToExpr(init, false)); path.get("init").replaceWith(varDeclToExpr(init, false));
} }
}, },
ForXStatement: function(path) { ForXStatement: function ForXStatement(path) {
var left = path.get("left"); var left = path.get("left");
if (left.isVariableDeclaration()) { if (left.isVariableDeclaration()) {
left.replaceWith(varDeclToExpr(left.node, true)); left.replaceWith(varDeclToExpr(left.node, true));
} }
}, },
FunctionDeclaration: function(path) { FunctionDeclaration: function FunctionDeclaration(path) {
var node = path.node; var node = path.node;
vars[node.id.name] = node.id; vars[node.id.name] = node.id;
var parentNode = path.parent.node; var assignment = t.expressionStatement(t.assignmentExpression("=", node.id, t.functionExpression(node.id, node.params, node.body, node.generator, node.expression)));
var assignment = t.expressionStatement(
t.assignmentExpression(
"=",
node.id,
t.functionExpression(
node.id,
node.params,
node.body,
node.generator,
node.expression
)
)
);
if (path.parentPath.isBlockStatement()) { if (path.parentPath.isBlockStatement()) {
// Insert the assignment form before the first statement in the // Insert the assignment form before the first statement in the
@ -118,14 +108,14 @@ exports.hoist = function(funPath) {
path.skip(); path.skip();
}, },
FunctionExpression: function(path) { FunctionExpression: function FunctionExpression(path) {
// Don't descend into nested function expressions. // Don't descend into nested function expressions.
path.skip(); path.skip();
} }
}); });
var paramNames = {}; var paramNames = {};
funPath.get("params").forEach(function(paramPath) { funPath.get("params").forEach(function (paramPath) {
var param = paramPath.node; var param = paramPath.node;
if (t.isIdentifier(param)) { if (t.isIdentifier(param)) {
paramNames[param.name] = param; paramNames[param.name] = param;
@ -137,7 +127,7 @@ exports.hoist = function(funPath) {
var declarations = []; var declarations = [];
Object.keys(vars).forEach(function(name) { _Object$keys(vars).forEach(function (name) {
if (!hasOwn.call(paramNames, name)) { if (!hasOwn.call(paramNames, name)) {
declarations.push(t.variableDeclarator(vars[name], null)); declarations.push(t.variableDeclarator(vars[name], null));
} }
@ -148,4 +138,4 @@ exports.hoist = function(funPath) {
} }
return t.variableDeclaration("var", declarations); return t.variableDeclaration("var", declarations);
}; };

View File

@ -8,13 +8,24 @@
* the same directory. * the same directory.
*/ */
var assert = require("assert"); "use strict";
var t = require("babel-types");
var inherits = require("util").inherits; var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
var hasOwn = Object.prototype.hasOwnProperty;
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var _assert = require("assert");
var _assert2 = _interopRequireDefault(_assert);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _util = require("util");
function Entry() { function Entry() {
assert.ok(this instanceof Entry); _assert2["default"].ok(this instanceof Entry);
} }
function FunctionEntry(returnLoc) { function FunctionEntry(returnLoc) {
@ -23,7 +34,7 @@ function FunctionEntry(returnLoc) {
this.returnLoc = returnLoc; this.returnLoc = returnLoc;
} }
inherits(FunctionEntry, Entry); _util.inherits(FunctionEntry, Entry);
exports.FunctionEntry = FunctionEntry; exports.FunctionEntry = FunctionEntry;
function LoopEntry(breakLoc, continueLoc, label) { function LoopEntry(breakLoc, continueLoc, label) {
@ -43,7 +54,7 @@ function LoopEntry(breakLoc, continueLoc, label) {
this.label = label; this.label = label;
} }
inherits(LoopEntry, Entry); _util.inherits(LoopEntry, Entry);
exports.LoopEntry = LoopEntry; exports.LoopEntry = LoopEntry;
function SwitchEntry(breakLoc) { function SwitchEntry(breakLoc) {
@ -52,7 +63,7 @@ function SwitchEntry(breakLoc) {
this.breakLoc = breakLoc; this.breakLoc = breakLoc;
} }
inherits(SwitchEntry, Entry); _util.inherits(SwitchEntry, Entry);
exports.SwitchEntry = SwitchEntry; exports.SwitchEntry = SwitchEntry;
function TryEntry(firstLoc, catchEntry, finallyEntry) { function TryEntry(firstLoc, catchEntry, finallyEntry) {
@ -61,26 +72,26 @@ function TryEntry(firstLoc, catchEntry, finallyEntry) {
t.assertLiteral(firstLoc); t.assertLiteral(firstLoc);
if (catchEntry) { if (catchEntry) {
assert.ok(catchEntry instanceof CatchEntry); _assert2["default"].ok(catchEntry instanceof CatchEntry);
} else { } else {
catchEntry = null; catchEntry = null;
} }
if (finallyEntry) { if (finallyEntry) {
assert.ok(finallyEntry instanceof FinallyEntry); _assert2["default"].ok(finallyEntry instanceof FinallyEntry);
} else { } else {
finallyEntry = null; finallyEntry = null;
} }
// Have to have one or the other (or both). // Have to have one or the other (or both).
assert.ok(catchEntry || finallyEntry); _assert2["default"].ok(catchEntry || finallyEntry);
this.firstLoc = firstLoc; this.firstLoc = firstLoc;
this.catchEntry = catchEntry; this.catchEntry = catchEntry;
this.finallyEntry = finallyEntry; this.finallyEntry = finallyEntry;
} }
inherits(TryEntry, Entry); _util.inherits(TryEntry, Entry);
exports.TryEntry = TryEntry; exports.TryEntry = TryEntry;
function CatchEntry(firstLoc, paramId) { function CatchEntry(firstLoc, paramId) {
@ -93,7 +104,7 @@ function CatchEntry(firstLoc, paramId) {
this.paramId = paramId; this.paramId = paramId;
} }
inherits(CatchEntry, Entry); _util.inherits(CatchEntry, Entry);
exports.CatchEntry = CatchEntry; exports.CatchEntry = CatchEntry;
function FinallyEntry(firstLoc, afterLoc) { function FinallyEntry(firstLoc, afterLoc) {
@ -104,7 +115,7 @@ function FinallyEntry(firstLoc, afterLoc) {
this.afterLoc = afterLoc; this.afterLoc = afterLoc;
} }
inherits(FinallyEntry, Entry); _util.inherits(FinallyEntry, Entry);
exports.FinallyEntry = FinallyEntry; exports.FinallyEntry = FinallyEntry;
function LabeledEntry(breakLoc, label) { function LabeledEntry(breakLoc, label) {
@ -117,14 +128,14 @@ function LabeledEntry(breakLoc, label) {
this.label = label; this.label = label;
} }
inherits(LabeledEntry, Entry); _util.inherits(LabeledEntry, Entry);
exports.LabeledEntry = LabeledEntry; exports.LabeledEntry = LabeledEntry;
function LeapManager(emitter) { function LeapManager(emitter) {
assert.ok(this instanceof LeapManager); _assert2["default"].ok(this instanceof LeapManager);
var Emitter = require("./emit").Emitter; var Emitter = require("./emit").Emitter;
assert.ok(emitter instanceof Emitter); _assert2["default"].ok(emitter instanceof Emitter);
this.emitter = emitter; this.emitter = emitter;
this.entryStack = [new FunctionEntry(emitter.finalLoc)]; this.entryStack = [new FunctionEntry(emitter.finalLoc)];
@ -133,43 +144,42 @@ function LeapManager(emitter) {
var LMp = LeapManager.prototype; var LMp = LeapManager.prototype;
exports.LeapManager = LeapManager; exports.LeapManager = LeapManager;
LMp.withEntry = function(entry, callback) { LMp.withEntry = function (entry, callback) {
assert.ok(entry instanceof Entry); _assert2["default"].ok(entry instanceof Entry);
this.entryStack.push(entry); this.entryStack.push(entry);
try { try {
callback.call(this.emitter); callback.call(this.emitter);
} finally { } finally {
var popped = this.entryStack.pop(); var popped = this.entryStack.pop();
assert.strictEqual(popped, entry); _assert2["default"].strictEqual(popped, entry);
} }
}; };
LMp._findLeapLocation = function(property, label) { LMp._findLeapLocation = function (property, label) {
for (var i = this.entryStack.length - 1; i >= 0; --i) { for (var i = this.entryStack.length - 1; i >= 0; --i) {
var entry = this.entryStack[i]; var entry = this.entryStack[i];
var loc = entry[property]; var loc = entry[property];
if (loc) { if (loc) {
if (label) { if (label) {
if (entry.label && if (entry.label && entry.label.name === label.name) {
entry.label.name === label.name) {
return loc; return loc;
} }
} else if (entry instanceof LabeledEntry) { } else if (entry instanceof LabeledEntry) {
// Ignore LabeledEntry entries unless we are actually breaking to // Ignore LabeledEntry entries unless we are actually breaking to
// a label. // a label.
} else { } else {
return loc; return loc;
} }
} }
} }
return null; return null;
}; };
LMp.getBreakLoc = function(label) { LMp.getBreakLoc = function (label) {
return this._findLeapLocation("breakLoc", label); return this._findLeapLocation("breakLoc", label);
}; };
LMp.getContinueLoc = function(label) { LMp.getContinueLoc = function (label) {
return this._findLeapLocation("continueLoc", label); return this._findLeapLocation("continueLoc", label);
}; };

View File

@ -8,9 +8,22 @@
* the same directory. * the same directory.
*/ */
var assert = require("assert"); "use strict";
var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var _assert = require("assert");
var _assert2 = _interopRequireDefault(_assert);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var m = require("private").makeAccessor(); var m = require("private").makeAccessor();
var t = require("babel-types");
var hasOwn = Object.prototype.hasOwnProperty; var hasOwn = Object.prototype.hasOwnProperty;
function makePredicate(propertyName, knownTypes) { function makePredicate(propertyName, knownTypes) {
@ -24,11 +37,11 @@ function makePredicate(propertyName, knownTypes) {
if (result) { if (result) {
// Do nothing. // Do nothing.
} else if (Array.isArray(child)) { } else if (Array.isArray(child)) {
child.some(check); child.some(check);
} else if (t.isNode(child)) { } else if (t.isNode(child)) {
assert.strictEqual(result, false); _assert2["default"].strictEqual(result, false);
result = predicate(child); result = predicate(child);
} }
return result; return result;
} }
@ -48,16 +61,13 @@ function makePredicate(propertyName, knownTypes) {
t.assertNode(node); t.assertNode(node);
var meta = m(node); var meta = m(node);
if (hasOwn.call(meta, propertyName)) if (hasOwn.call(meta, propertyName)) return meta[propertyName];
return meta[propertyName];
// Certain types are "opaque," which means they have no side // Certain types are "opaque," which means they have no side
// effects or leaps and we don't care about their subexpressions. // effects or leaps and we don't care about their subexpressions.
if (hasOwn.call(opaqueTypes, node.type)) if (hasOwn.call(opaqueTypes, node.type)) return meta[propertyName] = false;
return meta[propertyName] = false;
if (hasOwn.call(knownTypes, node.type)) if (hasOwn.call(knownTypes, node.type)) return meta[propertyName] = true;
return meta[propertyName] = true;
return meta[propertyName] = onlyChildren(node); return meta[propertyName] = onlyChildren(node);
} }
@ -100,4 +110,4 @@ for (var type in leapTypes) {
} }
exports.hasSideEffects = makePredicate("hasSideEffects", sideEffectTypes); exports.hasSideEffects = makePredicate("hasSideEffects", sideEffectTypes);
exports.containsLeap = makePredicate("containsLeap", leapTypes); exports.containsLeap = makePredicate("containsLeap", leapTypes);

View File

@ -8,16 +8,22 @@
* the same directory. * the same directory.
*/ */
var t = require("babel-types"); "use strict";
exports.runtimeProperty = function(name) { var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
return t.memberExpression(
t.identifier("regeneratorRuntime"),
t.identifier(name),
false
);
};
exports.isReference = function(path) { exports.__esModule = true;
exports.runtimeProperty = runtimeProperty;
exports.isReference = isReference;
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
function runtimeProperty(name) {
return t.memberExpression(t.identifier("regeneratorRuntime"), t.identifier(name), false);
}
function isReference(path) {
return path.isReferenced() || path.parentPath.isAssignmentExpression({ left: path.node }); return path.isReferenced() || path.parentPath.isAssignmentExpression({ left: path.node });
}; }

View File

@ -8,20 +8,33 @@
* the same directory. * the same directory.
*/ */
var traverse = require("babel-traverse"); "use strict";
var babylon = require("babylon");
var assert = require("assert"); var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
var fs = require("fs");
var t = require("babel-types"); var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var hoist = require("./hoist").hoist;
var Emitter = require("./emit").Emitter; var _assert = require("assert");
var util = require("./util");
var runtimeProperty = util.runtimeProperty; var _assert2 = _interopRequireDefault(_assert);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _hoist = require("./hoist");
var _emit = require("./emit");
var _util = require("./util");
var util = _interopRequireWildcard(_util);
var getMarkInfo = require("private").makeAccessor(); var getMarkInfo = require("private").makeAccessor();
exports.visitor = { exports.visitor = {
Function: { Function: {
exit: function(path, state) { exit: function exit(path, state) {
var node = path.node; var node = path.node;
if (node.generator) { if (node.generator) {
@ -43,9 +56,7 @@ exports.visitor = {
if (node.expression) { if (node.expression) {
// Transform expression lambdas into normal functions. // Transform expression lambdas into normal functions.
node.expression = false; node.expression = false;
node.body = t.blockStatement([ node.body = t.blockStatement([t.returnStatement(node.body)]);
t.returnStatement(node.body)
]);
} }
if (node.async) { if (node.async) {
@ -56,7 +67,7 @@ exports.visitor = {
var outerBody = []; var outerBody = [];
var innerBody = []; var innerBody = [];
bodyBlockPath.get("body").forEach(function(childPath) { bodyBlockPath.get("body").forEach(function (childPath) {
var node = childPath.node; var node = childPath.node;
if (node && node._blockHoist != null) { if (node && node._blockHoist != null) {
outerBody.push(node); outerBody.push(node);
@ -82,41 +93,33 @@ exports.visitor = {
// Turn all declarations into vars, and replace the original // Turn all declarations into vars, and replace the original
// declarations with equivalent assignment expressions. // declarations with equivalent assignment expressions.
var vars = hoist(path); var vars = _hoist.hoist(path);
var didRenameArguments = renameArguments(path, argsId); var didRenameArguments = renameArguments(path, argsId);
if (didRenameArguments) { if (didRenameArguments) {
vars = vars || t.variableDeclaration("var", []); vars = vars || t.variableDeclaration("var", []);
vars.declarations.push(t.variableDeclarator( vars.declarations.push(t.variableDeclarator(argsId, t.identifier("arguments")));
argsId, t.identifier("arguments")
));
} }
var emitter = new Emitter(contextId); var emitter = new _emit.Emitter(contextId);
emitter.explode(path.get("body")); emitter.explode(path.get("body"));
if (vars && vars.declarations.length > 0) { if (vars && vars.declarations.length > 0) {
outerBody.push(vars); outerBody.push(vars);
} }
var wrapArgs = [ var wrapArgs = [emitter.getContextFunction(innerFnId),
emitter.getContextFunction(innerFnId), // Async functions that are not generators don't care about the
// Async functions that are not generators don't care about the // outer function because they don't need it to be marked and don't
// outer function because they don't need it to be marked and don't // inherit from its .prototype.
// inherit from its .prototype. node.generator ? outerFnExpr : t.nullLiteral(), t.thisExpression()];
node.generator ? outerFnExpr : t.nullLiteral(),
t.thisExpression()
];
var tryLocsList = emitter.getTryLocsList(); var tryLocsList = emitter.getTryLocsList();
if (tryLocsList) { if (tryLocsList) {
wrapArgs.push(tryLocsList); wrapArgs.push(tryLocsList);
} }
var wrapCall = t.callExpression( var wrapCall = t.callExpression(util.runtimeProperty(node.async ? "async" : "wrap"), wrapArgs);
runtimeProperty(node.async ? "async" : "wrap"),
wrapArgs
);
outerBody.push(t.returnStatement(wrapCall)); outerBody.push(t.returnStatement(wrapCall));
node.body = t.blockStatement(outerBody); node.body = t.blockStatement(outerBody);
@ -130,9 +133,8 @@ exports.visitor = {
node.async = false; node.async = false;
} }
if (wasGeneratorFunction && if (wasGeneratorFunction && t.isExpression(node)) {
t.isExpression(node)) { path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node]));
path.replaceWith(t.callExpression(runtimeProperty("mark"), [node]));
} }
} }
} }
@ -147,7 +149,7 @@ function getOuterFnExpr(funPath) {
t.assertFunction(node); t.assertFunction(node);
if (node.generator && // Non-generator functions don't need to be marked. if (node.generator && // Non-generator functions don't need to be marked.
t.isFunctionDeclaration(node)) { t.isFunctionDeclaration(node)) {
var pp = funPath.findParent(function (path) { var pp = funPath.findParent(function (path) {
return path.isProgram() || path.isBlockStatement(); return path.isProgram() || path.isBlockStatement();
}); });
@ -164,40 +166,22 @@ function getOuterFnExpr(funPath) {
var index = funDeclIdArray.elements.length; var index = funDeclIdArray.elements.length;
funDeclIdArray.elements.push(node.id); funDeclIdArray.elements.push(node.id);
return t.memberExpression( return t.memberExpression(markedArray, t.numericLiteral(index), true);
markedArray,
t.numericLiteral(index),
true
);
} }
return node.id || ( return node.id || (node.id = funPath.scope.parent.generateUidIdentifier("callee"));
node.id = funPath.scope.parent.generateUidIdentifier("callee")
);
} }
function getRuntimeMarkDecl(blockPath) { function getRuntimeMarkDecl(blockPath) {
var block = blockPath.node; var block = blockPath.node;
assert.ok(Array.isArray(block.body)); _assert2["default"].ok(Array.isArray(block.body));
var info = getMarkInfo(block); var info = getMarkInfo(block);
if (info.decl) { if (info.decl) {
return info.decl; return info.decl;
} }
info.decl = t.variableDeclaration("var", [ info.decl = t.variableDeclaration("var", [t.variableDeclarator(blockPath.scope.generateUidIdentifier("marked"), t.callExpression(t.memberExpression(t.arrayExpression([]), t.identifier("map"), false), [util.runtimeProperty("mark")]))]);
t.variableDeclarator(
blockPath.scope.generateUidIdentifier("marked"),
t.callExpression(
t.memberExpression(
t.arrayExpression([]),
t.identifier("map"),
false
),
[runtimeProperty("mark")]
)
)
]);
blockPath.unshiftContainer("body", info.decl); blockPath.unshiftContainer("body", info.decl);
@ -220,11 +204,11 @@ function renameArguments(funcPath, argsId) {
} }
var argumentsVisitor = { var argumentsVisitor = {
"FunctionExpression|FunctionDeclaration": function(path) { "FunctionExpression|FunctionDeclaration": function FunctionExpressionFunctionDeclaration(path) {
path.skip(); path.skip();
}, },
Identifier: function(path, state) { Identifier: function Identifier(path, state) {
if (path.node.name === "arguments" && util.isReference(path)) { if (path.node.name === "arguments" && util.isReference(path)) {
path.replaceWith(state.argsId); path.replaceWith(state.argsId);
state.didRenameArguments = true; state.didRenameArguments = true;
@ -233,23 +217,17 @@ var argumentsVisitor = {
}; };
var awaitVisitor = { var awaitVisitor = {
Function: function(path) { Function: function Function(path) {
path.skip(); // Don't descend into nested function scopes. path.skip(); // Don't descend into nested function scopes.
}, },
AwaitExpression: function(path) { AwaitExpression: function AwaitExpression(path) {
// Convert await and await* expressions to yield expressions. // Convert await and await* expressions to yield expressions.
var argument = path.node.argument; var argument = path.node.argument;
// Transforming `await x` to `yield regeneratorRuntime.awrap(x)` // Transforming `await x` to `yield regeneratorRuntime.awrap(x)`
// causes the argument to be wrapped in such a way that the runtime // causes the argument to be wrapped in such a way that the runtime
// can distinguish between awaited and merely yielded values. // can distinguish between awaited and merely yielded values.
path.replaceWith(t.yieldExpression( path.replaceWith(t.yieldExpression(t.callExpression(util.runtimeProperty("awrap"), [argument]), false));
t.callExpression(
runtimeProperty("awrap"),
[argument]
),
false
));
} }
}; };

View File

@ -1,34 +0,0 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
var asyncFunctionSyntax = require("babel-plugin-syntax-async-functions");
var blockScopingPlugin = require("babel-plugin-transform-es2015-block-scoping");
var forOfPlugin = require("babel-plugin-transform-es2015-for-of");
var babel = require("babel-core");
var regenerator = module.exports = function() {
return require("./lib/visit");
};
regenerator.compile = function(code, opts) {
// todo: includeRuntime
return babel.transform(code, buildBabelOptions(opts));
};
regenerator.transform = function (ast, opts) {
return babel.transformFromAst(ast, null, buildBabelOptions(opts));
};
function buildBabelOptions(opts) {
return {
plugins: [regenerator, blockScopingPlugin, asyncFunctionSyntax, forOfPlugin],
sourceType: "script"
};
}

View File

@ -1,32 +1,10 @@
{ {
"author": "Ben Newman <bn@cs.stanford.edu>",
"name": "babel-plugin-transform-regenerator", "name": "babel-plugin-transform-regenerator",
"description": "Source transformer enabling ECMAScript 6 generator functions (yield) in JavaScript-of-today (ES5)", "author": "Ben Newman <bn@cs.stanford.edu>",
"keywords": [ "description": "",
"generator",
"yield",
"coroutine",
"rewriting",
"transformation",
"syntax",
"codegen",
"rewriting",
"refactoring",
"transpiler",
"desugaring",
"ES6"
],
"version": "6.0.18", "version": "6.0.18",
"homepage": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator", "homepage": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator",
"repository": { "main": "lib/index.js",
"type": "git",
"url": "git://github.com/facebook/regenerator.git"
},
"main": "main.js",
"bin": "bin/regenerator",
"scripts": {
"test": "node test/run.js"
},
"dependencies": { "dependencies": {
"commoner": "~0.10.3", "commoner": "~0.10.3",
"babel-plugin-transform-es2015-block-scoping": "^6.0.18", "babel-plugin-transform-es2015-block-scoping": "^6.0.18",
@ -39,13 +17,5 @@
"private": "~0.1.5", "private": "~0.1.5",
"through": "~2.3.8" "through": "~2.3.8"
}, },
"devDependencies": { "license": "BSD"
"mocha": "~2.3.3",
"promise": "~7.0.4",
"semver": "~5.0.3"
},
"license": "BSD",
"engines": {
"node": ">= 0.6"
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,148 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
import * as t from "babel-types";
let hasOwn = Object.prototype.hasOwnProperty;
// The hoist function takes a FunctionExpression or FunctionDeclaration
// and replaces any Declaration nodes in its body with assignments, then
// returns a VariableDeclaration containing just the names of the removed
// declarations.
exports.hoist = function(funPath) {
t.assertFunction(funPath.node);
let vars = {};
function varDeclToExpr(vdec, includeIdentifiers) {
t.assertVariableDeclaration(vdec);
// TODO assert.equal(vdec.kind, "var");
let exprs = [];
vdec.declarations.forEach(function(dec) {
vars[dec.id.name] = dec.id;
if (dec.init) {
exprs.push(t.assignmentExpression(
"=", dec.id, dec.init
));
} else if (includeIdentifiers) {
exprs.push(dec.id);
}
});
if (exprs.length === 0)
return null;
if (exprs.length === 1)
return exprs[0];
return t.sequenceExpression(exprs);
}
funPath.get("body").traverse({
VariableDeclaration: {
exit: function(path) {
let expr = varDeclToExpr(path.node, false);
if (expr === null) {
path.remove();
} else {
// We don't need to traverse this expression any further because
// there can't be any new declarations inside an expression.
path.replaceWith(t.expressionStatement(expr));
}
// Since the original node has been either removed or replaced,
// avoid traversing it any further.
path.skip();
}
},
ForStatement: function(path) {
let init = path.node.init;
if (t.isVariableDeclaration(init)) {
path.get("init").replaceWith(varDeclToExpr(init, false));
}
},
ForXStatement: function(path) {
let left = path.get("left");
if (left.isVariableDeclaration()) {
left.replaceWith(varDeclToExpr(left.node, true));
}
},
FunctionDeclaration: function(path) {
let node = path.node;
vars[node.id.name] = node.id;
let assignment = t.expressionStatement(
t.assignmentExpression(
"=",
node.id,
t.functionExpression(
node.id,
node.params,
node.body,
node.generator,
node.expression
)
)
);
if (path.parentPath.isBlockStatement()) {
// Insert the assignment form before the first statement in the
// enclosing block.
path.parentPath.unshiftContainer("body", assignment);
// Remove the function declaration now that we've inserted the
// equivalent assignment form at the beginning of the block.
path.remove();
} else {
// If the parent node is not a block statement, then we can just
// replace the declaration with the equivalent assignment form
// without worrying about hoisting it.
path.replaceWith(assignment);
}
// Don't hoist variables out of inner functions.
path.skip();
},
FunctionExpression: function(path) {
// Don't descend into nested function expressions.
path.skip();
}
});
let paramNames = {};
funPath.get("params").forEach(function(paramPath) {
let param = paramPath.node;
if (t.isIdentifier(param)) {
paramNames[param.name] = param;
} else {
// Variables declared by destructuring parameter patterns will be
// harmlessly re-declared.
}
});
let declarations = [];
Object.keys(vars).forEach(function(name) {
if (!hasOwn.call(paramNames, name)) {
declarations.push(t.variableDeclarator(vars[name], null));
}
});
if (declarations.length === 0) {
return null; // Be sure to handle this case!
}
return t.variableDeclaration("var", declarations);
};

View File

@ -0,0 +1,13 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
export default function () {
return require("./visit");
}

View File

@ -0,0 +1,174 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
import assert from "assert";
import * as t from "babel-types";
import { inherits } from "util";
function Entry() {
assert.ok(this instanceof Entry);
}
function FunctionEntry(returnLoc) {
Entry.call(this);
t.assertLiteral(returnLoc);
this.returnLoc = returnLoc;
}
inherits(FunctionEntry, Entry);
exports.FunctionEntry = FunctionEntry;
function LoopEntry(breakLoc, continueLoc, label) {
Entry.call(this);
t.assertLiteral(breakLoc);
t.assertLiteral(continueLoc);
if (label) {
t.assertIdentifier(label);
} else {
label = null;
}
this.breakLoc = breakLoc;
this.continueLoc = continueLoc;
this.label = label;
}
inherits(LoopEntry, Entry);
exports.LoopEntry = LoopEntry;
function SwitchEntry(breakLoc) {
Entry.call(this);
t.assertLiteral(breakLoc);
this.breakLoc = breakLoc;
}
inherits(SwitchEntry, Entry);
exports.SwitchEntry = SwitchEntry;
function TryEntry(firstLoc, catchEntry, finallyEntry) {
Entry.call(this);
t.assertLiteral(firstLoc);
if (catchEntry) {
assert.ok(catchEntry instanceof CatchEntry);
} else {
catchEntry = null;
}
if (finallyEntry) {
assert.ok(finallyEntry instanceof FinallyEntry);
} else {
finallyEntry = null;
}
// Have to have one or the other (or both).
assert.ok(catchEntry || finallyEntry);
this.firstLoc = firstLoc;
this.catchEntry = catchEntry;
this.finallyEntry = finallyEntry;
}
inherits(TryEntry, Entry);
exports.TryEntry = TryEntry;
function CatchEntry(firstLoc, paramId) {
Entry.call(this);
t.assertLiteral(firstLoc);
t.assertIdentifier(paramId);
this.firstLoc = firstLoc;
this.paramId = paramId;
}
inherits(CatchEntry, Entry);
exports.CatchEntry = CatchEntry;
function FinallyEntry(firstLoc, afterLoc) {
Entry.call(this);
t.assertLiteral(firstLoc);
t.assertLiteral(afterLoc);
this.firstLoc = firstLoc;
this.afterLoc = afterLoc;
}
inherits(FinallyEntry, Entry);
exports.FinallyEntry = FinallyEntry;
function LabeledEntry(breakLoc, label) {
Entry.call(this);
t.assertLiteral(breakLoc);
t.assertIdentifier(label);
this.breakLoc = breakLoc;
this.label = label;
}
inherits(LabeledEntry, Entry);
exports.LabeledEntry = LabeledEntry;
function LeapManager(emitter) {
assert.ok(this instanceof LeapManager);
let Emitter = require("./emit").Emitter;
assert.ok(emitter instanceof Emitter);
this.emitter = emitter;
this.entryStack = [new FunctionEntry(emitter.finalLoc)];
}
let LMp = LeapManager.prototype;
exports.LeapManager = LeapManager;
LMp.withEntry = function(entry, callback) {
assert.ok(entry instanceof Entry);
this.entryStack.push(entry);
try {
callback.call(this.emitter);
} finally {
let popped = this.entryStack.pop();
assert.strictEqual(popped, entry);
}
};
LMp._findLeapLocation = function(property, label) {
for (let i = this.entryStack.length - 1; i >= 0; --i) {
let entry = this.entryStack[i];
let loc = entry[property];
if (loc) {
if (label) {
if (entry.label &&
entry.label.name === label.name) {
return loc;
}
} else if (entry instanceof LabeledEntry) {
// Ignore LabeledEntry entries unless we are actually breaking to
// a label.
} else {
return loc;
}
}
}
return null;
};
LMp.getBreakLoc = function(label) {
return this._findLeapLocation("breakLoc", label);
};
LMp.getContinueLoc = function(label) {
return this._findLeapLocation("continueLoc", label);
};

View File

@ -0,0 +1,103 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
import assert from "assert";
let m = require("private").makeAccessor();
import * as t from "babel-types";
let hasOwn = Object.prototype.hasOwnProperty;
function makePredicate(propertyName, knownTypes) {
function onlyChildren(node) {
t.assertNode(node);
// Assume no side effects until we find out otherwise.
let result = false;
function check(child) {
if (result) {
// Do nothing.
} else if (Array.isArray(child)) {
child.some(check);
} else if (t.isNode(child)) {
assert.strictEqual(result, false);
result = predicate(child);
}
return result;
}
let keys = t.VISITOR_KEYS[node.type];
if (keys) {
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let child = node[key];
check(child);
}
}
return result;
}
function predicate(node) {
t.assertNode(node);
let meta = m(node);
if (hasOwn.call(meta, propertyName))
return meta[propertyName];
// Certain types are "opaque," which means they have no side
// effects or leaps and we don't care about their subexpressions.
if (hasOwn.call(opaqueTypes, node.type))
return meta[propertyName] = false;
if (hasOwn.call(knownTypes, node.type))
return meta[propertyName] = true;
return meta[propertyName] = onlyChildren(node);
}
predicate.onlyChildren = onlyChildren;
return predicate;
}
let opaqueTypes = {
FunctionExpression: true
};
// These types potentially have side effects regardless of what side
// effects their subexpressions have.
let sideEffectTypes = {
CallExpression: true, // Anything could happen!
ForInStatement: true, // Modifies the key variable.
UnaryExpression: true, // Think delete.
BinaryExpression: true, // Might invoke .toString() or .valueOf().
AssignmentExpression: true, // Side-effecting by definition.
UpdateExpression: true, // Updates are essentially assignments.
NewExpression: true // Similar to CallExpression.
};
// These types are the direct cause of all leaps in control flow.
let leapTypes = {
YieldExpression: true,
BreakStatement: true,
ContinueStatement: true,
ReturnStatement: true,
ThrowStatement: true
};
// All leap types are also side effect types.
for (let type in leapTypes) {
if (hasOwn.call(leapTypes, type)) {
sideEffectTypes[type] = leapTypes[type];
}
}
exports.hasSideEffects = makePredicate("hasSideEffects", sideEffectTypes);
exports.containsLeap = makePredicate("containsLeap", leapTypes);

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
import * as t from "babel-types";
export function runtimeProperty(name) {
return t.memberExpression(
t.identifier("regeneratorRuntime"),
t.identifier(name),
false
);
}
export function isReference(path) {
return path.isReferenced() || path.parentPath.isAssignmentExpression({ left: path.node });
}

View File

@ -0,0 +1,252 @@
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
import assert from "assert";
import * as t from "babel-types";
import { hoist } from "./hoist";
import { Emitter } from "./emit";
import * as util from "./util";
let getMarkInfo = require("private").makeAccessor();
exports.visitor = {
Function: {
exit: function(path, state) {
let node = path.node;
if (node.generator) {
if (node.async) {
// Async generator
if (state.opts.asyncGenerators === false) return;
} else {
// Plain generator
if (state.opts.generators === false) return;
}
} else if (node.async) {
// Async function
if (state.opts.async === false) return;
} else {
// Not a generator or async function.
return;
}
if (node.expression) {
// Transform expression lambdas into normal functions.
node.expression = false;
node.body = t.blockStatement([
t.returnStatement(node.body)
]);
}
if (node.async) {
path.get("body").traverse(awaitVisitor);
}
let bodyBlockPath = path.get("body");
let outerBody = [];
let innerBody = [];
bodyBlockPath.get("body").forEach(function(childPath) {
let node = childPath.node;
if (node && node._blockHoist != null) {
outerBody.push(node);
} else {
innerBody.push(node);
}
});
if (outerBody.length > 0) {
// Only replace the inner body if we actually hoisted any statements
// to the outer body.
bodyBlockPath.node.body = innerBody;
}
let outerFnExpr = getOuterFnExpr(path);
// Note that getOuterFnExpr has the side-effect of ensuring that the
// function has a name (so node.id will always be an Identifier), even
// if a temporary name has to be synthesized.
t.assertIdentifier(node.id);
let innerFnId = t.identifier(node.id.name + "$");
let contextId = path.scope.generateUidIdentifier("context");
let argsId = path.scope.generateUidIdentifier("args");
// Turn all declarations into vars, and replace the original
// declarations with equivalent assignment expressions.
let vars = hoist(path);
let didRenameArguments = renameArguments(path, argsId);
if (didRenameArguments) {
vars = vars || t.variableDeclaration("var", []);
vars.declarations.push(t.variableDeclarator(
argsId, t.identifier("arguments")
));
}
let emitter = new Emitter(contextId);
emitter.explode(path.get("body"));
if (vars && vars.declarations.length > 0) {
outerBody.push(vars);
}
let wrapArgs = [
emitter.getContextFunction(innerFnId),
// Async functions that are not generators don't care about the
// outer function because they don't need it to be marked and don't
// inherit from its .prototype.
node.generator ? outerFnExpr : t.nullLiteral(),
t.thisExpression()
];
let tryLocsList = emitter.getTryLocsList();
if (tryLocsList) {
wrapArgs.push(tryLocsList);
}
let wrapCall = t.callExpression(
util.runtimeProperty(node.async ? "async" : "wrap"),
wrapArgs
);
outerBody.push(t.returnStatement(wrapCall));
node.body = t.blockStatement(outerBody);
let wasGeneratorFunction = node.generator;
if (wasGeneratorFunction) {
node.generator = false;
}
if (node.async) {
node.async = false;
}
if (wasGeneratorFunction &&
t.isExpression(node)) {
path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node]));
}
}
}
};
// Given a NodePath for a Function, return an Expression node that can be
// used to refer reliably to the function object from inside the function.
// This expression is essentially a replacement for arguments.callee, with
// the key advantage that it works in strict mode.
function getOuterFnExpr(funPath) {
let node = funPath.node;
t.assertFunction(node);
if (node.generator && // Non-generator functions don't need to be marked.
t.isFunctionDeclaration(node)) {
let pp = funPath.findParent(function (path) {
return path.isProgram() || path.isBlockStatement();
});
if (!pp) {
return node.id;
}
let markDecl = getRuntimeMarkDecl(pp);
let markedArray = markDecl.declarations[0].id;
let funDeclIdArray = markDecl.declarations[0].init.callee.object;
t.assertArrayExpression(funDeclIdArray);
let index = funDeclIdArray.elements.length;
funDeclIdArray.elements.push(node.id);
return t.memberExpression(
markedArray,
t.numericLiteral(index),
true
);
}
return node.id || (
node.id = funPath.scope.parent.generateUidIdentifier("callee")
);
}
function getRuntimeMarkDecl(blockPath) {
let block = blockPath.node;
assert.ok(Array.isArray(block.body));
let info = getMarkInfo(block);
if (info.decl) {
return info.decl;
}
info.decl = t.variableDeclaration("var", [
t.variableDeclarator(
blockPath.scope.generateUidIdentifier("marked"),
t.callExpression(
t.memberExpression(
t.arrayExpression([]),
t.identifier("map"),
false
),
[util.runtimeProperty("mark")]
)
)
]);
blockPath.unshiftContainer("body", info.decl);
return info.decl;
}
function renameArguments(funcPath, argsId) {
let state = {
didRenameArguments: false,
argsId: argsId
};
funcPath.traverse(argumentsVisitor, state);
// If the traversal replaced any arguments references, then we need to
// alias the outer function's arguments binding (be it the implicit
// arguments object or some other parameter or variable) to the variable
// named by argsId.
return state.didRenameArguments;
}
let argumentsVisitor = {
"FunctionExpression|FunctionDeclaration": function(path) {
path.skip();
},
Identifier: function(path, state) {
if (path.node.name === "arguments" && util.isReference(path)) {
path.replaceWith(state.argsId);
state.didRenameArguments = true;
}
}
};
let awaitVisitor = {
Function: function(path) {
path.skip(); // Don't descend into nested function scopes.
},
AwaitExpression: function(path) {
// Convert await and await* expressions to yield expressions.
let argument = path.node.argument;
// Transforming `await x` to `yield regeneratorRuntime.awrap(x)`
// causes the argument to be wrapped in such a way that the runtime
// can distinguish between awaited and merely yielded values.
path.replaceWith(t.yieldExpression(
t.callExpression(
util.runtimeProperty("awrap"),
[argument]
),
false
));
}
};