From fd932e20e98d2844f42cc62fb1111407a1559850 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 29 Sep 2014 13:36:39 +1000 Subject: [PATCH] hoist var declarations to before function definition when let block scoping --- lib/6to5/templates/assign.js | 1 + lib/6to5/templates/variable-declare.js | 1 + lib/6to5/transformers/block-binding.js | 49 +++++++++++++++++-- lib/6to5/traverse/index.js | 23 ++++++++- .../block-binding/hoist-multiple/actual.js | 2 + .../block-binding/hoist-multiple/expected.js | 7 +++ test/fixtures/block-binding/hoist/actual.js | 2 + test/fixtures/block-binding/hoist/expected.js | 5 ++ test/fixtures/constants/program/expected.js | 3 +- 9 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 lib/6to5/templates/assign.js create mode 100644 lib/6to5/templates/variable-declare.js create mode 100644 test/fixtures/block-binding/hoist-multiple/actual.js create mode 100644 test/fixtures/block-binding/hoist-multiple/expected.js create mode 100644 test/fixtures/block-binding/hoist/actual.js create mode 100644 test/fixtures/block-binding/hoist/expected.js diff --git a/lib/6to5/templates/assign.js b/lib/6to5/templates/assign.js new file mode 100644 index 0000000000..e42061de63 --- /dev/null +++ b/lib/6to5/templates/assign.js @@ -0,0 +1 @@ +KEY = VALUE; diff --git a/lib/6to5/templates/variable-declare.js b/lib/6to5/templates/variable-declare.js new file mode 100644 index 0000000000..28836348d8 --- /dev/null +++ b/lib/6to5/templates/variable-declare.js @@ -0,0 +1 @@ +var KEY; diff --git a/lib/6to5/transformers/block-binding.js b/lib/6to5/transformers/block-binding.js index bd1a8e00a5..a6a4b50758 100644 --- a/lib/6to5/transformers/block-binding.js +++ b/lib/6to5/transformers/block-binding.js @@ -5,6 +5,7 @@ var _ = require("lodash"); var isLet = function (node) { if (node.type === "VariableDeclaration" && node.kind === "let") { node.kind = "var"; + node._ignoreBlockBindingHoist = true; return true; } }; @@ -21,7 +22,7 @@ var hasLet = function (nodes) { exports.Program = function (node) { if (hasLet(node.body)) { - node.body = [buildNode(node.body)]; + node.body = buildNode(node.body); } }; @@ -31,7 +32,7 @@ exports.BlockStatement = function (node, parent) { // ignore if we're the body of a closure already if (parent.type === "FunctionExpression") return; - node.body = [buildNode(node.body)]; + node.body = buildNode(node.body); }; exports.ForInStatement = function (node) { @@ -43,13 +44,47 @@ exports.ForStatement = function (node) { }; var buildNode = function (node) { + var nodes = []; + + // hoist normal variable declarations + + node = [].concat(node); + node = node.map(function (node) { + if (node._ignoreBlockBindingHoist) return node; + + if (node.type === "VariableDeclaration" && node.kind === "var") { + _.each(node.declarations, function (declar) { + nodes.push(util.template("variable-declare", { + KEY: declar.id + })); + }); + + return node.declarations.map(function (declar) { + return util.template("assign", { + KEY: declar.id, + VALUE: declar.init + }, true); + }); + } else if (node.type === "ForInStatement" && !node.left._ignoreBlockBindingHoist) { + var id = node.left.declarations[0].id; + node.left = id; + nodes.push(util.template("variable-declare", { + KEY: id + })); + } + + return node; + }); + + // + var func = { type: "FunctionExpression", params: [], defaults: [], body: { type: "BlockStatement", - body: [].concat(node) + body: node } }; @@ -58,7 +93,11 @@ var buildNode = function (node) { templateName = "function-call-this"; } - return util.template(templateName, { + // + + nodes.push(util.template(templateName, { FUNCTION: func - }, true); + }, true)); + + return nodes; }; diff --git a/lib/6to5/traverse/index.js b/lib/6to5/traverse/index.js index 7478df20d0..4bcaa2b67f 100644 --- a/lib/6to5/traverse/index.js +++ b/lib/6to5/traverse/index.js @@ -30,11 +30,17 @@ var traverse = module.exports = function (parent, callback) { _.each(nodes, function (node, i) { handle(nodes, i); }); - parent[key] = _.flatten(nodes).filter(function (node) { + + parent[key] = _.flatten(parent[key]).filter(function (node) { return node !== traverse.Delete; }); } else { handle(parent, key); + + if (parent[key] === traverse.Delete) { + throw new Error("trying to delete property " + key + " from " + + parent.type + " but can't because it's required"); + } } }); }; @@ -66,3 +72,18 @@ traverse.replace = function (node, callback) { if (result != null) obj[key] = result; }); }; + +traverse.replace.shallow = function (node, callback) { + traverse(node, function (node, parent, obj, key) { + var result = callback(node, parent); + if (result != null) obj[key] = result; + return false; + }); +}; + +traverse.shallow = function (node, callback) { + traverse(node, function () { + callback.apply(this, arguments); + return false; + }); +}; diff --git a/test/fixtures/block-binding/hoist-multiple/actual.js b/test/fixtures/block-binding/hoist-multiple/actual.js new file mode 100644 index 0000000000..7a036ab60b --- /dev/null +++ b/test/fixtures/block-binding/hoist-multiple/actual.js @@ -0,0 +1,2 @@ +let MULTIPLIER = 5; +var foo = "bar", bar = "foo"; diff --git a/test/fixtures/block-binding/hoist-multiple/expected.js b/test/fixtures/block-binding/hoist-multiple/expected.js new file mode 100644 index 0000000000..370bab694d --- /dev/null +++ b/test/fixtures/block-binding/hoist-multiple/expected.js @@ -0,0 +1,7 @@ +var foo; +var bar; +(function () { + var MULTIPLIER = 5; + foo = "bar"; + bar = "foo"; +})(); diff --git a/test/fixtures/block-binding/hoist/actual.js b/test/fixtures/block-binding/hoist/actual.js new file mode 100644 index 0000000000..6fb20a8759 --- /dev/null +++ b/test/fixtures/block-binding/hoist/actual.js @@ -0,0 +1,2 @@ +let MULTIPLIER = 5; +var foo = "bar"; diff --git a/test/fixtures/block-binding/hoist/expected.js b/test/fixtures/block-binding/hoist/expected.js new file mode 100644 index 0000000000..87197e5dc3 --- /dev/null +++ b/test/fixtures/block-binding/hoist/expected.js @@ -0,0 +1,5 @@ +var foo; +(function () { + var MULTIPLIER = 5; + foo = "bar"; +})(); diff --git a/test/fixtures/constants/program/expected.js b/test/fixtures/constants/program/expected.js index ab1e06254b..e3c52ab40f 100644 --- a/test/fixtures/constants/program/expected.js +++ b/test/fixtures/constants/program/expected.js @@ -1,6 +1,7 @@ +var i; (function () { var MULTIPLIER = 5; - for (var i in arr) { + for (i in arr) { console.log(arr[i] * MULTIPLIER); } }());