"use strict"; var evaluate = require("babel-helper-evaluate-path"); var jsesc = require("jsesc"); module.exports = function (_ref) { var t = _ref.types, traverse = _ref.traverse; var seen = Symbol("seen"); return { name: "minify-constant-folding", visitor: { // Evaluate string expressions that are next to each other // but are not actually a binary expression. // "a" + b + "c" + "d" -> "a" + b + "cd" BinaryExpression(path) { var literal = void 0, bin = void 0; if (path.get("right").isStringLiteral()) { literal = path.get("right"); if (path.get("left").isBinaryExpression({ operator: "+" })) { bin = path.get("left"); } else { return; } } else if (path.get("left").isStringLiteral()) { literal = path.get("left"); if (path.get("right").isBinaryExpression({ operator: "+" })) { bin = path.get("right"); } else { return; } } else { return; } var relevant = getLeaf(bin, literal.key); if (!relevant) { return; } var value = literal.key === "right" ? relevant.node.value + literal.node.value : literal.node.value + relevant.node.value; relevant.replaceWith(t.stringLiteral(value)); path.replaceWith(bin.node); function getLeaf(path, direction) { if (path.isStringLiteral()) { return path; } else if (path.isBinaryExpression({ operator: "+" })) { return getLeaf(path.get(direction), direction); } } }, // TODO: look into evaluating binding too (could result in more code, but gzip?) Expression(path) { var node = path.node; if (node[seen]) { return; } if (path.isLiteral()) { return; } if (!path.isPure()) { return; } if (traverse.hasType(node, path.scope, "Identifier", t.FUNCTION_TYPES)) { return; } // -0 maybe compared via dividing and then checking against -Infinity // Also -X will always be -X. if (t.isUnaryExpression(node, { operator: "-" }) && t.isNumericLiteral(node.argument)) { return; } // We have a transform that converts true/false to !0/!1 if (t.isUnaryExpression(node, { operator: "!" }) && t.isNumericLiteral(node.argument)) { if (node.argument.value === 0 || node.argument.value === 1) { return; } } // void 0 is used for undefined. if (t.isUnaryExpression(node, { operator: "void" }) && t.isNumericLiteral(node.argument, { value: 0 })) { return; } var res = evaluate(path); if (res.confident) { // Avoid fractions because they can be longer than the original expression. // There is also issues with number percision? if (typeof res.value === "number" && !Number.isInteger(res.value)) { return; } // Preserve -0 if (typeof res.value === "number" && res.value === 0) { if (1 / res.value === -Infinity) { var _node2 = t.unaryExpression("-", t.numericLiteral(0), true); _node2[seen] = true; path.replaceWith(_node2); return; } } // https://github.com/babel/babili/issues/382 if (typeof res.value === "string") { res.value = jsesc(res.value, { isScriptContext: true }); } var _node = t.valueToNode(res.value); _node[seen] = true; path.replaceWith(_node); } } } }; };