Skip to content

Commit 7e2b315

Browse files
j-f1boopathi
authored andcommitted
Evaluate some String and Array instance methods at compile time (#505)
* Evaluate some String and Array instance methods at compile time * Fix a few bugs and increase test coverage * npm run format * undefined -> void 0 * undefined -> void 0 in tests * `isUndef(thing)` checks if `thing` is `undefined` * I can typo! * Rename `other` handler * Pacify prettier * npm run format * To trigger CircleCI
1 parent cb993ed commit 7e2b315

File tree

4 files changed

+358
-4
lines changed

4 files changed

+358
-4
lines changed

packages/babel-plugin-minify-constant-folding/README.md

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# babel-plugin-minify-constant-folding
22

3-
Tries to evaluate expressions and inline the result. For now only deals with numbers and strings.
3+
Tries to evaluate expressions and inline the result.
44

55
## Example
66

@@ -10,7 +10,22 @@ Tries to evaluate expressions and inline the result. For now only deals with num
1010
"a" + "b"
1111
2 * 3;
1212
4 | 3;
13-
"b" + a + "c" + "d" + g + z + "f" + "h" + "z"
13+
"b" + a + "c" + "d" + g + z + "f" + "h" + "i"
14+
15+
[a, b, c].concat([d, e], f, g, [h]);
16+
["a", "b", "c"].join();
17+
["a", "b", "c"].join('@');
18+
[1, 2, 3].length;
19+
[1, 2, 3][1];
20+
[1, 2, 3].shift();
21+
[1, 2, 3].slice(0, 2);
22+
[a, b, c].pop();
23+
[a, b, c].reverse();
24+
"a,b,c".split(",");
25+
"abc"[0];
26+
"abc".charAt();
27+
"abc".charAt(1);
28+
"abc".length;
1429
```
1530

1631
**Out**
@@ -19,7 +34,23 @@ Tries to evaluate expressions and inline the result. For now only deals with num
1934
"ab";
2035
6;
2136
7;
22-
"b" + a + "cd" + g + z + "fhz";
37+
"b" + a + "cd" + g + z + "fhi";
38+
39+
[a, b, c, d, e, f, g, h];
40+
"a,b,c";
41+
"a@b@c";
42+
3;
43+
2;
44+
2;
45+
[1, 2];
46+
c;
47+
[c, b, a];
48+
["a", "b", "c"];
49+
"a";
50+
"a";
51+
"a";
52+
"b";
53+
3;
2354
```
2455

2556
## Installation

packages/babel-plugin-minify-constant-folding/__tests__/constant-folding-test.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,142 @@ describe("constant-folding-plugin", () => {
103103
`);
104104
expect(transform(source)).toBe(expected);
105105
});
106+
107+
it("should handle Array methods on array literals", () => {
108+
const source = unpad(
109+
`
110+
[1, 2, 3].concat([4, 5, 6]);
111+
[a, b, c].concat([d, e], f, g, [h]);
112+
[1, 2, 3]["concat"]([4, 5, 6]);
113+
[1, 2, 3].push([4, 5, 6]);
114+
115+
[1, 2, 3].join();
116+
["a", "b", "c"].join();
117+
["a", "b", "c"].join("@");
118+
119+
[1, 2, 3].length;
120+
[1, 2, 3][1];
121+
[1, 2, 3]["1"];
122+
[1, 2, 3][4];
123+
124+
[].shift();
125+
[1, 2, 3].shift();
126+
127+
[1, 2, 3].slice();
128+
[1, 2, 3].slice(1);
129+
[1, 2, 3].slice(0, 2);
130+
[1, 2, 3].slice(0, -1);
131+
132+
[1, 2, 3].pop();
133+
[a, b, c].pop();
134+
[].pop();
135+
136+
[a, b, c].reverse();
137+
[1, 2, 3].reverse();
138+
139+
[1, 2, 3].splice(1);
140+
[1, 2, 3, 4].splice(1, 2);
141+
`
142+
);
143+
const expected = unpad(
144+
`
145+
[1, 2, 3, 4, 5, 6];
146+
[a, b, c, d, e, f, g, h];
147+
[1, 2, 3, 4, 5, 6];
148+
4;
149+
150+
"1,2,3";
151+
"a,b,c";
152+
"a@b@c";
153+
154+
3;
155+
2;
156+
2;
157+
void 0;
158+
159+
void 0;
160+
2;
161+
162+
[1, 2, 3];
163+
[2, 3];
164+
[1, 2];
165+
[1, 2, 3].slice(0, -1);
166+
167+
3;
168+
c;
169+
void 0;
170+
171+
[c, b, a];
172+
[3, 2, 1];
173+
174+
[2, 3];
175+
[2, 3];
176+
`
177+
);
178+
expect(transform(source)).toBe(expected);
179+
});
180+
it("should ignore bad calls to array expression methods", () => {
181+
const source = unpad(
182+
`
183+
[1, 2, 3][concat]([4, 5, 6]);
184+
[a, "b", "c"].join();
185+
["a", "b", "c"].join(a);
186+
[1, 2, 3].splice("a");
187+
`
188+
);
189+
expect(transform(source)).toBe(source);
190+
});
191+
it("should ignore bad calls to string expression methods", () => {
192+
const source = unpad(
193+
`
194+
"abc".something;
195+
"abc"["something"];
196+
`
197+
);
198+
expect(transform(source)).toBe(source);
199+
});
200+
it("should handle String methods on string literals", () => {
201+
const source = unpad(
202+
`
203+
"a,b,c".split(",");
204+
"a,b,c".split("");
205+
"a,b,c".split();
206+
"abc"[0];
207+
"abc"["0"];
208+
"abc"[4];
209+
"abc".charAt();
210+
"abc".charAt(1);
211+
"abc".charCodeAt();
212+
"abc".charCodeAt(1);
213+
"abc".length;
214+
215+
"\u{1f44d}".charCodeAt();
216+
"\u{1f44d}".charCodeAt(1);
217+
"\u{1f44d}".codePointAt();
218+
"\u{1f44d}".codePointAt(1);
219+
`
220+
);
221+
222+
const expected = unpad(
223+
`
224+
["a", "b", "c"];
225+
["a", ",", "b", ",", "c"];
226+
["a,b,c"];
227+
"a";
228+
"a";
229+
void 0;
230+
"a";
231+
"b";
232+
97;
233+
98;
234+
3;
235+
236+
${0xd83d};
237+
${0xdc4d};
238+
${0x1f44d};
239+
${0xdc4d};
240+
`
241+
);
242+
expect(transform(source)).toBe(expected);
243+
});
106244
});

packages/babel-plugin-minify-constant-folding/src/index.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,45 @@
22

33
const evaluate = require("babel-helper-evaluate-path");
44

5-
module.exports = ({ types: t, traverse }) => {
5+
const { FALLBACK_HANDLER } = require("./replacements");
6+
7+
function getName(member) {
8+
if (member.computed) {
9+
switch (member.property.type) {
10+
case "StringLiteral":
11+
case "NumericLiteral":
12+
return member.property.value;
13+
case "TemplateLiteral":
14+
return;
15+
}
16+
} else {
17+
return member.property.name;
18+
}
19+
}
20+
21+
function swap(path, member, handlers, ...args) {
22+
const key = getName(member);
23+
if (key === undefined) return;
24+
let handler = handlers[key];
25+
if (typeof handler !== "function") {
26+
if (typeof handlers[FALLBACK_HANDLER] === "function") {
27+
handler = handlers[FALLBACK_HANDLER].bind(member.object, key);
28+
} else {
29+
return false;
30+
}
31+
}
32+
const replacement = handler.apply(member.object, args);
33+
if (replacement) {
34+
path.replaceWith(replacement);
35+
return true;
36+
}
37+
return false;
38+
}
39+
40+
module.exports = babel => {
41+
const replacements = require("./replacements.js")(babel);
642
const seen = Symbol("seen");
43+
const { types: t, traverse } = babel;
744

845
return {
946
name: "minify-constant-folding",
@@ -124,6 +161,21 @@ module.exports = ({ types: t, traverse }) => {
124161
node[seen] = true;
125162
path.replaceWith(node);
126163
}
164+
},
165+
CallExpression(path) {
166+
const { node } = path;
167+
const { callee: member } = node;
168+
if (t.isMemberExpression(member)) {
169+
const helpers = replacements[member.object.type];
170+
if (!helpers || !helpers.calls) return;
171+
swap(path, member, helpers.calls, ...node.arguments);
172+
}
173+
},
174+
MemberExpression(path) {
175+
const { node: member } = path;
176+
const helpers = replacements[member.object.type];
177+
if (!helpers || !helpers.members) return;
178+
swap(path, member, helpers.members);
127179
}
128180
}
129181
};

0 commit comments

Comments
 (0)