diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2024-08-30 17:21:18 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2024-08-30 17:21:18 +0800 |
| commit | efbc2ffe6a1ee5ec38a4396305561d6515d0943e (patch) | |
| tree | 2b4c94f18f2c792dfda813050755d5c0273fae22 /node_modules/babel-plugin-minify-constant-folding/lib/index.js | |
| parent | 43acceb75957c880a5ba5a31aae221394a36d5f6 (diff) | |
node fixes, updates
Diffstat (limited to 'node_modules/babel-plugin-minify-constant-folding/lib/index.js')
| -rw-r--r-- | node_modules/babel-plugin-minify-constant-folding/lib/index.js | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/node_modules/babel-plugin-minify-constant-folding/lib/index.js b/node_modules/babel-plugin-minify-constant-folding/lib/index.js new file mode 100644 index 00000000..6d50411b --- /dev/null +++ b/node_modules/babel-plugin-minify-constant-folding/lib/index.js @@ -0,0 +1,230 @@ +"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); +}
\ No newline at end of file |
