actually implement continue and break statement support to block binding
This commit is contained in:
@@ -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
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user