UNPKG

luhn-generator

Version:

A generator of numbers that passes the validation of Luhn algorithm or Luhn formula, also known as the 'modulus 10' or 'mod 10' algorithm

230 lines (185 loc) 6.1 kB
"use strict"; const evaluate = require("babel-helper-evaluate-path"); const _require = require("./replacements"), FALLBACK_HANDLER = _require.FALLBACK_HANDLER; function getName(member) { if (member.computed) { switch (member.property.type) { case "StringLiteral": case "NumericLiteral": return member.property.value; case "TemplateLiteral": return; } } else { return member.property.name; } } function swap(path, member, handlers, ...args) { const key = getName(member.node); if (key === void 0) return false; let handler; if (hop(handlers, key) && typeof handlers[key] === "function") { handler = handlers[key]; } else if (typeof handlers[FALLBACK_HANDLER] === "function") { handler = handlers[FALLBACK_HANDLER].bind(member.get("object"), key); } else { return false; } const replacement = handler.apply(member.get("object"), args); if (replacement) { path.replaceWith(replacement); return true; } return false; } module.exports = babel => { const replacements = require("./replacements.js")(babel); const seen = Symbol("seen"); const t = babel.types; 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) { if (!path.isBinaryExpression({ operator: "+" })) { return; } let literal, bin; const left = path.get("left"); const right = path.get("right"); if (right.isStringLiteral()) { literal = right; if (left.isBinaryExpression({ operator: "+" })) { bin = left; } else { return; } } else if (left.isStringLiteral()) { literal = left; if (right.isBinaryExpression({ operator: "+" })) { bin = right; } else { return; } } else { return; } const relevant = getLeaf(bin, literal.key); if (!relevant) { return; } const 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, { opts: { tdz = false } = {} }) { const node = path.node, parent = path.parent; if (node[seen]) { return; } if (path.isLiteral()) { return; } if (!path.isPure()) { return; } // Avoid replacing the values for identifiers in exports if (t.isExportSpecifier(parent)) { 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; } const res = evaluate(path, { tdz }); if (res.confident) { // Avoid fractions because they can be longer than the original expression. // There is also issues with number precision? 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) { const node = t.unaryExpression("-", t.numericLiteral(0), true); node[seen] = true; path.replaceWith(node); return; } } // this will convert object to object but // t.valueToNode has other effects where property name // is not treated for the respective environment. // So we bail here for objects and let other plugins // take care of converting String literal to Identifier if (typeof res.value === "object") { return; } const node = t.valueToNode(res.value); node[seen] = true; path.replaceWith(node); } }, CallExpression(path) { const node = path.node; const member = path.get("callee"); if (t.isMemberExpression(member)) { const helpers = replacements[member.node.object.type]; if (!helpers || !helpers.calls) return; // find if the input can be constant folded if (typeof helpers.canReplace === "function" && !helpers.canReplace.call(member.get("object"))) { return; } swap(path, member, helpers.calls, ...node.arguments); } }, MemberExpression(path) { const node = path.node; const helpers = replacements[node.object.type]; if (!helpers || !helpers.members) return; swap(path, path, helpers.members); } } }; }; function hop(o, key) { return Object.prototype.hasOwnProperty.call(o, key); }