mirror of
https://github.com/frappe/frappe_docker.git
synced 2025-02-10 07:58:52 +00:00
1657 lines
52 KiB
JavaScript
1657 lines
52 KiB
JavaScript
"use strict";
|
|
|
|
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
|
|
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
var PatternMatch = require("./pattern-match");
|
|
|
|
module.exports = function (_ref) {
|
|
var t = _ref.types;
|
|
|
|
var flipExpressions = require("babel-helper-flip-expressions")(t);
|
|
var toMultipleSequenceExpressions = require("babel-helper-to-multiple-sequence-expressions")(t);
|
|
|
|
var VOID_0 = t.unaryExpression("void", t.numericLiteral(0), true);
|
|
var condExprSeen = Symbol("condExprSeen");
|
|
var seqExprSeen = Symbol("seqExprSeen");
|
|
var shouldRevisit = Symbol("shouldRevisit");
|
|
|
|
// Types as symbols for comparisions
|
|
var types = {};
|
|
t.TYPES.forEach(function (type) {
|
|
types[type] = Symbol.for(type);
|
|
});
|
|
var isNodeOfType = function isNodeOfType(node, typeSymbol) {
|
|
if (typeof typeSymbol !== "symbol") return false;
|
|
return t["is" + Symbol.keyFor(typeSymbol)](node);
|
|
};
|
|
|
|
// small abstractions
|
|
var not = function not(node) {
|
|
return t.unaryExpression("!", node);
|
|
};
|
|
var notnot = function notnot(node) {
|
|
return not(not(node));
|
|
};
|
|
var or = function or(a, b) {
|
|
return t.logicalExpression("||", a, b);
|
|
};
|
|
var and = function and(a, b) {
|
|
return t.logicalExpression("&&", a, b);
|
|
};
|
|
|
|
var operators = new Set(["+", "-", "*", "%", "<<", ">>", ">>>", "&", "|", "^", "/", "**"]);
|
|
|
|
var updateOperators = new Set(["+", "-"]);
|
|
|
|
function areArraysEqual(arr1, arr2) {
|
|
return arr1.every(function (value, index) {
|
|
return String(value) === String(arr2[index]);
|
|
});
|
|
}
|
|
|
|
function getName(node) {
|
|
if (node.type === "ThisExpression") {
|
|
return "this";
|
|
}
|
|
if (node.type === "Super") {
|
|
return "super";
|
|
}
|
|
if (node.type === "NullLiteral") {
|
|
return "null";
|
|
}
|
|
// augment identifiers so that they don't match
|
|
// string/number literals
|
|
// but still match against each other
|
|
return node.name ? node.name + "_" : node.value /* Literal */;
|
|
}
|
|
|
|
function getPropNames(path) {
|
|
if (!path.isMemberExpression()) {
|
|
return;
|
|
}
|
|
|
|
var obj = path.get("object");
|
|
|
|
var prop = path.get("property");
|
|
var propNames = [getName(prop.node)];
|
|
|
|
while (obj.type === "MemberExpression") {
|
|
var node = obj.get("property").node;
|
|
if (node) {
|
|
propNames.push(getName(node));
|
|
}
|
|
obj = obj.get("object");
|
|
}
|
|
propNames.push(getName(obj.node));
|
|
|
|
return propNames;
|
|
}
|
|
var OP_AND = function OP_AND(input) {
|
|
return input === "&&";
|
|
};
|
|
var OP_OR = function OP_OR(input) {
|
|
return input === "||";
|
|
};
|
|
|
|
return {
|
|
name: "minify-simplify",
|
|
visitor: {
|
|
Statement: {
|
|
exit(path) {
|
|
if (path.node[shouldRevisit]) {
|
|
delete path.node[shouldRevisit];
|
|
path.visit();
|
|
}
|
|
}
|
|
},
|
|
|
|
// CallExpression(path) {
|
|
// const { node } = path;
|
|
|
|
/* (function() {})() -> !function() {}()
|
|
There is a bug in babel in printing this. Disabling for now.
|
|
if (t.isFunctionExpression(node.callee) &&
|
|
(t.isExpressionStatement(parent) ||
|
|
(t.isSequenceExpression(parent) && parent.expressions[0] === node))
|
|
) {
|
|
path.replaceWith(
|
|
t.callExpression(
|
|
t.unaryExpression("!", node.callee, true),
|
|
node.arguments
|
|
)
|
|
);
|
|
return;
|
|
}*/
|
|
// },
|
|
|
|
UnaryExpression: {
|
|
enter: [
|
|
|
|
// Demorgans.
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.operator !== "!" || flipExpressions.hasSeen(node)) {
|
|
return;
|
|
}
|
|
|
|
var expr = node.argument;
|
|
|
|
// We need to make sure that the return type will always be boolean.
|
|
if (!(t.isLogicalExpression(expr) || t.isConditionalExpression(expr) || t.isBinaryExpression(expr))) {
|
|
return;
|
|
}
|
|
if (t.isBinaryExpression(expr) && t.COMPARISON_BINARY_OPERATORS.indexOf(expr.operator) === -1) {
|
|
return;
|
|
}
|
|
|
|
if (flipExpressions.shouldFlip(expr, 1)) {
|
|
var newNode = flipExpressions.flip(expr);
|
|
path.replaceWith(newNode);
|
|
}
|
|
},
|
|
|
|
// !(a, b, c) -> a, b, !c
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.operator !== "!") {
|
|
return;
|
|
}
|
|
|
|
if (!t.isSequenceExpression(node.argument)) {
|
|
return;
|
|
}
|
|
|
|
var seq = node.argument.expressions;
|
|
var expr = seq[seq.length - 1];
|
|
seq[seq.length - 1] = t.unaryExpression("!", expr, true);
|
|
path.replaceWith(node.argument);
|
|
},
|
|
|
|
// !(a ? b : c) -> a ? !b : !c
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.operator !== "!") {
|
|
return;
|
|
}
|
|
|
|
if (!t.isConditional(node.argument)) {
|
|
return;
|
|
}
|
|
|
|
var cond = node.argument;
|
|
cond.alternate = t.unaryExpression("!", cond.alternate, true);
|
|
cond.consequent = t.unaryExpression("!", cond.consequent, true);
|
|
path.replaceWith(node.argument);
|
|
}]
|
|
},
|
|
|
|
LogicalExpression: {
|
|
exit(path) {
|
|
// cache of path.evaluate()
|
|
var evaluateMemo = new Map();
|
|
|
|
var TRUTHY = function TRUTHY(input) {
|
|
// !NaN and !undefined are truthy
|
|
// separate check here as they are considered impure by babel
|
|
if (input.isUnaryExpression() && input.get("argument").isIdentifier()) {
|
|
if (input.node.argument.name === "NaN" || input.node.argument.name === "undefined") {
|
|
return true;
|
|
}
|
|
}
|
|
var evalResult = input.evaluate();
|
|
evaluateMemo.set(input, evalResult);
|
|
return evalResult.confident && input.isPure() && evalResult.value;
|
|
};
|
|
|
|
var FALSY = function FALSY(input) {
|
|
// NaN and undefined are falsy
|
|
// separate check here as they are considered impure by babel
|
|
if (input.isIdentifier()) {
|
|
if (input.node.name === "NaN" || input.node.name === "undefined") {
|
|
return true;
|
|
}
|
|
}
|
|
var evalResult = input.evaluate();
|
|
evaluateMemo.set(input, evalResult);
|
|
return evalResult.confident && input.isPure() && !evalResult.value;
|
|
};
|
|
|
|
var EX = types.Expression;
|
|
|
|
// Convention:
|
|
// [left, operator, right, handler(leftNode, rightNode)]
|
|
|
|
var matcher = new PatternMatch([[TRUTHY, OP_AND, EX, function (l, r) {
|
|
return r;
|
|
}], [FALSY, OP_AND, EX, function (l) {
|
|
return l;
|
|
}], [TRUTHY, OP_OR, EX, function (l) {
|
|
return l;
|
|
}], [FALSY, OP_OR, EX, function (l, r) {
|
|
return r;
|
|
}]]);
|
|
|
|
var left = path.get("left");
|
|
var right = path.get("right");
|
|
var operator = path.node.operator;
|
|
|
|
var result = matcher.match([left, operator, right], isPatternMatchesPath);
|
|
|
|
if (result.match) {
|
|
// here we are sure that left.evaluate is always confident becuase
|
|
// it satisfied one of TRUTHY/FALSY paths
|
|
var value = void 0;
|
|
if (evaluateMemo.has(left)) {
|
|
value = evaluateMemo.get(left).value;
|
|
} else {
|
|
value = left.evaluate().value;
|
|
}
|
|
path.replaceWith(result.value(t.valueToNode(value), right.node));
|
|
}
|
|
}
|
|
},
|
|
|
|
AssignmentExpression(path) {
|
|
|
|
var rightExpr = path.get("right");
|
|
var leftExpr = path.get("left");
|
|
|
|
if (path.node.operator !== "=") {
|
|
return;
|
|
}
|
|
|
|
var canBeUpdateExpression = rightExpr.get("right").isNumericLiteral() && rightExpr.get("right").node.value === 1 && updateOperators.has(rightExpr.node.operator);
|
|
|
|
if (leftExpr.isMemberExpression()) {
|
|
|
|
var leftPropNames = getPropNames(leftExpr);
|
|
var rightPropNames = getPropNames(rightExpr.get("left"));
|
|
|
|
if (!leftPropNames || leftPropNames.indexOf(undefined) > -1 || !rightPropNames || rightPropNames.indexOf(undefined) > -1 || !operators.has(rightExpr.node.operator) || !areArraysEqual(leftPropNames, rightPropNames)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!rightExpr.isBinaryExpression() || !operators.has(rightExpr.node.operator) || leftExpr.node.name !== rightExpr.node.left.name) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
var newExpression = void 0;
|
|
|
|
// special case x=x+1 --> ++x
|
|
if (canBeUpdateExpression) {
|
|
newExpression = t.updateExpression(rightExpr.node.operator + rightExpr.node.operator, t.clone(leftExpr.node), true /* prefix */);
|
|
} else {
|
|
newExpression = t.assignmentExpression(rightExpr.node.operator + "=", t.clone(leftExpr.node), t.clone(rightExpr.node.right));
|
|
}
|
|
|
|
path.replaceWith(newExpression);
|
|
},
|
|
|
|
ConditionalExpression: {
|
|
enter: [
|
|
// !foo ? 'foo' : 'bar' -> foo ? 'bar' : 'foo'
|
|
// foo !== 'lol' ? 'foo' : 'bar' -> foo === 'lol' ? 'bar' : 'foo'
|
|
function flipIfOrConditional(path) {
|
|
var node = path.node;
|
|
|
|
if (!path.get("test").isLogicalExpression()) {
|
|
flipNegation(node);
|
|
return;
|
|
}
|
|
|
|
if (flipExpressions.shouldFlip(node.test)) {
|
|
node.test = flipExpressions.flip(node.test);
|
|
var _ref2 = [node.consequent, node.alternate];
|
|
node.alternate = _ref2[0];
|
|
node.consequent = _ref2[1];
|
|
}
|
|
}, function simplifyPatterns(path) {
|
|
var test = path.get("test");
|
|
var consequent = path.get("consequent");
|
|
var alternate = path.get("alternate");
|
|
|
|
var EX = types.Expression,
|
|
LE = types.LogicalExpression;
|
|
|
|
// Convention:
|
|
// ===============
|
|
// for each pattern [test, consequent, alternate, handler(expr, cons, alt)]
|
|
|
|
var matcher = new PatternMatch([[LE, true, false, function (e) {
|
|
return e;
|
|
}], [EX, true, false, function (e) {
|
|
return notnot(e);
|
|
}], [EX, false, true, function (e) {
|
|
return not(e);
|
|
}], [LE, true, EX, function (e, c, a) {
|
|
return or(e, a);
|
|
}], [EX, true, EX, function (e, c, a) {
|
|
return or(notnot(e), a);
|
|
}], [EX, false, EX, function (e, c, a) {
|
|
return and(not(e), a);
|
|
}], [EX, EX, true, function (e, c) {
|
|
return or(not(e), c);
|
|
}], [LE, EX, false, function (e, c) {
|
|
return and(e, c);
|
|
}], [EX, EX, false, function (e, c) {
|
|
return and(notnot(e), c);
|
|
}]]);
|
|
|
|
var result = matcher.match([test, consequent, alternate], isPatternMatchesPath);
|
|
|
|
if (result.match) {
|
|
path.replaceWith(result.value(test.node, consequent.node, alternate.node));
|
|
}
|
|
}],
|
|
|
|
exit: [
|
|
// a ? x = foo : b ? x = bar : x = baz;
|
|
// x = a ? foo : b ? bar : baz;
|
|
function (topPath) {
|
|
if (!topPath.parentPath.isExpressionStatement() && !topPath.parentPath.isSequenceExpression()) {
|
|
return;
|
|
}
|
|
|
|
var mutations = [];
|
|
var firstLeft = null;
|
|
var operator = null;
|
|
function visit(path) {
|
|
if (path.isConditionalExpression()) {
|
|
var _bail = visit(path.get("consequent"));
|
|
if (_bail) {
|
|
return true;
|
|
}
|
|
_bail = visit(path.get("alternate"));
|
|
return _bail;
|
|
}
|
|
|
|
if (operator == null) {
|
|
operator = path.node.operator;
|
|
} else if (path.node.operator !== operator) {
|
|
return true;
|
|
}
|
|
|
|
if (!path.isAssignmentExpression() || !(path.get("left").isIdentifier() || path.get("left").isMemberExpression())) {
|
|
return true;
|
|
}
|
|
|
|
var left = path.get("left").node;
|
|
if (firstLeft == null) {
|
|
firstLeft = left;
|
|
} else if (!t.isNodesEquivalent(left, firstLeft)) {
|
|
return true;
|
|
}
|
|
|
|
mutations.push(function () {
|
|
return path.replaceWith(path.get("right").node);
|
|
});
|
|
}
|
|
|
|
var bail = visit(topPath);
|
|
if (bail) {
|
|
return;
|
|
}
|
|
|
|
mutations.forEach(function (f) {
|
|
return f();
|
|
});
|
|
topPath.replaceWith(t.assignmentExpression(operator, firstLeft, topPath.node));
|
|
},
|
|
|
|
// bar ? void 0 : void 0
|
|
// (bar, void 0)
|
|
// TODO: turn this into general equiv check
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
if (isVoid0(node.consequent) && isVoid0(node.alternate)) {
|
|
path.replaceWith(t.sequenceExpression([path.node.test, VOID_0]));
|
|
}
|
|
},
|
|
|
|
// bar ? void 0 : foo ? void 0 : <etc>
|
|
// bar || foo : void 0
|
|
// TODO: turn this into general equiv check
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node[condExprSeen] || !isVoid0(node.consequent)) {
|
|
return;
|
|
}
|
|
|
|
node[condExprSeen] = true;
|
|
|
|
var tests = [node.test];
|
|
var mutations = [];
|
|
var alt = void 0;
|
|
|
|
var _loop = function _loop(next) {
|
|
next.node[condExprSeen] = true;
|
|
alt = next.node.alternate;
|
|
|
|
if (isVoid0(next.node.consequent)) {
|
|
tests.push(next.node.test);
|
|
mutations.push(function () {
|
|
return next.remove();
|
|
});
|
|
} else {
|
|
alt = next.node;
|
|
return "break";
|
|
}
|
|
};
|
|
|
|
for (var next = path.get("alternate"); next.isConditionalExpression(); next = next.get("alternate")) {
|
|
var _ret = _loop(next);
|
|
|
|
if (_ret === "break") break;
|
|
}
|
|
|
|
if (tests.length === 1) {
|
|
return;
|
|
}
|
|
|
|
var test = tests.reduce(function (expr, curTest) {
|
|
return t.logicalExpression("||", expr, curTest);
|
|
});
|
|
|
|
path.replaceWith(t.conditionalExpression(test, VOID_0, alt));
|
|
}]
|
|
},
|
|
|
|
// concat
|
|
VariableDeclaration: {
|
|
enter: [
|
|
// Put vars with no init at the top.
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.declarations.length < 2) {
|
|
return;
|
|
}
|
|
|
|
var inits = [];
|
|
var empty = [];
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = node.declarations[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var decl = _step.value;
|
|
|
|
if (!decl.init) {
|
|
empty.push(decl);
|
|
} else {
|
|
inits.push(decl);
|
|
}
|
|
}
|
|
|
|
// This is based on exprimintation but there is a significant
|
|
// imrpovement when we place empty vars at the top in smaller
|
|
// files. Whereas it hurts in larger files.
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.fitsInSlidingWindow) {
|
|
node.declarations = empty.concat(inits);
|
|
} else {
|
|
node.declarations = inits.concat(empty);
|
|
}
|
|
}]
|
|
},
|
|
|
|
Function: {
|
|
exit(path) {
|
|
earlyReturnTransform(path);
|
|
|
|
if (!path.node[shouldRevisit]) {
|
|
return;
|
|
}
|
|
|
|
delete path.node[shouldRevisit];
|
|
path.visit();
|
|
}
|
|
},
|
|
|
|
For: {
|
|
enter: earlyContinueTransform,
|
|
exit: earlyContinueTransform
|
|
},
|
|
|
|
ForStatement: {
|
|
|
|
// Merge previous expressions in the init part of the for.
|
|
enter(path) {
|
|
var node = path.node;
|
|
|
|
if (!path.inList || node.init && !t.isExpression(node.init)) {
|
|
return;
|
|
}
|
|
|
|
var prev = path.getSibling(path.key - 1);
|
|
var consumed = false;
|
|
if (prev.isVariableDeclaration()) {
|
|
var referencedOutsideLoop = false;
|
|
|
|
// we don't care if vars are referenced outside the loop as they are fn scope
|
|
if (prev.node.kind === "let" || prev.node.kind === "const") {
|
|
var ids = Object.keys(prev.getBindingIdentifiers());
|
|
|
|
idloop: for (var i = 0; i < ids.length; i++) {
|
|
var binding = prev.scope.bindings[ids[i]];
|
|
// TODO
|
|
// Temporary Fix
|
|
// if there is no binding, we assume it is referenced outside
|
|
// and deopt to avoid bugs
|
|
if (!binding) {
|
|
referencedOutsideLoop = true;
|
|
break idloop;
|
|
}
|
|
var refs = binding.referencePaths;
|
|
for (var j = 0; j < refs.length; j++) {
|
|
if (!isAncestor(path, refs[j])) {
|
|
referencedOutsideLoop = true;
|
|
break idloop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!node.init && !referencedOutsideLoop) {
|
|
node.init = prev.node;
|
|
consumed = true;
|
|
}
|
|
} else if (prev.isExpressionStatement()) {
|
|
var expr = prev.node.expression;
|
|
if (node.init) {
|
|
if (t.isSequenceExpression(expr)) {
|
|
expr.expressions.push(node.init);
|
|
node.init = expr;
|
|
} else {
|
|
node.init = t.sequenceExpression([expr, node.init]);
|
|
}
|
|
} else {
|
|
node.init = expr;
|
|
}
|
|
consumed = true;
|
|
}
|
|
if (consumed) {
|
|
prev.remove();
|
|
}
|
|
},
|
|
|
|
exit(path) {
|
|
var node = path.node;
|
|
|
|
if (!node.test) {
|
|
return;
|
|
}
|
|
|
|
if (!path.get("body").isBlockStatement()) {
|
|
var bodyNode = path.get("body").node;
|
|
if (!t.isIfStatement(bodyNode)) {
|
|
return;
|
|
}
|
|
|
|
if (t.isBreakStatement(bodyNode.consequent, { label: null })) {
|
|
node.test = t.logicalExpression("&&", node.test, t.unaryExpression("!", bodyNode.test, true));
|
|
node.body = bodyNode.alternate || t.emptyStatement();
|
|
return;
|
|
}
|
|
|
|
if (t.isBreakStatement(bodyNode.alternate, { label: null })) {
|
|
node.test = t.logicalExpression("&&", node.test, bodyNode.test);
|
|
node.body = bodyNode.consequent || t.emptyStatement();
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
var statements = node.body.body;
|
|
var exprs = [];
|
|
var ifStatement = null;
|
|
var breakAt = null;
|
|
var i = 0;
|
|
for (var statement; statement = statements[i]; i++) {
|
|
if (t.isIfStatement(statement)) {
|
|
if (t.isBreakStatement(statement.consequent, { label: null })) {
|
|
ifStatement = statement;
|
|
breakAt = "consequent";
|
|
} else if (t.isBreakStatement(statement.alternate, { label: null })) {
|
|
ifStatement = statement;
|
|
breakAt = "alternate";
|
|
}
|
|
break;
|
|
}
|
|
|
|
// A statement appears before the break statement then bail.
|
|
if (!t.isExpressionStatement(statement)) {
|
|
return;
|
|
}
|
|
|
|
exprs.push(statement.expression);
|
|
}
|
|
|
|
if (!ifStatement) {
|
|
return;
|
|
}
|
|
|
|
var rest = [];
|
|
|
|
if (breakAt = "consequent") {
|
|
if (t.isBlockStatement(ifStatement.alternate)) {
|
|
rest.push.apply(rest, _toConsumableArray(ifStatement.alternate.body));
|
|
} else if (ifStatement.alternate) {
|
|
rest.push(ifStatement.alternate);
|
|
}
|
|
} else {
|
|
if (t.isBlockStatement(ifStatement.consequent)) {
|
|
rest.push.apply(rest, _toConsumableArray(ifStatement.consequent.body));
|
|
} else if (ifStatement.consequent) {
|
|
rest.push(ifStatement.consequent);
|
|
}
|
|
}
|
|
|
|
rest.push.apply(rest, _toConsumableArray(statements.slice(i + 1)));
|
|
|
|
var test = breakAt === "consequent" ? t.unaryExpression("!", ifStatement.test, true) : ifStatement.test;
|
|
var expr = void 0;
|
|
if (exprs.length === 1) {
|
|
expr = t.sequenceExpression([exprs[0], test]);
|
|
} else if (exprs.length) {
|
|
exprs.push(test);
|
|
expr = t.sequenceExpression(exprs);
|
|
} else {
|
|
expr = test;
|
|
}
|
|
|
|
node.test = t.logicalExpression("&&", node.test, expr);
|
|
if (rest.length === 1) {
|
|
node.body = rest[0];
|
|
} else if (rest.length) {
|
|
node.body = t.blockStatement(rest);
|
|
} else {
|
|
node.body = t.emptyStatement();
|
|
}
|
|
}
|
|
},
|
|
|
|
Program(path) {
|
|
// An approximation of the resultant gzipped code after minification
|
|
this.fitsInSlidingWindow = path.getSource().length / 10 < 33000;
|
|
|
|
var node = path.node;
|
|
|
|
var statements = toMultipleSequenceExpressions(node.body);
|
|
if (!statements.length) {
|
|
return;
|
|
}
|
|
node.body = statements;
|
|
|
|
// this additional traversal is horrible but it's done to fix
|
|
// https://github.com/babel/babili/issues/323
|
|
// in which type annotation somehow gets messed up
|
|
// during sequence expression transformation
|
|
path.traverse({
|
|
Identifier: function Identifier(path) {
|
|
path.getTypeAnnotation();
|
|
}
|
|
});
|
|
},
|
|
|
|
BlockStatement: {
|
|
enter(path) {
|
|
var node = path.node,
|
|
parent = path.parent;
|
|
|
|
|
|
var top = [];
|
|
var bottom = [];
|
|
|
|
for (var i = 0; i < node.body.length; i++) {
|
|
var bodyNode = node.body[i];
|
|
if (t.isFunctionDeclaration(bodyNode)) {
|
|
top.push(bodyNode);
|
|
} else {
|
|
bottom.push(bodyNode);
|
|
}
|
|
}
|
|
|
|
var statements = top.concat(toMultipleSequenceExpressions(bottom));
|
|
|
|
if (!statements.length) {
|
|
return;
|
|
}
|
|
|
|
if (statements.length > 1 || needsBlock(node, parent) || node.directives) {
|
|
node.body = statements;
|
|
return;
|
|
}
|
|
|
|
if (statements.length) {
|
|
path.replaceWith(statements[0]);
|
|
return;
|
|
}
|
|
},
|
|
|
|
exit(path) {
|
|
var node = path.node,
|
|
parent = path.parent;
|
|
|
|
|
|
if (needsBlock(node, parent)) {
|
|
return;
|
|
}
|
|
|
|
if (node.body.length === 1) {
|
|
path.get("body")[0].inList = false;
|
|
path.replaceWith(node.body[0]);
|
|
return;
|
|
}
|
|
|
|
if (node.body.length === 0) {
|
|
path.replaceWith(t.emptyStatement());
|
|
return;
|
|
}
|
|
|
|
// Check if oppurtinties to merge statements are available.
|
|
var statements = node.body;
|
|
if (!statements.length) {
|
|
return;
|
|
}
|
|
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = statements[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var statement = _step2.value;
|
|
|
|
if (!t.isExpressionStatement(statement)) {
|
|
return;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
|
|
path.visit();
|
|
}
|
|
},
|
|
|
|
ThrowStatement: createPrevExpressionEater("throw"),
|
|
|
|
// Try to merge previous statements into a sequence
|
|
ReturnStatement: {
|
|
enter: [createPrevExpressionEater("return"),
|
|
|
|
// Remove return if last statement with no argument.
|
|
// Replace return with `void` argument with argument.
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!path.parentPath.parentPath.isFunction() || path.getSibling(path.key + 1).node) {
|
|
return;
|
|
}
|
|
|
|
if (!node.argument) {
|
|
path.remove();
|
|
return;
|
|
}
|
|
|
|
if (t.isUnaryExpression(node.argument, { operator: "void" })) {
|
|
path.replaceWith(node.argument.argument);
|
|
}
|
|
}]
|
|
},
|
|
|
|
// turn blocked ifs into single statements
|
|
IfStatement: {
|
|
exit: [
|
|
// Merge nested if statements if possible
|
|
function (_ref3) {
|
|
var node = _ref3.node;
|
|
|
|
if (!t.isIfStatement(node.consequent)) {
|
|
return;
|
|
}
|
|
|
|
if (node.alternate || node.consequent.alternate) {
|
|
return;
|
|
}
|
|
|
|
node.test = t.logicalExpression("&&", node.test, node.consequent.test);
|
|
node.consequent = node.consequent.consequent;
|
|
}, function (path) {
|
|
var node = path.node;
|
|
|
|
// No alternate, make into a guarded expression
|
|
|
|
if (node.consequent && !node.alternate && node.consequent.type === "ExpressionStatement") {
|
|
var op = "&&";
|
|
if (t.isUnaryExpression(node.test, { operator: "!" })) {
|
|
node.test = node.test.argument;
|
|
op = "||";
|
|
}
|
|
|
|
path.replaceWith(t.expressionStatement(t.logicalExpression(op, node.test, node.consequent.expression)));
|
|
return;
|
|
}
|
|
|
|
// Easy, both are expressions, turn into ternary
|
|
if (t.isExpressionStatement(node.consequent) && t.isExpressionStatement(node.alternate)) {
|
|
path.replaceWith(t.conditionalExpression(node.test, node.consequent.expression, node.alternate.expression));
|
|
return;
|
|
}
|
|
|
|
// Easy: consequent and alternate are return -- conditional.
|
|
if (t.isReturnStatement(node.consequent) && t.isReturnStatement(node.alternate)) {
|
|
if (!node.consequent.argument && !node.alternate.argument) {
|
|
path.replaceWith(t.expressionStatement(node.test));
|
|
return;
|
|
}
|
|
|
|
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument || VOID_0, node.alternate.argument || VOID_0)));
|
|
return;
|
|
}
|
|
|
|
// There is nothing after this block. And one or both
|
|
// of the consequent and alternate are either expression statment
|
|
// or return statements.
|
|
if (!path.getSibling(path.key + 1).node && path.parentPath && path.parentPath.parentPath && path.parentPath.parentPath.isFunction()) {
|
|
// Only the consequent is a return, void the alternate.
|
|
if (t.isReturnStatement(node.consequent) && t.isExpressionStatement(node.alternate)) {
|
|
if (!node.consequent.argument) {
|
|
path.replaceWith(t.expressionStatement(t.logicalExpression("||", node.test, node.alternate.expression)));
|
|
return;
|
|
}
|
|
|
|
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument || VOID_0, t.unaryExpression("void", node.alternate.expression, true))));
|
|
return;
|
|
}
|
|
|
|
// Only the alternate is a return, void the consequent.
|
|
if (t.isReturnStatement(node.alternate) && t.isExpressionStatement(node.consequent)) {
|
|
if (!node.alternate.argument) {
|
|
path.replaceWith(t.expressionStatement(t.logicalExpression("&&", node.test, node.consequent.expression)));
|
|
return;
|
|
}
|
|
|
|
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, t.unaryExpression("void", node.consequent.expression, true), node.alternate.argument || VOID_0)));
|
|
return;
|
|
}
|
|
|
|
if (t.isReturnStatement(node.consequent) && !node.alternate) {
|
|
if (!node.consequent.argument) {
|
|
path.replaceWith(t.expressionStatement(node.test));
|
|
return;
|
|
}
|
|
|
|
// This would only be worth it if the previous statement was an if
|
|
// because then we may merge to create a conditional.
|
|
if (path.getSibling(path.key - 1).isIfStatement()) {
|
|
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument || VOID_0, VOID_0)));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (t.isReturnStatement(node.alternate) && !node.consequent) {
|
|
if (!node.alternate.argument) {
|
|
path.replaceWith(t.expressionStatement(node.test));
|
|
return;
|
|
}
|
|
|
|
// Same as above.
|
|
if (path.getSibling(path.key - 1).isIfStatement()) {
|
|
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.alternate.argument || VOID_0, VOID_0)));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
var next = path.getSibling(path.key + 1);
|
|
|
|
// If the next satatement(s) is an if statement and we can simplify that
|
|
// to potentailly be an expression (or a return) then this will make it
|
|
// easier merge.
|
|
if (next.isIfStatement()) {
|
|
next.pushContext(path.context);
|
|
next.visit();
|
|
next.popContext();
|
|
next = path.getSibling(path.key + 1);
|
|
}
|
|
|
|
// Some other visitor might have deleted our node. OUR NODE ;_;
|
|
if (!path.node) {
|
|
return;
|
|
}
|
|
|
|
// No alternate but the next statement is a return
|
|
// also turn into a return conditional
|
|
if (t.isReturnStatement(node.consequent) && !node.alternate && next.isReturnStatement()) {
|
|
var nextArg = next.node.argument || VOID_0;
|
|
next.remove();
|
|
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument || VOID_0, nextArg)));
|
|
return;
|
|
}
|
|
|
|
// Next is the last expression, turn into a return while void'ing the exprs
|
|
if (path.parentPath && path.parentPath.parentPath && path.parentPath.parentPath.isFunction() && !path.getSibling(path.key + 2).node && t.isReturnStatement(node.consequent) && !node.alternate && next.isExpressionStatement()) {
|
|
var nextExpr = next.node.expression;
|
|
next.remove();
|
|
if (node.consequent.argument) {
|
|
path.replaceWith(t.returnStatement(t.conditionalExpression(node.test, node.consequent.argument, t.unaryExpression("void", nextExpr, true))));
|
|
return;
|
|
}
|
|
|
|
path.replaceWith(t.logicalExpression("||", node.test, nextExpr));
|
|
return;
|
|
}
|
|
|
|
if (node.consequent && node.alternate && (t.isReturnStatement(node.consequent) || t.isBlockStatement(node.consequent) && t.isReturnStatement(node.consequent.body[node.consequent.body.length - 1]))) {
|
|
path.insertAfter(t.isBlockStatement(node.alternate) ? node.alternate.body : node.alternate);
|
|
node.alternate = null;
|
|
return;
|
|
}
|
|
},
|
|
|
|
// If the consequent is if and the altenrate is not then
|
|
// switch them out. That way we know we don't have to print
|
|
// a block.x
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!node.alternate) {
|
|
return;
|
|
}
|
|
|
|
if (!t.isIfStatement(node.consequent)) {
|
|
return;
|
|
}
|
|
|
|
if (t.isIfStatement(node.alternate)) {
|
|
return;
|
|
}
|
|
|
|
node.test = t.unaryExpression("!", node.test, true);
|
|
var _ref4 = [node.consequent, node.alternate];
|
|
node.alternate = _ref4[0];
|
|
node.consequent = _ref4[1];
|
|
},
|
|
|
|
// Make if statements with conditional returns in the body into
|
|
// an if statement that guards the rest of the block.
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!path.inList || !path.get("consequent").isBlockStatement() || node.alternate) {
|
|
return;
|
|
}
|
|
|
|
var ret = void 0;
|
|
var test = void 0;
|
|
var exprs = [];
|
|
var statements = node.consequent.body;
|
|
|
|
for (var i = 0, statement; statement = statements[i]; i++) {
|
|
if (t.isExpressionStatement(statement)) {
|
|
exprs.push(statement.expression);
|
|
} else if (t.isIfStatement(statement)) {
|
|
if (i < statements.length - 1) {
|
|
// This isn't the last statement. Bail.
|
|
return;
|
|
}
|
|
if (statement.alternate) {
|
|
return;
|
|
}
|
|
if (!t.isReturnStatement(statement.consequent)) {
|
|
return;
|
|
}
|
|
ret = statement.consequent;
|
|
test = statement.test;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!test || !ret) {
|
|
return;
|
|
}
|
|
|
|
exprs.push(test);
|
|
|
|
var expr = exprs.length === 1 ? exprs[0] : t.sequenceExpression(exprs);
|
|
|
|
var replacement = t.logicalExpression("&&", node.test, expr);
|
|
|
|
path.replaceWith(t.ifStatement(replacement, ret, null));
|
|
}, createPrevExpressionEater("if")]
|
|
},
|
|
|
|
WhileStatement(path) {
|
|
var node = path.node;
|
|
|
|
path.replaceWith(t.forStatement(null, node.test, null, node.body));
|
|
},
|
|
|
|
ForInStatement: createPrevExpressionEater("for-in"),
|
|
|
|
// Flatten sequence expressions.
|
|
SequenceExpression: {
|
|
exit(path) {
|
|
if (path.node[seqExprSeen]) {
|
|
return;
|
|
}
|
|
|
|
function flatten(node) {
|
|
node[seqExprSeen] = true;
|
|
var ret = [];
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = node.expressions[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var n = _step3.value;
|
|
|
|
if (t.isSequenceExpression(n)) {
|
|
ret.push.apply(ret, _toConsumableArray(flatten(n)));
|
|
} else {
|
|
ret.push(n);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
path.node.expressions = flatten(path.node);
|
|
}
|
|
},
|
|
|
|
SwitchCase(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!node.consequent.length) {
|
|
return;
|
|
}
|
|
|
|
node.consequent = toMultipleSequenceExpressions(node.consequent);
|
|
},
|
|
|
|
SwitchStatement: {
|
|
exit: [
|
|
|
|
// Convert switch statements with all returns in their cases
|
|
// to return conditional.
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
// Need to be careful of side-effects.
|
|
|
|
if (!t.isIdentifier(node.discriminant)) {
|
|
return;
|
|
}
|
|
|
|
if (!node.cases.length) {
|
|
return;
|
|
}
|
|
|
|
var consTestPairs = [];
|
|
var fallThru = [];
|
|
var defaultRet = void 0;
|
|
var _iteratorNormalCompletion4 = true;
|
|
var _didIteratorError4 = false;
|
|
var _iteratorError4 = undefined;
|
|
|
|
try {
|
|
for (var _iterator4 = node.cases[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
|
var switchCase = _step4.value;
|
|
|
|
if (switchCase.consequent.length > 1) {
|
|
return;
|
|
}
|
|
|
|
var cons = switchCase.consequent[0];
|
|
|
|
if (!switchCase.test) {
|
|
if (!t.isReturnStatement(cons)) {
|
|
return;
|
|
}
|
|
defaultRet = cons;
|
|
continue;
|
|
}
|
|
|
|
if (!switchCase.consequent.length) {
|
|
if (fallThru.length) {
|
|
fallThru.push(switchCase.test);
|
|
} else {
|
|
fallThru = [switchCase.test];
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// TODO: can we void it?
|
|
if (!t.isReturnStatement(cons)) {
|
|
return;
|
|
}
|
|
|
|
var test = t.binaryExpression("===", node.discriminant, switchCase.test);
|
|
if (fallThru.length) {
|
|
test = fallThru.reduceRight(function (right, test) {
|
|
return t.logicalExpression("||", t.binaryExpression("===", node.discriminant, test), right);
|
|
}, test);
|
|
fallThru = [];
|
|
}
|
|
|
|
consTestPairs.push([test, cons.argument || VOID_0]);
|
|
}
|
|
|
|
// Bail if we have any remaining fallthrough
|
|
} catch (err) {
|
|
_didIteratorError4 = true;
|
|
_iteratorError4 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
|
_iterator4.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError4) {
|
|
throw _iteratorError4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fallThru.length) {
|
|
return;
|
|
}
|
|
|
|
// We need the default to be there to make sure there is an oppurtinity
|
|
// not to return.
|
|
if (!defaultRet) {
|
|
if (path.inList) {
|
|
var nextPath = path.getSibling(path.key + 1);
|
|
if (nextPath.isReturnStatement()) {
|
|
defaultRet = nextPath.node;
|
|
nextPath.remove();
|
|
} else if (!nextPath.node && path.parentPath.parentPath.isFunction()) {
|
|
// If this is the last statement in a function we just fake a void return.
|
|
defaultRet = t.returnStatement(VOID_0);
|
|
} else {
|
|
return;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
var cond = consTestPairs.reduceRight(function (alt, _ref5) {
|
|
var _ref6 = _slicedToArray(_ref5, 2),
|
|
test = _ref6[0],
|
|
cons = _ref6[1];
|
|
|
|
return t.conditionalExpression(test, cons, alt);
|
|
}, defaultRet.argument || VOID_0);
|
|
|
|
path.replaceWith(t.returnStatement(cond));
|
|
|
|
// Maybe now we can merge with some previous switch statement.
|
|
if (path.inList) {
|
|
var prev = path.getSibling(path.key - 1);
|
|
if (prev.isSwitchStatement()) {
|
|
prev.visit();
|
|
}
|
|
}
|
|
},
|
|
|
|
// Convert switches into conditionals.
|
|
function (path) {
|
|
var node = path.node;
|
|
|
|
// Need to be careful of side-effects.
|
|
|
|
if (!t.isIdentifier(node.discriminant)) {
|
|
return;
|
|
}
|
|
|
|
if (!node.cases.length) {
|
|
return;
|
|
}
|
|
|
|
var exprTestPairs = [];
|
|
var fallThru = [];
|
|
var defaultExpr = void 0;
|
|
var _iteratorNormalCompletion5 = true;
|
|
var _didIteratorError5 = false;
|
|
var _iteratorError5 = undefined;
|
|
|
|
try {
|
|
for (var _iterator5 = node.cases[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
|
var switchCase = _step5.value;
|
|
|
|
if (!switchCase.test) {
|
|
if (switchCase.consequent.length !== 1) {
|
|
return;
|
|
}
|
|
if (!t.isExpressionStatement(switchCase.consequent[0])) {
|
|
return;
|
|
}
|
|
defaultExpr = switchCase.consequent[0].expression;
|
|
continue;
|
|
}
|
|
|
|
if (!switchCase.consequent.length) {
|
|
if (fallThru.length) {
|
|
fallThru.push(switchCase.test);
|
|
} else {
|
|
fallThru = [switchCase.test];
|
|
}
|
|
continue;
|
|
}
|
|
|
|
var _switchCase$consequen = _slicedToArray(switchCase.consequent, 2),
|
|
cons = _switchCase$consequen[0],
|
|
breakStatement = _switchCase$consequen[1];
|
|
|
|
if (switchCase === node.cases[node.cases.length - 1]) {
|
|
if (breakStatement && !t.isBreakStatement(breakStatement)) {
|
|
return;
|
|
}
|
|
} else if (!t.isBreakStatement(breakStatement)) {
|
|
return;
|
|
}
|
|
|
|
if (!t.isExpressionStatement(cons) || switchCase.consequent.length > 2) {
|
|
return;
|
|
}
|
|
|
|
var test = t.binaryExpression("===", node.discriminant, switchCase.test);
|
|
if (fallThru.length) {
|
|
test = fallThru.reduceRight(function (right, test) {
|
|
return t.logicalExpression("||", t.binaryExpression("===", node.discriminant, test), right);
|
|
}, test);
|
|
fallThru = [];
|
|
}
|
|
|
|
exprTestPairs.push([test, cons.expression]);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError5 = true;
|
|
_iteratorError5 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
|
_iterator5.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError5) {
|
|
throw _iteratorError5;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fallThru.length) {
|
|
return;
|
|
}
|
|
|
|
var cond = exprTestPairs.reduceRight(function (alt, _ref7) {
|
|
var _ref8 = _slicedToArray(_ref7, 2),
|
|
test = _ref8[0],
|
|
cons = _ref8[1];
|
|
|
|
return t.conditionalExpression(test, cons, alt);
|
|
}, defaultExpr || VOID_0);
|
|
|
|
path.replaceWith(cond);
|
|
}, function (path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!node.cases.length) {
|
|
return;
|
|
}
|
|
|
|
var lastCase = path.get("cases")[node.cases.length - 1];
|
|
if (!lastCase.node.consequent.length) {
|
|
return;
|
|
}
|
|
|
|
var potentialBreak = lastCase.get("consequent")[lastCase.node.consequent.length - 1];
|
|
if (t.isBreakStatement(potentialBreak) && potentialBreak.node.label === null) {
|
|
potentialBreak.remove();
|
|
}
|
|
}, createPrevExpressionEater("switch")]
|
|
}
|
|
}
|
|
};
|
|
|
|
function flipNegation(node) {
|
|
if (!node.consequent || !node.alternate) {
|
|
return;
|
|
}
|
|
|
|
var test = node.test;
|
|
var flip = false;
|
|
|
|
if (t.isBinaryExpression(test)) {
|
|
if (test.operator === "!==") {
|
|
test.operator = "===";
|
|
flip = true;
|
|
}
|
|
|
|
if (test.operator === "!=") {
|
|
test.operator = "==";
|
|
flip = true;
|
|
}
|
|
}
|
|
|
|
if (t.isUnaryExpression(test, { operator: "!" })) {
|
|
node.test = test.argument;
|
|
flip = true;
|
|
}
|
|
|
|
if (flip) {
|
|
var consequent = node.consequent;
|
|
node.consequent = node.alternate;
|
|
node.alternate = consequent;
|
|
}
|
|
}
|
|
|
|
function needsBlock(node, parent) {
|
|
return t.isFunction(parent) && node === parent.body || t.isTryStatement(parent) || t.isCatchClause(parent) || t.isSwitchStatement(parent) || isSingleBlockScopeDeclaration(node) && t.isIfStatement(parent);
|
|
}
|
|
|
|
function isSingleBlockScopeDeclaration(block) {
|
|
return t.isBlockStatement(block) && block.body.length === 1 && (t.isVariableDeclaration(block.body[0], { kind: "let" }) || t.isVariableDeclaration(block.body[0], { kind: "const" }) || t.isFunctionDeclaration(block.body[0]));
|
|
}
|
|
|
|
function isVoid0(expr) {
|
|
return expr === VOID_0 || t.isUnaryExpression(expr, { operator: "void" }) && t.isNumericLiteral(expr.argument, { value: 0 });
|
|
}
|
|
|
|
function earlyReturnTransform(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!t.isBlockStatement(node.body)) {
|
|
return;
|
|
}
|
|
|
|
for (var i = node.body.body.length; i >= 0; i--) {
|
|
var statement = node.body.body[i];
|
|
if (t.isIfStatement(statement) && !statement.alternate && t.isReturnStatement(statement.consequent) && !statement.consequent.argument) {
|
|
genericEarlyExitTransform(path.get("body").get("body")[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function earlyContinueTransform(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!t.isBlockStatement(node.body)) {
|
|
return;
|
|
}
|
|
|
|
for (var i = node.body.body.length; i >= 0; i--) {
|
|
var statement = node.body.body[i];
|
|
if (t.isIfStatement(statement) && !statement.alternate && t.isContinueStatement(statement.consequent) && !statement.consequent.label) {
|
|
genericEarlyExitTransform(path.get("body").get("body")[i]);
|
|
}
|
|
}
|
|
|
|
// We may have reduced the body to a single statement.
|
|
if (node.body.body.length === 1) {
|
|
path.get("body").replaceWith(node.body.body[0]);
|
|
}
|
|
}
|
|
|
|
function genericEarlyExitTransform(path) {
|
|
var node = path.node;
|
|
|
|
|
|
var statements = path.parentPath.get(path.listKey).slice(path.key + 1).filter(function (stmt) {
|
|
return !stmt.isFunctionDeclaration();
|
|
});
|
|
|
|
// deopt for any block scoped bindings
|
|
// issue#399
|
|
var deopt = !statements.every(function (stmt) {
|
|
if (!(stmt.isVariableDeclaration({ kind: "let" }) || stmt.isVariableDeclaration({ kind: "const" }))) {
|
|
return true;
|
|
}
|
|
var ids = Object.keys(stmt.getBindingIdentifiers());
|
|
var _iteratorNormalCompletion6 = true;
|
|
var _didIteratorError6 = false;
|
|
var _iteratorError6 = undefined;
|
|
|
|
try {
|
|
for (var _iterator6 = ids[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
|
|
var id = _step6.value;
|
|
|
|
var binding = path.scope.getBinding(id);
|
|
// TODO
|
|
// Temporary Fix
|
|
// if there is no binding, we assume it is referenced outside
|
|
// and deopt to avoid bugs
|
|
if (!binding) {
|
|
return false;
|
|
}
|
|
var refs = [].concat(_toConsumableArray(binding.referencePaths), _toConsumableArray(binding.constantViolations));
|
|
var _iteratorNormalCompletion7 = true;
|
|
var _didIteratorError7 = false;
|
|
var _iteratorError7 = undefined;
|
|
|
|
try {
|
|
for (var _iterator7 = refs[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
|
|
var ref = _step7.value;
|
|
|
|
if (!ref.isIdentifier()) return false;
|
|
if (ref.getFunctionParent().scope !== path.scope) return false;
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError7 = true;
|
|
_iteratorError7 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion7 && _iterator7.return) {
|
|
_iterator7.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError7) {
|
|
throw _iteratorError7;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError6 = true;
|
|
_iteratorError6 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion6 && _iterator6.return) {
|
|
_iterator6.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError6) {
|
|
throw _iteratorError6;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (deopt) {
|
|
path.visit();
|
|
return false;
|
|
}
|
|
|
|
if (!statements.length) {
|
|
path.replaceWith(t.expressionStatement(node.test));
|
|
return;
|
|
}
|
|
|
|
var test = node.test;
|
|
if (t.isBinaryExpression(test) && test.operator === "!==") {
|
|
test.operator = "===";
|
|
} else if (t.isBinaryExpression(test) && test.operator === "!=") {
|
|
test.operator = "==";
|
|
} else if (t.isUnaryExpression(test, { operator: "!" })) {
|
|
node.test = test.argument;
|
|
} else {
|
|
node.test = t.unaryExpression("!", node.test, true);
|
|
}
|
|
|
|
path.get("consequent").replaceWith(t.blockStatement(statements.map(function (stmt) {
|
|
return t.clone(stmt.node);
|
|
})));
|
|
|
|
var l = statements.length;
|
|
while (l-- > 0) {
|
|
if (!statements[l].isFunctionDeclaration()) {
|
|
path.getSibling(path.key + 1).remove();
|
|
}
|
|
}
|
|
|
|
// this should take care of removing the block
|
|
path.visit();
|
|
}
|
|
|
|
function createPrevExpressionEater(keyword) {
|
|
var key = void 0;
|
|
switch (keyword) {
|
|
case "switch":
|
|
key = "discriminant";break;
|
|
case "throw":
|
|
case "return":
|
|
key = "argument";break;
|
|
case "if":
|
|
key = "test";break;
|
|
case "for-in":
|
|
key = "right";break;
|
|
}
|
|
|
|
return function (path) {
|
|
if (!path.inList) {
|
|
return;
|
|
}
|
|
|
|
var node = path.node;
|
|
|
|
var prev = path.getSibling(path.key - 1);
|
|
if (!prev.isExpressionStatement()) {
|
|
return;
|
|
}
|
|
|
|
var seq = prev.node.expression;
|
|
if (node[key]) {
|
|
if (t.isSequenceExpression(seq)) {
|
|
seq.expressions.push(node[key]);
|
|
} else {
|
|
seq = t.sequenceExpression([seq, node[key]]);
|
|
}
|
|
} else {
|
|
if (t.isSequenceExpression(seq)) {
|
|
var lastExpr = seq.expressions[seq.expressions.length - 1];
|
|
seq.expressions[seq.expressions.length - 1] = t.unaryExpression("void", lastExpr, true);
|
|
} else {
|
|
seq = t.unaryExpression("void", seq, true);
|
|
}
|
|
}
|
|
|
|
if (seq) {
|
|
node[key] = seq;
|
|
prev.remove();
|
|
|
|
// Since we were able to merge some stuff it's possible that this has opened
|
|
// oppurtinties for other transforms to happen.
|
|
// TODO: Look into changing the traversal order from bottom to up to avoid
|
|
// having to revisit things.
|
|
if (path.parentPath.parent) {
|
|
path.parentPath.parent[shouldRevisit] = true;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function isPatternMatchesPath(patternValue, inputPath) {
|
|
if (Array.isArray(patternValue)) {
|
|
for (var i = 0; i < patternValue.length; i++) {
|
|
if (isPatternMatchesPath(patternValue[i], inputPath)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
if (typeof patternValue === "function") {
|
|
return patternValue(inputPath);
|
|
}
|
|
if (isNodeOfType(inputPath.node, patternValue)) return true;
|
|
var evalResult = inputPath.evaluate();
|
|
if (!evalResult.confident || !inputPath.isPure()) return false;
|
|
return evalResult.value === patternValue;
|
|
}
|
|
|
|
// path1 -> path2
|
|
// is path1 an ancestor of path2
|
|
function isAncestor(path1, path2) {
|
|
return !!path2.findParent(function (parent) {
|
|
return parent === path1;
|
|
});
|
|
}
|
|
}; |