UNPKG

5.47 kBJavaScriptView Raw
1"use strict";
2
3function _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); } }
4
5var evaluate = require("babel-helper-evaluate-path");
6
7var _require = require("./replacements"),
8 FALLBACK_HANDLER = _require.FALLBACK_HANDLER;
9
10function getName(member) {
11 if (member.computed) {
12 switch (member.property.type) {
13 case "StringLiteral":
14 case "NumericLiteral":
15 return member.property.value;
16 case "TemplateLiteral":
17 return;
18 }
19 } else {
20 return member.property.name;
21 }
22}
23
24function swap(path, member, handlers) {
25 var key = getName(member);
26 if (key === undefined) return;
27 var handler = handlers[key];
28 if (typeof handler !== "function") {
29 if (typeof handlers[FALLBACK_HANDLER] === "function") {
30 handler = handlers[FALLBACK_HANDLER].bind(member.object, key);
31 } else {
32 return false;
33 }
34 }
35
36 for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
37 args[_key - 3] = arguments[_key];
38 }
39
40 var replacement = handler.apply(member.object, args);
41 if (replacement) {
42 path.replaceWith(replacement);
43 return true;
44 }
45 return false;
46}
47
48module.exports = function (babel) {
49 var replacements = require("./replacements.js")(babel);
50 var seen = Symbol("seen");
51 var t = babel.types,
52 traverse = babel.traverse;
53
54
55 return {
56 name: "minify-constant-folding",
57 visitor: {
58 // Evaluate string expressions that are next to each other
59 // but are not actually a binary expression.
60 // "a" + b + "c" + "d" -> "a" + b + "cd"
61 BinaryExpression(path) {
62 var literal = void 0,
63 bin = void 0;
64 if (path.get("right").isStringLiteral()) {
65 literal = path.get("right");
66 if (path.get("left").isBinaryExpression({ operator: "+" })) {
67 bin = path.get("left");
68 } else {
69 return;
70 }
71 } else if (path.get("left").isStringLiteral()) {
72 literal = path.get("left");
73 if (path.get("right").isBinaryExpression({ operator: "+" })) {
74 bin = path.get("right");
75 } else {
76 return;
77 }
78 } else {
79 return;
80 }
81
82 var relevant = getLeaf(bin, literal.key);
83
84 if (!relevant) {
85 return;
86 }
87
88 var value = literal.key === "right" ? relevant.node.value + literal.node.value : literal.node.value + relevant.node.value;
89
90 relevant.replaceWith(t.stringLiteral(value));
91 path.replaceWith(bin.node);
92
93 function getLeaf(path, direction) {
94 if (path.isStringLiteral()) {
95 return path;
96 } else if (path.isBinaryExpression({ operator: "+" })) {
97 return getLeaf(path.get(direction), direction);
98 }
99 }
100 },
101
102 // TODO: look into evaluating binding too (could result in more code, but gzip?)
103 Expression(path) {
104 var node = path.node;
105
106
107 if (node[seen]) {
108 return;
109 }
110
111 if (path.isLiteral()) {
112 return;
113 }
114
115 if (!path.isPure()) {
116 return;
117 }
118
119 if (traverse.hasType(node, path.scope, "Identifier", t.FUNCTION_TYPES)) {
120 return;
121 }
122
123 // -0 maybe compared via dividing and then checking against -Infinity
124 // Also -X will always be -X.
125 if (t.isUnaryExpression(node, { operator: "-" }) && t.isNumericLiteral(node.argument)) {
126 return;
127 }
128
129 // We have a transform that converts true/false to !0/!1
130 if (t.isUnaryExpression(node, { operator: "!" }) && t.isNumericLiteral(node.argument)) {
131 if (node.argument.value === 0 || node.argument.value === 1) {
132 return;
133 }
134 }
135
136 // void 0 is used for undefined.
137 if (t.isUnaryExpression(node, { operator: "void" }) && t.isNumericLiteral(node.argument, { value: 0 })) {
138 return;
139 }
140
141 var res = evaluate(path);
142 if (res.confident) {
143 // Avoid fractions because they can be longer than the original expression.
144 // There is also issues with number percision?
145 if (typeof res.value === "number" && !Number.isInteger(res.value)) {
146 return;
147 }
148
149 // Preserve -0
150 if (typeof res.value === "number" && res.value === 0) {
151 if (1 / res.value === -Infinity) {
152 var _node2 = t.unaryExpression("-", t.numericLiteral(0), true);
153 _node2[seen] = true;
154 path.replaceWith(_node2);
155 return;
156 }
157 }
158
159 var _node = t.valueToNode(res.value);
160 _node[seen] = true;
161 path.replaceWith(_node);
162 }
163 },
164 CallExpression(path) {
165 var node = path.node;
166 var member = node.callee;
167
168 if (t.isMemberExpression(member)) {
169 var helpers = replacements[member.object.type];
170 if (!helpers || !helpers.calls) return;
171 swap.apply(undefined, [path, member, helpers.calls].concat(_toConsumableArray(node.arguments)));
172 }
173 },
174 MemberExpression(path) {
175 var member = path.node;
176
177 var helpers = replacements[member.object.type];
178 if (!helpers || !helpers.members) return;
179 swap(path, member, helpers.members);
180 }
181 }
182 };
183};
\No newline at end of file