1
0
mirror of https://github.com/S2-/minifyfromhtml.git synced 2025-08-03 12:20:04 +02:00

add some packages

This commit is contained in:
s2
2018-05-05 13:54:07 +02:00
parent 48c1138518
commit ff6e20677d
3738 changed files with 215920 additions and 0 deletions

View File

@@ -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);
}

View File

@@ -0,0 +1,160 @@
"use strict";
const FALLBACK_HANDLER = Symbol("fallback handler");
module.exports = ({
types: t
}) => {
const undef = t.unaryExpression("void", t.numericLiteral(0));
function isUndef(ob) {
return ob === undefined || t.isIdentifier(ob, {
name: "undefined"
}) || t.isUnaryExpression(ob, {
operator: "void"
});
}
function defaultZero(cb) {
return function (i = t.numericLiteral(0), ...args) {
if (t.isNumericLiteral(i)) {
return cb.call(this.node, this.node, i.value, ...args);
}
};
}
function hasSpread(node) {
return node.elements.some(el => t.isSpreadElement(el));
}
return {
ArrayExpression: {
canReplace() {
return !hasSpread(this.node);
},
members: {
length() {
if (hasSpread(this.node)) {
return;
}
return t.numericLiteral(this.node.elements.length);
},
[FALLBACK_HANDLER](i) {
if (hasSpread(this.node)) {
return;
}
if (typeof i === "number" || i.match(/^\d+$/)) {
return this.node.elements[i] || undef;
}
}
},
calls: {
join(sep = t.stringLiteral(",")) {
if (!t.isStringLiteral(sep)) return;
let bad = false;
const str = this.get("elements").map(el => {
const evaled = el.evaluate();
if (!evaled.confident) {
bad = true;
return;
}
return evaled.value;
}).join(sep.value);
return bad ? void 0 : t.stringLiteral(str);
},
push(...args) {
return t.numericLiteral(this.node.elements.length + args.length);
},
shift() {
if (this.node.elements.length === 0) {
return undef;
}
return t.numericLiteral(this.node.elements.length - 1);
},
slice(start = t.numericLiteral(0), end) {
if (!t.isNumericLiteral(start) || end && !t.isNumericLiteral(end)) {
return;
}
return t.arrayExpression(this.node.elements.slice(start.value, end && end.value));
},
pop() {
return this.node.elements[this.node.elements.length - 1] || undef;
},
reverse() {
return t.arrayExpression(this.node.elements.reverse());
},
splice(start, end, ...args) {
if (!t.isNumericLiteral(start) || end && !t.isNumericLiteral(end)) {
return;
}
if (end) {
args.unshift(end.value);
}
return t.arrayExpression(this.node.elements.slice().splice(start.value, ...args));
}
}
},
StringLiteral: {
members: {
length() {
return t.numericLiteral(this.node.value.length);
},
[FALLBACK_HANDLER](i) {
if (typeof i === "number" || i.match(/^\d+$/)) {
const ch = this.node.value[i];
return ch ? t.stringLiteral(ch) : undef;
}
}
},
calls: {
split(sep = undef) {
let realSep = null;
if (t.isStringLiteral(sep)) {
realSep = sep.value;
}
if (isUndef(sep)) {
realSep = sep;
}
if (realSep !== null) {
return t.arrayExpression(this.node.value.split(realSep).map(str => t.stringLiteral(str)));
}
},
charAt: defaultZero(({
value
}, i) => t.stringLiteral(value.charAt(i))),
charCodeAt: defaultZero(({
value
}, i) => t.numericLiteral(value.charCodeAt(i))),
codePointAt: defaultZero(({
value
}, i) => t.numericLiteral(value.codePointAt(i)))
}
}
};
};
module.exports.FALLBACK_HANDLER = FALLBACK_HANDLER;