WIP - add extractor, generate snippet_data

This commit is contained in:
Stefan Fejes
2019-08-20 15:52:05 +02:00
parent 88084d3d30
commit cc8f1d8a7a
37396 changed files with 4588842 additions and 133 deletions

View File

@ -0,0 +1,90 @@
# babel-plugin-minify-constant-folding
Tries to evaluate expressions and inline the result.
## Example
**In**
```javascript
"a" + "b"
2 * 3;
4 | 3;
"b" + a + "c" + "d" + g + z + "f" + "h" + "i"
[a, b, c].concat([d, e], f, g, [h]);
["a", "b", "c"].join();
["a", "b", "c"].join('@');
[1, 2, 3].length;
[1, 2, 3][1];
[1, 2, 3].shift();
[1, 2, 3].slice(0, 2);
[a, b, c].pop();
[a, b, c].reverse();
"a,b,c".split(",");
"abc"[0];
"abc".charAt();
"abc".charAt(1);
"abc".length;
```
**Out**
```javascript
"ab";
6;
7;
"b" + a + "cd" + g + z + "fhi";
[a, b, c, d, e, f, g, h];
"a,b,c";
"a@b@c";
3;
2;
2;
[1, 2];
c;
[c, b, a];
["a", "b", "c"];
"a";
"a";
"a";
"b";
3;
```
## Installation
```sh
npm install babel-plugin-minify-constant-folding
```
## Usage
### Via `.babelrc` (Recommended)
**.babelrc**
```json
{
"plugins": ["minify-constant-folding"]
}
```
### Via CLI
```sh
babel --plugins minify-constant-folding script.js
```
### Via Node API
```javascript
require("babel-core").transform("code", {
plugins: ["minify-constant-folding"]
});
```
## Options
+ `tdz` - Account for TDZ (Temporal Dead Zone)

View File

@ -0,0 +1,212 @@
"use strict";
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 evaluate = require("babel-helper-evaluate-path");
var _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) {
var key = getName(member.node);
if (key === void 0) return false;
var handler = void 0;
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;
}
for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
args[_key - 3] = arguments[_key];
}
var replacement = handler.apply(member.get("object"), args);
if (replacement) {
path.replaceWith(replacement);
return true;
}
return false;
}
module.exports = function (babel) {
var replacements = require("./replacements.js")(babel);
var seen = Symbol("seen");
var t = babel.types,
traverse = babel.traverse;
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;
}
var literal = void 0,
bin = void 0;
var left = path.get("left");
var 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;
}
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, _ref) {
var _ref$opts = _ref.opts;
_ref$opts = _ref$opts === undefined ? {} : _ref$opts;
var _ref$opts$tdz = _ref$opts.tdz,
tdz = _ref$opts$tdz === undefined ? false : _ref$opts$tdz;
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, { tdz });
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;
}
}
// 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;
}
var _node = t.valueToNode(res.value);
_node[seen] = true;
path.replaceWith(_node);
}
},
CallExpression(path) {
var node = path.node;
var member = path.get("callee");
if (t.isMemberExpression(member)) {
var 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.apply(undefined, [path, member, helpers.calls].concat(_toConsumableArray(node.arguments)));
}
},
MemberExpression(path) {
var node = path.node;
var 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,158 @@
"use strict";
var FALLBACK_HANDLER = Symbol("fallback handler");
module.exports = function (_ref) {
var t = _ref.types;
var 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 () {
var i = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : t.numericLiteral(0);
if (t.isNumericLiteral(i)) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
return cb.call.apply(cb, [this.node, this.node, i.value].concat(args));
}
};
}
function hasSpread(node) {
return node.elements.some(function (el) {
return 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() {
var sep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : t.stringLiteral(",");
if (!t.isStringLiteral(sep)) return;
var bad = false;
var str = this.get("elements").map(function (el) {
var evaled = el.evaluate();
if (!evaled.confident) {
bad = true;
return;
}
return evaled.value;
}).join(sep.value);
return bad ? void 0 : t.stringLiteral(str);
},
push() {
return t.numericLiteral(this.node.elements.length + arguments.length);
},
shift() {
if (this.node.elements.length === 0) {
return undef;
}
return t.numericLiteral(this.node.elements.length - 1);
},
slice() {
var start = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : t.numericLiteral(0);
var end = arguments[1];
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) {
var _node$elements$slice;
if (!t.isNumericLiteral(start) || end && !t.isNumericLiteral(end)) {
return;
}
for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
args[_key2 - 2] = arguments[_key2];
}
if (end) {
args.unshift(end.value);
}
return t.arrayExpression((_node$elements$slice = this.node.elements.slice()).splice.apply(_node$elements$slice, [start.value].concat(args)));
}
}
},
StringLiteral: {
members: {
length() {
return t.numericLiteral(this.node.value.length);
},
[FALLBACK_HANDLER](i) {
if (typeof i === "number" || i.match(/^\d+$/)) {
var ch = this.node.value[i];
return ch ? t.stringLiteral(ch) : undef;
}
}
},
calls: {
split() {
var sep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undef;
var 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(function (str) {
return t.stringLiteral(str);
}));
}
},
charAt: defaultZero(function (_ref2, i) {
var value = _ref2.value;
return t.stringLiteral(value.charAt(i));
}),
charCodeAt: defaultZero(function (_ref3, i) {
var value = _ref3.value;
return t.numericLiteral(value.charCodeAt(i));
}),
codePointAt: defaultZero(function (_ref4, i) {
var value = _ref4.value;
return t.numericLiteral(value.codePointAt(i));
})
}
}
};
};
module.exports.FALLBACK_HANDLER = FALLBACK_HANDLER;

View File

@ -0,0 +1,49 @@
{
"_from": "babel-plugin-minify-constant-folding@^0.3.0",
"_id": "babel-plugin-minify-constant-folding@0.3.0",
"_inBundle": false,
"_integrity": "sha512-1XeRpx+aY1BuNY6QU/cm6P+FtEi3ar3XceYbmC+4q4W+2Ewq5pL7V68oHg1hKXkBIE0Z4/FjSoHz6vosZLOe/A==",
"_location": "/babel-plugin-minify-constant-folding",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "babel-plugin-minify-constant-folding@^0.3.0",
"name": "babel-plugin-minify-constant-folding",
"escapedName": "babel-plugin-minify-constant-folding",
"rawSpec": "^0.3.0",
"saveSpec": null,
"fetchSpec": "^0.3.0"
},
"_requiredBy": [
"/babel-preset-minify"
],
"_resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.3.0.tgz",
"_shasum": "687e40336bd4ddd921e0e197f0006235ac184bb9",
"_spec": "babel-plugin-minify-constant-folding@^0.3.0",
"_where": "/Users/stefanfejes/Projects/30-seconds-of-python-code/node_modules/babel-preset-minify",
"author": {
"name": "amasad"
},
"bugs": {
"url": "https://github.com/babel/minify/issues"
},
"bundleDependencies": false,
"dependencies": {
"babel-helper-evaluate-path": "^0.3.0"
},
"deprecated": false,
"description": "Tries to evaluate expressions and inline the result.",
"homepage": "https://github.com/babel/minify#readme",
"keywords": [
"babel-plugin"
],
"license": "MIT",
"main": "lib/index.js",
"name": "babel-plugin-minify-constant-folding",
"repository": {
"type": "git",
"url": "https://github.com/babel/minify/tree/master/packages/babel-plugin-minify-constant-folding"
},
"version": "0.3.0"
}