diff --git a/lib/6to5/transformers/block-binding.js b/lib/6to5/transformers/block-binding.js index 31a3e53c66..6edca44a2f 100644 --- a/lib/6to5/transformers/block-binding.js +++ b/lib/6to5/transformers/block-binding.js @@ -3,7 +3,7 @@ var util = require("../util"); var b = require("ast-types").builders; var _ = require("lodash"); -var funcTypes = ["FunctionDeclaration", "FunctionExpression"]; +var blockTypes = ["FunctionDeclaration", "FunctionExpression", "BlockStatement"]; var isLet = function (node) { if (node && node.type === "VariableDeclaration" && node.kind === "let") { @@ -24,52 +24,59 @@ var hasLet = function (nodes) { }; exports.Program = function (node) { - if (hasLet(node.body)) node.body = buildNode(node.body); + if (hasLet(node.body)) node.body = buildNode(node.body).node; }; -exports.BlockStatement = function (node, parent) { +exports.BlockStatement = function (node, parent, opts, generateUid) { if (!hasLet(node.body)) return; // ignore if we're the body of a closure already if (parent.type === "FunctionExpression") return; var body = node.body; - node.body = buildNode(node.body, true); - var container = _.last(node.body); - traverse.replace(container, function (node) { + var built = buildNode(node.body, true); + node.body = built.node; + + traverse.replace(built.body, function (node) { if (node.type === "ContinueStatement") { return b.returnStatement(null); } - }, funcTypes); + }, blockTypes); - if (traverse.hasType(body, "BreakStatement", funcTypes)) { - var id = b.identifier("_break"); + if (traverse.hasType(body, "BreakStatement", blockTypes)) { + var id = b.identifier(generateUid("break")); node.body.unshift(b.variableDeclaration("var", [ b.variableDeclarator(id, b.literal(false)) ])); - traverse.replace(container, function (node) { + traverse.replace(built.body, function (node) { if (node.type === "BreakStatement") { return b.returnStatement(b.assignmentExpression("=", id, b.literal(true))); } - }, funcTypes); + }, blockTypes); node.body.push(b.ifStatement(id, b.breakStatement())); } }; -exports.ForOfStatement = -exports.ForInStatement = function (node) { - if (isLet(node.left)) return buildNode(node); +var buildForStatement = function (key) { + return function (node, parent) { + if (isLet(node[key])) { + if (parent.type === "LabeledStatement") { + throw util.errorWithNode(parent, "Label statements not supported with block binding yet."); + } + + return buildNode(node).node; + } + }; }; -exports.ForStatement = function (node) { - if (isLet(node.init)) return buildNode(node); -}; +exports.ForOfStatement = exports.ForInStatement = buildForStatement("left"); +exports.ForStatement = buildForStatement("init"); -var buildNode = function (node, isBlock) { +var buildNode = function (node) { var nodes = []; // hoist normal variable declarations @@ -91,7 +98,7 @@ var buildNode = function (node, isBlock) { KEY: declar.id }, true); }); - } else if (node.type === "ForInStatement" && !node.left._ignoreBlockBindingHoist) { + } else if (node.type === "ForInStatement" && node.left.type === "VariableDeclaration" && !node.left._ignoreBlockBindingHoist) { var id = node.left.declarations[0].id; node.left = id; nodes.push(util.template("variable-declare", { @@ -120,5 +127,8 @@ var buildNode = function (node, isBlock) { FUNCTION: func }, true)); - return nodes; + return { + node: nodes, + body: block + }; }; diff --git a/lib/6to5/traverse/index.js b/lib/6to5/traverse/index.js index 164983728f..1e62883520 100644 --- a/lib/6to5/traverse/index.js +++ b/lib/6to5/traverse/index.js @@ -2,6 +2,7 @@ var VISITOR_KEYS = require("./visitor-keys"); var _ = require("lodash"); var traverse = module.exports = function (parent, callback, blacklistTypes) { + if (!parent) return; if (_.isArray(parent)) { _.each(parent, function (node) { @@ -18,15 +19,16 @@ var traverse = module.exports = function (parent, callback, blacklistTypes) { if (!nodes) return; var handle = function (obj, key) { - if (!obj[key]) return; + var node = obj[key]; + if (!node) return; + if (blacklistTypes.indexOf(node.type) >= 0) return; // strict references in case the callback modified/replaced the node - if (blacklistTypes.indexOf(obj[key].type) >= 0) return; var result = callback(obj[key], parent, obj, key); if (result === false) return; - traverse(obj[key], callback); + traverse(obj[key], callback, blacklistTypes); }; if (_.isArray(nodes)) { @@ -72,10 +74,10 @@ traverse.hasType = function (tree, type, blacklistTypes) { return has; }; -traverse.replace = function (node, callback) { +traverse.replace = function (node, callback, blacklistTypes) { traverse(node, function (node, parent, obj, key) { var result = callback(node, parent); if (result === false) return false; if (result != null) obj[key] = result; - }); + }, blacklistTypes); }; diff --git a/package.json b/package.json index 8527627f4d..025b017a96 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "6to5", "description": "Turn ES6 code into vanilla ES5 with no runtime required", - "version": "0.0.8", + "version": "0.0.9", "repository": { "type": "git", "url": "https://github.com/sebmck/6to5.git" diff --git a/test/fixtures/block-binding/for-in-break-multiple/actual.js b/test/fixtures/block-binding/for-in-break-multiple/actual.js new file mode 100644 index 0000000000..0c9ac188ee --- /dev/null +++ b/test/fixtures/block-binding/for-in-break-multiple/actual.js @@ -0,0 +1,10 @@ +for (var i in arr) { + let val = arr[i]; + + for (i in arr) { + let val2 = arr[i]; + break; + } + + break; +} diff --git a/test/fixtures/block-binding/for-in-break-multiple/expected.js b/test/fixtures/block-binding/for-in-break-multiple/expected.js new file mode 100644 index 0000000000..6b78a8ad6b --- /dev/null +++ b/test/fixtures/block-binding/for-in-break-multiple/expected.js @@ -0,0 +1,16 @@ +for (var i in arr) { + var _break = false; + (function () { + var val = arr[i]; + for (i in arr) { + var _break2 = false; + (function () { + var val2 = arr[i]; + return _break2 = true; + }()); + if (_break2) break; + } + return _break = true; + }()); + if (_break) break; +} diff --git a/test/fixtures/block-binding/for-in-block-wrap-break/actual.js b/test/fixtures/block-binding/for-in-break/actual.js similarity index 78% rename from test/fixtures/block-binding/for-in-block-wrap-break/actual.js rename to test/fixtures/block-binding/for-in-break/actual.js index 1d4c41d2a6..fb28aa3109 100644 --- a/test/fixtures/block-binding/for-in-block-wrap-break/actual.js +++ b/test/fixtures/block-binding/for-in-break/actual.js @@ -1,4 +1,3 @@ -var arr = [1, 2, 3]; for (let i in arr) { let val = arr[i]; console.log(val * 2); diff --git a/test/fixtures/block-binding/for-in-block-wrap-break/expected.js b/test/fixtures/block-binding/for-in-break/expected.js similarity index 87% rename from test/fixtures/block-binding/for-in-block-wrap-break/expected.js rename to test/fixtures/block-binding/for-in-break/expected.js index 32919ef144..9a0e8bc03b 100644 --- a/test/fixtures/block-binding/for-in-block-wrap-break/expected.js +++ b/test/fixtures/block-binding/for-in-break/expected.js @@ -1,8 +1,4 @@ -var arr = [ - 1, - 2, - 3 -]; + (function () { for (var i in arr) { var _break = false; diff --git a/test/fixtures/block-binding/for-in-block-wrap-continue/actual.js b/test/fixtures/block-binding/for-in-continue/actual.js similarity index 79% rename from test/fixtures/block-binding/for-in-block-wrap-continue/actual.js rename to test/fixtures/block-binding/for-in-continue/actual.js index e4929dc916..3951978153 100644 --- a/test/fixtures/block-binding/for-in-block-wrap-continue/actual.js +++ b/test/fixtures/block-binding/for-in-continue/actual.js @@ -1,4 +1,3 @@ -var arr = [1, 2, 3]; for (let i in arr) { let val = arr[i]; console.log(val * 2); diff --git a/test/fixtures/block-binding/for-in-block-wrap-continue/expected.js b/test/fixtures/block-binding/for-in-continue/expected.js similarity index 83% rename from test/fixtures/block-binding/for-in-block-wrap-continue/expected.js rename to test/fixtures/block-binding/for-in-continue/expected.js index 0281eb9c40..26a4c89000 100644 --- a/test/fixtures/block-binding/for-in-block-wrap-continue/expected.js +++ b/test/fixtures/block-binding/for-in-continue/expected.js @@ -1,8 +1,3 @@ -var arr = [ - 1, - 2, - 3 -]; (function () { for (var i in arr) { (function () { diff --git a/test/fixtures/block-binding/for-in-multiple/actual.js b/test/fixtures/block-binding/for-in-multiple/actual.js new file mode 100644 index 0000000000..9d15042c0c --- /dev/null +++ b/test/fixtures/block-binding/for-in-multiple/actual.js @@ -0,0 +1,9 @@ +for (var i in arr) { + let val = arr[i]; + console.log(val * 2); + + for (i in arr) { + let x = arr[i]; + console.log(x * 2); + } +} diff --git a/test/fixtures/block-binding/for-in-multiple/expected.js b/test/fixtures/block-binding/for-in-multiple/expected.js new file mode 100644 index 0000000000..de1e630fd2 --- /dev/null +++ b/test/fixtures/block-binding/for-in-multiple/expected.js @@ -0,0 +1,12 @@ +for (var i in arr) { + (function () { + var val = arr[i]; + console.log(val * 2); + for (i in arr) { + (function () { + var x = arr[i]; + console.log(x * 2); + }()); + } + }()); +} diff --git a/test/fixtures/block-binding/for-in-block-wrap/actual.js b/test/fixtures/block-binding/for-in/actual.js similarity index 76% rename from test/fixtures/block-binding/for-in-block-wrap/actual.js rename to test/fixtures/block-binding/for-in/actual.js index e746a86613..b03a71fbc8 100644 --- a/test/fixtures/block-binding/for-in-block-wrap/actual.js +++ b/test/fixtures/block-binding/for-in/actual.js @@ -1,4 +1,3 @@ -var arr = [1, 2, 3]; for (let i in arr) { let val = arr[i]; console.log(val * 2); diff --git a/test/fixtures/block-binding/for-in-block-wrap/expected.js b/test/fixtures/block-binding/for-in/expected.js similarity index 81% rename from test/fixtures/block-binding/for-in-block-wrap/expected.js rename to test/fixtures/block-binding/for-in/expected.js index b86f42396d..8460525f6a 100644 --- a/test/fixtures/block-binding/for-in-block-wrap/expected.js +++ b/test/fixtures/block-binding/for-in/expected.js @@ -1,8 +1,3 @@ -var arr = [ - 1, - 2, - 3 -]; (function () { for (var i in arr) { (function () { diff --git a/test/fixtures/block-binding/for-block-wrap/actual.js b/test/fixtures/block-binding/for/actual.js similarity index 100% rename from test/fixtures/block-binding/for-block-wrap/actual.js rename to test/fixtures/block-binding/for/actual.js diff --git a/test/fixtures/block-binding/for-block-wrap/expected.js b/test/fixtures/block-binding/for/expected.js similarity index 100% rename from test/fixtures/block-binding/for-block-wrap/expected.js rename to test/fixtures/block-binding/for/expected.js