Compare commits
650 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
294861ba96 | ||
|
|
11b0efdf84 | ||
|
|
5486b68850 | ||
|
|
bdd8e34f63 | ||
|
|
6547437725 | ||
|
|
9662228f6a | ||
|
|
31a9b05c96 | ||
|
|
63b01fe8f9 | ||
|
|
7a4ed9d200 | ||
|
|
d5c651a5e5 | ||
|
|
cdba43cfa4 | ||
|
|
a123e232b9 | ||
|
|
601780acc1 | ||
|
|
7c3fee9e31 | ||
|
|
929de2b0de | ||
|
|
12e6ad326c | ||
|
|
00c8d1d241 | ||
|
|
af2472d85e | ||
|
|
3eb9101918 | ||
|
|
0a38a688f9 | ||
|
|
f4c2ea37bf | ||
|
|
915f907186 | ||
|
|
799509e145 | ||
|
|
b5a7197ae5 | ||
|
|
1b703349cf | ||
|
|
4a7179ff91 | ||
|
|
f97da4294a | ||
|
|
918c17bd88 | ||
|
|
8b71c6559b | ||
|
|
26641f3fb2 | ||
|
|
ebe118dc79 | ||
|
|
70e5b6f15b | ||
|
|
57e0fafd5c | ||
|
|
8439c8ba98 | ||
|
|
5c4e470d43 | ||
|
|
6605d15783 | ||
|
|
ac8db977b9 | ||
|
|
88b77ddaa9 | ||
|
|
fe4e9f9d97 | ||
|
|
8c6af09ae0 | ||
|
|
6f3e35bb3f | ||
|
|
174404c0f3 | ||
|
|
60c4030a4d | ||
|
|
ac810dc07a | ||
|
|
0cabedc526 | ||
|
|
5cd26c005b | ||
|
|
bd99b00413 | ||
|
|
9e2f9f7910 | ||
|
|
e87c77ed41 | ||
|
|
774bda13cd | ||
|
|
15b5f70338 | ||
|
|
7f48d5b33c | ||
|
|
b6968b6bd2 | ||
|
|
08b80302eb | ||
|
|
645626ebe8 | ||
|
|
d895c09c70 | ||
|
|
08623aa6a7 | ||
|
|
c898a26117 | ||
|
|
619adb0308 | ||
|
|
7691bebea5 | ||
|
|
3c4346728e | ||
|
|
18d37ac761 | ||
|
|
63d35f8f6d | ||
|
|
7dbe961b2d | ||
|
|
94c4daaf9e | ||
|
|
37ee9de902 | ||
|
|
83db98ad3b | ||
|
|
bd0ae6569f | ||
|
|
841a661071 | ||
|
|
7491d07666 | ||
|
|
335e349314 | ||
|
|
2a88d07b3a | ||
|
|
a887cde9f2 | ||
|
|
b5623b19d4 | ||
|
|
6b2861e086 | ||
|
|
d5138f7467 | ||
|
|
eac67b2816 | ||
|
|
ce10072824 | ||
|
|
dff54a6552 | ||
|
|
1940fb682c | ||
|
|
17eef5a3c2 | ||
|
|
9f1f21b810 | ||
|
|
a8e67d157e | ||
|
|
e870c7db45 | ||
|
|
6500f8c52c | ||
|
|
4d2f7d83af | ||
|
|
99945fcd04 | ||
|
|
0d952ae43d | ||
|
|
593677d2ff | ||
|
|
c69294c449 | ||
|
|
2a06c7758e | ||
|
|
7ee1ec91a2 | ||
|
|
233fb62bd8 | ||
|
|
6637c267a5 | ||
|
|
99233c44cc | ||
|
|
33528002b4 | ||
|
|
20542a37a8 | ||
|
|
5fd12451f9 | ||
|
|
ba939ccd6c | ||
|
|
3a5f354846 | ||
|
|
fcde6109b0 | ||
|
|
e3bd223dac | ||
|
|
6c8db6eae1 | ||
|
|
3ff0b9e0c9 | ||
|
|
464a942a95 | ||
|
|
d7a4a4a462 | ||
|
|
759b3f7d6d | ||
|
|
958b6c2e57 | ||
|
|
ab15d676d7 | ||
|
|
66761d7ecf | ||
|
|
3afad58a93 | ||
|
|
170e8b519e | ||
|
|
f8684f418a | ||
|
|
881bda7f59 | ||
|
|
9854deb626 | ||
|
|
d6814050dd | ||
|
|
252fc65558 | ||
|
|
8108c7ffaf | ||
|
|
ba9936a572 | ||
|
|
905b601178 | ||
|
|
63fb2d5a44 | ||
|
|
85a5fc0aeb | ||
|
|
4fba3e0b80 | ||
|
|
9d398d999c | ||
|
|
f47b2b52a5 | ||
|
|
fedb6191a1 | ||
|
|
5bf617ebde | ||
|
|
0b82e1cd5b | ||
|
|
9aef34a816 | ||
|
|
0ac6918a41 | ||
|
|
65ee5af78c | ||
|
|
c6fa291571 | ||
|
|
bce4307e9e | ||
|
|
96ad94ab41 | ||
|
|
a5b60217ce | ||
|
|
44fd6694eb | ||
|
|
e48db3a8b6 | ||
|
|
e637bdaf4e | ||
|
|
d558abbdb7 | ||
|
|
4aed0830e5 | ||
|
|
d2dda34b2a | ||
|
|
c3a10c135e | ||
|
|
92e4340732 | ||
|
|
7b22f2031f | ||
|
|
3b14582d6b | ||
|
|
274e1b3dc7 | ||
|
|
de58b0289d | ||
|
|
efea52a4f4 | ||
|
|
763bd36b60 | ||
|
|
0552dbd93c | ||
|
|
18c63ff3d8 | ||
|
|
e04ef56243 | ||
|
|
5d60484553 | ||
|
|
3c846e6f7b | ||
|
|
2850dc69fd | ||
|
|
94205c3a37 | ||
|
|
2ada34b229 | ||
|
|
db396da734 | ||
|
|
0262b4244c | ||
|
|
73ca767d06 | ||
|
|
3ec11c781b | ||
|
|
a79ff060d0 | ||
|
|
43991f8d2f | ||
|
|
6b82069e1a | ||
|
|
276b9a31cd | ||
|
|
5801fa39e9 | ||
|
|
f0ab1b02e6 | ||
|
|
36c28e02fd | ||
|
|
e1c3861832 | ||
|
|
ecfd881ac6 | ||
|
|
81b7335267 | ||
|
|
bb010c2253 | ||
|
|
03b6121194 | ||
|
|
3ef092332b | ||
|
|
540c19792f | ||
|
|
80d1c8206b | ||
|
|
d36faffeca | ||
|
|
7c8c9b94bc | ||
|
|
f5eeed7665 | ||
|
|
80cfd063e2 | ||
|
|
aa45f6586e | ||
|
|
0c80d21e01 | ||
|
|
375c88245a | ||
|
|
ea3430102c | ||
|
|
9de7199b88 | ||
|
|
ae07714927 | ||
|
|
0e41a3fad4 | ||
|
|
61e850ceb5 | ||
|
|
992b6b9fcc | ||
|
|
7b71344051 | ||
|
|
605362f89d | ||
|
|
fbbaa42ee5 | ||
|
|
099992ecae | ||
|
|
d78ae20e64 | ||
|
|
5c02d65ddb | ||
|
|
d36067cd35 | ||
|
|
f1b2134dd1 | ||
|
|
74cda80d3b | ||
|
|
9a3a848cc8 | ||
|
|
a1a4c2ada7 | ||
|
|
189dbf02b6 | ||
|
|
42ecd42ac0 | ||
|
|
a10f6a96d7 | ||
|
|
0d232a1422 | ||
|
|
285bffd2c6 | ||
|
|
61c233a08e | ||
|
|
d2d716483a | ||
|
|
f16033aafd | ||
|
|
ae5366a31d | ||
|
|
6b23cbc852 | ||
|
|
7f9bc9e863 | ||
|
|
13219cebcb | ||
|
|
93a6e5780e | ||
|
|
fe55e0d93d | ||
|
|
e1f0747e4c | ||
|
|
e37b67d013 | ||
|
|
ad18689d92 | ||
|
|
0f80b1058d | ||
|
|
0d48af3f36 | ||
|
|
4613644cce | ||
|
|
718e475613 | ||
|
|
aa5dd15352 | ||
|
|
5bff65c132 | ||
|
|
24bc09b79b | ||
|
|
37c17d5541 | ||
|
|
120948fa48 | ||
|
|
66e6f0c3cb | ||
|
|
f7447efa8c | ||
|
|
f4d36a58c2 | ||
|
|
6d1c3e1aec | ||
|
|
73cc0505f5 | ||
|
|
c75f5a1fd8 | ||
|
|
39d8880f2c | ||
|
|
5538ec7bd8 | ||
|
|
f101d6429b | ||
|
|
fe06fc85d3 | ||
|
|
f36a1eaa8b | ||
|
|
a64bdda9ae | ||
|
|
01d19b4b52 | ||
|
|
f0c1a01bc2 | ||
|
|
7be680d3f8 | ||
|
|
57dab1e1db | ||
|
|
21b3c890a1 | ||
|
|
fb0ec720a4 | ||
|
|
7971ed33d1 | ||
|
|
885835a655 | ||
|
|
4c64554808 | ||
|
|
548beeb6b1 | ||
|
|
e3066f9577 | ||
|
|
e391367488 | ||
|
|
18ddf2f7b5 | ||
|
|
f8ee5a0785 | ||
|
|
b467a3c877 | ||
|
|
f2d48e9019 | ||
|
|
5e314bf3e9 | ||
|
|
05ba26c7c8 | ||
|
|
87b72364a4 | ||
|
|
0e3ff1f970 | ||
|
|
ec3e74d7f4 | ||
|
|
62bda71c85 | ||
|
|
83e0939088 | ||
|
|
9798d96e37 | ||
|
|
6006dd933d | ||
|
|
ac2caf1088 | ||
|
|
8511e80f48 | ||
|
|
91bc3f1f92 | ||
|
|
8463b48f90 | ||
|
|
e3342a3cf6 | ||
|
|
524a8a42a4 | ||
|
|
7bf59b5bcd | ||
|
|
025f3e9596 | ||
|
|
8258edd8a5 | ||
|
|
8669ca219b | ||
|
|
71652690b6 | ||
|
|
37693d2812 | ||
|
|
8fbe200012 | ||
|
|
1a34a13e33 | ||
|
|
ef772b0049 | ||
|
|
6fcabbde08 | ||
|
|
14f290f8ab | ||
|
|
e2e09d5754 | ||
|
|
448a8d3845 | ||
|
|
514936beb8 | ||
|
|
f5c09d0bbf | ||
|
|
014f655c5f | ||
|
|
bf30dc3038 | ||
|
|
ef2ef07cbd | ||
|
|
1a4440080d | ||
|
|
ac0086a745 | ||
|
|
2494daaf68 | ||
|
|
9b404f9de6 | ||
|
|
5344b7dab8 | ||
|
|
0007a53b9c | ||
|
|
1dd05f44eb | ||
|
|
bf7b122ab2 | ||
|
|
e29048b54a | ||
|
|
2eeb640eca | ||
|
|
ceb200fe81 | ||
|
|
f5f8239057 | ||
|
|
6f9d051784 | ||
|
|
931862e97f | ||
|
|
1d0127de21 | ||
|
|
2d8fc61677 | ||
|
|
1e31011874 | ||
|
|
75cdbf19aa | ||
|
|
4339bd5cfa | ||
|
|
1ab2fdaa10 | ||
|
|
eda540f6ec | ||
|
|
90a330da16 | ||
|
|
cad1f9cbd1 | ||
|
|
f6203bd5a8 | ||
|
|
03cf94ebe8 | ||
|
|
c3087dd179 | ||
|
|
2c305af478 | ||
|
|
72e6f64ca8 | ||
|
|
b9fac687ff | ||
|
|
2c88eb6fbe | ||
|
|
a67e3bfdd3 | ||
|
|
27142df4f5 | ||
|
|
5e4c7f4245 | ||
|
|
b521b4b926 | ||
|
|
aa9de76370 | ||
|
|
5a083a938d | ||
|
|
7a30d826b8 | ||
|
|
be55a09edf | ||
|
|
15a148ff6d | ||
|
|
428e19fed2 | ||
|
|
f65e55dff4 | ||
|
|
b634018618 | ||
|
|
fa3300f314 | ||
|
|
bd0886a2c0 | ||
|
|
248f304f02 | ||
|
|
dc5f70eab5 | ||
|
|
df8c5623af | ||
|
|
a790c09c91 | ||
|
|
8f35a363d9 | ||
|
|
d2190c2bf3 | ||
|
|
ea10642572 | ||
|
|
547561a568 | ||
|
|
c16d538ce7 | ||
|
|
73d082df2e | ||
|
|
50b8d7272c | ||
|
|
7d11b96f48 | ||
|
|
eab99a1c3d | ||
|
|
19e2fb134d | ||
|
|
f4919e3a25 | ||
|
|
bb700daa4c | ||
|
|
263577d5eb | ||
|
|
63287c0e68 | ||
|
|
c5ed2292bf | ||
|
|
b70670b69f | ||
|
|
9dd97605bc | ||
|
|
e4c5302406 | ||
|
|
bea3d90771 | ||
|
|
785c6064cc | ||
|
|
b214d3786f | ||
|
|
7cf79c302b | ||
|
|
a14c6b6574 | ||
|
|
f1b7094a57 | ||
|
|
0358e376f0 | ||
|
|
b47f7b76b9 | ||
|
|
582cc55cff | ||
|
|
8979579e55 | ||
|
|
0d6e08c541 | ||
|
|
e2daee9a65 | ||
|
|
9cd118ca3d | ||
|
|
cfd5c6155c | ||
|
|
1a5a4bd631 | ||
|
|
63e1a8e1fd | ||
|
|
7055af8221 | ||
|
|
aafe2e1db3 | ||
|
|
118105db43 | ||
|
|
63d04fff69 | ||
|
|
8c9cc920fb | ||
|
|
d09f0adae3 | ||
|
|
3fa9265ce4 | ||
|
|
3a81f60982 | ||
|
|
f2348dd98b | ||
|
|
253c7c2325 | ||
|
|
bb0a762d12 | ||
|
|
8cc86fee60 | ||
|
|
88fb83aa81 | ||
|
|
95b4507c02 | ||
|
|
afdaeba37d | ||
|
|
037199bfe2 | ||
|
|
583fac0a0f | ||
|
|
e8158279ff | ||
|
|
78e98d2611 | ||
|
|
8d14efe818 | ||
|
|
83ba338bd0 | ||
|
|
7c10b25346 | ||
|
|
cb9d16fbe4 | ||
|
|
5d8da864c5 | ||
|
|
85b527ba3d | ||
|
|
1c6efdae34 | ||
|
|
b0ca896d98 | ||
|
|
78a217b94c | ||
|
|
a89d233318 | ||
|
|
c28e1a0237 | ||
|
|
1a95007ec1 | ||
|
|
ed80b4a534 | ||
|
|
4f09df238e | ||
|
|
d9ad3c7cbf | ||
|
|
6ea3f7fe34 | ||
|
|
4c4dc2137c | ||
|
|
4aa4b3e694 | ||
|
|
2604aadb37 | ||
|
|
964d5b9aa4 | ||
|
|
b7adbcab1f | ||
|
|
3435af494f | ||
|
|
41c627379c | ||
|
|
e54df2226f | ||
|
|
dfa395f6ff | ||
|
|
b1febde3e9 | ||
|
|
193049af19 | ||
|
|
4a0bab0fa3 | ||
|
|
9243b0cb9d | ||
|
|
fc9ba323c4 | ||
|
|
d0689c81bb | ||
|
|
02a84385a0 | ||
|
|
a4889a0f2e | ||
|
|
f29f07aabd | ||
|
|
188e28efd7 | ||
|
|
2df48924cc | ||
|
|
9fc6796d2a | ||
|
|
9fc8a52142 | ||
|
|
3a21861580 | ||
|
|
1dbffd48ea | ||
|
|
22a038e6a2 | ||
|
|
f652372c9a | ||
|
|
ad1fc3b71a | ||
|
|
2b40a5ac62 | ||
|
|
ca3388cf5a | ||
|
|
caa8896a8a | ||
|
|
d13aa3954d | ||
|
|
f64539fb76 | ||
|
|
d56ebd7d7b | ||
|
|
3edfe7d0ee | ||
|
|
7f77edadb3 | ||
|
|
a9511dfbe5 | ||
|
|
064e7aa1bb | ||
|
|
46814f88d9 | ||
|
|
4a19802d0c | ||
|
|
1e9f98aa51 | ||
|
|
11e24d53a1 | ||
|
|
0f509f8336 | ||
|
|
a6ed2c84ac | ||
|
|
a1958aad56 | ||
|
|
672699613e | ||
|
|
645d5bdbc5 | ||
|
|
9af2bbffde | ||
|
|
fcd544cc10 | ||
|
|
1e3bc0caa0 | ||
|
|
8227e8795b | ||
|
|
790b3bcdc6 | ||
|
|
d6e6458f68 | ||
|
|
a54b6703c0 | ||
|
|
8e6266136d | ||
|
|
5c22a1bdf5 | ||
|
|
9794ebf88c | ||
|
|
68394eed93 | ||
|
|
753b4b6cc8 | ||
|
|
a9c1b9f138 | ||
|
|
5af144522a | ||
|
|
483e0cadfb | ||
|
|
4b818056cf | ||
|
|
b956e5f1d9 | ||
|
|
37d7cb8565 | ||
|
|
2b8e206fec | ||
|
|
a869b854fa | ||
|
|
81f5efe39a | ||
|
|
69dde0462b | ||
|
|
7628bcac01 | ||
|
|
75f0bbe6e8 | ||
|
|
478bf4dbdd | ||
|
|
e0f67baf2d | ||
|
|
b14d3df3d2 | ||
|
|
24e58ee70c | ||
|
|
9b1a40dfc3 | ||
|
|
e4b078cff7 | ||
|
|
3bd7ca9961 | ||
|
|
f83aca65b7 | ||
|
|
aebafad41e | ||
|
|
26746ce316 | ||
|
|
dac6efb43d | ||
|
|
8880f4824c | ||
|
|
cb0c576bdd | ||
|
|
3a591c43fe | ||
|
|
db66eca958 | ||
|
|
f2767452e6 | ||
|
|
916faf0a48 | ||
|
|
f36e4e9a78 | ||
|
|
fdf8b5eb71 | ||
|
|
de7ec7f1b7 | ||
|
|
3c8a0bdff4 | ||
|
|
9e8ba27dcd | ||
|
|
719a8fd102 | ||
|
|
3a22e917de | ||
|
|
a9af2c9e62 | ||
|
|
31e99cebe7 | ||
|
|
a5b209470c | ||
|
|
e9a571b2a1 | ||
|
|
8bf83f42ea | ||
|
|
522566ea80 | ||
|
|
297af47c89 | ||
|
|
faa354f5ca | ||
|
|
1529ab965a | ||
|
|
605f330e69 | ||
|
|
f0909bdc8f | ||
|
|
c13e7e621d | ||
|
|
ad071f8017 | ||
|
|
c058d8b9cd | ||
|
|
1d8871a092 | ||
|
|
16953c2064 | ||
|
|
6b14f7c224 | ||
|
|
130c623be7 | ||
|
|
47c9895d59 | ||
|
|
ba403331c5 | ||
|
|
e82e89d1b0 | ||
|
|
83a4ebfedc | ||
|
|
9916d0e547 | ||
|
|
31c4a37e37 | ||
|
|
08219f0cee | ||
|
|
c4993e1e5c | ||
|
|
6064bea3db | ||
|
|
98978fc827 | ||
|
|
16430acc1f | ||
|
|
320c110b33 | ||
|
|
dbe33bbfc5 | ||
|
|
b5c3253b49 | ||
|
|
5cc90db7d0 | ||
|
|
f427e5efc7 | ||
|
|
e48802ad29 | ||
|
|
13c4dfcabd | ||
|
|
1abde9c8b0 | ||
|
|
4f555e2232 | ||
|
|
642ba2e92c | ||
|
|
089ac908b7 | ||
|
|
0d3fd2ef30 | ||
|
|
e98119496a | ||
|
|
bdfcbf496b | ||
|
|
dba8da4800 | ||
|
|
60c0f40250 | ||
|
|
e02771a5f2 | ||
|
|
f96f796f71 | ||
|
|
a9fa178f86 | ||
|
|
53355bdb24 | ||
|
|
f05c99d89f | ||
|
|
b49230ab8d | ||
|
|
78856a3dab | ||
|
|
1e5e13ed81 | ||
|
|
64270b9778 | ||
|
|
e312c5c2a7 | ||
|
|
1a5fd3e052 | ||
|
|
5a7e54cf72 | ||
|
|
39f8a62703 | ||
|
|
46be3f2bf1 | ||
|
|
258b46f4dc | ||
|
|
80da21dab4 | ||
|
|
bb0e4d7126 | ||
|
|
5276a4a873 | ||
|
|
a1ae0c8609 | ||
|
|
a90c1aeafe | ||
|
|
ff388a8d2d | ||
|
|
5346fb94bb | ||
|
|
a4f6d46118 | ||
|
|
7f5f4d60b7 | ||
|
|
ffccb233e5 | ||
|
|
fba0c1aafe | ||
|
|
774f2ded94 | ||
|
|
85af942d64 | ||
|
|
8413787efc | ||
|
|
dde57452aa | ||
|
|
cf409800be | ||
|
|
18270dd9f3 | ||
|
|
d4c25c571b | ||
|
|
5248b79506 | ||
|
|
abe0ebbf02 | ||
|
|
0852f5595e | ||
|
|
cb3cafa14d | ||
|
|
202fb93799 | ||
|
|
7b87d2ef83 | ||
|
|
70fd2b1f33 | ||
|
|
30faaf13ed | ||
|
|
41be8632d3 | ||
|
|
bee01dc1be | ||
|
|
12f71e01d0 | ||
|
|
3a72deacab | ||
|
|
fc8314e810 | ||
|
|
11dffe950e | ||
|
|
6f45928a73 | ||
|
|
afb7faa6fa | ||
|
|
6aa56f92fe | ||
|
|
4fe4257c69 | ||
|
|
a5e75c5a21 | ||
|
|
4482fdd63f | ||
|
|
253bd8559b | ||
|
|
6a099fba66 | ||
|
|
a21f3c6cdd | ||
|
|
8f66458598 | ||
|
|
6472f9410e | ||
|
|
8957b3a694 | ||
|
|
1ffd526554 | ||
|
|
fcc0229087 | ||
|
|
b071c9d079 | ||
|
|
851b48e4a3 | ||
|
|
708abb1ab1 | ||
|
|
370d3e0917 | ||
|
|
b51fe0dcc3 | ||
|
|
70d205c447 | ||
|
|
8149be551e | ||
|
|
ba3df646c0 | ||
|
|
1b6f8d463f | ||
|
|
731fa9c236 | ||
|
|
72cb5328ee | ||
|
|
fc39553714 | ||
|
|
d9d67317b1 | ||
|
|
fb5c01c073 | ||
|
|
f4584af42c | ||
|
|
172aa7a93c | ||
|
|
5053a29bc0 | ||
|
|
f322b32e0e | ||
|
|
9cdaed9860 | ||
|
|
dacce1b1fa | ||
|
|
f26f3b44bc | ||
|
|
c5ecbfc756 | ||
|
|
3799ac8973 | ||
|
|
86182afa7f | ||
|
|
4807c6e756 | ||
|
|
a84d07e312 | ||
|
|
88beddfa91 | ||
|
|
1b0aab2ce9 | ||
|
|
9ead49641d | ||
|
|
e1862cd36f | ||
|
|
2c025f23db | ||
|
|
9dfcd47ec8 | ||
|
|
203ecaf85b | ||
|
|
c967f0b0fe | ||
|
|
dfc04e6677 | ||
|
|
42ea3c95e0 | ||
|
|
d4970b35ac | ||
|
|
dd8286bce1 | ||
|
|
093a9031dc | ||
|
|
80a18fe2fa | ||
|
|
fe1411bba1 | ||
|
|
455ac5435d | ||
|
|
4a2b91220a | ||
|
|
a1e0885930 | ||
|
|
7ae09120ed | ||
|
|
42c25d901c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
|
/node_modules/
|
||||||
|
/npm-debug.log
|
||||||
tmp/
|
tmp/
|
||||||
|
|||||||
9
.travis.yml
Normal file
9
.travis.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
language: node_js
|
||||||
|
before_install: "npm install -g npm"
|
||||||
|
node_js:
|
||||||
|
- "0.12"
|
||||||
|
- "0.10"
|
||||||
|
- "4"
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
sudo: false
|
||||||
29
LICENSE
Normal file
29
LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
UglifyJS is released under the BSD license:
|
||||||
|
|
||||||
|
Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
682
README.md
682
README.md
@@ -1,15 +1,39 @@
|
|||||||
UglifyJS 2
|
UglifyJS 2
|
||||||
==========
|
==========
|
||||||
|
[](https://travis-ci.org/mishoo/UglifyJS2)
|
||||||
|
|
||||||
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
||||||
|
|
||||||
For now this page documents the command line utility. More advanced
|
This page documents the command line utility. For
|
||||||
API documentation will be made available later.
|
[API and internals documentation see my website](http://lisperator.net/uglifyjs/).
|
||||||
|
There's also an
|
||||||
|
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
|
||||||
|
Chrome and probably Safari).
|
||||||
|
|
||||||
|
Install
|
||||||
|
-------
|
||||||
|
|
||||||
|
First make sure you have installed the latest version of [node.js](http://nodejs.org/)
|
||||||
|
(You may need to restart your computer after this step).
|
||||||
|
|
||||||
|
From NPM for use as a command line app:
|
||||||
|
|
||||||
|
npm install uglify-js -g
|
||||||
|
|
||||||
|
From NPM for programmatic use:
|
||||||
|
|
||||||
|
npm install uglify-js
|
||||||
|
|
||||||
|
From Git:
|
||||||
|
|
||||||
|
git clone git://github.com/mishoo/UglifyJS2.git
|
||||||
|
cd UglifyJS2
|
||||||
|
npm link .
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
uglifyjs2 [input files] [options]
|
uglifyjs [input files] [options]
|
||||||
|
|
||||||
UglifyJS2 can take multiple input files. It's recommended that you pass the
|
UglifyJS2 can take multiple input files. It's recommended that you pass the
|
||||||
input files first, then pass the options. UglifyJS will parse input files
|
input files first, then pass the options. UglifyJS will parse input files
|
||||||
@@ -20,40 +44,101 @@ variable/function declared in another file will be matched properly.
|
|||||||
If you want to read from STDIN instead, pass a single dash instead of input
|
If you want to read from STDIN instead, pass a single dash instead of input
|
||||||
files.
|
files.
|
||||||
|
|
||||||
|
If you wish to pass your options before the input files, separate the two with
|
||||||
|
a double dash to prevent input files being used as option arguments:
|
||||||
|
|
||||||
|
uglifyjs --compress --mangle -- input.js
|
||||||
|
|
||||||
The available options are:
|
The available options are:
|
||||||
|
|
||||||
--source-map Specify an output file where to generate source map.
|
```
|
||||||
[string]
|
--source-map Specify an output file where to generate source
|
||||||
--source-map-root The path to the original source to be included in the
|
map.
|
||||||
source map. [string]
|
--source-map-root The path to the original source to be included
|
||||||
--in-source-map Input source map, useful if you're compressing JS that was
|
in the source map.
|
||||||
generated from some other original code.
|
--source-map-url The path to the source map to be added in //#
|
||||||
-p, --prefix Skip prefix for original filenames that appear in source
|
sourceMappingURL. Defaults to the value passed
|
||||||
maps. For example -p 3 will drop 3 directories from file
|
with --source-map.
|
||||||
names and ensure they are relative paths.
|
--source-map-include-sources Pass this flag if you want to include the
|
||||||
|
content of source files in the source map as
|
||||||
|
sourcesContent property.
|
||||||
|
--in-source-map Input source map, useful if you're compressing
|
||||||
|
JS that was generated from some other original
|
||||||
|
code.
|
||||||
|
--screw-ie8 Pass this flag if you don't care about full
|
||||||
|
compliance with Internet Explorer 6-8 quirks
|
||||||
|
(by default UglifyJS will try to be IE-proof).
|
||||||
|
--expr Parse a single expression, rather than a
|
||||||
|
program (for parsing JSON)
|
||||||
|
-p, --prefix Skip prefix for original filenames that appear
|
||||||
|
in source maps. For example -p 3 will drop 3
|
||||||
|
directories from file names and ensure they are
|
||||||
|
relative paths. You can also specify -p
|
||||||
|
relative, which will make UglifyJS figure out
|
||||||
|
itself the relative paths between original
|
||||||
|
sources, the source map and the output file.
|
||||||
-o, --output Output file (default STDOUT).
|
-o, --output Output file (default STDOUT).
|
||||||
-b, --beautify Beautify output/specify output options. [string]
|
-b, --beautify Beautify output/specify output options.
|
||||||
-m, --mangle Mangle names/pass mangler options. [string]
|
-m, --mangle Mangle names/pass mangler options.
|
||||||
-r, --reserved Reserved names to exclude from mangling.
|
-r, --reserved Reserved names to exclude from mangling.
|
||||||
-c, --compress Enable compressor/pass compressor options. Pass options
|
-c, --compress Enable compressor/pass compressor options. Pass
|
||||||
like -c hoist_vars=false,if_return=false. Use -c with no
|
options like -c
|
||||||
argument to use the default compression options. [string]
|
hoist_vars=false,if_return=false. Use -c with
|
||||||
-d, --define Global definitions [string]
|
no argument to use the default compression
|
||||||
--comments Preserve copyright comments in the output. By default this
|
options.
|
||||||
works like Google Closure, keeping JSDoc-style comments
|
-d, --define Global definitions
|
||||||
that contain "@license" or "@preserve". You can optionally
|
-e, --enclose Embed everything in a big function, with a
|
||||||
pass one of the following arguments to this flag:
|
configurable parameter/argument list.
|
||||||
|
--comments Preserve copyright comments in the output. By
|
||||||
|
default this works like Google Closure, keeping
|
||||||
|
JSDoc-style comments that contain "@license" or
|
||||||
|
"@preserve". You can optionally pass one of the
|
||||||
|
following arguments to this flag:
|
||||||
- "all" to keep all comments
|
- "all" to keep all comments
|
||||||
- a valid JS regexp (needs to start with a slash) to keep
|
- a valid JS regexp (needs to start with a
|
||||||
only comments that match.
|
slash) to keep only comments that match.
|
||||||
Note that currently not *all* comments can be kept when
|
Note that currently not *all* comments can be
|
||||||
compression is on, because of dead code removal or
|
kept when compression is on, because of dead
|
||||||
cascading statements into sequences. [string]
|
code removal or cascading statements into
|
||||||
--stats Display operations run time on STDERR. [boolean]
|
sequences.
|
||||||
--acorn Use Acorn for parsing. [boolean]
|
--preamble Preamble to prepend to the output. You can use
|
||||||
--spidermonkey Assume input fles are SpiderMonkey AST format (as JSON).
|
this to insert a comment, for example for
|
||||||
[boolean]
|
licensing information. This will not be
|
||||||
-v, --verbose Verbose [boolean]
|
parsed, but the source map will adjust for its
|
||||||
|
presence.
|
||||||
|
--stats Display operations run time on STDERR.
|
||||||
|
--acorn Use Acorn for parsing.
|
||||||
|
--spidermonkey Assume input files are SpiderMonkey AST format
|
||||||
|
(as JSON).
|
||||||
|
--self Build itself (UglifyJS2) as a library (implies
|
||||||
|
--wrap=UglifyJS --export-all)
|
||||||
|
--wrap Embed everything in a big function, making the
|
||||||
|
“exports” and “global” variables available. You
|
||||||
|
need to pass an argument to this option to
|
||||||
|
specify the name that your module will take
|
||||||
|
when included in, say, a browser.
|
||||||
|
--export-all Only used when --wrap, this tells UglifyJS to
|
||||||
|
add code to automatically export all globals.
|
||||||
|
--lint Display some scope warnings
|
||||||
|
-v, --verbose Verbose
|
||||||
|
-V, --version Print version number and exit.
|
||||||
|
--noerr Don't throw an error for unknown options in -c,
|
||||||
|
-b or -m.
|
||||||
|
--bare-returns Allow return outside of functions. Useful when
|
||||||
|
minifying CommonJS modules and Userscripts that
|
||||||
|
may be anonymous function wrapped (IIFE) by the
|
||||||
|
.user.js engine `caller`.
|
||||||
|
--keep-fnames Do not mangle/drop function names. Useful for
|
||||||
|
code relying on Function.prototype.name.
|
||||||
|
--reserved-file File containing reserved names
|
||||||
|
--reserve-domprops Make (most?) DOM properties reserved for
|
||||||
|
--mangle-props
|
||||||
|
--mangle-props Mangle property names
|
||||||
|
--mangle-regex Only mangle property names matching the regex
|
||||||
|
--name-cache File to hold mangled names mappings
|
||||||
|
--pure-funcs List of functions that can be safely removed if
|
||||||
|
their return value is not used [array]
|
||||||
|
```
|
||||||
|
|
||||||
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
||||||
goes to STDOUT.
|
goes to STDOUT.
|
||||||
@@ -73,7 +158,7 @@ map.
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
uglifyjs2 /home/doe/work/foo/src/js/file1.js \
|
uglifyjs /home/doe/work/foo/src/js/file1.js \
|
||||||
/home/doe/work/foo/src/js/file2.js \
|
/home/doe/work/foo/src/js/file2.js \
|
||||||
-o foo.min.js \
|
-o foo.min.js \
|
||||||
--source-map foo.min.js.map \
|
--source-map foo.min.js.map \
|
||||||
@@ -104,21 +189,95 @@ input files from the command line.
|
|||||||
|
|
||||||
## Mangler options
|
## Mangler options
|
||||||
|
|
||||||
To enable the mangler you need to pass `--mangle` (`-m`). Optionally you
|
To enable the mangler you need to pass `--mangle` (`-m`). The following
|
||||||
can pass `-m sort` (we'll possibly have other flags in the future) in order
|
(comma-separated) options are supported:
|
||||||
to assign shorter names to most frequently used variables. This saves a few
|
|
||||||
hundred bytes on jQuery before gzip, but the output is _bigger_ after gzip
|
- `sort` — to assign shorter names to most frequently used variables. This
|
||||||
(and seems to happen for other libraries I tried it on) therefore it's not
|
saves a few hundred bytes on jQuery before gzip, but the output is
|
||||||
enabled by default.
|
_bigger_ after gzip (and seems to happen for other libraries I tried it
|
||||||
|
on) therefore it's not enabled by default.
|
||||||
|
|
||||||
|
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||||
|
default).
|
||||||
|
|
||||||
|
- `eval` — mangle names visible in scopes where `eval` or `with` are used
|
||||||
|
(disabled by default).
|
||||||
|
|
||||||
When mangling is enabled but you want to prevent certain names from being
|
When mangling is enabled but you want to prevent certain names from being
|
||||||
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
||||||
comma-separated list of names. For example:
|
comma-separated list of names. For example:
|
||||||
|
|
||||||
uglifyjs2 ... -m -r '$,require,exports'
|
uglifyjs ... -m -r '$,require,exports'
|
||||||
|
|
||||||
to prevent the `require`, `exports` and `$` names from being changed.
|
to prevent the `require`, `exports` and `$` names from being changed.
|
||||||
|
|
||||||
|
### Mangling property names (`--mangle-props`)
|
||||||
|
|
||||||
|
**Note:** this will probably break your code. Mangling property names is a
|
||||||
|
separate step, different from variable name mangling. Pass
|
||||||
|
`--mangle-props`. It will mangle all properties that are seen in some
|
||||||
|
object literal, or that are assigned to. For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var x = {
|
||||||
|
foo: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
x.bar = 2;
|
||||||
|
x["baz"] = 3;
|
||||||
|
x[condition ? "moo" : "boo"] = 4;
|
||||||
|
console.log(x.something());
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
|
||||||
|
with single characters, while `something()` will be left as is.
|
||||||
|
|
||||||
|
In order for this to be of any use, we should avoid mangling standard JS
|
||||||
|
names. For instance, if your code would contain `x.length = 10`, then
|
||||||
|
`length` becomes a candidate for mangling and it will be mangled throughout
|
||||||
|
the code, regardless if it's being used as part of your own objects or
|
||||||
|
accessing an array's length. To avoid that, you can use `--reserved-file`
|
||||||
|
to pass a filename that should contain the names to be excluded from
|
||||||
|
mangling. This file can be used both for excluding variable names and
|
||||||
|
property names. It could look like this, for example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"vars": [ "define", "require", ... ],
|
||||||
|
"props": [ "length", "prototype", ... ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`--reserved-file` can be an array of file names (either a single
|
||||||
|
comma-separated argument, or you can pass multiple `--reserved-file`
|
||||||
|
arguments) — in this case it will exclude names from all those files.
|
||||||
|
|
||||||
|
A default exclusion file is provided in `tools/domprops.json` which should
|
||||||
|
cover most standard JS and DOM properties defined in various browsers. Pass
|
||||||
|
`--reserve-domprops` to read that in.
|
||||||
|
|
||||||
|
You can also use a regular expression to define which property names should be
|
||||||
|
mangled. For example, `--mangle-regex="/^_/"` will only mangle property names
|
||||||
|
that start with an underscore.
|
||||||
|
|
||||||
|
When you compress multiple files using this option, in order for them to
|
||||||
|
work together in the end we need to ensure somehow that one property gets
|
||||||
|
mangled to the same name in all of them. For this, pass `--name-cache
|
||||||
|
filename.json` and UglifyJS will maintain these mappings in a file which can
|
||||||
|
then be reused. It should be initially empty. Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
rm -f /tmp/cache.json # start fresh
|
||||||
|
uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
|
||||||
|
uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, `part1.js` and `part2.js` will be consistent with each other in terms
|
||||||
|
of mangled property names.
|
||||||
|
|
||||||
|
Using the name cache is not necessary if you compress all your files in a
|
||||||
|
single call to UglifyJS.
|
||||||
|
|
||||||
## Compressor options
|
## Compressor options
|
||||||
|
|
||||||
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
|
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
|
||||||
@@ -126,46 +285,110 @@ you can pass a comma-separated list of options. Options are in the form
|
|||||||
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
|
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
|
||||||
to set `true`; it's effectively a shortcut for `foo=true`).
|
to set `true`; it's effectively a shortcut for `foo=true`).
|
||||||
|
|
||||||
The defaults should be tuned for maximum compression on most code. Here are
|
|
||||||
the available options (all are `true` by default, except `hoist_vars`):
|
|
||||||
|
|
||||||
- `sequences` -- join consecutive simple statements using the comma operator
|
- `sequences` -- join consecutive simple statements using the comma operator
|
||||||
|
|
||||||
- `properties` -- rewrite property access using the dot notation, for
|
- `properties` -- rewrite property access using the dot notation, for
|
||||||
example `foo["bar"] → foo.bar`
|
example `foo["bar"] → foo.bar`
|
||||||
- `dead-code` -- remove unreachable code
|
|
||||||
- `drop-debugger` -- remove `debugger;` statements
|
- `dead_code` -- remove unreachable code
|
||||||
- `unsafe` -- apply "unsafe" transformations (discussion below)
|
|
||||||
|
- `drop_debugger` -- remove `debugger;` statements
|
||||||
|
|
||||||
|
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
|
||||||
|
|
||||||
- `conditionals` -- apply optimizations for `if`-s and conditional
|
- `conditionals` -- apply optimizations for `if`-s and conditional
|
||||||
expressions
|
expressions
|
||||||
|
|
||||||
- `comparisons` -- apply certain optimizations to binary nodes, for example:
|
- `comparisons` -- apply certain optimizations to binary nodes, for example:
|
||||||
`!(a <= b) → a > b` (only when `unsafe`), attempts to negate binary nodes,
|
`!(a <= b) → a > b` (only when `unsafe`), attempts to negate binary nodes,
|
||||||
e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
|
e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
|
||||||
|
|
||||||
- `evaluate` -- attempt to evaluate constant expressions
|
- `evaluate` -- attempt to evaluate constant expressions
|
||||||
|
|
||||||
- `booleans` -- various optimizations for boolean context, for example `!!a
|
- `booleans` -- various optimizations for boolean context, for example `!!a
|
||||||
? b : c → a ? b : c`
|
? b : c → a ? b : c`
|
||||||
|
|
||||||
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
|
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
|
||||||
statically determine the condition
|
statically determine the condition
|
||||||
|
|
||||||
- `unused` -- drop unreferenced functions and variables
|
- `unused` -- drop unreferenced functions and variables
|
||||||
- `hoist-funs` -- hoist function declarations
|
|
||||||
- `hoist-vars` -- hoist `var` declarations (this is `false` by default
|
- `hoist_funs` -- hoist function declarations
|
||||||
because it seems to increase the size of the output in general)
|
|
||||||
- `if-return` -- optimizations for if/return and if/continue
|
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
|
||||||
- `join-vars` -- join consecutive `var` statements
|
by default because it seems to increase the size of the output in general)
|
||||||
|
|
||||||
|
- `if_return` -- optimizations for if/return and if/continue
|
||||||
|
|
||||||
|
- `join_vars` -- join consecutive `var` statements
|
||||||
|
|
||||||
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
|
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
|
||||||
and `x = something(), x` into `x = something()`
|
and `x = something(), x` into `x = something()`
|
||||||
|
|
||||||
|
- `collapse_vars` -- default `false`. Collapse single-use `var` and `const`
|
||||||
|
definitions when possible.
|
||||||
|
|
||||||
- `warnings` -- display warnings when dropping unreachable code or unused
|
- `warnings` -- display warnings when dropping unreachable code or unused
|
||||||
declarations etc.
|
declarations etc.
|
||||||
|
|
||||||
|
- `negate_iife` -- negate "Immediately-Called Function Expressions"
|
||||||
|
where the return value is discarded, to avoid the parens that the
|
||||||
|
code generator would insert.
|
||||||
|
|
||||||
|
- `pure_getters` -- the default is `false`. If you pass `true` for
|
||||||
|
this, UglifyJS will assume that object property access
|
||||||
|
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
|
||||||
|
|
||||||
|
- `pure_funcs` -- default `null`. You can pass an array of names and
|
||||||
|
UglifyJS will assume that those functions do not produce side
|
||||||
|
effects. DANGER: will not check if the name is redefined in scope.
|
||||||
|
An example case here, for instance `var q = Math.floor(a/b)`. If
|
||||||
|
variable `q` is not used elsewhere, UglifyJS will drop it, but will
|
||||||
|
still keep the `Math.floor(a/b)`, not knowing what it does. You can
|
||||||
|
pass `pure_funcs: [ 'Math.floor' ]` to let it know that this
|
||||||
|
function won't produce any side effect, in which case the whole
|
||||||
|
statement would get discarded. The current implementation adds some
|
||||||
|
overhead (compression will be slower).
|
||||||
|
|
||||||
|
- `drop_console` -- default `false`. Pass `true` to discard calls to
|
||||||
|
`console.*` functions.
|
||||||
|
|
||||||
|
- `keep_fargs` -- default `true`. Prevents the
|
||||||
|
compressor from discarding unused function arguments. You need this
|
||||||
|
for code which relies on `Function.length`.
|
||||||
|
|
||||||
|
- `keep_fnames` -- default `false`. Pass `true` to prevent the
|
||||||
|
compressor from mangling/discarding function names. Useful for code relying on
|
||||||
|
`Function.prototype.name`.
|
||||||
|
|
||||||
|
|
||||||
|
### The `unsafe` option
|
||||||
|
|
||||||
|
It enables some transformations that *might* break code logic in certain
|
||||||
|
contrived cases, but should be fine for most code. You might want to try it
|
||||||
|
on your own code, it should reduce the minified size. Here's what happens
|
||||||
|
when this flag is on:
|
||||||
|
|
||||||
|
- `new Array(1, 2, 3)` or `Array(1, 2, 3)` → `[ 1, 2, 3 ]`
|
||||||
|
- `new Object()` → `{}`
|
||||||
|
- `String(exp)` or `exp.toString()` → `"" + exp`
|
||||||
|
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
|
||||||
|
- `typeof foo == "undefined"` → `foo === void 0`
|
||||||
|
- `void 0` → `undefined` (if there is a variable named "undefined" in
|
||||||
|
scope; we do it because the variable name will be mangled, typically
|
||||||
|
reduced to a single character)
|
||||||
|
|
||||||
### Conditional compilation
|
### Conditional compilation
|
||||||
|
|
||||||
You can use the `--define` (`-d`) switch in order to declare global
|
You can use the `--define` (`-d`) switch in order to declare global
|
||||||
variables that UglifyJS will assume to be constants (unless defined in
|
variables that UglifyJS will assume to be constants (unless defined in
|
||||||
scope). For example if you pass `--define DEBUG=false` then, coupled with
|
scope). For example if you pass `--define DEBUG=false` then, coupled with
|
||||||
dead code removal UglifyJS will discard the following from the output:
|
dead code removal UglifyJS will discard the following from the output:
|
||||||
|
```javascript
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.log("debug stuff");
|
console.log("debug stuff");
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
UglifyJS will warn about the condition being always false and about dropping
|
UglifyJS will warn about the condition being always false and about dropping
|
||||||
unreachable code; for now there is no option to turn off only this specific
|
unreachable code; for now there is no option to turn off only this specific
|
||||||
@@ -174,20 +397,24 @@ warning, you can pass `warnings=false` to turn off *all* warnings.
|
|||||||
Another way of doing that is to declare your globals as constants in a
|
Another way of doing that is to declare your globals as constants in a
|
||||||
separate file and include it into the build. For example you can have a
|
separate file and include it into the build. For example you can have a
|
||||||
`build/defines.js` file with the following:
|
`build/defines.js` file with the following:
|
||||||
|
```javascript
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
const PRODUCTION = true;
|
const PRODUCTION = true;
|
||||||
|
// Alternative for environments that don't support `const`
|
||||||
|
/** @const */ var STAGING = false;
|
||||||
// etc.
|
// etc.
|
||||||
|
```
|
||||||
|
|
||||||
and build your code like this:
|
and build your code like this:
|
||||||
|
|
||||||
uglifyjs2 build/defines.js js/foo.js js/bar.js... -c
|
uglifyjs build/defines.js js/foo.js js/bar.js... -c
|
||||||
|
|
||||||
UglifyJS will notice the constants and, since they cannot be altered, it
|
UglifyJS will notice the constants and, since they cannot be altered, it
|
||||||
will evaluate references to them to the value itself and drop unreachable
|
will evaluate references to them to the value itself and drop unreachable
|
||||||
code as usual. The possible downside of this approach is that the build
|
code as usual. The build will contain the `const` declarations if you use
|
||||||
will contain the `const` declarations.
|
them. If you are targeting < ES6 environments, use `/** @const */ var`.
|
||||||
|
|
||||||
|
<a name="codegen-options"></a>
|
||||||
## Beautifier options
|
## Beautifier options
|
||||||
|
|
||||||
The code generator tries to output shortest code possible by default. In
|
The code generator tries to output shortest code possible by default. In
|
||||||
@@ -213,25 +440,37 @@ can pass additional arguments that control the code output:
|
|||||||
It doesn't work very well currently, but it does make the code generated
|
It doesn't work very well currently, but it does make the code generated
|
||||||
by UglifyJS more readable.
|
by UglifyJS more readable.
|
||||||
- `max-line-len` (default 32000) -- maximum line length (for uglified code)
|
- `max-line-len` (default 32000) -- maximum line length (for uglified code)
|
||||||
- `ie-proof` (default `true`) -- generate “IE-proof” code (for now this
|
|
||||||
means add brackets around the do/while in code like this: `if (foo) do
|
|
||||||
something(); while (bar); else ...`.
|
|
||||||
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
||||||
`do`, `while` or `with` statements, even if their body is a single
|
`do`, `while` or `with` statements, even if their body is a single
|
||||||
statement.
|
statement.
|
||||||
|
- `semicolons` (default `true`) -- separate statements with semicolons. If
|
||||||
|
you pass `false` then whenever possible we will use a newline instead of a
|
||||||
|
semicolon, leading to more readable output of uglified code (size before
|
||||||
|
gzip could be smaller; size after gzip insignificantly larger).
|
||||||
|
- `preamble` (default `null`) -- when passed it must be a string and
|
||||||
|
it will be prepended to the output literally. The source map will
|
||||||
|
adjust for this text. Can be used to insert a comment containing
|
||||||
|
licensing information, for example.
|
||||||
|
- `quote_style` (default `0`) -- preferred quote style for strings (affects
|
||||||
|
quoted property names and directives as well):
|
||||||
|
- `0` -- prefers double quotes, switches to single quotes when there are
|
||||||
|
more double quotes in the string itself.
|
||||||
|
- `1` -- always use single quotes
|
||||||
|
- `2` -- always use double quotes
|
||||||
|
- `3` -- always use the original quotes
|
||||||
|
|
||||||
### Keeping copyright notices or other comments
|
### Keeping copyright notices or other comments
|
||||||
|
|
||||||
You can pass `--comments` to retain certain comments in the output. By
|
You can pass `--comments` to retain certain comments in the output. By
|
||||||
default it will keep JSDoc-style comments that contain "@preserve" or
|
default it will keep JSDoc-style comments that contain "@preserve",
|
||||||
"@license". You can pass `--comments all` to keep all the comments, or a
|
"@license" or "@cc_on" (conditional compilation for IE). You can pass
|
||||||
valid JavaScript regexp to keep only comments that match this regexp. For
|
`--comments all` to keep all the comments, or a valid JavaScript regexp to
|
||||||
example `--comments '/foo|bar/'` will keep only comments that contain "foo"
|
keep only comments that match this regexp. For example `--comments
|
||||||
or "bar".
|
'/foo|bar/'` will keep only comments that contain "foo" or "bar".
|
||||||
|
|
||||||
Note, however, that there might be situations where comments are lost. For
|
Note, however, that there might be situations where comments are lost. For
|
||||||
example:
|
example:
|
||||||
|
```javascript
|
||||||
function f() {
|
function f() {
|
||||||
/** @preserve Foo Bar */
|
/** @preserve Foo Bar */
|
||||||
function g() {
|
function g() {
|
||||||
@@ -239,13 +478,14 @@ example:
|
|||||||
}
|
}
|
||||||
return something();
|
return something();
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Even though it has "@preserve", the comment will be lost because the inner
|
Even though it has "@preserve", the comment will be lost because the inner
|
||||||
function `g` (which is the AST node to which the comment is attached to) is
|
function `g` (which is the AST node to which the comment is attached to) is
|
||||||
discarded by the compressor as not referenced.
|
discarded by the compressor as not referenced.
|
||||||
|
|
||||||
The safest comments where to place copyright information (or other info that
|
The safest comments where to place copyright information (or other info that
|
||||||
needs to me kept in the output) are comments attached to toplevel nodes.
|
needs to be kept in the output) are comments attached to toplevel nodes.
|
||||||
|
|
||||||
## Support for the SpiderMonkey AST
|
## Support for the SpiderMonkey AST
|
||||||
|
|
||||||
@@ -254,12 +494,12 @@ UglifyJS2 has its own abstract syntax tree format; for
|
|||||||
we can't easily change to using the SpiderMonkey AST internally. However,
|
we can't easily change to using the SpiderMonkey AST internally. However,
|
||||||
UglifyJS now has a converter which can import a SpiderMonkey AST.
|
UglifyJS now has a converter which can import a SpiderMonkey AST.
|
||||||
|
|
||||||
For example [Acorn](https://github.com/marijnh/acorn) is a super-fast parser
|
For example [Acorn][acorn] is a super-fast parser that produces a
|
||||||
that produces a SpiderMonkey AST. It has a small CLI utility that parses
|
SpiderMonkey AST. It has a small CLI utility that parses one file and dumps
|
||||||
one file and dumps the AST in JSON on the standard output. To use UglifyJS
|
the AST in JSON on the standard output. To use UglifyJS to mangle and
|
||||||
to mangle and compress that:
|
compress that:
|
||||||
|
|
||||||
acorn file.js | uglifyjs2 --spidermonkey -m -c
|
acorn file.js | uglifyjs --spidermonkey -m -c
|
||||||
|
|
||||||
The `--spidermonkey` option tells UglifyJS that all input files are not
|
The `--spidermonkey` option tells UglifyJS that all input files are not
|
||||||
JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we
|
JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we
|
||||||
@@ -269,11 +509,303 @@ internal AST.
|
|||||||
### Use Acorn for parsing
|
### Use Acorn for parsing
|
||||||
|
|
||||||
More for fun, I added the `--acorn` option which will use Acorn to do all
|
More for fun, I added the `--acorn` option which will use Acorn to do all
|
||||||
the parsing. If you pass this option, UglifyJS will `require("acorn")`. At
|
the parsing. If you pass this option, UglifyJS will `require("acorn")`.
|
||||||
the time I'm writing this it needs
|
|
||||||
[this commit](https://github.com/mishoo/acorn/commit/17c0d189c7f9ce5447293569036949b5d0a05fef)
|
|
||||||
in Acorn to support multiple input files and properly generate source maps.
|
|
||||||
|
|
||||||
Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
|
Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
|
||||||
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
|
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
|
||||||
in total it's a bit more than just using UglifyJS's own parser.
|
in total it's a bit more than just using UglifyJS's own parser.
|
||||||
|
|
||||||
|
### Using UglifyJS to transform SpiderMonkey AST
|
||||||
|
|
||||||
|
Now you can use UglifyJS as any other intermediate tool for transforming
|
||||||
|
JavaScript ASTs in SpiderMonkey format.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function uglify(ast, options, mangle) {
|
||||||
|
// Conversion from SpiderMonkey AST to internal format
|
||||||
|
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
|
||||||
|
|
||||||
|
// Compression
|
||||||
|
uAST.figure_out_scope();
|
||||||
|
uAST = uAST.transform(UglifyJS.Compressor(options));
|
||||||
|
|
||||||
|
// Mangling (optional)
|
||||||
|
if (mangle) {
|
||||||
|
uAST.figure_out_scope();
|
||||||
|
uAST.compute_char_frequency();
|
||||||
|
uAST.mangle_names();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back-conversion to SpiderMonkey AST
|
||||||
|
return uAST.to_mozilla_ast();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Check out
|
||||||
|
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
|
||||||
|
for details.
|
||||||
|
|
||||||
|
API Reference
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Assuming installation via NPM, you can load UglifyJS in your application
|
||||||
|
like this:
|
||||||
|
```javascript
|
||||||
|
var UglifyJS = require("uglify-js");
|
||||||
|
```
|
||||||
|
|
||||||
|
It exports a lot of names, but I'll discuss here the basics that are needed
|
||||||
|
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
||||||
|
parse, (2) compress, (3) mangle, (4) generate output code.
|
||||||
|
|
||||||
|
### The simple way
|
||||||
|
|
||||||
|
There's a single toplevel function which combines all the steps. If you
|
||||||
|
don't need additional customization, you might want to go with `minify`.
|
||||||
|
Example:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify("/path/to/file.js");
|
||||||
|
console.log(result.code); // minified output
|
||||||
|
// if you need to pass code instead of file name
|
||||||
|
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also compress multiple files:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
|
||||||
|
console.log(result.code);
|
||||||
|
```
|
||||||
|
|
||||||
|
To generate a source map:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
|
outSourceMap: "out.js.map"
|
||||||
|
});
|
||||||
|
console.log(result.code); // minified output
|
||||||
|
console.log(result.map);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the source map is not saved in a file, it's just returned in
|
||||||
|
`result.map`. The value passed for `outSourceMap` is only used to set the
|
||||||
|
`file` attribute in the source map (see [the spec][sm-spec]).
|
||||||
|
|
||||||
|
You can also specify sourceRoot property to be included in source map:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
|
outSourceMap: "out.js.map",
|
||||||
|
sourceRoot: "http://example.com/src"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're compressing compiled JavaScript and have a source map for it, you
|
||||||
|
can use the `inSourceMap` argument:
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify("compiled.js", {
|
||||||
|
inSourceMap: "compiled.js.map",
|
||||||
|
outSourceMap: "minified.js.map"
|
||||||
|
});
|
||||||
|
// same as before, it returns `code` and `map`
|
||||||
|
```
|
||||||
|
|
||||||
|
If your input source map is not in a file, you can pass it in as an object
|
||||||
|
using the `inSourceMap` argument:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var result = UglifyJS.minify("compiled.js", {
|
||||||
|
inSourceMap: JSON.parse(my_source_map_string),
|
||||||
|
outSourceMap: "minified.js.map"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
||||||
|
no sense otherwise).
|
||||||
|
|
||||||
|
Other options:
|
||||||
|
|
||||||
|
- `warnings` (default `false`) — pass `true` to display compressor warnings.
|
||||||
|
|
||||||
|
- `fromString` (default `false`) — if you pass `true` then you can pass
|
||||||
|
JavaScript source code, rather than file names.
|
||||||
|
|
||||||
|
- `mangle` — pass `false` to skip mangling names.
|
||||||
|
|
||||||
|
- `mangleProperties` (default `false`) — pass an object to specify custom
|
||||||
|
mangle property options.
|
||||||
|
|
||||||
|
- `output` (default `null`) — pass an object if you wish to specify
|
||||||
|
additional [output options][codegen]. The defaults are optimized
|
||||||
|
for best compression.
|
||||||
|
|
||||||
|
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
|
||||||
|
Pass an object to specify custom [compressor options][compressor].
|
||||||
|
|
||||||
|
- `parse` (default {}) — pass an object if you wish to specify some
|
||||||
|
additional [parser options][parser]. (not all options available... see below)
|
||||||
|
|
||||||
|
##### mangleProperties options
|
||||||
|
|
||||||
|
- `regex` — Pass a RegExp to only mangle certain names (maps to the `--mange-regex` CLI arguments option)
|
||||||
|
|
||||||
|
We could add more options to `UglifyJS.minify` — if you need additional
|
||||||
|
functionality please suggest!
|
||||||
|
|
||||||
|
### The hard way
|
||||||
|
|
||||||
|
Following there's more detailed API info, in case the `minify` function is
|
||||||
|
too simple for your needs.
|
||||||
|
|
||||||
|
#### The parser
|
||||||
|
```javascript
|
||||||
|
var toplevel_ast = UglifyJS.parse(code, options);
|
||||||
|
```
|
||||||
|
|
||||||
|
`options` is optional and if present it must be an object. The following
|
||||||
|
properties are available:
|
||||||
|
|
||||||
|
- `strict` — disable automatic semicolon insertion and support for trailing
|
||||||
|
comma in arrays and objects
|
||||||
|
- `bare_returns` — Allow return outside of functions. (maps to the
|
||||||
|
`--bare-returns` CLI arguments option and available to `minify` `parse`
|
||||||
|
other options object)
|
||||||
|
- `filename` — the name of the file where this code is coming from
|
||||||
|
- `toplevel` — a `toplevel` node (as returned by a previous invocation of
|
||||||
|
`parse`)
|
||||||
|
|
||||||
|
The last two options are useful when you'd like to minify multiple files and
|
||||||
|
get a single file as the output and a proper source map. Our CLI tool does
|
||||||
|
something like this:
|
||||||
|
```javascript
|
||||||
|
var toplevel = null;
|
||||||
|
files.forEach(function(file){
|
||||||
|
var code = fs.readFileSync(file, "utf8");
|
||||||
|
toplevel = UglifyJS.parse(code, {
|
||||||
|
filename: file,
|
||||||
|
toplevel: toplevel
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
After this, we have in `toplevel` a big AST containing all our files, with
|
||||||
|
each token having proper information about where it came from.
|
||||||
|
|
||||||
|
#### Scope information
|
||||||
|
|
||||||
|
UglifyJS contains a scope analyzer that you need to call manually before
|
||||||
|
compressing or mangling. Basically it augments various nodes in the AST
|
||||||
|
with information about where is a name defined, how many times is a name
|
||||||
|
referenced, if it is a global or not, if a function is using `eval` or the
|
||||||
|
`with` statement etc. I will discuss this some place else, for now what's
|
||||||
|
important to know is that you need to call the following before doing
|
||||||
|
anything with the tree:
|
||||||
|
```javascript
|
||||||
|
toplevel.figure_out_scope()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Compression
|
||||||
|
|
||||||
|
Like this:
|
||||||
|
```javascript
|
||||||
|
var compressor = UglifyJS.Compressor(options);
|
||||||
|
var compressed_ast = toplevel.transform(compressor);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `options` can be missing. Available options are discussed above in
|
||||||
|
“Compressor options”. Defaults should lead to best compression in most
|
||||||
|
scripts.
|
||||||
|
|
||||||
|
The compressor is destructive, so don't rely that `toplevel` remains the
|
||||||
|
original tree.
|
||||||
|
|
||||||
|
#### Mangling
|
||||||
|
|
||||||
|
After compression it is a good idea to call again `figure_out_scope` (since
|
||||||
|
the compressor might drop unused variables / unreachable code and this might
|
||||||
|
change the number of identifiers or their position). Optionally, you can
|
||||||
|
call a trick that helps after Gzip (counting character frequency in
|
||||||
|
non-mangleable words). Example:
|
||||||
|
```javascript
|
||||||
|
compressed_ast.figure_out_scope();
|
||||||
|
compressed_ast.compute_char_frequency();
|
||||||
|
compressed_ast.mangle_names();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Generating output
|
||||||
|
|
||||||
|
AST nodes have a `print` method that takes an output stream. Essentially,
|
||||||
|
to generate code you do this:
|
||||||
|
```javascript
|
||||||
|
var stream = UglifyJS.OutputStream(options);
|
||||||
|
compressed_ast.print(stream);
|
||||||
|
var code = stream.toString(); // this is your minified code
|
||||||
|
```
|
||||||
|
|
||||||
|
or, for a shortcut you can do:
|
||||||
|
```javascript
|
||||||
|
var code = compressed_ast.print_to_string(options);
|
||||||
|
```
|
||||||
|
|
||||||
|
As usual, `options` is optional. The output stream accepts a lot of options,
|
||||||
|
most of them documented above in section “Beautifier options”. The two
|
||||||
|
which we care about here are `source_map` and `comments`.
|
||||||
|
|
||||||
|
#### Keeping comments in the output
|
||||||
|
|
||||||
|
In order to keep certain comments in the output you need to pass the
|
||||||
|
`comments` option. Pass a RegExp or a function. If you pass a RegExp, only
|
||||||
|
those comments whose body matches the regexp will be kept. Note that body
|
||||||
|
means without the initial `//` or `/*`. If you pass a function, it will be
|
||||||
|
called for every comment in the tree and will receive two arguments: the
|
||||||
|
node that the comment is attached to, and the comment token itself.
|
||||||
|
|
||||||
|
The comment token has these properties:
|
||||||
|
|
||||||
|
- `type`: "comment1" for single-line comments or "comment2" for multi-line
|
||||||
|
comments
|
||||||
|
- `value`: the comment body
|
||||||
|
- `pos` and `endpos`: the start/end positions (zero-based indexes) in the
|
||||||
|
original code where this comment appears
|
||||||
|
- `line` and `col`: the line and column where this comment appears in the
|
||||||
|
original code
|
||||||
|
- `file` — the file name of the original file
|
||||||
|
- `nlb` — true if there was a newline before this comment in the original
|
||||||
|
code, or if this comment contains a newline.
|
||||||
|
|
||||||
|
Your function should return `true` to keep the comment, or a falsy value
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
#### Generating a source mapping
|
||||||
|
|
||||||
|
You need to pass the `source_map` argument when calling `print`. It needs
|
||||||
|
to be a `SourceMap` object (which is a thin wrapper on top of the
|
||||||
|
[source-map][source-map] library).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```javascript
|
||||||
|
var source_map = UglifyJS.SourceMap(source_map_options);
|
||||||
|
var stream = UglifyJS.OutputStream({
|
||||||
|
...
|
||||||
|
source_map: source_map
|
||||||
|
});
|
||||||
|
compressed_ast.print(stream);
|
||||||
|
|
||||||
|
var code = stream.toString();
|
||||||
|
var map = source_map.toString(); // json output for your source map
|
||||||
|
```
|
||||||
|
|
||||||
|
The `source_map_options` (optional) can contain the following properties:
|
||||||
|
|
||||||
|
- `file`: the name of the JavaScript output file that this mapping refers to
|
||||||
|
- `root`: the `sourceRoot` property (see the [spec][sm-spec])
|
||||||
|
- `orig`: the "original source map", handy when you compress generated JS
|
||||||
|
and want to map the minified output back to the original code where it
|
||||||
|
came from. It can be simply a string in JSON, or a JSON object containing
|
||||||
|
the original source map.
|
||||||
|
|
||||||
|
[acorn]: https://github.com/ternjs/acorn
|
||||||
|
[source-map]: https://github.com/mozilla/source-map
|
||||||
|
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
|
||||||
|
[codegen]: http://lisperator.net/uglifyjs/codegen
|
||||||
|
[compressor]: http://lisperator.net/uglifyjs/compress
|
||||||
|
[parser]: http://lisperator.net/uglifyjs/parser
|
||||||
|
|||||||
77
bin/extract-props.js
Executable file
77
bin/extract-props.js
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#! /usr/bin/env node
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var U2 = require("../tools/node");
|
||||||
|
var fs = require("fs");
|
||||||
|
var yargs = require("yargs");
|
||||||
|
var ARGS = yargs
|
||||||
|
.describe("o", "Output file")
|
||||||
|
.argv;
|
||||||
|
var files = ARGS._.slice();
|
||||||
|
var output = {
|
||||||
|
vars: {},
|
||||||
|
props: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ARGS.o) try {
|
||||||
|
output = JSON.parse(fs.readFileSync(ARGS.o, "utf8"));
|
||||||
|
} catch(ex) {}
|
||||||
|
|
||||||
|
files.forEach(getProps);
|
||||||
|
|
||||||
|
if (ARGS.o) {
|
||||||
|
fs.writeFileSync(ARGS.o, JSON.stringify(output, null, 2), "utf8");
|
||||||
|
} else {
|
||||||
|
console.log("%s", JSON.stringify(output, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProps(filename) {
|
||||||
|
var code = fs.readFileSync(filename, "utf8");
|
||||||
|
var ast = U2.parse(code);
|
||||||
|
|
||||||
|
ast.walk(new U2.TreeWalker(function(node){
|
||||||
|
if (node instanceof U2.AST_ObjectKeyVal) {
|
||||||
|
add(node.key);
|
||||||
|
}
|
||||||
|
else if (node instanceof U2.AST_ObjectProperty) {
|
||||||
|
add(node.key.name);
|
||||||
|
}
|
||||||
|
else if (node instanceof U2.AST_Dot) {
|
||||||
|
add(node.property);
|
||||||
|
}
|
||||||
|
else if (node instanceof U2.AST_Sub) {
|
||||||
|
addStrings(node.property);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
function addStrings(node) {
|
||||||
|
var out = {};
|
||||||
|
try {
|
||||||
|
(function walk(node){
|
||||||
|
node.walk(new U2.TreeWalker(function(node){
|
||||||
|
if (node instanceof U2.AST_Seq) {
|
||||||
|
walk(node.cdr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof U2.AST_String) {
|
||||||
|
add(node.value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof U2.AST_Conditional) {
|
||||||
|
walk(node.consequent);
|
||||||
|
walk(node.alternative);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw out;
|
||||||
|
}));
|
||||||
|
})(node);
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== out) throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(name) {
|
||||||
|
output.props[name] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
573
bin/uglifyjs
Executable file
573
bin/uglifyjs
Executable file
@@ -0,0 +1,573 @@
|
|||||||
|
#! /usr/bin/env node
|
||||||
|
// -*- js -*-
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var UglifyJS = require("../tools/node");
|
||||||
|
var sys = require("util");
|
||||||
|
var yargs = require("yargs");
|
||||||
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
var async = require("async");
|
||||||
|
var acorn;
|
||||||
|
var ARGS = yargs
|
||||||
|
.usage("$0 input1.js [input2.js ...] [options]\n\
|
||||||
|
Use a single dash to read input from the standard input.\
|
||||||
|
\n\n\
|
||||||
|
NOTE: by default there is no mangling/compression.\n\
|
||||||
|
Without [options] it will simply parse input files and dump the AST\n\
|
||||||
|
with whitespace and comments discarded. To achieve compression and\n\
|
||||||
|
mangling you need to use `-c` and `-m`.\
|
||||||
|
")
|
||||||
|
.describe("source-map", "Specify an output file where to generate source map.")
|
||||||
|
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
||||||
|
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
|
||||||
|
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
|
||||||
|
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
||||||
|
.describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).")
|
||||||
|
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
|
||||||
|
.describe("p", "Skip prefix for original filenames that appear in source maps. \
|
||||||
|
For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
|
||||||
|
You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \
|
||||||
|
the source map and the output file.")
|
||||||
|
.describe("o", "Output file (default STDOUT).")
|
||||||
|
.describe("b", "Beautify output/specify output options.")
|
||||||
|
.describe("m", "Mangle names/pass mangler options.")
|
||||||
|
.describe("r", "Reserved names to exclude from mangling.")
|
||||||
|
.describe("c", "Enable compressor/pass compressor options. \
|
||||||
|
Pass options like -c hoist_vars=false,if_return=false. \
|
||||||
|
Use -c with no argument to use the default compression options.")
|
||||||
|
.describe("d", "Global definitions")
|
||||||
|
.describe("e", "Embed everything in a big function, with a configurable parameter/argument list.")
|
||||||
|
|
||||||
|
.describe("comments", "Preserve copyright comments in the output. \
|
||||||
|
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
|
||||||
|
You can optionally pass one of the following arguments to this flag:\n\
|
||||||
|
- \"all\" to keep all comments\n\
|
||||||
|
- a valid JS regexp (needs to start with a slash) to keep only comments that match.\n\
|
||||||
|
\
|
||||||
|
Note that currently not *all* comments can be kept when compression is on, \
|
||||||
|
because of dead code removal or cascading statements into sequences.")
|
||||||
|
|
||||||
|
.describe("preamble", "Preamble to prepend to the output. You can use this to insert a \
|
||||||
|
comment, for example for licensing information. This will not be \
|
||||||
|
parsed, but the source map will adjust for its presence.")
|
||||||
|
|
||||||
|
.describe("stats", "Display operations run time on STDERR.")
|
||||||
|
.describe("acorn", "Use Acorn for parsing.")
|
||||||
|
.describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
|
||||||
|
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
|
||||||
|
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
|
||||||
|
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
|
||||||
|
.describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
|
||||||
|
.describe("lint", "Display some scope warnings")
|
||||||
|
.describe("v", "Verbose")
|
||||||
|
.describe("V", "Print version number and exit.")
|
||||||
|
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
|
||||||
|
.describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.")
|
||||||
|
.describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.")
|
||||||
|
.describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
|
||||||
|
.describe("reserved-file", "File containing reserved names")
|
||||||
|
.describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
|
||||||
|
.describe("mangle-props", "Mangle property names")
|
||||||
|
.describe("mangle-regex", "Only mangle property names matching the regex")
|
||||||
|
.describe("name-cache", "File to hold mangled names mappings")
|
||||||
|
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
|
||||||
|
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
|
||||||
|
|
||||||
|
.alias("p", "prefix")
|
||||||
|
.alias("o", "output")
|
||||||
|
.alias("v", "verbose")
|
||||||
|
.alias("b", "beautify")
|
||||||
|
.alias("m", "mangle")
|
||||||
|
.alias("c", "compress")
|
||||||
|
.alias("d", "define")
|
||||||
|
.alias("r", "reserved")
|
||||||
|
.alias("V", "version")
|
||||||
|
.alias("e", "enclose")
|
||||||
|
.alias("q", "quotes")
|
||||||
|
|
||||||
|
.string("source-map")
|
||||||
|
.string("source-map-root")
|
||||||
|
.string("source-map-url")
|
||||||
|
.string("b")
|
||||||
|
.string("beautify")
|
||||||
|
.string("m")
|
||||||
|
.string("mangle")
|
||||||
|
.string("c")
|
||||||
|
.string("compress")
|
||||||
|
.string("d")
|
||||||
|
.string("define")
|
||||||
|
.string("e")
|
||||||
|
.string("enclose")
|
||||||
|
.string("comments")
|
||||||
|
.string("wrap")
|
||||||
|
.string("p")
|
||||||
|
.string("prefix")
|
||||||
|
.string("name-cache")
|
||||||
|
.array("reserved-file")
|
||||||
|
.array("pure-funcs")
|
||||||
|
|
||||||
|
.boolean("expr")
|
||||||
|
.boolean("source-map-include-sources")
|
||||||
|
.boolean("screw-ie8")
|
||||||
|
.boolean("export-all")
|
||||||
|
.boolean("self")
|
||||||
|
.boolean("v")
|
||||||
|
.boolean("verbose")
|
||||||
|
.boolean("stats")
|
||||||
|
.boolean("acorn")
|
||||||
|
.boolean("spidermonkey")
|
||||||
|
.boolean("dump-spidermonkey-ast")
|
||||||
|
.boolean("lint")
|
||||||
|
.boolean("V")
|
||||||
|
.boolean("version")
|
||||||
|
.boolean("noerr")
|
||||||
|
.boolean("bare-returns")
|
||||||
|
.boolean("keep-fnames")
|
||||||
|
.boolean("mangle-props")
|
||||||
|
.boolean("reserve-domprops")
|
||||||
|
|
||||||
|
.wrap(80)
|
||||||
|
|
||||||
|
.argv
|
||||||
|
;
|
||||||
|
|
||||||
|
normalize(ARGS);
|
||||||
|
|
||||||
|
if (ARGS.noerr) {
|
||||||
|
UglifyJS.DefaultsError.croak = function(msg, defs) {
|
||||||
|
print_error("WARN: " + msg);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.version || ARGS.V) {
|
||||||
|
var json = require("../package.json");
|
||||||
|
print(json.name + ' ' + json.version);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.ast_help) {
|
||||||
|
var desc = UglifyJS.describe_ast();
|
||||||
|
print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.h || ARGS.help) {
|
||||||
|
print(yargs.help());
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.acorn) {
|
||||||
|
acorn = require("acorn");
|
||||||
|
}
|
||||||
|
|
||||||
|
var COMPRESS = getOptions("c", true);
|
||||||
|
var MANGLE = getOptions("m", true);
|
||||||
|
var BEAUTIFY = getOptions("b", true);
|
||||||
|
var RESERVED = null;
|
||||||
|
|
||||||
|
if (ARGS.reserved_file) ARGS.reserved_file.forEach(function(filename){
|
||||||
|
RESERVED = UglifyJS.readReservedFile(filename, RESERVED);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ARGS.reserve_domprops) {
|
||||||
|
RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.d) {
|
||||||
|
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.pure_funcs) {
|
||||||
|
if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.r) {
|
||||||
|
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RESERVED && MANGLE) {
|
||||||
|
if (!MANGLE.except) MANGLE.except = RESERVED.vars;
|
||||||
|
else MANGLE.except = MANGLE.except.concat(RESERVED.vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readNameCache(key) {
|
||||||
|
return UglifyJS.readNameCache(ARGS.name_cache, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeNameCache(key, cache) {
|
||||||
|
return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractRegex(str) {
|
||||||
|
if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
|
||||||
|
var regex_pos = str.lastIndexOf("/");
|
||||||
|
return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid regular expression: " + str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.quotes === true) {
|
||||||
|
ARGS.quotes = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
var OUTPUT_OPTIONS = {
|
||||||
|
beautify : BEAUTIFY ? true : false,
|
||||||
|
preamble : ARGS.preamble || null,
|
||||||
|
quote_style : ARGS.quotes != null ? ARGS.quotes : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ARGS.screw_ie8) {
|
||||||
|
if (COMPRESS) COMPRESS.screw_ie8 = true;
|
||||||
|
if (MANGLE) MANGLE.screw_ie8 = true;
|
||||||
|
OUTPUT_OPTIONS.screw_ie8 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.keep_fnames) {
|
||||||
|
if (COMPRESS) COMPRESS.keep_fnames = true;
|
||||||
|
if (MANGLE) MANGLE.keep_fnames = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BEAUTIFY)
|
||||||
|
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
|
||||||
|
|
||||||
|
if (ARGS.comments != null) {
|
||||||
|
if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) {
|
||||||
|
try {
|
||||||
|
OUTPUT_OPTIONS.comments = extractRegex(ARGS.comments);
|
||||||
|
} catch (e) {
|
||||||
|
print_error("ERROR: Invalid --comments: " + e.message);
|
||||||
|
}
|
||||||
|
} else if (ARGS.comments == "all") {
|
||||||
|
OUTPUT_OPTIONS.comments = true;
|
||||||
|
} else {
|
||||||
|
OUTPUT_OPTIONS.comments = function(node, comment) {
|
||||||
|
var text = comment.value;
|
||||||
|
var type = comment.type;
|
||||||
|
if (type == "comment2") {
|
||||||
|
// multiline comment
|
||||||
|
return /@preserve|@license|@cc_on/i.test(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var files = ARGS._.slice();
|
||||||
|
|
||||||
|
if (ARGS.self) {
|
||||||
|
if (files.length > 0) {
|
||||||
|
print_error("WARN: Ignoring input files since --self was passed");
|
||||||
|
}
|
||||||
|
files = UglifyJS.FILES;
|
||||||
|
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
|
||||||
|
}
|
||||||
|
|
||||||
|
var ORIG_MAP = ARGS.in_source_map;
|
||||||
|
|
||||||
|
if (ORIG_MAP) {
|
||||||
|
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
|
||||||
|
if (files.length == 0) {
|
||||||
|
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
|
||||||
|
files = [ ORIG_MAP.file ];
|
||||||
|
}
|
||||||
|
if (ARGS.source_map_root == null) {
|
||||||
|
ARGS.source_map_root = ORIG_MAP.sourceRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.length == 0) {
|
||||||
|
files = [ "-" ];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.indexOf("-") >= 0 && ARGS.source_map) {
|
||||||
|
print_error("ERROR: Source map doesn't work with input from STDIN");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.filter(function(el){ return el == "-" }).length > 1) {
|
||||||
|
print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var STATS = {};
|
||||||
|
var OUTPUT_FILE = ARGS.o;
|
||||||
|
var TOPLEVEL = null;
|
||||||
|
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
|
||||||
|
var SOURCES_CONTENT = {};
|
||||||
|
|
||||||
|
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
||||||
|
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
|
||||||
|
root: ARGS.source_map_root,
|
||||||
|
orig: ORIG_MAP,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
|
||||||
|
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex instanceof UglifyJS.DefaultsError) {
|
||||||
|
print_error(ex.msg);
|
||||||
|
print_error("Supported options:");
|
||||||
|
print_error(sys.inspect(ex.defs));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async.eachLimit(files, 1, function (file, cb) {
|
||||||
|
read_whole_file(file, function (err, code) {
|
||||||
|
if (err) {
|
||||||
|
print_error("ERROR: can't read file: " + file);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (ARGS.p != null) {
|
||||||
|
if (P_RELATIVE) {
|
||||||
|
file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
|
||||||
|
} else {
|
||||||
|
var p = parseInt(ARGS.p, 10);
|
||||||
|
if (!isNaN(p)) {
|
||||||
|
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SOURCES_CONTENT[file] = code;
|
||||||
|
time_it("parse", function(){
|
||||||
|
if (ARGS.spidermonkey) {
|
||||||
|
var program = JSON.parse(code);
|
||||||
|
if (!TOPLEVEL) TOPLEVEL = program;
|
||||||
|
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
|
||||||
|
}
|
||||||
|
else if (ARGS.acorn) {
|
||||||
|
TOPLEVEL = acorn.parse(code, {
|
||||||
|
locations : true,
|
||||||
|
sourceFile : file,
|
||||||
|
program : TOPLEVEL
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
TOPLEVEL = UglifyJS.parse(code, {
|
||||||
|
filename : file,
|
||||||
|
toplevel : TOPLEVEL,
|
||||||
|
expression : ARGS.expr,
|
||||||
|
bare_returns : ARGS.bare_returns,
|
||||||
|
});
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||||
|
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
|
||||||
|
print_error(ex.message);
|
||||||
|
print_error(ex.stack);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}, function () {
|
||||||
|
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
|
||||||
|
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ARGS.wrap != null) {
|
||||||
|
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.enclose != null) {
|
||||||
|
var arg_parameter_list = ARGS.enclose;
|
||||||
|
if (arg_parameter_list === true) {
|
||||||
|
arg_parameter_list = [];
|
||||||
|
}
|
||||||
|
else if (!(arg_parameter_list instanceof Array)) {
|
||||||
|
arg_parameter_list = [arg_parameter_list];
|
||||||
|
}
|
||||||
|
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.mangle_props || ARGS.name_cache) (function(){
|
||||||
|
var reserved = RESERVED ? RESERVED.props : null;
|
||||||
|
var cache = readNameCache("props");
|
||||||
|
var regex;
|
||||||
|
|
||||||
|
try {
|
||||||
|
regex = ARGS.mangle_regex ? extractRegex(ARGS.mangle_regex) : null;
|
||||||
|
} catch (e) {
|
||||||
|
print_error("ERROR: Invalid --mangle-regex: " + e.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, {
|
||||||
|
reserved : reserved,
|
||||||
|
cache : cache,
|
||||||
|
only_cache : !ARGS.mangle_props,
|
||||||
|
regex : regex
|
||||||
|
});
|
||||||
|
writeNameCache("props", cache);
|
||||||
|
})();
|
||||||
|
|
||||||
|
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
|
||||||
|
var TL_CACHE = readNameCache("vars");
|
||||||
|
|
||||||
|
if (SCOPE_IS_NEEDED) {
|
||||||
|
time_it("scope", function(){
|
||||||
|
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
|
||||||
|
if (ARGS.lint) {
|
||||||
|
TOPLEVEL.scope_warnings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (COMPRESS) {
|
||||||
|
time_it("squeeze", function(){
|
||||||
|
TOPLEVEL = TOPLEVEL.transform(compressor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SCOPE_IS_NEEDED) {
|
||||||
|
time_it("scope", function(){
|
||||||
|
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
|
||||||
|
if (MANGLE && !TL_CACHE) {
|
||||||
|
TOPLEVEL.compute_char_frequency(MANGLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MANGLE) time_it("mangle", function(){
|
||||||
|
MANGLE.cache = TL_CACHE;
|
||||||
|
TOPLEVEL.mangle_names(MANGLE);
|
||||||
|
});
|
||||||
|
|
||||||
|
writeNameCache("vars", TL_CACHE);
|
||||||
|
|
||||||
|
if (ARGS.source_map_include_sources) {
|
||||||
|
for (var file in SOURCES_CONTENT) {
|
||||||
|
if (SOURCES_CONTENT.hasOwnProperty(file)) {
|
||||||
|
SOURCE_MAP.get().setSourceContent(file, SOURCES_CONTENT[file]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.dump_spidermonkey_ast) {
|
||||||
|
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
|
||||||
|
} else {
|
||||||
|
time_it("generate", function(){
|
||||||
|
TOPLEVEL.print(output);
|
||||||
|
});
|
||||||
|
|
||||||
|
output = output.get();
|
||||||
|
|
||||||
|
if (SOURCE_MAP) {
|
||||||
|
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
||||||
|
var source_map_url = ARGS.source_map_url || (
|
||||||
|
P_RELATIVE
|
||||||
|
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
|
||||||
|
: ARGS.source_map
|
||||||
|
);
|
||||||
|
output += "\n//# sourceMappingURL=" + source_map_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OUTPUT_FILE) {
|
||||||
|
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
||||||
|
} else {
|
||||||
|
print(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.stats) {
|
||||||
|
print_error(UglifyJS.string_template("Timing information (compressed {count} files):", {
|
||||||
|
count: files.length
|
||||||
|
}));
|
||||||
|
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
|
||||||
|
print_error(UglifyJS.string_template("- {name}: {time}s", {
|
||||||
|
name: i,
|
||||||
|
time: (STATS[i] / 1000).toFixed(3)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -----[ functions ]----- */
|
||||||
|
|
||||||
|
function normalize(o) {
|
||||||
|
for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
|
||||||
|
o[i.replace(/-/g, "_")] = o[i];
|
||||||
|
delete o[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOptions(flag, constants) {
|
||||||
|
var x = ARGS[flag];
|
||||||
|
if (x == null || x === false) return null;
|
||||||
|
var ret = {};
|
||||||
|
if (x !== "") {
|
||||||
|
if (Array.isArray(x)) x = x.map(function (v) { return "(" + v + ")"; }).join(", ");
|
||||||
|
|
||||||
|
var ast;
|
||||||
|
try {
|
||||||
|
ast = UglifyJS.parse(x, { expression: true });
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||||
|
print_error("Error parsing arguments for flag `" + flag + "': " + x);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.walk(new UglifyJS.TreeWalker(function(node){
|
||||||
|
if (node instanceof UglifyJS.AST_Seq) return; // descend
|
||||||
|
if (node instanceof UglifyJS.AST_Assign) {
|
||||||
|
var name = node.left.print_to_string({ beautify: false }).replace(/-/g, "_");
|
||||||
|
var value = node.right;
|
||||||
|
if (constants)
|
||||||
|
value = new Function("return (" + value.print_to_string() + ")")();
|
||||||
|
ret[name] = value;
|
||||||
|
return true; // no descend
|
||||||
|
}
|
||||||
|
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
|
||||||
|
var name = node.print_to_string({ beautify: false }).replace(/-/g, "_");
|
||||||
|
ret[name] = true;
|
||||||
|
return true; // no descend
|
||||||
|
}
|
||||||
|
print_error(node.TYPE)
|
||||||
|
print_error("Error parsing arguments for flag `" + flag + "': " + x);
|
||||||
|
process.exit(1);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_whole_file(filename, cb) {
|
||||||
|
if (filename == "-") {
|
||||||
|
var chunks = [];
|
||||||
|
process.stdin.setEncoding('utf-8');
|
||||||
|
process.stdin.on('data', function (chunk) {
|
||||||
|
chunks.push(chunk);
|
||||||
|
}).on('end', function () {
|
||||||
|
cb(null, chunks.join(""));
|
||||||
|
});
|
||||||
|
process.openStdin();
|
||||||
|
} else {
|
||||||
|
fs.readFile(filename, "utf-8", cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function time_it(name, cont) {
|
||||||
|
var t1 = new Date().getTime();
|
||||||
|
var ret = cont();
|
||||||
|
if (ARGS.stats) {
|
||||||
|
var spent = new Date().getTime() - t1;
|
||||||
|
if (STATS[name]) STATS[name] += spent;
|
||||||
|
else STATS[name] = spent;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_error(msg) {
|
||||||
|
console.error("%s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function print(txt) {
|
||||||
|
console.log("%s", txt);
|
||||||
|
}
|
||||||
310
bin/uglifyjs2
310
bin/uglifyjs2
@@ -1,310 +0,0 @@
|
|||||||
#! /usr/bin/env node
|
|
||||||
// -*- js -*-
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var UglifyJS = require("../tools/node");
|
|
||||||
var sys = require("util");
|
|
||||||
var optimist = require("optimist");
|
|
||||||
var fs = require("fs");
|
|
||||||
var acorn;
|
|
||||||
var ARGS = optimist
|
|
||||||
.usage("$0 input1.js [input2.js ...] [options]\n\
|
|
||||||
Use a single dash to read input from the standard input.\
|
|
||||||
\n\n\
|
|
||||||
NOTE: by default there is no mangling/compression.\n\
|
|
||||||
Without [options] it will simply parse input files and dump the AST\n\
|
|
||||||
with whitespace and comments discarded. To achieve compression and\n\
|
|
||||||
mangling you need to use `-c` and `-m`.\
|
|
||||||
")
|
|
||||||
.describe("source-map", "Specify an output file where to generate source map.")
|
|
||||||
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
|
||||||
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
|
||||||
.describe("p", "Skip prefix for original filenames that appear in source maps. \
|
|
||||||
For example -p 3 will drop 3 directories from file names and ensure they are relative paths.")
|
|
||||||
.describe("o", "Output file (default STDOUT).")
|
|
||||||
.describe("b", "Beautify output/specify output options.")
|
|
||||||
.describe("m", "Mangle names/pass mangler options.")
|
|
||||||
.describe("r", "Reserved names to exclude from mangling.")
|
|
||||||
.describe("c", "Enable compressor/pass compressor options. \
|
|
||||||
Pass options like -c hoist_vars=false,if_return=false. \
|
|
||||||
Use -c with no argument to use the default compression options.")
|
|
||||||
.describe("d", "Global definitions")
|
|
||||||
|
|
||||||
.describe("comments", "Preserve copyright comments in the output. \
|
|
||||||
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
|
|
||||||
You can optionally pass one of the following arguments to this flag:\n\
|
|
||||||
- \"all\" to keep all comments\n\
|
|
||||||
- a valid JS regexp (needs to start with a slash) to keep only comments that match.\n\
|
|
||||||
\
|
|
||||||
Note that currently not *all* comments can be kept when compression is on, \
|
|
||||||
because of dead code removal or cascading statements into sequences.")
|
|
||||||
|
|
||||||
.describe("stats", "Display operations run time on STDERR.")
|
|
||||||
.describe("acorn", "Use Acorn for parsing.")
|
|
||||||
.describe("spidermonkey", "Assume input fles are SpiderMonkey AST format (as JSON).")
|
|
||||||
.describe("v", "Verbose")
|
|
||||||
|
|
||||||
.alias("p", "prefix")
|
|
||||||
.alias("o", "output")
|
|
||||||
.alias("v", "verbose")
|
|
||||||
.alias("b", "beautify")
|
|
||||||
.alias("m", "mangle")
|
|
||||||
.alias("c", "compress")
|
|
||||||
.alias("d", "define")
|
|
||||||
.alias("r", "reserved")
|
|
||||||
|
|
||||||
.string("source-map")
|
|
||||||
.string("source-map-root")
|
|
||||||
.string("b")
|
|
||||||
.string("m")
|
|
||||||
.string("c")
|
|
||||||
.string("d")
|
|
||||||
.string("comments")
|
|
||||||
.boolean("v")
|
|
||||||
.boolean("stats")
|
|
||||||
.boolean("acorn")
|
|
||||||
.boolean("spidermonkey")
|
|
||||||
|
|
||||||
.wrap(80)
|
|
||||||
|
|
||||||
.argv
|
|
||||||
;
|
|
||||||
|
|
||||||
if (ARGS.h || ARGS.help) {
|
|
||||||
sys.puts(optimist.help());
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ARGS.acorn) {
|
|
||||||
acorn = require("acorn");
|
|
||||||
}
|
|
||||||
|
|
||||||
normalize(ARGS);
|
|
||||||
|
|
||||||
var COMPRESS = getOptions("c");
|
|
||||||
var MANGLE = getOptions("m");
|
|
||||||
var BEAUTIFY = getOptions("b");
|
|
||||||
|
|
||||||
if (COMPRESS && ARGS.d) {
|
|
||||||
COMPRESS.global_defs = getOptions("d");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MANGLE && ARGS.r) {
|
|
||||||
MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
var OUTPUT_OPTIONS = {
|
|
||||||
beautify: BEAUTIFY ? true : false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (BEAUTIFY)
|
|
||||||
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
|
|
||||||
|
|
||||||
if (ARGS.comments) {
|
|
||||||
if (/^\//.test(ARGS.comments)) {
|
|
||||||
OUTPUT_OPTIONS.comments = new Function("return(" + ARGS.comments + ")")();
|
|
||||||
} else if (ARGS.comments == "all") {
|
|
||||||
OUTPUT_OPTIONS.comments = true;
|
|
||||||
} else {
|
|
||||||
OUTPUT_OPTIONS.comments = function(node, comment) {
|
|
||||||
var text = comment.value;
|
|
||||||
var type = comment.type;
|
|
||||||
if (type == "comment2") {
|
|
||||||
// multiline comment
|
|
||||||
return text.indexOf("@preserve") >= 0
|
|
||||||
|| text.indexOf("@license") >= 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var files = ARGS._.slice();
|
|
||||||
|
|
||||||
var ORIG_MAP = ARGS.in_source_map;
|
|
||||||
|
|
||||||
if (ORIG_MAP) {
|
|
||||||
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
|
|
||||||
if (files.length == 0) {
|
|
||||||
sys.error("INFO: Using file from the input source map: " + ORIG_MAP.file);
|
|
||||||
files = [ ORIG_MAP.file ];
|
|
||||||
}
|
|
||||||
if (ARGS.source_map_root == null) {
|
|
||||||
ARGS.source_map_root = ORIG_MAP.sourceRoot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.length == 0) {
|
|
||||||
files = [ "-" ];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.indexOf("-") >= 0 && ARGS.source_map) {
|
|
||||||
sys.error("ERROR: Source map doesn't work with input from STDIN");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.filter(function(el){ return el == "-" }).length > 1) {
|
|
||||||
sys.error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var STATS = {};
|
|
||||||
var OUTPUT_FILE = ARGS.o;
|
|
||||||
var TOPLEVEL = null;
|
|
||||||
|
|
||||||
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
|
||||||
file: OUTPUT_FILE,
|
|
||||||
root: ARGS.source_map_root,
|
|
||||||
orig: ORIG_MAP,
|
|
||||||
}) : null;
|
|
||||||
|
|
||||||
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
|
|
||||||
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
|
|
||||||
} catch(ex) {
|
|
||||||
if (ex instanceof UglifyJS.DefaultsError) {
|
|
||||||
sys.error(ex.msg);
|
|
||||||
sys.error("Supported options:");
|
|
||||||
sys.error(sys.inspect(ex.defs));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files.forEach(function(file) {
|
|
||||||
var code = read_whole_file(file);
|
|
||||||
if (ARGS.p != null) {
|
|
||||||
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
|
|
||||||
}
|
|
||||||
time_it("parse", function(){
|
|
||||||
if (ARGS.spidermonkey) {
|
|
||||||
var program = JSON.parse(code);
|
|
||||||
if (!TOPLEVEL) TOPLEVEL = program;
|
|
||||||
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
|
|
||||||
}
|
|
||||||
else if (ARGS.acorn) {
|
|
||||||
TOPLEVEL = acorn.parse(code, {
|
|
||||||
locations : true,
|
|
||||||
trackComments : true,
|
|
||||||
sourceFile : file,
|
|
||||||
program : TOPLEVEL
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
TOPLEVEL = UglifyJS.parse(code, {
|
|
||||||
filename: file,
|
|
||||||
toplevel: TOPLEVEL
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
|
|
||||||
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
|
|
||||||
});
|
|
||||||
|
|
||||||
var SCOPE_IS_NEEDED = COMPRESS || MANGLE;
|
|
||||||
|
|
||||||
if (SCOPE_IS_NEEDED) {
|
|
||||||
time_it("scope", function(){
|
|
||||||
TOPLEVEL.figure_out_scope();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (COMPRESS) {
|
|
||||||
time_it("squeeze", function(){
|
|
||||||
TOPLEVEL = TOPLEVEL.transform(compressor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SCOPE_IS_NEEDED) {
|
|
||||||
time_it("scope", function(){
|
|
||||||
TOPLEVEL.figure_out_scope();
|
|
||||||
if (MANGLE) {
|
|
||||||
TOPLEVEL.compute_char_frequency();
|
|
||||||
UglifyJS.base54.sort();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MANGLE) time_it("mangle", function(){
|
|
||||||
TOPLEVEL.mangle_names(MANGLE);
|
|
||||||
});
|
|
||||||
time_it("generate", function(){
|
|
||||||
TOPLEVEL.print(output);
|
|
||||||
});
|
|
||||||
|
|
||||||
output = output.get();
|
|
||||||
|
|
||||||
if (SOURCE_MAP) {
|
|
||||||
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
|
||||||
output += "\n//@ sourceMappingURL=" + ARGS.source_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OUTPUT_FILE) {
|
|
||||||
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
|
||||||
} else {
|
|
||||||
sys.print(output);
|
|
||||||
sys.error("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ARGS.stats) {
|
|
||||||
sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", {
|
|
||||||
count: files.length
|
|
||||||
}));
|
|
||||||
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
|
|
||||||
sys.error(UglifyJS.string_template("- {name}: {time}s", {
|
|
||||||
name: i,
|
|
||||||
time: (STATS[i] / 1000).toFixed(3)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -----[ functions ]----- */
|
|
||||||
|
|
||||||
function normalize(o) {
|
|
||||||
for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
|
|
||||||
o[i.replace(/-/g, "_")] = o[i];
|
|
||||||
delete o[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOptions(x) {
|
|
||||||
x = ARGS[x];
|
|
||||||
if (!x) return null;
|
|
||||||
var ret = {};
|
|
||||||
if (x !== true) {
|
|
||||||
x.replace(/^\s+|\s+$/g).split(/\s*,+\s*/).forEach(function(opt){
|
|
||||||
var a = opt.split(/\s*[=:]\s*/);
|
|
||||||
ret[a[0]] = a.length > 1 ? new Function("return(" + a[1] + ")")() : true;
|
|
||||||
});
|
|
||||||
normalize(ret);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function read_whole_file(filename) {
|
|
||||||
if (filename == "-") {
|
|
||||||
// XXX: this sucks. How does one read the whole STDIN
|
|
||||||
// synchronously?
|
|
||||||
filename = "/dev/stdin";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return fs.readFileSync(filename, "utf8");
|
|
||||||
} catch(ex) {
|
|
||||||
sys.error("ERROR: can't read file: " + filename);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function time_it(name, cont) {
|
|
||||||
var t1 = new Date().getTime();
|
|
||||||
var ret = cont();
|
|
||||||
if (ARGS.stats) {
|
|
||||||
var spent = new Date().getTime() - t1;
|
|
||||||
if (STATS[name]) STATS[name] += spent;
|
|
||||||
else STATS[name] = spent;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
393
lib/ast.js
393
lib/ast.js
@@ -43,8 +43,6 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var NODE_HIERARCHY = {};
|
|
||||||
|
|
||||||
function DEFNODE(type, props, methods, base) {
|
function DEFNODE(type, props, methods, base) {
|
||||||
if (arguments.length < 4) base = AST_Node;
|
if (arguments.length < 4) base = AST_Node;
|
||||||
if (!props) props = [];
|
if (!props) props = [];
|
||||||
@@ -59,21 +57,21 @@ function DEFNODE(type, props, methods, base) {
|
|||||||
var proto = base && new base;
|
var proto = base && new base;
|
||||||
if (proto && proto.initialize || (methods && methods.initialize))
|
if (proto && proto.initialize || (methods && methods.initialize))
|
||||||
code += "this.initialize();";
|
code += "this.initialize();";
|
||||||
code += " } ";
|
code += "}}";
|
||||||
code += "if (!this.$self) this.$self = this;";
|
|
||||||
code += " } ";
|
|
||||||
var ctor = new Function(code)();
|
var ctor = new Function(code)();
|
||||||
if (proto) {
|
if (proto) {
|
||||||
ctor.prototype = proto;
|
ctor.prototype = proto;
|
||||||
ctor.BASE = base;
|
ctor.BASE = base;
|
||||||
}
|
}
|
||||||
|
if (base) base.SUBCLASSES.push(ctor);
|
||||||
ctor.prototype.CTOR = ctor;
|
ctor.prototype.CTOR = ctor;
|
||||||
ctor.PROPS = props || null;
|
ctor.PROPS = props || null;
|
||||||
ctor.SELF_PROPS = self_props;
|
ctor.SELF_PROPS = self_props;
|
||||||
|
ctor.SUBCLASSES = [];
|
||||||
if (type) {
|
if (type) {
|
||||||
ctor.prototype.TYPE = ctor.TYPE = type;
|
ctor.prototype.TYPE = ctor.TYPE = type;
|
||||||
}
|
}
|
||||||
if (methods) for (i in methods) if (HOP(methods, i)) {
|
if (methods) for (i in methods) if (methods.hasOwnProperty(i)) {
|
||||||
if (/^\$/.test(i)) {
|
if (/^\$/.test(i)) {
|
||||||
ctor[i.substr(1)] = methods[i];
|
ctor[i.substr(1)] = methods[i];
|
||||||
} else {
|
} else {
|
||||||
@@ -83,21 +81,22 @@ function DEFNODE(type, props, methods, base) {
|
|||||||
ctor.DEFMETHOD = function(name, method) {
|
ctor.DEFMETHOD = function(name, method) {
|
||||||
this.prototype[name] = method;
|
this.prototype[name] = method;
|
||||||
};
|
};
|
||||||
NODE_HIERARCHY[type] = {
|
exports["AST_" + type] = ctor;
|
||||||
def: ctor,
|
|
||||||
base: base
|
|
||||||
};
|
|
||||||
return ctor;
|
return ctor;
|
||||||
};
|
};
|
||||||
|
|
||||||
var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {
|
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
var AST_Node = DEFNODE("Node", "$self start end", {
|
var AST_Node = DEFNODE("Node", "start end", {
|
||||||
clone: function() {
|
clone: function() {
|
||||||
return new this.CTOR(this);
|
return new this.CTOR(this);
|
||||||
},
|
},
|
||||||
$documentation: "Base class of all AST nodes",
|
$documentation: "Base class of all AST nodes",
|
||||||
|
$propdoc: {
|
||||||
|
start: "[AST_Token] The first token of this node",
|
||||||
|
end: "[AST_Token] The last token of this node"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this);
|
return visitor._visit(this);
|
||||||
},
|
},
|
||||||
@@ -122,12 +121,20 @@ var AST_Debugger = DEFNODE("Debugger", null, {
|
|||||||
$documentation: "Represents a debugger statement",
|
$documentation: "Represents a debugger statement",
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_Directive = DEFNODE("Directive", "value scope", {
|
var AST_Directive = DEFNODE("Directive", "value scope quote", {
|
||||||
$documentation: "Represents a directive, like \"use strict\";",
|
$documentation: "Represents a directive, like \"use strict\";",
|
||||||
|
$propdoc: {
|
||||||
|
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
|
||||||
|
scope: "[AST_Scope/S] The scope that this directive affects",
|
||||||
|
quote: "[string] the original quote character"
|
||||||
|
},
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
||||||
$documentation: "A statement consisting of an expression, i.e. a = 1 + 2.",
|
$documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
|
||||||
|
$propdoc: {
|
||||||
|
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.body._walk(visitor);
|
this.body._walk(visitor);
|
||||||
@@ -135,17 +142,6 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
|||||||
}
|
}
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_BlockStatement = DEFNODE("BlockStatement", "body", {
|
|
||||||
$documentation: "A block statement.",
|
|
||||||
_walk: function(visitor) {
|
|
||||||
return visitor._visit(this, function(){
|
|
||||||
this.body.forEach(function(stat){
|
|
||||||
stat._walk(visitor);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, AST_Statement);
|
|
||||||
|
|
||||||
function walk_body(node, visitor) {
|
function walk_body(node, visitor) {
|
||||||
if (node.body instanceof AST_Statement) {
|
if (node.body instanceof AST_Statement) {
|
||||||
node.body._walk(visitor);
|
node.body._walk(visitor);
|
||||||
@@ -156,7 +152,10 @@ function walk_body(node, visitor) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var AST_Block = DEFNODE("Block", "body", {
|
var AST_Block = DEFNODE("Block", "body", {
|
||||||
$documentation: "A block of statements (usually always bracketed)",
|
$documentation: "A body of statements (usually bracketed)",
|
||||||
|
$propdoc: {
|
||||||
|
body: "[AST_Statement*] an array of statements"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
walk_body(this, visitor);
|
walk_body(this, visitor);
|
||||||
@@ -164,15 +163,22 @@ var AST_Block = DEFNODE("Block", "body", {
|
|||||||
}
|
}
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
|
var AST_BlockStatement = DEFNODE("BlockStatement", null, {
|
||||||
|
$documentation: "A block statement",
|
||||||
|
}, AST_Block);
|
||||||
|
|
||||||
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
||||||
$documentation: "The empty statement (empty block or simply a semicolon).",
|
$documentation: "The empty statement (empty block or simply a semicolon)",
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this);
|
return visitor._visit(this);
|
||||||
}
|
}
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
|
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
|
||||||
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`.",
|
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
|
||||||
|
$propdoc: {
|
||||||
|
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.body._walk(visitor);
|
this.body._walk(visitor);
|
||||||
@@ -182,6 +188,9 @@ var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
|
|||||||
|
|
||||||
var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
|
var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
|
||||||
$documentation: "Statement with a label",
|
$documentation: "Statement with a label",
|
||||||
|
$propdoc: {
|
||||||
|
label: "[AST_Label] a label definition"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.label._walk(visitor);
|
this.label._walk(visitor);
|
||||||
@@ -190,26 +199,44 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
|
|||||||
}
|
}
|
||||||
}, AST_StatementWithBody);
|
}, AST_StatementWithBody);
|
||||||
|
|
||||||
|
var AST_IterationStatement = DEFNODE("IterationStatement", null, {
|
||||||
|
$documentation: "Internal class. All loops inherit from it."
|
||||||
|
}, AST_StatementWithBody);
|
||||||
|
|
||||||
var AST_DWLoop = DEFNODE("DWLoop", "condition", {
|
var AST_DWLoop = DEFNODE("DWLoop", "condition", {
|
||||||
$documentation: "Base class for do/while statements.",
|
$documentation: "Base class for do/while statements",
|
||||||
|
$propdoc: {
|
||||||
|
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
|
||||||
|
}
|
||||||
|
}, AST_IterationStatement);
|
||||||
|
|
||||||
|
var AST_Do = DEFNODE("Do", null, {
|
||||||
|
$documentation: "A `do` statement",
|
||||||
|
_walk: function(visitor) {
|
||||||
|
return visitor._visit(this, function(){
|
||||||
|
this.body._walk(visitor);
|
||||||
|
this.condition._walk(visitor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, AST_DWLoop);
|
||||||
|
|
||||||
|
var AST_While = DEFNODE("While", null, {
|
||||||
|
$documentation: "A `while` statement",
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.condition._walk(visitor);
|
this.condition._walk(visitor);
|
||||||
this.body._walk(visitor);
|
this.body._walk(visitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, AST_StatementWithBody);
|
|
||||||
|
|
||||||
var AST_Do = DEFNODE("Do", null, {
|
|
||||||
$documentation: "A `do` statement"
|
|
||||||
}, AST_DWLoop);
|
|
||||||
|
|
||||||
var AST_While = DEFNODE("While", null, {
|
|
||||||
$documentation: "A `while` statement"
|
|
||||||
}, AST_DWLoop);
|
}, AST_DWLoop);
|
||||||
|
|
||||||
var AST_For = DEFNODE("For", "init condition step", {
|
var AST_For = DEFNODE("For", "init condition step", {
|
||||||
$documentation: "A `for` statement",
|
$documentation: "A `for` statement",
|
||||||
|
$propdoc: {
|
||||||
|
init: "[AST_Node?] the `for` initialization code, or null if empty",
|
||||||
|
condition: "[AST_Node?] the `for` termination clause, or null if empty",
|
||||||
|
step: "[AST_Node?] the `for` update clause, or null if empty"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
if (this.init) this.init._walk(visitor);
|
if (this.init) this.init._walk(visitor);
|
||||||
@@ -218,10 +245,15 @@ var AST_For = DEFNODE("For", "init condition step", {
|
|||||||
this.body._walk(visitor);
|
this.body._walk(visitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, AST_StatementWithBody);
|
}, AST_IterationStatement);
|
||||||
|
|
||||||
var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
||||||
$documentation: "A `for ... in` statement",
|
$documentation: "A `for ... in` statement",
|
||||||
|
$propdoc: {
|
||||||
|
init: "[AST_Node] the `for/in` initialization code",
|
||||||
|
name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
|
||||||
|
object: "[AST_Node] the object that we're looping through"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.init._walk(visitor);
|
this.init._walk(visitor);
|
||||||
@@ -229,10 +261,13 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
|||||||
this.body._walk(visitor);
|
this.body._walk(visitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, AST_StatementWithBody);
|
}, AST_IterationStatement);
|
||||||
|
|
||||||
var AST_With = DEFNODE("With", "expression", {
|
var AST_With = DEFNODE("With", "expression", {
|
||||||
$documentation: "A `with` statement",
|
$documentation: "A `with` statement",
|
||||||
|
$propdoc: {
|
||||||
|
expression: "[AST_Node] the `with` expression"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.expression._walk(visitor);
|
this.expression._walk(visitor);
|
||||||
@@ -245,14 +280,92 @@ var AST_With = DEFNODE("With", "expression", {
|
|||||||
|
|
||||||
var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
|
var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
|
||||||
$documentation: "Base class for all statements introducing a lexical scope",
|
$documentation: "Base class for all statements introducing a lexical scope",
|
||||||
|
$propdoc: {
|
||||||
|
directives: "[string*/S] an array of directives declared in this scope",
|
||||||
|
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
||||||
|
functions: "[Object/S] like `variables`, but only lists function declarations",
|
||||||
|
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
|
||||||
|
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
||||||
|
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
||||||
|
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||||
|
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
||||||
|
},
|
||||||
}, AST_Block);
|
}, AST_Block);
|
||||||
|
|
||||||
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||||
$documentation: "The toplevel scope"
|
$documentation: "The toplevel scope",
|
||||||
|
$propdoc: {
|
||||||
|
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
|
||||||
|
},
|
||||||
|
wrap_enclose: function(arg_parameter_pairs) {
|
||||||
|
var self = this;
|
||||||
|
var args = [];
|
||||||
|
var parameters = [];
|
||||||
|
|
||||||
|
arg_parameter_pairs.forEach(function(pair) {
|
||||||
|
var splitAt = pair.lastIndexOf(":");
|
||||||
|
|
||||||
|
args.push(pair.substr(0, splitAt));
|
||||||
|
parameters.push(pair.substr(splitAt + 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
|
||||||
|
wrapped_tl = parse(wrapped_tl);
|
||||||
|
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
|
||||||
|
if (node instanceof AST_Directive && node.value == "$ORIG") {
|
||||||
|
return MAP.splice(self.body);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return wrapped_tl;
|
||||||
|
},
|
||||||
|
wrap_commonjs: function(name, export_all) {
|
||||||
|
var self = this;
|
||||||
|
var to_export = [];
|
||||||
|
if (export_all) {
|
||||||
|
self.figure_out_scope();
|
||||||
|
self.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
|
||||||
|
if (!find_if(function(n){ return n.name == node.name }, to_export))
|
||||||
|
to_export.push(node);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))";
|
||||||
|
wrapped_tl = parse(wrapped_tl);
|
||||||
|
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
|
||||||
|
if (node instanceof AST_Directive) {
|
||||||
|
switch (node.value) {
|
||||||
|
case "$ORIG":
|
||||||
|
return MAP.splice(self.body);
|
||||||
|
case "$EXPORTS":
|
||||||
|
var body = [];
|
||||||
|
to_export.forEach(function(sym){
|
||||||
|
body.push(new AST_SimpleStatement({
|
||||||
|
body: new AST_Assign({
|
||||||
|
left: new AST_Sub({
|
||||||
|
expression: new AST_SymbolRef({ name: "exports" }),
|
||||||
|
property: new AST_String({ value: sym.name }),
|
||||||
|
}),
|
||||||
|
operator: "=",
|
||||||
|
right: new AST_SymbolRef(sym),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
return MAP.splice(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return wrapped_tl;
|
||||||
|
}
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
|
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
|
||||||
$documentation: "Base class for functions",
|
$documentation: "Base class for functions",
|
||||||
|
$propdoc: {
|
||||||
|
name: "[AST_SymbolDeclaration?] the name of this function",
|
||||||
|
argnames: "[AST_SymbolFunarg*] array of function arguments",
|
||||||
|
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
if (this.name) this.name._walk(visitor);
|
if (this.name) this.name._walk(visitor);
|
||||||
@@ -264,6 +377,10 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
|
|||||||
}
|
}
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
|
var AST_Accessor = DEFNODE("Accessor", null, {
|
||||||
|
$documentation: "A setter/getter function. The `name` property is always null."
|
||||||
|
}, AST_Lambda);
|
||||||
|
|
||||||
var AST_Function = DEFNODE("Function", null, {
|
var AST_Function = DEFNODE("Function", null, {
|
||||||
$documentation: "A function expression"
|
$documentation: "A function expression"
|
||||||
}, AST_Lambda);
|
}, AST_Lambda);
|
||||||
@@ -280,6 +397,9 @@ var AST_Jump = DEFNODE("Jump", null, {
|
|||||||
|
|
||||||
var AST_Exit = DEFNODE("Exit", "value", {
|
var AST_Exit = DEFNODE("Exit", "value", {
|
||||||
$documentation: "Base class for “exits” (`return` and `throw`)",
|
$documentation: "Base class for “exits” (`return` and `throw`)",
|
||||||
|
$propdoc: {
|
||||||
|
value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, this.value && function(){
|
return visitor._visit(this, this.value && function(){
|
||||||
this.value._walk(visitor);
|
this.value._walk(visitor);
|
||||||
@@ -295,8 +415,11 @@ var AST_Throw = DEFNODE("Throw", null, {
|
|||||||
$documentation: "A `throw` statement"
|
$documentation: "A `throw` statement"
|
||||||
}, AST_Exit);
|
}, AST_Exit);
|
||||||
|
|
||||||
var AST_LoopControl = DEFNODE("LoopControl", "label loopcontrol_target", {
|
var AST_LoopControl = DEFNODE("LoopControl", "label", {
|
||||||
$documentation: "Base class for loop control statements (`break` and `continue`)",
|
$documentation: "Base class for loop control statements (`break` and `continue`)",
|
||||||
|
$propdoc: {
|
||||||
|
label: "[AST_LabelRef?] the label, or null if none",
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, this.label && function(){
|
return visitor._visit(this, this.label && function(){
|
||||||
this.label._walk(visitor);
|
this.label._walk(visitor);
|
||||||
@@ -316,6 +439,10 @@ var AST_Continue = DEFNODE("Continue", null, {
|
|||||||
|
|
||||||
var AST_If = DEFNODE("If", "condition alternative", {
|
var AST_If = DEFNODE("If", "condition alternative", {
|
||||||
$documentation: "A `if` statement",
|
$documentation: "A `if` statement",
|
||||||
|
$propdoc: {
|
||||||
|
condition: "[AST_Node] the `if` condition",
|
||||||
|
alternative: "[AST_Statement?] the `else` part, or null if not present"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.condition._walk(visitor);
|
this.condition._walk(visitor);
|
||||||
@@ -327,15 +454,18 @@ var AST_If = DEFNODE("If", "condition alternative", {
|
|||||||
|
|
||||||
/* -----[ SWITCH ]----- */
|
/* -----[ SWITCH ]----- */
|
||||||
|
|
||||||
var AST_Switch = DEFNODE("Switch", "body expression", {
|
var AST_Switch = DEFNODE("Switch", "expression", {
|
||||||
$documentation: "A `switch` statement",
|
$documentation: "A `switch` statement",
|
||||||
|
$propdoc: {
|
||||||
|
expression: "[AST_Node] the `switch` “discriminant”"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.expression._walk(visitor);
|
this.expression._walk(visitor);
|
||||||
walk_body(this, visitor);
|
walk_body(this, visitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, AST_Statement);
|
}, AST_Block);
|
||||||
|
|
||||||
var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
|
var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
|
||||||
$documentation: "Base class for `switch` branches",
|
$documentation: "Base class for `switch` branches",
|
||||||
@@ -347,6 +477,9 @@ var AST_Default = DEFNODE("Default", null, {
|
|||||||
|
|
||||||
var AST_Case = DEFNODE("Case", "expression", {
|
var AST_Case = DEFNODE("Case", "expression", {
|
||||||
$documentation: "A `case` switch branch",
|
$documentation: "A `case` switch branch",
|
||||||
|
$propdoc: {
|
||||||
|
expression: "[AST_Node] the `case` expression"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.expression._walk(visitor);
|
this.expression._walk(visitor);
|
||||||
@@ -359,6 +492,10 @@ var AST_Case = DEFNODE("Case", "expression", {
|
|||||||
|
|
||||||
var AST_Try = DEFNODE("Try", "bcatch bfinally", {
|
var AST_Try = DEFNODE("Try", "bcatch bfinally", {
|
||||||
$documentation: "A `try` statement",
|
$documentation: "A `try` statement",
|
||||||
|
$propdoc: {
|
||||||
|
bcatch: "[AST_Catch?] the catch block, or null if not present",
|
||||||
|
bfinally: "[AST_Finally?] the finally block, or null if not present"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
walk_body(this, visitor);
|
walk_body(this, visitor);
|
||||||
@@ -368,14 +505,11 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
|
|||||||
}
|
}
|
||||||
}, AST_Block);
|
}, AST_Block);
|
||||||
|
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the catch block
|
|
||||||
// should introduce another scope, as the argname should be visible
|
|
||||||
// only inside the catch block. However, doing it this way because of
|
|
||||||
// IE which simply introduces the name in the surrounding scope. If
|
|
||||||
// we ever want to fix this then AST_Catch should inherit from
|
|
||||||
// AST_Scope.
|
|
||||||
var AST_Catch = DEFNODE("Catch", "argname", {
|
var AST_Catch = DEFNODE("Catch", "argname", {
|
||||||
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
||||||
|
$propdoc: {
|
||||||
|
argname: "[AST_SymbolCatch] symbol for the exception"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.argname._walk(visitor);
|
this.argname._walk(visitor);
|
||||||
@@ -392,6 +526,9 @@ var AST_Finally = DEFNODE("Finally", null, {
|
|||||||
|
|
||||||
var AST_Definitions = DEFNODE("Definitions", "definitions", {
|
var AST_Definitions = DEFNODE("Definitions", "definitions", {
|
||||||
$documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
|
$documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
|
||||||
|
$propdoc: {
|
||||||
|
definitions: "[AST_VarDef*] array of variable definitions"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.definitions.forEach(function(def){
|
this.definitions.forEach(function(def){
|
||||||
@@ -411,6 +548,10 @@ var AST_Const = DEFNODE("Const", null, {
|
|||||||
|
|
||||||
var AST_VarDef = DEFNODE("VarDef", "name value", {
|
var AST_VarDef = DEFNODE("VarDef", "name value", {
|
||||||
$documentation: "A variable declaration; only appears in a AST_Definitions node",
|
$documentation: "A variable declaration; only appears in a AST_Definitions node",
|
||||||
|
$propdoc: {
|
||||||
|
name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
|
||||||
|
value: "[AST_Node?] initializer, or null of there's no initializer"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.name._walk(visitor);
|
this.name._walk(visitor);
|
||||||
@@ -423,6 +564,10 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
|
|||||||
|
|
||||||
var AST_Call = DEFNODE("Call", "expression args", {
|
var AST_Call = DEFNODE("Call", "expression args", {
|
||||||
$documentation: "A function call expression",
|
$documentation: "A function call expression",
|
||||||
|
$propdoc: {
|
||||||
|
expression: "[AST_Node] expression to invoke as function",
|
||||||
|
args: "[AST_Node*] array of arguments"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.expression._walk(visitor);
|
this.expression._walk(visitor);
|
||||||
@@ -434,11 +579,15 @@ var AST_Call = DEFNODE("Call", "expression args", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var AST_New = DEFNODE("New", null, {
|
var AST_New = DEFNODE("New", null, {
|
||||||
$documentation: "An object instantiation. Derives from a function call since it has exactly the same properties."
|
$documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
|
||||||
}, AST_Call);
|
}, AST_Call);
|
||||||
|
|
||||||
var AST_Seq = DEFNODE("Seq", "car cdr", {
|
var AST_Seq = DEFNODE("Seq", "car cdr", {
|
||||||
$documentation: "A sequence expression (two comma-separated expressions)",
|
$documentation: "A sequence expression (two comma-separated expressions)",
|
||||||
|
$propdoc: {
|
||||||
|
car: "[AST_Node] first element in sequence",
|
||||||
|
cdr: "[AST_Node] second element in sequence"
|
||||||
|
},
|
||||||
$cons: function(x, y) {
|
$cons: function(x, y) {
|
||||||
var seq = new AST_Seq(x);
|
var seq = new AST_Seq(x);
|
||||||
seq.car = x;
|
seq.car = x;
|
||||||
@@ -462,6 +611,18 @@ var AST_Seq = DEFNODE("Seq", "car cdr", {
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
},
|
},
|
||||||
|
to_array: function() {
|
||||||
|
var p = this, a = [];
|
||||||
|
while (p) {
|
||||||
|
a.push(p.car);
|
||||||
|
if (p.cdr && !(p.cdr instanceof AST_Seq)) {
|
||||||
|
a.push(p.cdr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = p.cdr;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
},
|
||||||
add: function(node) {
|
add: function(node) {
|
||||||
var p = this;
|
var p = this;
|
||||||
while (p) {
|
while (p) {
|
||||||
@@ -481,7 +642,11 @@ var AST_Seq = DEFNODE("Seq", "car cdr", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
|
var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
|
||||||
$documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`"
|
$documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
|
||||||
|
$propdoc: {
|
||||||
|
expression: "[AST_Node] the “container” expression",
|
||||||
|
property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_Dot = DEFNODE("Dot", null, {
|
var AST_Dot = DEFNODE("Dot", null, {
|
||||||
@@ -505,6 +670,10 @@ var AST_Sub = DEFNODE("Sub", null, {
|
|||||||
|
|
||||||
var AST_Unary = DEFNODE("Unary", "operator expression", {
|
var AST_Unary = DEFNODE("Unary", "operator expression", {
|
||||||
$documentation: "Base class for unary expressions",
|
$documentation: "Base class for unary expressions",
|
||||||
|
$propdoc: {
|
||||||
|
operator: "[string] the operator",
|
||||||
|
expression: "[AST_Node] expression that this unary operator applies to"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.expression._walk(visitor);
|
this.expression._walk(visitor);
|
||||||
@@ -522,6 +691,11 @@ var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
|
|||||||
|
|
||||||
var AST_Binary = DEFNODE("Binary", "left operator right", {
|
var AST_Binary = DEFNODE("Binary", "left operator right", {
|
||||||
$documentation: "Binary expression, i.e. `a + b`",
|
$documentation: "Binary expression, i.e. `a + b`",
|
||||||
|
$propdoc: {
|
||||||
|
left: "[AST_Node] left-hand side expression",
|
||||||
|
operator: "[string] the operator",
|
||||||
|
right: "[AST_Node] right-hand side expression"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.left._walk(visitor);
|
this.left._walk(visitor);
|
||||||
@@ -532,6 +706,11 @@ var AST_Binary = DEFNODE("Binary", "left operator right", {
|
|||||||
|
|
||||||
var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
|
var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
|
||||||
$documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
|
$documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
|
||||||
|
$propdoc: {
|
||||||
|
condition: "[AST_Node]",
|
||||||
|
consequent: "[AST_Node]",
|
||||||
|
alternative: "[AST_Node]"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.condition._walk(visitor);
|
this.condition._walk(visitor);
|
||||||
@@ -541,7 +720,7 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative",
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_Assign = DEFNODE("Assign", "left operator right", {
|
var AST_Assign = DEFNODE("Assign", null, {
|
||||||
$documentation: "An assignment expression — `a = b + 5`",
|
$documentation: "An assignment expression — `a = b + 5`",
|
||||||
}, AST_Binary);
|
}, AST_Binary);
|
||||||
|
|
||||||
@@ -549,6 +728,9 @@ var AST_Assign = DEFNODE("Assign", "left operator right", {
|
|||||||
|
|
||||||
var AST_Array = DEFNODE("Array", "elements", {
|
var AST_Array = DEFNODE("Array", "elements", {
|
||||||
$documentation: "An array literal",
|
$documentation: "An array literal",
|
||||||
|
$propdoc: {
|
||||||
|
elements: "[AST_Node*] array of elements"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.elements.forEach(function(el){
|
this.elements.forEach(function(el){
|
||||||
@@ -560,6 +742,9 @@ var AST_Array = DEFNODE("Array", "elements", {
|
|||||||
|
|
||||||
var AST_Object = DEFNODE("Object", "properties", {
|
var AST_Object = DEFNODE("Object", "properties", {
|
||||||
$documentation: "An object literal",
|
$documentation: "An object literal",
|
||||||
|
$propdoc: {
|
||||||
|
properties: "[AST_ObjectProperty*] array of properties"
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.properties.forEach(function(prop){
|
this.properties.forEach(function(prop){
|
||||||
@@ -571,6 +756,10 @@ var AST_Object = DEFNODE("Object", "properties", {
|
|||||||
|
|
||||||
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||||
$documentation: "Base class for literal object properties",
|
$documentation: "Base class for literal object properties",
|
||||||
|
$propdoc: {
|
||||||
|
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.",
|
||||||
|
value: "[AST_Node] property value. For setters and getters this is an AST_Function."
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.value._walk(visitor);
|
this.value._walk(visitor);
|
||||||
@@ -578,8 +767,11 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyval", null, {
|
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
||||||
$documentation: "A key: value object property",
|
$documentation: "A key: value object property",
|
||||||
|
$propdoc: {
|
||||||
|
quote: "[string] the original quote character"
|
||||||
|
}
|
||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
|
|
||||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
||||||
@@ -591,11 +783,23 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
|
|||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
|
|
||||||
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
||||||
|
$propdoc: {
|
||||||
|
name: "[string] name of this symbol",
|
||||||
|
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
|
||||||
|
thedef: "[SymbolDef/S] the definition of this symbol"
|
||||||
|
},
|
||||||
$documentation: "Base class for all symbols",
|
$documentation: "Base class for all symbols",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
|
||||||
|
$documentation: "The name of a property accessor (setter/getter function)"
|
||||||
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
||||||
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
||||||
|
$propdoc: {
|
||||||
|
init: "[AST_Node*/S] array of initializers for this declaration."
|
||||||
|
}
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolVar = DEFNODE("SymbolVar", null, {
|
var AST_SymbolVar = DEFNODE("SymbolVar", null, {
|
||||||
@@ -622,9 +826,16 @@ var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
|
|||||||
$documentation: "Symbol naming the exception in catch",
|
$documentation: "Symbol naming the exception in catch",
|
||||||
}, AST_SymbolDeclaration);
|
}, AST_SymbolDeclaration);
|
||||||
|
|
||||||
var AST_Label = DEFNODE("Label", "references label_target", {
|
var AST_Label = DEFNODE("Label", "references", {
|
||||||
$documentation: "Symbol naming a label (declaration)",
|
$documentation: "Symbol naming a label (declaration)",
|
||||||
}, AST_SymbolDeclaration);
|
$propdoc: {
|
||||||
|
references: "[AST_LoopControl*] a list of nodes referring to this label"
|
||||||
|
},
|
||||||
|
initialize: function() {
|
||||||
|
this.references = [];
|
||||||
|
this.thedef = this;
|
||||||
|
}
|
||||||
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolRef = DEFNODE("SymbolRef", null, {
|
var AST_SymbolRef = DEFNODE("SymbolRef", null, {
|
||||||
$documentation: "Reference to some symbol (not definition/declaration)",
|
$documentation: "Reference to some symbol (not definition/declaration)",
|
||||||
@@ -632,7 +843,7 @@ var AST_SymbolRef = DEFNODE("SymbolRef", null, {
|
|||||||
|
|
||||||
var AST_LabelRef = DEFNODE("LabelRef", null, {
|
var AST_LabelRef = DEFNODE("LabelRef", null, {
|
||||||
$documentation: "Reference to a label symbol",
|
$documentation: "Reference to a label symbol",
|
||||||
}, AST_SymbolRef);
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_This = DEFNODE("This", null, {
|
var AST_This = DEFNODE("This", null, {
|
||||||
$documentation: "The `this` symbol",
|
$documentation: "The `this` symbol",
|
||||||
@@ -645,18 +856,26 @@ var AST_Constant = DEFNODE("Constant", null, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_String = DEFNODE("String", "value", {
|
var AST_String = DEFNODE("String", "value quote", {
|
||||||
$documentation: "A string literal",
|
$documentation: "A string literal",
|
||||||
|
$propdoc: {
|
||||||
|
value: "[string] the contents of this string",
|
||||||
|
quote: "[string] the original quote character"
|
||||||
|
}
|
||||||
}, AST_Constant);
|
}, AST_Constant);
|
||||||
|
|
||||||
var AST_Number = DEFNODE("Number", "value", {
|
var AST_Number = DEFNODE("Number", "value literal", {
|
||||||
$documentation: "A number literal",
|
$documentation: "A number literal",
|
||||||
|
$propdoc: {
|
||||||
|
value: "[number] the numeric value",
|
||||||
|
literal: "[string] numeric value as string (optional)"
|
||||||
|
}
|
||||||
}, AST_Constant);
|
}, AST_Constant);
|
||||||
|
|
||||||
var AST_RegExp = DEFNODE("Regexp", "pattern mods", {
|
var AST_RegExp = DEFNODE("RegExp", "value", {
|
||||||
$documentation: "A regexp literal",
|
$documentation: "A regexp literal",
|
||||||
initialize: function() {
|
$propdoc: {
|
||||||
this.value = new RegExp(this.pattern, this.mods);
|
value: "[RegExp] the actual regexp"
|
||||||
}
|
}
|
||||||
}, AST_Constant);
|
}, AST_Constant);
|
||||||
|
|
||||||
@@ -679,6 +898,16 @@ var AST_Undefined = DEFNODE("Undefined", null, {
|
|||||||
value: (function(){}())
|
value: (function(){}())
|
||||||
}, AST_Atom);
|
}, AST_Atom);
|
||||||
|
|
||||||
|
var AST_Hole = DEFNODE("Hole", null, {
|
||||||
|
$documentation: "A hole in an array",
|
||||||
|
value: (function(){}())
|
||||||
|
}, AST_Atom);
|
||||||
|
|
||||||
|
var AST_Infinity = DEFNODE("Infinity", null, {
|
||||||
|
$documentation: "The `Infinity` value",
|
||||||
|
value: 1/0
|
||||||
|
}, AST_Atom);
|
||||||
|
|
||||||
var AST_Boolean = DEFNODE("Boolean", null, {
|
var AST_Boolean = DEFNODE("Boolean", null, {
|
||||||
$documentation: "Base class for booleans",
|
$documentation: "Base class for booleans",
|
||||||
}, AST_Atom);
|
}, AST_Atom);
|
||||||
@@ -698,27 +927,36 @@ var AST_True = DEFNODE("True", null, {
|
|||||||
function TreeWalker(callback) {
|
function TreeWalker(callback) {
|
||||||
this.visit = callback;
|
this.visit = callback;
|
||||||
this.stack = [];
|
this.stack = [];
|
||||||
|
this.directives = Object.create(null);
|
||||||
};
|
};
|
||||||
TreeWalker.prototype = {
|
TreeWalker.prototype = {
|
||||||
_visit: function(node, descend) {
|
_visit: function(node, descend) {
|
||||||
this.stack.push(node);
|
this.push(node);
|
||||||
var ret = this.visit(node, descend ? function(){
|
var ret = this.visit(node, descend ? function(){
|
||||||
descend.call(node);
|
descend.call(node);
|
||||||
} : noop);
|
} : noop);
|
||||||
if (!ret && descend) {
|
if (!ret && descend) {
|
||||||
descend.call(node);
|
descend.call(node);
|
||||||
}
|
}
|
||||||
this.stack.pop();
|
this.pop(node);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
parent: function(n) {
|
parent: function(n) {
|
||||||
return this.stack[this.stack.length - 2 - (n || 0)];
|
return this.stack[this.stack.length - 2 - (n || 0)];
|
||||||
},
|
},
|
||||||
push: function (node) {
|
push: function (node) {
|
||||||
|
if (node instanceof AST_Lambda) {
|
||||||
|
this.directives = Object.create(this.directives);
|
||||||
|
} else if (node instanceof AST_Directive) {
|
||||||
|
this.directives[node.value] = this.directives[node.value] ? "up" : true;
|
||||||
|
}
|
||||||
this.stack.push(node);
|
this.stack.push(node);
|
||||||
},
|
},
|
||||||
pop: function() {
|
pop: function(node) {
|
||||||
return this.stack.pop();
|
this.stack.pop();
|
||||||
|
if (node instanceof AST_Lambda) {
|
||||||
|
this.directives = Object.getPrototypeOf(this.directives);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
self: function() {
|
self: function() {
|
||||||
return this.stack[this.stack.length - 1];
|
return this.stack[this.stack.length - 1];
|
||||||
@@ -730,6 +968,18 @@ TreeWalker.prototype = {
|
|||||||
if (x instanceof type) return x;
|
if (x instanceof type) return x;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
has_directive: function(type) {
|
||||||
|
var dir = this.directives[type];
|
||||||
|
if (dir) return dir;
|
||||||
|
var node = this.stack[this.stack.length - 1];
|
||||||
|
if (node instanceof AST_Scope) {
|
||||||
|
for (var i = 0; i < node.body.length; ++i) {
|
||||||
|
var st = node.body[i];
|
||||||
|
if (!(st instanceof AST_Directive)) break;
|
||||||
|
if (st.value == type) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
in_boolean_context: function() {
|
in_boolean_context: function() {
|
||||||
var stack = this.stack;
|
var stack = this.stack;
|
||||||
var i = stack.length, self = stack[--i];
|
var i = stack.length, self = stack[--i];
|
||||||
@@ -748,4 +998,17 @@ TreeWalker.prototype = {
|
|||||||
self = p;
|
self = p;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
loopcontrol_target: function(label) {
|
||||||
|
var stack = this.stack;
|
||||||
|
if (label) for (var i = stack.length; --i >= 0;) {
|
||||||
|
var x = stack[i];
|
||||||
|
if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
|
||||||
|
return x.body;
|
||||||
|
}
|
||||||
|
} else for (var i = stack.length; --i >= 0;) {
|
||||||
|
var x = stack[i];
|
||||||
|
if (x instanceof AST_Switch || x instanceof AST_IterationStatement)
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
1645
lib/compress.js
1645
lib/compress.js
File diff suppressed because it is too large
Load Diff
@@ -46,37 +46,44 @@
|
|||||||
(function(){
|
(function(){
|
||||||
|
|
||||||
var MOZ_TO_ME = {
|
var MOZ_TO_ME = {
|
||||||
|
ExpressionStatement: function(M) {
|
||||||
|
var expr = M.expression;
|
||||||
|
if (expr.type === "Literal" && typeof expr.value === "string") {
|
||||||
|
return new AST_Directive({
|
||||||
|
start: my_start_token(M),
|
||||||
|
end: my_end_token(M),
|
||||||
|
value: expr.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new AST_SimpleStatement({
|
||||||
|
start: my_start_token(M),
|
||||||
|
end: my_end_token(M),
|
||||||
|
body: from_moz(expr)
|
||||||
|
});
|
||||||
|
},
|
||||||
TryStatement: function(M) {
|
TryStatement: function(M) {
|
||||||
|
var handlers = M.handlers || [M.handler];
|
||||||
|
if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) {
|
||||||
|
throw new Error("Multiple catch clauses are not supported.");
|
||||||
|
}
|
||||||
return new AST_Try({
|
return new AST_Try({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M),
|
end : my_end_token(M),
|
||||||
body : from_moz(M.block).body,
|
body : from_moz(M.block).body,
|
||||||
bcatch : from_moz(M.handlers[0]),
|
bcatch : from_moz(handlers[0]),
|
||||||
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
|
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
CatchClause : function(M) {
|
Property: function(M) {
|
||||||
return new AST_Catch({
|
var key = M.key;
|
||||||
start : my_start_token(M),
|
|
||||||
end : my_start_token(M),
|
|
||||||
argname : from_moz(M.param),
|
|
||||||
body : from_moz(M.body).body
|
|
||||||
});
|
|
||||||
},
|
|
||||||
ObjectExpression : function(M) {
|
|
||||||
return new AST_Object({
|
|
||||||
start : my_start_token(M),
|
|
||||||
end : my_end_token(M),
|
|
||||||
properties : M.properties.map(function(prop){
|
|
||||||
var key = prop.key;
|
|
||||||
var name = key.type == "Identifier" ? key.name : key.value;
|
var name = key.type == "Identifier" ? key.name : key.value;
|
||||||
var args = {
|
var args = {
|
||||||
start : my_start_token(key),
|
start : my_start_token(key),
|
||||||
end : my_end_token(prop.value),
|
end : my_end_token(M.value),
|
||||||
key : name,
|
key : name,
|
||||||
value : from_moz(prop.value)
|
value : from_moz(M.value)
|
||||||
};
|
};
|
||||||
switch (prop.kind) {
|
switch (M.kind) {
|
||||||
case "init":
|
case "init":
|
||||||
return new AST_ObjectKeyVal(args);
|
return new AST_ObjectKeyVal(args);
|
||||||
case "set":
|
case "set":
|
||||||
@@ -86,6 +93,14 @@
|
|||||||
args.value.name = from_moz(key);
|
args.value.name = from_moz(key);
|
||||||
return new AST_ObjectGetter(args);
|
return new AST_ObjectGetter(args);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
ObjectExpression: function(M) {
|
||||||
|
return new AST_Object({
|
||||||
|
start : my_start_token(M),
|
||||||
|
end : my_end_token(M),
|
||||||
|
properties : M.properties.map(function(prop){
|
||||||
|
prop.type = "Property";
|
||||||
|
return from_moz(prop)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -95,7 +110,7 @@
|
|||||||
MemberExpression: function(M) {
|
MemberExpression: function(M) {
|
||||||
return new (M.computed ? AST_Sub : AST_Dot)({
|
return new (M.computed ? AST_Sub : AST_Dot)({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_start_token(M),
|
end : my_end_token(M),
|
||||||
property : M.computed ? from_moz(M.property) : M.property.name,
|
property : M.computed ? from_moz(M.property) : M.property.name,
|
||||||
expression : from_moz(M.object)
|
expression : from_moz(M.object)
|
||||||
});
|
});
|
||||||
@@ -103,11 +118,18 @@
|
|||||||
SwitchCase: function(M) {
|
SwitchCase: function(M) {
|
||||||
return new (M.test ? AST_Case : AST_Default)({
|
return new (M.test ? AST_Case : AST_Default)({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_start_token(M),
|
end : my_end_token(M),
|
||||||
expression : from_moz(M.test),
|
expression : from_moz(M.test),
|
||||||
body : M.consequent.map(from_moz)
|
body : M.consequent.map(from_moz)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
VariableDeclaration: function(M) {
|
||||||
|
return new (M.kind === "const" ? AST_Const : AST_Var)({
|
||||||
|
start : my_start_token(M),
|
||||||
|
end : my_end_token(M),
|
||||||
|
definitions : M.declarations.map(from_moz)
|
||||||
|
});
|
||||||
|
},
|
||||||
Literal: function(M) {
|
Literal: function(M) {
|
||||||
var val = M.value, args = {
|
var val = M.value, args = {
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
@@ -124,19 +146,20 @@
|
|||||||
case "boolean":
|
case "boolean":
|
||||||
return new (val ? AST_True : AST_False)(args);
|
return new (val ? AST_True : AST_False)(args);
|
||||||
default:
|
default:
|
||||||
args.value = val;
|
var rx = M.regex;
|
||||||
var m = /\/(.*)\/(.*)/.exec(val+"");
|
if (rx && rx.pattern) {
|
||||||
args.pattern = m[1];
|
// RegExpLiteral as per ESTree AST spec
|
||||||
args.mods = m[2];
|
args.value = new RegExp(rx.pattern, rx.flags).toString();
|
||||||
|
} else {
|
||||||
|
// support legacy RegExp
|
||||||
|
args.value = M.regex && M.raw ? M.raw : val;
|
||||||
|
}
|
||||||
return new AST_RegExp(args);
|
return new AST_RegExp(args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UnaryExpression: From_Moz_Unary,
|
|
||||||
UpdateExpression: From_Moz_Unary,
|
|
||||||
Identifier: function(M) {
|
Identifier: function(M) {
|
||||||
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
|
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
|
||||||
return new (M.name == "this" ? AST_This
|
return new ( p.type == "LabeledStatement" ? AST_Label
|
||||||
: p.type == "LabeledStatement" ? AST_Label
|
|
||||||
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
|
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
|
||||||
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
|
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
|
||||||
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
|
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
|
||||||
@@ -150,23 +173,21 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function From_Moz_Unary(M) {
|
MOZ_TO_ME.UpdateExpression =
|
||||||
return new (M.prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) {
|
||||||
|
var prefix = "prefix" in M ? M.prefix
|
||||||
|
: M.type == "UnaryExpression" ? true : false;
|
||||||
|
return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M),
|
end : my_end_token(M),
|
||||||
operator : M.operator,
|
operator : M.operator,
|
||||||
expression : from_moz(M.argument)
|
expression : from_moz(M.argument)
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var ME_TO_MOZ = {};
|
|
||||||
|
|
||||||
map("Node", AST_Node);
|
|
||||||
map("Program", AST_Toplevel, "body@body");
|
map("Program", AST_Toplevel, "body@body");
|
||||||
map("Function", AST_Function, "id>name, params@argnames, body%body");
|
|
||||||
map("EmptyStatement", AST_EmptyStatement);
|
map("EmptyStatement", AST_EmptyStatement);
|
||||||
map("BlockStatement", AST_BlockStatement, "body@body");
|
map("BlockStatement", AST_BlockStatement, "body@body");
|
||||||
map("ExpressionStatement", AST_SimpleStatement, "expression>body");
|
|
||||||
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
|
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
|
||||||
map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
|
map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
|
||||||
map("BreakStatement", AST_Break, "label>label");
|
map("BreakStatement", AST_Break, "label>label");
|
||||||
@@ -181,73 +202,284 @@
|
|||||||
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
|
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
|
||||||
map("DebuggerStatement", AST_Debugger);
|
map("DebuggerStatement", AST_Debugger);
|
||||||
map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
|
map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
|
||||||
map("VariableDeclaration", AST_Var, "declarations@definitions");
|
|
||||||
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
|
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
|
||||||
|
map("CatchClause", AST_Catch, "param>argname, body%body");
|
||||||
|
|
||||||
map("ThisExpression", AST_This);
|
map("ThisExpression", AST_This);
|
||||||
map("ArrayExpression", AST_Array, "elements@elements");
|
map("ArrayExpression", AST_Array, "elements@elements");
|
||||||
map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
|
map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
|
||||||
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
|
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
|
||||||
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
|
|
||||||
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
|
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
|
||||||
|
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
|
||||||
map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
|
map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
|
||||||
map("NewExpression", AST_New, "callee>expression, arguments@args");
|
map("NewExpression", AST_New, "callee>expression, arguments@args");
|
||||||
map("CallExpression", AST_Call, "callee>expression, arguments@args");
|
map("CallExpression", AST_Call, "callee>expression, arguments@args");
|
||||||
|
|
||||||
|
def_to_moz(AST_Directive, function To_Moz_Directive(M) {
|
||||||
|
return {
|
||||||
|
type: "ExpressionStatement",
|
||||||
|
expression: {
|
||||||
|
type: "Literal",
|
||||||
|
value: M.value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) {
|
||||||
|
return {
|
||||||
|
type: "ExpressionStatement",
|
||||||
|
expression: to_moz(M.body)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) {
|
||||||
|
return {
|
||||||
|
type: "SwitchCase",
|
||||||
|
test: to_moz(M.expression),
|
||||||
|
consequent: M.body.map(to_moz)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Try, function To_Moz_TryStatement(M) {
|
||||||
|
return {
|
||||||
|
type: "TryStatement",
|
||||||
|
block: to_moz_block(M),
|
||||||
|
handler: to_moz(M.bcatch),
|
||||||
|
guardedHandlers: [],
|
||||||
|
finalizer: to_moz(M.bfinally)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Catch, function To_Moz_CatchClause(M) {
|
||||||
|
return {
|
||||||
|
type: "CatchClause",
|
||||||
|
param: to_moz(M.argname),
|
||||||
|
guard: null,
|
||||||
|
body: to_moz_block(M)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
|
||||||
|
return {
|
||||||
|
type: "VariableDeclaration",
|
||||||
|
kind: M instanceof AST_Const ? "const" : "var",
|
||||||
|
declarations: M.definitions.map(to_moz)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
|
||||||
|
return {
|
||||||
|
type: "SequenceExpression",
|
||||||
|
expressions: M.to_array().map(to_moz)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) {
|
||||||
|
var isComputed = M instanceof AST_Sub;
|
||||||
|
return {
|
||||||
|
type: "MemberExpression",
|
||||||
|
object: to_moz(M.expression),
|
||||||
|
computed: isComputed,
|
||||||
|
property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Unary, function To_Moz_Unary(M) {
|
||||||
|
return {
|
||||||
|
type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression",
|
||||||
|
operator: M.operator,
|
||||||
|
prefix: M instanceof AST_UnaryPrefix,
|
||||||
|
argument: to_moz(M.expression)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) {
|
||||||
|
return {
|
||||||
|
type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression",
|
||||||
|
left: to_moz(M.left),
|
||||||
|
operator: M.operator,
|
||||||
|
right: to_moz(M.right)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) {
|
||||||
|
return {
|
||||||
|
type: "ObjectExpression",
|
||||||
|
properties: M.properties.map(to_moz)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
|
||||||
|
var key = (
|
||||||
|
is_identifier(M.key)
|
||||||
|
? {type: "Identifier", name: M.key}
|
||||||
|
: {type: "Literal", value: M.key}
|
||||||
|
);
|
||||||
|
var kind;
|
||||||
|
if (M instanceof AST_ObjectKeyVal) {
|
||||||
|
kind = "init";
|
||||||
|
} else
|
||||||
|
if (M instanceof AST_ObjectGetter) {
|
||||||
|
kind = "get";
|
||||||
|
} else
|
||||||
|
if (M instanceof AST_ObjectSetter) {
|
||||||
|
kind = "set";
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "Property",
|
||||||
|
kind: kind,
|
||||||
|
key: key,
|
||||||
|
value: to_moz(M.value)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Symbol, function To_Moz_Identifier(M) {
|
||||||
|
var def = M.definition();
|
||||||
|
return {
|
||||||
|
type: "Identifier",
|
||||||
|
name: def ? def.mangled_name || def.name : M.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
|
||||||
|
var value = M.value;
|
||||||
|
return {
|
||||||
|
type: "Literal",
|
||||||
|
value: value,
|
||||||
|
raw: value.toString(),
|
||||||
|
regex: {
|
||||||
|
pattern: value.source,
|
||||||
|
flags: value.toString().match(/[gimuy]*$/)[0]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Constant, function To_Moz_Literal(M) {
|
||||||
|
var value = M.value;
|
||||||
|
if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
|
||||||
|
return {
|
||||||
|
type: "UnaryExpression",
|
||||||
|
operator: "-",
|
||||||
|
prefix: true,
|
||||||
|
argument: {
|
||||||
|
type: "Literal",
|
||||||
|
value: -value,
|
||||||
|
raw: M.start.raw
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "Literal",
|
||||||
|
value: value,
|
||||||
|
raw: M.start.raw
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Atom, function To_Moz_Atom(M) {
|
||||||
|
return {
|
||||||
|
type: "Identifier",
|
||||||
|
name: String(M.value)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
|
||||||
|
AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
|
||||||
|
AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null });
|
||||||
|
|
||||||
|
AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast);
|
||||||
|
AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast);
|
||||||
|
|
||||||
/* -----[ tools ]----- */
|
/* -----[ tools ]----- */
|
||||||
|
|
||||||
|
function raw_token(moznode) {
|
||||||
|
if (moznode.type == "Literal") {
|
||||||
|
return moznode.raw != null ? moznode.raw : moznode.value + "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function my_start_token(moznode) {
|
function my_start_token(moznode) {
|
||||||
|
var loc = moznode.loc, start = loc && loc.start;
|
||||||
|
var range = moznode.range;
|
||||||
return new AST_Token({
|
return new AST_Token({
|
||||||
file : moznode.loc && moznode.loc.source,
|
file : loc && loc.source,
|
||||||
line : moznode.loc && moznode.loc.start.line,
|
line : start && start.line,
|
||||||
col : moznode.loc && moznode.loc.start.column,
|
col : start && start.column,
|
||||||
pos : moznode.start,
|
pos : range ? range[0] : moznode.start,
|
||||||
endpos : moznode.start
|
endline : start && start.line,
|
||||||
|
endcol : start && start.column,
|
||||||
|
endpos : range ? range[0] : moznode.start,
|
||||||
|
raw : raw_token(moznode),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function my_end_token(moznode) {
|
function my_end_token(moznode) {
|
||||||
|
var loc = moznode.loc, end = loc && loc.end;
|
||||||
|
var range = moznode.range;
|
||||||
return new AST_Token({
|
return new AST_Token({
|
||||||
file : moznode.loc && moznode.loc.source,
|
file : loc && loc.source,
|
||||||
line : moznode.loc && moznode.loc.end.line,
|
line : end && end.line,
|
||||||
col : moznode.loc && moznode.loc.end.column,
|
col : end && end.column,
|
||||||
pos : moznode.end,
|
pos : range ? range[1] : moznode.end,
|
||||||
endpos : moznode.end
|
endline : end && end.line,
|
||||||
|
endcol : end && end.column,
|
||||||
|
endpos : range ? range[1] : moznode.end,
|
||||||
|
raw : raw_token(moznode),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function map(moztype, mytype, propmap) {
|
function map(moztype, mytype, propmap) {
|
||||||
var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
|
var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
|
||||||
moz_to_me += "return new mytype({\n" +
|
moz_to_me += "return new U2." + mytype.name + "({\n" +
|
||||||
"start: my_start_token(M),\n" +
|
"start: my_start_token(M),\n" +
|
||||||
"end: my_end_token(M)";
|
"end: my_end_token(M)";
|
||||||
|
|
||||||
|
var me_to_moz = "function To_Moz_" + moztype + "(M){\n";
|
||||||
|
me_to_moz += "return {\n" +
|
||||||
|
"type: " + JSON.stringify(moztype);
|
||||||
|
|
||||||
if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
|
if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
|
||||||
var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
|
var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
|
||||||
if (!m) throw new Error("Can't understand property map: " + prop);
|
if (!m) throw new Error("Can't understand property map: " + prop);
|
||||||
var moz = "M." + m[1], how = m[2], my = m[3];
|
var moz = m[1], how = m[2], my = m[3];
|
||||||
moz_to_me += ",\n" + my + ": ";
|
moz_to_me += ",\n" + my + ": ";
|
||||||
if (how == "@") {
|
me_to_moz += ",\n" + moz + ": ";
|
||||||
moz_to_me += moz + ".map(from_moz)";
|
switch (how) {
|
||||||
} else if (how == ">") {
|
case "@":
|
||||||
moz_to_me += "from_moz(" + moz + ")";
|
moz_to_me += "M." + moz + ".map(from_moz)";
|
||||||
} else if (how == "=") {
|
me_to_moz += "M." + my + ".map(to_moz)";
|
||||||
moz_to_me += moz;
|
break;
|
||||||
} else if (how == "%") {
|
case ">":
|
||||||
moz_to_me += "from_moz(" + moz + ").body";
|
moz_to_me += "from_moz(M." + moz + ")";
|
||||||
} else if (how == "@>") {
|
me_to_moz += "to_moz(M." + my + ")";
|
||||||
moz_to_me += "from_moz(" + moz + "[0])";
|
break;
|
||||||
} else throw new Error("Can't understand operator in propmap: " + prop);
|
case "=":
|
||||||
|
moz_to_me += "M." + moz;
|
||||||
|
me_to_moz += "M." + my;
|
||||||
|
break;
|
||||||
|
case "%":
|
||||||
|
moz_to_me += "from_moz(M." + moz + ").body";
|
||||||
|
me_to_moz += "to_moz_block(M)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Can't understand operator in propmap: " + prop);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
moz_to_me += "\n})}";
|
|
||||||
|
moz_to_me += "\n})\n}";
|
||||||
|
me_to_moz += "\n}\n}";
|
||||||
|
|
||||||
//moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
|
//moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
|
||||||
|
//me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
|
||||||
//console.log(moz_to_me);
|
//console.log(moz_to_me);
|
||||||
|
|
||||||
moz_to_me = new Function("mytype", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
|
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
|
||||||
mytype, my_start_token, my_end_token, from_moz
|
exports, my_start_token, my_end_token, from_moz
|
||||||
);
|
);
|
||||||
return MOZ_TO_ME[moztype] = moz_to_me;
|
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
|
||||||
|
to_moz, to_moz_block
|
||||||
|
);
|
||||||
|
MOZ_TO_ME[moztype] = moz_to_me;
|
||||||
|
def_to_moz(mytype, me_to_moz);
|
||||||
};
|
};
|
||||||
|
|
||||||
var FROM_MOZ_STACK = null;
|
var FROM_MOZ_STACK = null;
|
||||||
@@ -267,4 +499,39 @@
|
|||||||
return ast;
|
return ast;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function set_moz_loc(mynode, moznode, myparent) {
|
||||||
|
var start = mynode.start;
|
||||||
|
var end = mynode.end;
|
||||||
|
if (start.pos != null && end.endpos != null) {
|
||||||
|
moznode.range = [start.pos, end.endpos];
|
||||||
|
}
|
||||||
|
if (start.line) {
|
||||||
|
moznode.loc = {
|
||||||
|
start: {line: start.line, column: start.col},
|
||||||
|
end: end.endline ? {line: end.endline, column: end.endcol} : null
|
||||||
|
};
|
||||||
|
if (start.file) {
|
||||||
|
moznode.loc.source = start.file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moznode;
|
||||||
|
};
|
||||||
|
|
||||||
|
function def_to_moz(mytype, handler) {
|
||||||
|
mytype.DEFMETHOD("to_mozilla_ast", function() {
|
||||||
|
return set_moz_loc(this, handler(this));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function to_moz(node) {
|
||||||
|
return node != null ? node.to_mozilla_ast() : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function to_moz_block(node) {
|
||||||
|
return {
|
||||||
|
type: "BlockStatement",
|
||||||
|
body: node.body.map(to_moz)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
432
lib/output.js
432
lib/output.js
@@ -51,14 +51,20 @@ function OutputStream(options) {
|
|||||||
quote_keys : false,
|
quote_keys : false,
|
||||||
space_colon : true,
|
space_colon : true,
|
||||||
ascii_only : false,
|
ascii_only : false,
|
||||||
|
unescape_regexps : false,
|
||||||
inline_script : false,
|
inline_script : false,
|
||||||
width : 80,
|
width : 80,
|
||||||
max_line_len : 32000,
|
max_line_len : 32000,
|
||||||
ie_proof : true,
|
|
||||||
beautify : false,
|
beautify : false,
|
||||||
source_map : null,
|
source_map : null,
|
||||||
bracketize : false,
|
bracketize : false,
|
||||||
comments : false
|
semicolons : true,
|
||||||
|
comments : false,
|
||||||
|
shebang : true,
|
||||||
|
preserve_line : false,
|
||||||
|
screw_ie8 : false,
|
||||||
|
preamble : null,
|
||||||
|
quote_style : 0
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
var indentation = 0;
|
var indentation = 0;
|
||||||
@@ -67,47 +73,71 @@ function OutputStream(options) {
|
|||||||
var current_pos = 0;
|
var current_pos = 0;
|
||||||
var OUTPUT = "";
|
var OUTPUT = "";
|
||||||
|
|
||||||
function to_ascii(str) {
|
function to_ascii(str, identifier) {
|
||||||
return str.replace(/[\u0080-\uffff]/g, function(ch) {
|
return str.replace(/[\u0080-\uffff]/g, function(ch) {
|
||||||
var code = ch.charCodeAt(0).toString(16);
|
var code = ch.charCodeAt(0).toString(16);
|
||||||
|
if (code.length <= 2 && !identifier) {
|
||||||
|
while (code.length < 2) code = "0" + code;
|
||||||
|
return "\\x" + code;
|
||||||
|
} else {
|
||||||
while (code.length < 4) code = "0" + code;
|
while (code.length < 4) code = "0" + code;
|
||||||
return "\\u" + code;
|
return "\\u" + code;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function make_string(str) {
|
function make_string(str, quote) {
|
||||||
var dq = 0, sq = 0;
|
var dq = 0, sq = 0;
|
||||||
str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
|
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
|
||||||
switch (s) {
|
switch (s) {
|
||||||
case "\\": return "\\\\";
|
case "\\": return "\\\\";
|
||||||
case "\b": return "\\b";
|
case "\b": return "\\b";
|
||||||
case "\f": return "\\f";
|
case "\f": return "\\f";
|
||||||
case "\n": return "\\n";
|
case "\n": return "\\n";
|
||||||
case "\r": return "\\r";
|
case "\r": return "\\r";
|
||||||
|
case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
|
||||||
case "\u2028": return "\\u2028";
|
case "\u2028": return "\\u2028";
|
||||||
case "\u2029": return "\\u2029";
|
case "\u2029": return "\\u2029";
|
||||||
case '"': ++dq; return '"';
|
case '"': ++dq; return '"';
|
||||||
case "'": ++sq; return "'";
|
case "'": ++sq; return "'";
|
||||||
case "\0": return "\\0";
|
case "\0": return "\\x00";
|
||||||
|
case "\ufeff": return "\\ufeff";
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
|
function quote_single() {
|
||||||
|
return "'" + str.replace(/\x27/g, "\\'") + "'";
|
||||||
|
}
|
||||||
|
function quote_double() {
|
||||||
|
return '"' + str.replace(/\x22/g, '\\"') + '"';
|
||||||
|
}
|
||||||
if (options.ascii_only) str = to_ascii(str);
|
if (options.ascii_only) str = to_ascii(str);
|
||||||
if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
|
switch (options.quote_style) {
|
||||||
else return '"' + str.replace(/\x22/g, '\\"') + '"';
|
case 1:
|
||||||
|
return quote_single();
|
||||||
|
case 2:
|
||||||
|
return quote_double();
|
||||||
|
case 3:
|
||||||
|
return quote == "'" ? quote_single() : quote_double();
|
||||||
|
default:
|
||||||
|
return dq > sq ? quote_single() : quote_double();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function encode_string(str) {
|
function encode_string(str, quote) {
|
||||||
var ret = make_string(str);
|
var ret = make_string(str, quote);
|
||||||
if (options.inline_script)
|
if (options.inline_script) {
|
||||||
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
|
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
|
||||||
|
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
|
||||||
|
ret = ret.replace(/--\x3e/g, "--\\x3e");
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
function make_name(name) {
|
function make_name(name) {
|
||||||
name = name.toString();
|
name = name.toString();
|
||||||
if (options.ascii_only)
|
if (options.ascii_only)
|
||||||
name = to_ascii(name);
|
name = to_ascii(name, true);
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,20 +160,48 @@ function OutputStream(options) {
|
|||||||
print("\n");
|
print("\n");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var requireSemicolonChars = makePredicate("( [ + * / - , .");
|
||||||
|
|
||||||
function print(str) {
|
function print(str) {
|
||||||
str = String(str);
|
str = String(str);
|
||||||
var ch = str.charAt(0);
|
var ch = str.charAt(0);
|
||||||
if (might_need_semicolon) {
|
if (might_need_semicolon) {
|
||||||
if (";}".indexOf(ch) < 0 && !/[;]$/.test(last)) {
|
might_need_semicolon = false;
|
||||||
|
|
||||||
|
if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
|
||||||
|
if (options.semicolons || requireSemicolonChars(ch)) {
|
||||||
OUTPUT += ";";
|
OUTPUT += ";";
|
||||||
current_col++;
|
current_col++;
|
||||||
current_pos++;
|
current_pos++;
|
||||||
|
} else {
|
||||||
|
OUTPUT += "\n";
|
||||||
|
current_pos++;
|
||||||
|
current_line++;
|
||||||
|
current_col = 0;
|
||||||
|
|
||||||
|
if (/^\s+$/.test(str)) {
|
||||||
|
// reset the semicolon flag, since we didn't print one
|
||||||
|
// now and might still have to later
|
||||||
|
might_need_semicolon = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!options.beautify)
|
if (!options.beautify)
|
||||||
might_need_space = false;
|
might_need_space = false;
|
||||||
}
|
}
|
||||||
might_need_semicolon = false;
|
|
||||||
maybe_newline();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
|
||||||
|
var target_line = stack[stack.length - 1].start.line;
|
||||||
|
while (current_line < target_line) {
|
||||||
|
OUTPUT += "\n";
|
||||||
|
current_pos++;
|
||||||
|
current_line++;
|
||||||
|
current_col = 0;
|
||||||
|
might_need_space = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (might_need_space) {
|
if (might_need_space) {
|
||||||
var prev = last_char();
|
var prev = last_char();
|
||||||
if ((is_identifier_char(prev)
|
if ((is_identifier_char(prev)
|
||||||
@@ -191,7 +249,7 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
var newline = options.beautify ? function() {
|
var newline = options.beautify ? function() {
|
||||||
print("\n");
|
print("\n");
|
||||||
} : noop;
|
} : maybe_newline;
|
||||||
|
|
||||||
var semicolon = options.beautify ? function() {
|
var semicolon = options.beautify ? function() {
|
||||||
print(";");
|
print(";");
|
||||||
@@ -248,18 +306,33 @@ function OutputStream(options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var add_mapping = options.source_map ? function(token, name) {
|
var add_mapping = options.source_map ? function(token, name) {
|
||||||
options.source_map.add(
|
try {
|
||||||
token.file,
|
if (token) options.source_map.add(
|
||||||
|
token.file || "?",
|
||||||
current_line, current_col,
|
current_line, current_col,
|
||||||
token.line, token.col,
|
token.line, token.col,
|
||||||
(!name && token.type == "name") ? token.value : name
|
(!name && token.type == "name") ? token.value : name
|
||||||
);
|
);
|
||||||
|
} catch(ex) {
|
||||||
|
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
|
||||||
|
file: token.file,
|
||||||
|
line: token.line,
|
||||||
|
col: token.col,
|
||||||
|
cline: current_line,
|
||||||
|
ccol: current_col,
|
||||||
|
name: name || ""
|
||||||
|
})
|
||||||
|
}
|
||||||
} : noop;
|
} : noop;
|
||||||
|
|
||||||
function get() {
|
function get() {
|
||||||
return OUTPUT;
|
return OUTPUT;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (options.preamble) {
|
||||||
|
print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
|
||||||
|
}
|
||||||
|
|
||||||
var stack = [];
|
var stack = [];
|
||||||
return {
|
return {
|
||||||
get : get,
|
get : get,
|
||||||
@@ -276,8 +349,9 @@ function OutputStream(options) {
|
|||||||
last : function() { return last },
|
last : function() { return last },
|
||||||
semicolon : semicolon,
|
semicolon : semicolon,
|
||||||
force_semicolon : force_semicolon,
|
force_semicolon : force_semicolon,
|
||||||
|
to_ascii : to_ascii,
|
||||||
print_name : function(name) { print(make_name(name)) },
|
print_name : function(name) { print(make_name(name)) },
|
||||||
print_string : function(str) { print(encode_string(str)) },
|
print_string : function(str, quote) { print(encode_string(str, quote)) },
|
||||||
next_indent : next_indent,
|
next_indent : next_indent,
|
||||||
with_indent : with_indent,
|
with_indent : with_indent,
|
||||||
with_block : with_block,
|
with_block : with_block,
|
||||||
@@ -305,23 +379,32 @@ function OutputStream(options) {
|
|||||||
/* -----[ utils ]----- */
|
/* -----[ utils ]----- */
|
||||||
|
|
||||||
function DEFPRINT(nodetype, generator) {
|
function DEFPRINT(nodetype, generator) {
|
||||||
nodetype.DEFMETHOD("print", function(stream){
|
nodetype.DEFMETHOD("_codegen", generator);
|
||||||
var self = this;
|
};
|
||||||
stream.push_node(self);
|
|
||||||
if (self.needs_parens(stream)) {
|
var use_asm = false;
|
||||||
stream.with_parens(function(){
|
|
||||||
self.add_comments(stream);
|
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
||||||
self.add_source_map(stream);
|
var self = this, generator = self._codegen, prev_use_asm = use_asm;
|
||||||
generator(self, stream);
|
if (self instanceof AST_Directive && self.value == "use asm") {
|
||||||
});
|
use_asm = true;
|
||||||
} else {
|
}
|
||||||
|
function doit() {
|
||||||
self.add_comments(stream);
|
self.add_comments(stream);
|
||||||
self.add_source_map(stream);
|
self.add_source_map(stream);
|
||||||
generator(self, stream);
|
generator(self, stream);
|
||||||
}
|
}
|
||||||
|
stream.push_node(self);
|
||||||
|
if (force_parens || self.needs_parens(stream)) {
|
||||||
|
stream.with_parens(doit);
|
||||||
|
} else {
|
||||||
|
doit();
|
||||||
|
}
|
||||||
stream.pop_node();
|
stream.pop_node();
|
||||||
|
if (self instanceof AST_Lambda) {
|
||||||
|
use_asm = prev_use_asm;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
AST_Node.DEFMETHOD("print_to_string", function(options){
|
AST_Node.DEFMETHOD("print_to_string", function(options){
|
||||||
var s = OutputStream(options);
|
var s = OutputStream(options);
|
||||||
@@ -333,22 +416,52 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
AST_Node.DEFMETHOD("add_comments", function(output){
|
AST_Node.DEFMETHOD("add_comments", function(output){
|
||||||
var c = output.option("comments"), self = this;
|
var c = output.option("comments"), self = this;
|
||||||
if (c) {
|
|
||||||
var start = self.start;
|
var start = self.start;
|
||||||
if (start && !start._comments_dumped) {
|
if (start && !start._comments_dumped) {
|
||||||
start._comments_dumped = true;
|
start._comments_dumped = true;
|
||||||
var comments = start.comments_before;
|
var comments = start.comments_before || [];
|
||||||
if (c.test) {
|
|
||||||
|
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
|
||||||
|
// and https://github.com/mishoo/UglifyJS2/issues/372
|
||||||
|
if (self instanceof AST_Exit && self.value) {
|
||||||
|
self.value.walk(new TreeWalker(function(node){
|
||||||
|
if (node.start && node.start.comments_before) {
|
||||||
|
comments = comments.concat(node.start.comments_before);
|
||||||
|
node.start.comments_before = [];
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Function ||
|
||||||
|
node instanceof AST_Array ||
|
||||||
|
node instanceof AST_Object)
|
||||||
|
{
|
||||||
|
return true; // don't go inside.
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c) {
|
||||||
comments = comments.filter(function(comment) {
|
comments = comments.filter(function(comment) {
|
||||||
return c.test(comment.value);
|
return comment.type == "comment5";
|
||||||
|
});
|
||||||
|
} else if (c.test) {
|
||||||
|
comments = comments.filter(function(comment){
|
||||||
|
return comment.type == "comment5" || c.test(comment.value);
|
||||||
});
|
});
|
||||||
} else if (typeof c == "function") {
|
} else if (typeof c == "function") {
|
||||||
comments = comments.filter(function(comment){
|
comments = comments.filter(function(comment){
|
||||||
return c(self, comment);
|
return comment.type == "comment5" || c(self, comment);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep single line comments after nlb, after nlb
|
||||||
|
if (!output.option("beautify") && comments.length > 0 &&
|
||||||
|
/comment[134]/.test(comments[0].type) &&
|
||||||
|
output.col() !== 0 && comments[0].nlb)
|
||||||
|
{
|
||||||
|
output.print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
comments.forEach(function(c){
|
comments.forEach(function(c){
|
||||||
if (c.type == "comment1") {
|
if (/comment[134]/.test(c.type)) {
|
||||||
output.print("//" + c.value + "\n");
|
output.print("//" + c.value + "\n");
|
||||||
output.indent();
|
output.indent();
|
||||||
}
|
}
|
||||||
@@ -361,15 +474,24 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
else if (output.pos() === 0 && c.type == "comment5" && output.option("shebang")) {
|
||||||
|
output.print("#!" + c.value + "\n");
|
||||||
|
output.indent();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ PARENTHESES ]----- */
|
/* -----[ PARENTHESES ]----- */
|
||||||
|
|
||||||
function PARENS(nodetype, func) {
|
function PARENS(nodetype, func) {
|
||||||
|
if (Array.isArray(nodetype)) {
|
||||||
|
nodetype.forEach(function(nodetype){
|
||||||
|
PARENS(nodetype, func);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
nodetype.DEFMETHOD("needs_parens", func);
|
nodetype.DEFMETHOD("needs_parens", func);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PARENS(AST_Node, function(){
|
PARENS(AST_Node, function(){
|
||||||
@@ -388,12 +510,18 @@ function OutputStream(options) {
|
|||||||
return first_in_statement(output);
|
return first_in_statement(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PARENS([ AST_Unary, AST_Undefined ], function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
return p instanceof AST_PropAccess && p.expression === this;
|
||||||
|
});
|
||||||
|
|
||||||
PARENS(AST_Seq, function(output){
|
PARENS(AST_Seq, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|
||||||
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 7
|
|| p instanceof AST_Unary // !(foo, bar, baz)
|
||||||
|
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
|
||||||
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|
||||||
|| p instanceof AST_Dot // (1, {foo:2}).foo ==> 2
|
|| p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||||
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||||
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|
||||||
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
|
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||||
@@ -418,36 +546,61 @@ function OutputStream(options) {
|
|||||||
var so = this.operator, sp = PRECEDENCE[so];
|
var so = this.operator, sp = PRECEDENCE[so];
|
||||||
if (pp > sp
|
if (pp > sp
|
||||||
|| (pp == sp
|
|| (pp == sp
|
||||||
&& this === p.right
|
&& this === p.right)) {
|
||||||
&& !(so == po &&
|
|
||||||
(so == "*" ||
|
|
||||||
so == "&&" ||
|
|
||||||
so == "||")))) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// for (var i = (foo in bar);;); ← perhaps useless, but valid syntax
|
});
|
||||||
if (this.operator == "in") {
|
|
||||||
// the “NoIn” stuff :-\
|
PARENS(AST_PropAccess, function(output){
|
||||||
// UglifyJS 1.3.3 misses this one.
|
var p = output.parent();
|
||||||
if ((p instanceof AST_For || p instanceof AST_ForIn) && p.init === this)
|
if (p instanceof AST_New && p.expression === this) {
|
||||||
return true;
|
// i.e. new (foo.bar().baz)
|
||||||
if (p instanceof AST_VarDef) {
|
//
|
||||||
var v = output.parent(1), p2 = output.parent(2);
|
// if there's one call into this subtree, then we need
|
||||||
if ((p2 instanceof AST_For || p2 instanceof AST_ForIn) && p2.init === v)
|
// parens around it too, otherwise the call will be
|
||||||
|
// interpreted as passing the arguments to the upper New
|
||||||
|
// expression.
|
||||||
|
try {
|
||||||
|
this.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_Call) throw p;
|
||||||
|
}));
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== p) throw ex;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PARENS(AST_Call, function(output){
|
||||||
|
var p = output.parent(), p1;
|
||||||
|
if (p instanceof AST_New && p.expression === this)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// workaround for Safari bug.
|
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=123506
|
||||||
|
return this.expression instanceof AST_Function
|
||||||
|
&& p instanceof AST_PropAccess
|
||||||
|
&& p.expression === this
|
||||||
|
&& (p1 = output.parent(1)) instanceof AST_Assign
|
||||||
|
&& p1.left === p;
|
||||||
|
});
|
||||||
|
|
||||||
PARENS(AST_New, function(output){
|
PARENS(AST_New, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// (new Date).getTime();
|
if (no_constructor_parens(this, output)
|
||||||
if (p instanceof AST_Dot && no_constructor_parens(this, output))
|
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
|
||||||
|
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
function assign_and_conditional_paren_rules(output) {
|
PARENS(AST_Number, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
PARENS([ AST_Assign, AST_Conditional ], function (output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// !(a = false) → true
|
// !(a = false) → true
|
||||||
if (p instanceof AST_Unary)
|
if (p instanceof AST_Unary)
|
||||||
@@ -464,15 +617,12 @@ function OutputStream(options) {
|
|||||||
// (a = foo)["prop"] —or— (a = foo).prop
|
// (a = foo)["prop"] —or— (a = foo).prop
|
||||||
if (p instanceof AST_PropAccess && p.expression === this)
|
if (p instanceof AST_PropAccess && p.expression === this)
|
||||||
return true;
|
return true;
|
||||||
};
|
});
|
||||||
|
|
||||||
PARENS(AST_Assign, assign_and_conditional_paren_rules);
|
|
||||||
PARENS(AST_Conditional, assign_and_conditional_paren_rules);
|
|
||||||
|
|
||||||
/* -----[ PRINTERS ]----- */
|
/* -----[ PRINTERS ]----- */
|
||||||
|
|
||||||
DEFPRINT(AST_Directive, function(self, output){
|
DEFPRINT(AST_Directive, function(self, output){
|
||||||
output.print_string(self.value);
|
output.print_string(self.value, self.quote);
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Debugger, function(self, output){
|
DEFPRINT(AST_Debugger, function(self, output){
|
||||||
@@ -506,6 +656,7 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
DEFPRINT(AST_Toplevel, function(self, output){
|
DEFPRINT(AST_Toplevel, function(self, output){
|
||||||
display_body(self.body, true, output);
|
display_body(self.body, true, output);
|
||||||
|
output.print("");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_LabeledStatement, function(self, output){
|
DEFPRINT(AST_LabeledStatement, function(self, output){
|
||||||
self.label.print(output);
|
self.label.print(output);
|
||||||
@@ -553,8 +704,12 @@ function OutputStream(options) {
|
|||||||
output.print("for");
|
output.print("for");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function(){
|
output.with_parens(function(){
|
||||||
if (self.init) {
|
if (self.init && !(self.init instanceof AST_EmptyStatement)) {
|
||||||
|
if (self.init instanceof AST_Definitions) {
|
||||||
self.init.print(output);
|
self.init.print(output);
|
||||||
|
} else {
|
||||||
|
parenthesize_for_noin(self.init, output, true);
|
||||||
|
}
|
||||||
output.print(";");
|
output.print(";");
|
||||||
output.space();
|
output.space();
|
||||||
} else {
|
} else {
|
||||||
@@ -666,9 +821,9 @@ function OutputStream(options) {
|
|||||||
// to the inner IF). This function checks for this case and
|
// to the inner IF). This function checks for this case and
|
||||||
// adds the block brackets if needed.
|
// adds the block brackets if needed.
|
||||||
if (!self.body)
|
if (!self.body)
|
||||||
return output.semicolon();
|
return output.force_semicolon();
|
||||||
if (self.body instanceof AST_Do
|
if (self.body instanceof AST_Do
|
||||||
&& output.option("ie_proof")) {
|
&& !output.option("screw_ie8")) {
|
||||||
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
|
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
|
||||||
// croaks with "syntax error" on code like this: if (foo)
|
// croaks with "syntax error" on code like this: if (foo)
|
||||||
// do ... while(cond); else ... we need block brackets
|
// do ... while(cond); else ... we need block brackets
|
||||||
@@ -690,7 +845,7 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
self.body.print(output);
|
force_statement(self.body, output);
|
||||||
};
|
};
|
||||||
DEFPRINT(AST_If, function(self, output){
|
DEFPRINT(AST_If, function(self, output){
|
||||||
output.print("if");
|
output.print("if");
|
||||||
@@ -798,13 +953,32 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Const, function(self, output){
|
DEFPRINT(AST_Const, function(self, output){
|
||||||
self._do_print(output, "const");
|
self._do_print(output, "const");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function parenthesize_for_noin(node, output, noin) {
|
||||||
|
if (!noin) node.print(output);
|
||||||
|
else try {
|
||||||
|
// need to take some precautions here:
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/60
|
||||||
|
node.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_Binary && node.operator == "in")
|
||||||
|
throw output;
|
||||||
|
}));
|
||||||
|
node.print(output);
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== output) throw ex;
|
||||||
|
node.print(output, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
DEFPRINT(AST_VarDef, function(self, output){
|
DEFPRINT(AST_VarDef, function(self, output){
|
||||||
self.name.print(output);
|
self.name.print(output);
|
||||||
if (self.value) {
|
if (self.value) {
|
||||||
output.space();
|
output.space();
|
||||||
output.print("=");
|
output.print("=");
|
||||||
output.space();
|
output.space();
|
||||||
self.value.print(output);
|
var p = output.parent(1);
|
||||||
|
var noin = p instanceof AST_For || p instanceof AST_ForIn;
|
||||||
|
parenthesize_for_noin(self.value, output, noin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -823,7 +997,7 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_New, function(self, output){
|
DEFPRINT(AST_New, function(self, output){
|
||||||
output.print("new");
|
output.print("new");
|
||||||
output.space();
|
output.space();
|
||||||
AST_Call.prototype.print.call(self, output);
|
AST_Call.prototype._codegen(self, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Seq.DEFMETHOD("_do_print", function(output){
|
AST_Seq.DEFMETHOD("_do_print", function(output){
|
||||||
@@ -851,12 +1025,14 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Dot, function(self, output){
|
DEFPRINT(AST_Dot, function(self, output){
|
||||||
var expr = self.expression;
|
var expr = self.expression;
|
||||||
expr.print(output);
|
expr.print(output);
|
||||||
if (expr instanceof AST_Number) {
|
if (expr instanceof AST_Number && expr.getValue() >= 0) {
|
||||||
if (!/[xa-f.]/i.test(output.last())) {
|
if (!/[xa-f.]/i.test(output.last())) {
|
||||||
output.print(".");
|
output.print(".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output.print(".");
|
output.print(".");
|
||||||
|
// the name after dot would be mapped about here.
|
||||||
|
output.add_mapping(self.end);
|
||||||
output.print_name(self.property);
|
output.print_name(self.property);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Sub, function(self, output){
|
DEFPRINT(AST_Sub, function(self, output){
|
||||||
@@ -868,8 +1044,12 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_UnaryPrefix, function(self, output){
|
DEFPRINT(AST_UnaryPrefix, function(self, output){
|
||||||
var op = self.operator;
|
var op = self.operator;
|
||||||
output.print(op);
|
output.print(op);
|
||||||
if (/^[a-z]/i.test(op))
|
if (/^[a-z]/i.test(op)
|
||||||
|
|| (/[+-]$/.test(op)
|
||||||
|
&& self.expression instanceof AST_UnaryPrefix
|
||||||
|
&& /^[+-]/.test(self.expression.operator))) {
|
||||||
output.space();
|
output.space();
|
||||||
|
}
|
||||||
self.expression.print(output);
|
self.expression.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_UnaryPostfix, function(self, output){
|
DEFPRINT(AST_UnaryPostfix, function(self, output){
|
||||||
@@ -877,10 +1057,29 @@ function OutputStream(options) {
|
|||||||
output.print(self.operator);
|
output.print(self.operator);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Binary, function(self, output){
|
DEFPRINT(AST_Binary, function(self, output){
|
||||||
|
var op = self.operator;
|
||||||
self.left.print(output);
|
self.left.print(output);
|
||||||
|
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
|
||||||
|
&& self.left instanceof AST_UnaryPostfix
|
||||||
|
&& self.left.operator == "--") {
|
||||||
|
// space is mandatory to avoid outputting -->
|
||||||
|
output.print(" ");
|
||||||
|
} else {
|
||||||
|
// the space is optional depending on "beautify"
|
||||||
output.space();
|
output.space();
|
||||||
output.print(self.operator);
|
}
|
||||||
|
output.print(op);
|
||||||
|
if ((op == "<" || op == "<<")
|
||||||
|
&& self.right instanceof AST_UnaryPrefix
|
||||||
|
&& self.right.operator == "!"
|
||||||
|
&& self.right.expression instanceof AST_UnaryPrefix
|
||||||
|
&& self.right.expression.operator == "--") {
|
||||||
|
// space is mandatory to avoid outputting <!--
|
||||||
|
output.print(" ");
|
||||||
|
} else {
|
||||||
|
// the space is optional depending on "beautify"
|
||||||
output.space();
|
output.space();
|
||||||
|
}
|
||||||
self.right.print(output);
|
self.right.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Conditional, function(self, output){
|
DEFPRINT(AST_Conditional, function(self, output){
|
||||||
@@ -901,8 +1100,12 @@ function OutputStream(options) {
|
|||||||
if (len > 0) output.space();
|
if (len > 0) output.space();
|
||||||
a.forEach(function(exp, i){
|
a.forEach(function(exp, i){
|
||||||
if (i) output.comma();
|
if (i) output.comma();
|
||||||
if (!(exp instanceof AST_Undefined))
|
|
||||||
exp.print(output);
|
exp.print(output);
|
||||||
|
// If the final element is a hole, we need to make sure it
|
||||||
|
// doesn't look like a trailing comma, by inserting an actual
|
||||||
|
// trailing comma.
|
||||||
|
if (i === len - 1 && exp instanceof AST_Hole)
|
||||||
|
output.comma();
|
||||||
});
|
});
|
||||||
if (len > 0) output.space();
|
if (len > 0) output.space();
|
||||||
});
|
});
|
||||||
@@ -923,27 +1126,32 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||||
var key = self.key;
|
var key = self.key;
|
||||||
|
var quote = self.quote;
|
||||||
if (output.option("quote_keys")) {
|
if (output.option("quote_keys")) {
|
||||||
output.print_string(key);
|
output.print_string(key + "");
|
||||||
} else if ((typeof key == "number"
|
} else if ((typeof key == "number"
|
||||||
|| !output.option("beautify")
|
|| !output.option("beautify")
|
||||||
&& +key + "" == key)
|
&& +key + "" == key)
|
||||||
&& parseFloat(key) >= 0) {
|
&& parseFloat(key) >= 0) {
|
||||||
output.print(make_num(key));
|
output.print(make_num(key));
|
||||||
} else if (!is_identifier(key)) {
|
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
|
||||||
output.print_string(key);
|
|
||||||
} else {
|
|
||||||
output.print_name(key);
|
output.print_name(key);
|
||||||
|
} else {
|
||||||
|
output.print_string(key, quote);
|
||||||
}
|
}
|
||||||
output.colon();
|
output.colon();
|
||||||
self.value.print(output);
|
self.value.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_ObjectSetter, function(self, output){
|
DEFPRINT(AST_ObjectSetter, function(self, output){
|
||||||
output.print("set");
|
output.print("set");
|
||||||
|
output.space();
|
||||||
|
self.key.print(output);
|
||||||
self.value._do_print(output, true);
|
self.value._do_print(output, true);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_ObjectGetter, function(self, output){
|
DEFPRINT(AST_ObjectGetter, function(self, output){
|
||||||
output.print("get");
|
output.print("get");
|
||||||
|
output.space();
|
||||||
|
self.key.print(output);
|
||||||
self.value._do_print(output, true);
|
self.value._do_print(output, true);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Symbol, function(self, output){
|
DEFPRINT(AST_Symbol, function(self, output){
|
||||||
@@ -951,12 +1159,14 @@ function OutputStream(options) {
|
|||||||
output.print_name(def ? def.mangled_name || def.name : self.name);
|
output.print_name(def ? def.mangled_name || def.name : self.name);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Undefined, function(self, output){
|
DEFPRINT(AST_Undefined, function(self, output){
|
||||||
// XXX: should add more options for this
|
|
||||||
output.print("void 0");
|
output.print("void 0");
|
||||||
//output.print("[][0]");
|
});
|
||||||
|
DEFPRINT(AST_Hole, noop);
|
||||||
|
DEFPRINT(AST_Infinity, function(self, output){
|
||||||
|
output.print("Infinity");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_NaN, function(self, output){
|
DEFPRINT(AST_NaN, function(self, output){
|
||||||
output.print("0/0");
|
output.print("NaN");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_This, function(self, output){
|
DEFPRINT(AST_This, function(self, output){
|
||||||
output.print("this");
|
output.print("this");
|
||||||
@@ -965,16 +1175,60 @@ function OutputStream(options) {
|
|||||||
output.print(self.getValue());
|
output.print(self.getValue());
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_String, function(self, output){
|
DEFPRINT(AST_String, function(self, output){
|
||||||
output.print_string(self.getValue());
|
output.print_string(self.getValue(), self.quote);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Number, function(self, output){
|
DEFPRINT(AST_Number, function(self, output){
|
||||||
|
if (use_asm && self.start.raw != null) {
|
||||||
|
output.print(self.start.raw);
|
||||||
|
} else {
|
||||||
output.print(make_num(self.getValue()));
|
output.print(make_num(self.getValue()));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function regexp_safe_literal(code) {
|
||||||
|
return [
|
||||||
|
0x5c , // \
|
||||||
|
0x2f , // /
|
||||||
|
0x2e , // .
|
||||||
|
0x2b , // +
|
||||||
|
0x2a , // *
|
||||||
|
0x3f , // ?
|
||||||
|
0x28 , // (
|
||||||
|
0x29 , // )
|
||||||
|
0x5b , // [
|
||||||
|
0x5d , // ]
|
||||||
|
0x7b , // {
|
||||||
|
0x7d , // }
|
||||||
|
0x24 , // $
|
||||||
|
0x5e , // ^
|
||||||
|
0x3a , // :
|
||||||
|
0x7c , // |
|
||||||
|
0x21 , // !
|
||||||
|
0x0a , // \n
|
||||||
|
0x0d , // \r
|
||||||
|
0x00 , // \0
|
||||||
|
0xfeff , // Unicode BOM
|
||||||
|
0x2028 , // unicode "line separator"
|
||||||
|
0x2029 , // unicode "paragraph separator"
|
||||||
|
].indexOf(code) < 0;
|
||||||
|
};
|
||||||
|
|
||||||
DEFPRINT(AST_RegExp, function(self, output){
|
DEFPRINT(AST_RegExp, function(self, output){
|
||||||
output.print("/");
|
var str = self.getValue().toString();
|
||||||
output.print(self.pattern);
|
if (output.option("ascii_only")) {
|
||||||
output.print("/");
|
str = output.to_ascii(str);
|
||||||
if (self.mods) output.print(self.mods);
|
} else if (output.option("unescape_regexps")) {
|
||||||
|
str = str.split("\\\\").map(function(str){
|
||||||
|
return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
|
||||||
|
var code = parseInt(s.substr(2), 16);
|
||||||
|
return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
|
||||||
|
});
|
||||||
|
}).join("\\\\");
|
||||||
|
}
|
||||||
|
output.print(str);
|
||||||
|
var p = output.parent();
|
||||||
|
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
|
||||||
|
output.print(" ");
|
||||||
});
|
});
|
||||||
|
|
||||||
function force_statement(stat, output) {
|
function force_statement(stat, output) {
|
||||||
@@ -1005,7 +1259,7 @@ function OutputStream(options) {
|
|||||||
if (p instanceof AST_Statement && p.body === node)
|
if (p instanceof AST_Statement && p.body === node)
|
||||||
return true;
|
return true;
|
||||||
if ((p instanceof AST_Seq && p.car === node ) ||
|
if ((p instanceof AST_Seq && p.car === node ) ||
|
||||||
(p instanceof AST_Call && p.expression === node ) ||
|
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
|
||||||
(p instanceof AST_Dot && p.expression === node ) ||
|
(p instanceof AST_Dot && p.expression === node ) ||
|
||||||
(p instanceof AST_Sub && p.expression === node ) ||
|
(p instanceof AST_Sub && p.expression === node ) ||
|
||||||
(p instanceof AST_Conditional && p.condition === node ) ||
|
(p instanceof AST_Conditional && p.condition === node ) ||
|
||||||
@@ -1092,16 +1346,24 @@ function OutputStream(options) {
|
|||||||
DEFMAP(AST_Symbol, basic_sourcemap_gen);
|
DEFMAP(AST_Symbol, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_Jump, basic_sourcemap_gen);
|
DEFMAP(AST_Jump, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
|
DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
|
||||||
|
DEFMAP(AST_LabeledStatement, noop); // since the label symbol will mark it
|
||||||
DEFMAP(AST_Lambda, basic_sourcemap_gen);
|
DEFMAP(AST_Lambda, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_PropAccess, basic_sourcemap_gen);
|
|
||||||
DEFMAP(AST_Switch, basic_sourcemap_gen);
|
DEFMAP(AST_Switch, basic_sourcemap_gen);
|
||||||
|
DEFMAP(AST_SwitchBranch, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
|
DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_Toplevel, noop);
|
DEFMAP(AST_Toplevel, noop);
|
||||||
|
DEFMAP(AST_New, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_Try, basic_sourcemap_gen);
|
DEFMAP(AST_Try, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_Catch, basic_sourcemap_gen);
|
DEFMAP(AST_Catch, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_Finally, basic_sourcemap_gen);
|
DEFMAP(AST_Finally, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_Definitions, basic_sourcemap_gen);
|
DEFMAP(AST_Definitions, basic_sourcemap_gen);
|
||||||
DEFMAP(AST_Constant, basic_sourcemap_gen);
|
DEFMAP(AST_Constant, basic_sourcemap_gen);
|
||||||
|
DEFMAP(AST_ObjectSetter, function(self, output){
|
||||||
|
output.add_mapping(self.start, self.key.name);
|
||||||
|
});
|
||||||
|
DEFMAP(AST_ObjectGetter, function(self, output){
|
||||||
|
output.add_mapping(self.start, self.key.name);
|
||||||
|
});
|
||||||
DEFMAP(AST_ObjectProperty, function(self, output){
|
DEFMAP(AST_ObjectProperty, function(self, output){
|
||||||
output.add_mapping(self.start, self.key);
|
output.add_mapping(self.start, self.key);
|
||||||
});
|
});
|
||||||
|
|||||||
636
lib/parse.js
636
lib/parse.js
File diff suppressed because it is too large
Load Diff
223
lib/propmangle.js
Normal file
223
lib/propmangle.js
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
/***********************************************************************
|
||||||
|
|
||||||
|
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||||
|
https://github.com/mishoo/UglifyJS2
|
||||||
|
|
||||||
|
-------------------------------- (C) ---------------------------------
|
||||||
|
|
||||||
|
Author: Mihai Bazon
|
||||||
|
<mihai.bazon@gmail.com>
|
||||||
|
http://mihai.bazon.net/blog
|
||||||
|
|
||||||
|
Distributed under the BSD license:
|
||||||
|
|
||||||
|
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
|
|
||||||
|
***********************************************************************/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function find_builtins() {
|
||||||
|
var a = [];
|
||||||
|
[ Object, Array, Function, Number,
|
||||||
|
String, Boolean, Error, Math,
|
||||||
|
Date, RegExp
|
||||||
|
].forEach(function(ctor){
|
||||||
|
Object.getOwnPropertyNames(ctor).map(add);
|
||||||
|
if (ctor.prototype) {
|
||||||
|
Object.getOwnPropertyNames(ctor.prototype).map(add);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function add(name) {
|
||||||
|
push_uniq(a, name);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mangle_properties(ast, options) {
|
||||||
|
options = defaults(options, {
|
||||||
|
reserved : null,
|
||||||
|
cache : null,
|
||||||
|
only_cache : false,
|
||||||
|
regex : null
|
||||||
|
});
|
||||||
|
|
||||||
|
var reserved = options.reserved;
|
||||||
|
if (reserved == null)
|
||||||
|
reserved = find_builtins();
|
||||||
|
|
||||||
|
var cache = options.cache;
|
||||||
|
if (cache == null) {
|
||||||
|
cache = {
|
||||||
|
cname: -1,
|
||||||
|
props: new Dictionary()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var regex = options.regex;
|
||||||
|
|
||||||
|
var names_to_mangle = [];
|
||||||
|
var unmangleable = [];
|
||||||
|
|
||||||
|
// step 1: find candidates to mangle
|
||||||
|
ast.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_ObjectKeyVal) {
|
||||||
|
add(node.key);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_ObjectProperty) {
|
||||||
|
// setter or getter, since KeyVal is handled above
|
||||||
|
add(node.key.name);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Dot) {
|
||||||
|
if (this.parent() instanceof AST_Assign) {
|
||||||
|
add(node.property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Sub) {
|
||||||
|
if (this.parent() instanceof AST_Assign) {
|
||||||
|
addStrings(node.property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// step 2: transform the tree, renaming properties
|
||||||
|
return ast.transform(new TreeTransformer(function(node){
|
||||||
|
if (node instanceof AST_ObjectKeyVal) {
|
||||||
|
node.key = mangle(node.key);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_ObjectProperty) {
|
||||||
|
// setter or getter
|
||||||
|
node.key.name = mangle(node.key.name);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Dot) {
|
||||||
|
node.property = mangle(node.property);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Sub) {
|
||||||
|
node.property = mangleStrings(node.property);
|
||||||
|
}
|
||||||
|
// else if (node instanceof AST_String) {
|
||||||
|
// if (should_mangle(node.value)) {
|
||||||
|
// AST_Node.warn(
|
||||||
|
// "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
|
||||||
|
// file : node.start.file,
|
||||||
|
// line : node.start.line,
|
||||||
|
// col : node.start.col,
|
||||||
|
// prop : node.value
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}));
|
||||||
|
|
||||||
|
// only function declarations after this line
|
||||||
|
|
||||||
|
function can_mangle(name) {
|
||||||
|
if (unmangleable.indexOf(name) >= 0) return false;
|
||||||
|
if (reserved.indexOf(name) >= 0) return false;
|
||||||
|
if (options.only_cache) {
|
||||||
|
return cache.props.has(name);
|
||||||
|
}
|
||||||
|
if (/^[0-9.]+$/.test(name)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function should_mangle(name) {
|
||||||
|
if (regex && !regex.test(name)) return false;
|
||||||
|
if (reserved.indexOf(name) >= 0) return false;
|
||||||
|
return cache.props.has(name)
|
||||||
|
|| names_to_mangle.indexOf(name) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(name) {
|
||||||
|
if (can_mangle(name))
|
||||||
|
push_uniq(names_to_mangle, name);
|
||||||
|
|
||||||
|
if (!should_mangle(name)) {
|
||||||
|
push_uniq(unmangleable, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mangle(name) {
|
||||||
|
if (!should_mangle(name)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mangled = cache.props.get(name);
|
||||||
|
if (!mangled) {
|
||||||
|
do {
|
||||||
|
mangled = base54(++cache.cname);
|
||||||
|
} while (!can_mangle(mangled));
|
||||||
|
cache.props.set(name, mangled);
|
||||||
|
}
|
||||||
|
return mangled;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addStrings(node) {
|
||||||
|
var out = {};
|
||||||
|
try {
|
||||||
|
(function walk(node){
|
||||||
|
node.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_Seq) {
|
||||||
|
walk(node.cdr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_String) {
|
||||||
|
add(node.value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Conditional) {
|
||||||
|
walk(node.consequent);
|
||||||
|
walk(node.alternative);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw out;
|
||||||
|
}));
|
||||||
|
})(node);
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== out) throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mangleStrings(node) {
|
||||||
|
return node.transform(new TreeTransformer(function(node){
|
||||||
|
if (node instanceof AST_Seq) {
|
||||||
|
node.cdr = mangleStrings(node.cdr);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_String) {
|
||||||
|
node.value = mangle(node.value);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Conditional) {
|
||||||
|
node.consequent = mangleStrings(node.consequent);
|
||||||
|
node.alternative = mangleStrings(node.alternative);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
373
lib/scope.js
373
lib/scope.js
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function SymbolDef(scope, orig) {
|
function SymbolDef(scope, index, orig) {
|
||||||
this.name = orig.name;
|
this.name = orig.name;
|
||||||
this.orig = [ orig ];
|
this.orig = [ orig ];
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
@@ -52,91 +52,97 @@ function SymbolDef(scope, orig) {
|
|||||||
this.mangled_name = null;
|
this.mangled_name = null;
|
||||||
this.undeclared = false;
|
this.undeclared = false;
|
||||||
this.constant = false;
|
this.constant = false;
|
||||||
|
this.index = index;
|
||||||
};
|
};
|
||||||
|
|
||||||
SymbolDef.prototype = {
|
SymbolDef.prototype = {
|
||||||
unmangleable: function() {
|
unmangleable: function(options) {
|
||||||
return this.global || this.undeclared || this.scope.uses_eval || this.scope.uses_with;
|
if (!options) options = {};
|
||||||
|
|
||||||
|
return (this.global && !options.toplevel)
|
||||||
|
|| this.undeclared
|
||||||
|
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|
||||||
|
|| (options.keep_fnames
|
||||||
|
&& (this.orig[0] instanceof AST_SymbolLambda
|
||||||
|
|| this.orig[0] instanceof AST_SymbolDefun));
|
||||||
},
|
},
|
||||||
mangle: function() {
|
mangle: function(options) {
|
||||||
if (!this.mangled_name && !this.unmangleable())
|
var cache = options.cache && options.cache.props;
|
||||||
this.mangled_name = this.scope.next_mangled();
|
if (this.global && cache && cache.has(this.name)) {
|
||||||
|
this.mangled_name = cache.get(this.name);
|
||||||
|
}
|
||||||
|
else if (!this.mangled_name && !this.unmangleable(options)) {
|
||||||
|
var s = this.scope;
|
||||||
|
if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda)
|
||||||
|
s = s.parent_scope;
|
||||||
|
this.mangled_name = s.next_mangled(options, this);
|
||||||
|
if (this.global && cache) {
|
||||||
|
cache.set(this.name, this.mangled_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||||
// This does what ast_add_scope did in UglifyJS v1.
|
options = defaults(options, {
|
||||||
//
|
screw_ie8: false,
|
||||||
// Part of it could be done at parse time, but it would complicate
|
cache: null
|
||||||
// the parser (and it's already kinda complex). It's also worth
|
});
|
||||||
// having it separated because we might need to call it multiple
|
|
||||||
// times on the same tree.
|
|
||||||
|
|
||||||
// pass 1: setup scope chaining and handle definitions
|
// pass 1: setup scope chaining and handle definitions
|
||||||
var self = this;
|
var self = this;
|
||||||
var scope = self.parent_scope = null;
|
var scope = self.parent_scope = null;
|
||||||
var labels = {};
|
var labels = new Dictionary();
|
||||||
|
var defun = null;
|
||||||
|
var last_var_had_const_pragma = false;
|
||||||
|
var nesting = 0;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_Scope) {
|
if (options.screw_ie8 && node instanceof AST_Catch) {
|
||||||
node.init_scope_vars();
|
var save_scope = scope;
|
||||||
var save_scope = node.parent_scope = scope;
|
scope = new AST_Scope(node);
|
||||||
scope = node;
|
scope.init_scope_vars(nesting);
|
||||||
|
scope.parent_scope = save_scope;
|
||||||
descend();
|
descend();
|
||||||
scope = save_scope;
|
scope = save_scope;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Scope) {
|
||||||
|
node.init_scope_vars(nesting);
|
||||||
|
var save_scope = node.parent_scope = scope;
|
||||||
|
var save_defun = defun;
|
||||||
|
var save_labels = labels;
|
||||||
|
defun = scope = node;
|
||||||
|
labels = new Dictionary();
|
||||||
|
++nesting; descend(); --nesting;
|
||||||
|
scope = save_scope;
|
||||||
|
defun = save_defun;
|
||||||
|
labels = save_labels;
|
||||||
return true; // don't descend again in TreeWalker
|
return true; // don't descend again in TreeWalker
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Directive) {
|
if (node instanceof AST_LabeledStatement) {
|
||||||
node.scope = scope;
|
var l = node.label;
|
||||||
push_uniq(scope.directives, node.value);
|
if (labels.has(l.name)) {
|
||||||
return true;
|
throw new Error(string_template("Label {name} defined twice", l));
|
||||||
|
}
|
||||||
|
labels.set(l.name, l);
|
||||||
|
descend();
|
||||||
|
labels.del(l.name);
|
||||||
|
return true; // no descend again
|
||||||
}
|
}
|
||||||
if (node instanceof AST_With) {
|
if (node instanceof AST_With) {
|
||||||
for (var s = scope; s; s = s.parent_scope)
|
for (var s = scope; s; s = s.parent_scope)
|
||||||
s.uses_with = true;
|
s.uses_with = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabeledStatement) {
|
|
||||||
var l = node.label;
|
|
||||||
if (labels[l.name])
|
|
||||||
throw new Error(string_template("Label {name} defined twice", l));
|
|
||||||
labels[l.name] = l;
|
|
||||||
descend();
|
|
||||||
delete labels[l.name];
|
|
||||||
return true; // no descend again
|
|
||||||
}
|
|
||||||
if (node instanceof AST_SymbolDeclaration) {
|
|
||||||
node.init_scope_vars();
|
|
||||||
}
|
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
node.scope = scope;
|
node.scope = scope;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Label) {
|
if (node instanceof AST_Label) {
|
||||||
node.thedef = node;
|
node.thedef = node;
|
||||||
node.init_scope_vars();
|
node.references = [];
|
||||||
var p = tw.parent(); // AST_LabeledStatement
|
|
||||||
var block = p.body;
|
|
||||||
if (block instanceof AST_StatementWithBody)
|
|
||||||
block = block.body;
|
|
||||||
node.label_target = block;
|
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LoopControl) {
|
if (node instanceof AST_SymbolLambda) {
|
||||||
if (!node.label) {
|
defun.def_function(node);
|
||||||
var a = tw.stack, i = a.length - 1;
|
|
||||||
while (--i >= 0) {
|
|
||||||
var p = a[i];
|
|
||||||
if (p instanceof AST_For
|
|
||||||
|| p instanceof AST_ForIn
|
|
||||||
|| p instanceof AST_DWLoop
|
|
||||||
|| p instanceof AST_SwitchBranch) {
|
|
||||||
node.loopcontrol_target = p.body;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (node instanceof AST_SymbolLambda) {
|
|
||||||
scope.def_function(node);
|
|
||||||
node.init.push(tw.parent());
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolDefun) {
|
else if (node instanceof AST_SymbolDefun) {
|
||||||
// Careful here, the scope where this should be defined is
|
// Careful here, the scope where this should be defined is
|
||||||
@@ -144,27 +150,23 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// scope when we encounter the AST_Defun node (which is
|
// scope when we encounter the AST_Defun node (which is
|
||||||
// instanceof AST_Scope) but we get to the symbol a bit
|
// instanceof AST_Scope) but we get to the symbol a bit
|
||||||
// later.
|
// later.
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
(node.scope = defun.parent_scope).def_function(node);
|
||||||
node.init.push(tw.parent());
|
}
|
||||||
|
else if (node instanceof AST_Var) {
|
||||||
|
last_var_had_const_pragma = node.has_const_pragma();
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolVar
|
else if (node instanceof AST_SymbolVar
|
||||||
|| node instanceof AST_SymbolConst) {
|
|| node instanceof AST_SymbolConst) {
|
||||||
var def = scope.def_variable(node);
|
var def = defun.def_variable(node);
|
||||||
def.constant = node instanceof AST_SymbolConst;
|
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
|
||||||
def = tw.parent();
|
def.init = tw.parent().value;
|
||||||
if (def.value) node.init.push(def);
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
(options.screw_ie8 ? scope : defun)
|
||||||
// `catch` argument name should be visible only inside the
|
.def_variable(node);
|
||||||
// catch block. For a quick fix AST_Catch should inherit
|
|
||||||
// from AST_Scope. Keeping it this way because of IE,
|
|
||||||
// which doesn't obey the standard. (it introduces the
|
|
||||||
// identifier in the enclosing scope)
|
|
||||||
scope.def_variable(node);
|
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabelRef) {
|
else if (node instanceof AST_LabelRef) {
|
||||||
var sym = labels[node.name];
|
var sym = labels.get(node.name);
|
||||||
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
|
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
|
||||||
name: node.name,
|
name: node.name,
|
||||||
line: node.start.line,
|
line: node.start.line,
|
||||||
@@ -177,7 +179,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
|
|
||||||
// pass 2: find back references and eval
|
// pass 2: find back references and eval
|
||||||
var func = null;
|
var func = null;
|
||||||
var globals = self.globals = {};
|
var globals = self.globals = new Dictionary();
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_Lambda) {
|
if (node instanceof AST_Lambda) {
|
||||||
var prev_func = func;
|
var prev_func = func;
|
||||||
@@ -186,28 +188,30 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
func = prev_func;
|
func = prev_func;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabelRef) {
|
if (node instanceof AST_LoopControl && node.label) {
|
||||||
node.reference();
|
node.label.thedef.references.push(node);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
var name = node.name;
|
var name = node.name;
|
||||||
|
if (name == "eval" && tw.parent() instanceof AST_Call) {
|
||||||
|
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
|
||||||
|
s.uses_eval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
var sym = node.scope.find_variable(name);
|
var sym = node.scope.find_variable(name);
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
var g;
|
var g;
|
||||||
if (HOP(globals, name)) {
|
if (globals.has(name)) {
|
||||||
g = globals[name];
|
g = globals.get(name);
|
||||||
} else {
|
} else {
|
||||||
g = new SymbolDef(self, node);
|
g = new SymbolDef(self, globals.size(), node);
|
||||||
g.undeclared = true;
|
g.undeclared = true;
|
||||||
globals[name] = g;
|
g.global = true;
|
||||||
|
globals.set(name, g);
|
||||||
}
|
}
|
||||||
node.thedef = g;
|
node.thedef = g;
|
||||||
if (name == "eval") {
|
if (func && name == "arguments") {
|
||||||
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
|
|
||||||
s.uses_eval = true;
|
|
||||||
}
|
|
||||||
if (name == "arguments") {
|
|
||||||
func.uses_arguments = true;
|
func.uses_arguments = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -218,26 +222,30 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.walk(tw);
|
self.walk(tw);
|
||||||
|
|
||||||
|
if (options.cache) {
|
||||||
|
this.cname = options.cache.cname;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("init_scope_vars", function(){
|
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
||||||
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
|
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
||||||
this.variables = {}; // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
||||||
this.functions = {}; // map name to AST_SymbolDefun (functions defined in this scope)
|
|
||||||
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
||||||
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
||||||
this.parent_scope = null; // the parent scope
|
this.parent_scope = null; // the parent scope
|
||||||
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
|
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
|
||||||
this.cname = -1; // the current index for mangling functions/variables
|
this.cname = -1; // the current index for mangling functions/variables
|
||||||
});
|
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("strict", function(){
|
|
||||||
return this.has_directive("use strict");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||||
AST_Scope.prototype.init_scope_vars.call(this);
|
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||||
this.uses_arguments = false;
|
this.uses_arguments = false;
|
||||||
|
|
||||||
|
var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end });
|
||||||
|
var def = new SymbolDef(this, this.variables.size(), symbol);
|
||||||
|
this.variables.set(symbol.name, def);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_SymbolRef.DEFMETHOD("reference", function() {
|
AST_SymbolRef.DEFMETHOD("reference", function() {
|
||||||
@@ -246,75 +254,82 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
|
|||||||
var s = this.scope;
|
var s = this.scope;
|
||||||
while (s) {
|
while (s) {
|
||||||
push_uniq(s.enclosed, def);
|
push_uniq(s.enclosed, def);
|
||||||
|
if (s === def.scope) break;
|
||||||
s = s.parent_scope;
|
s = s.parent_scope;
|
||||||
}
|
}
|
||||||
});
|
this.frame = this.scope.nesting - def.scope.nesting;
|
||||||
|
|
||||||
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){
|
|
||||||
this.init = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
|
||||||
this.references = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_LabelRef.DEFMETHOD("reference", function(){
|
|
||||||
this.thedef.references.push(this);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("find_variable", function(name){
|
AST_Scope.DEFMETHOD("find_variable", function(name){
|
||||||
if (name instanceof AST_Symbol) name = name.name;
|
if (name instanceof AST_Symbol) name = name.name;
|
||||||
return HOP(this.variables, name)
|
return this.variables.get(name)
|
||||||
? this.variables[name]
|
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
||||||
: (this.parent_scope && this.parent_scope.find_variable(name));
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("has_directive", function(value){
|
|
||||||
return this.parent_scope && this.parent_scope.has_directive(value)
|
|
||||||
|| (this.directives.indexOf(value) >= 0 ? this : null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
||||||
this.functions[symbol.name] = this.def_variable(symbol);
|
this.functions.set(symbol.name, this.def_variable(symbol));
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
||||||
var def;
|
var def;
|
||||||
if (!HOP(this.variables, symbol.name)) {
|
if (!this.variables.has(symbol.name)) {
|
||||||
def = new SymbolDef(this, symbol);
|
def = new SymbolDef(this, this.variables.size(), symbol);
|
||||||
this.variables[symbol.name] = def;
|
this.variables.set(symbol.name, def);
|
||||||
def.global = !this.parent_scope;
|
def.global = !this.parent_scope;
|
||||||
} else {
|
} else {
|
||||||
def = this.variables[symbol.name];
|
def = this.variables.get(symbol.name);
|
||||||
def.orig.push(symbol);
|
def.orig.push(symbol);
|
||||||
}
|
}
|
||||||
return symbol.thedef = def;
|
return symbol.thedef = def;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("next_mangled", function(){
|
AST_Scope.DEFMETHOD("next_mangled", function(options){
|
||||||
var ext = this.enclosed, n = ext.length;
|
var ext = this.enclosed;
|
||||||
out: while (true) {
|
out: while (true) {
|
||||||
var m = base54(++this.cname);
|
var m = base54(++this.cname);
|
||||||
if (!is_identifier(m)) continue; // skip over "do"
|
if (!is_identifier(m)) continue; // skip over "do"
|
||||||
|
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
|
||||||
|
// shadow a name excepted from mangling.
|
||||||
|
if (options.except.indexOf(m) >= 0) continue;
|
||||||
|
|
||||||
// we must ensure that the mangled name does not shadow a name
|
// we must ensure that the mangled name does not shadow a name
|
||||||
// from some parent scope that is referenced in this or in
|
// from some parent scope that is referenced in this or in
|
||||||
// inner scopes.
|
// inner scopes.
|
||||||
for (var i = n; --i >= 0;) {
|
for (var i = ext.length; --i >= 0;) {
|
||||||
var sym = ext[i];
|
var sym = ext[i];
|
||||||
var name = sym.mangled_name || (sym.unmangleable() && sym.name);
|
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
|
||||||
if (m == name) continue out;
|
if (m == name) continue out;
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_Function.DEFMETHOD("next_mangled", function(options, def){
|
||||||
|
// #179, #326
|
||||||
|
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
|
||||||
|
// a function expression's argument cannot shadow the function expression's name
|
||||||
|
|
||||||
|
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
|
||||||
|
while (true) {
|
||||||
|
var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
|
||||||
|
if (!(tricky_def && tricky_def.mangled_name == name))
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("references", function(sym){
|
AST_Scope.DEFMETHOD("references", function(sym){
|
||||||
if (sym instanceof AST_Symbol) sym = sym.definition();
|
if (sym instanceof AST_Symbol) sym = sym.definition();
|
||||||
return this.enclosed.indexOf(sym) < 0 ? null : sym;
|
return this.enclosed.indexOf(sym) < 0 ? null : sym;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Symbol.DEFMETHOD("unmangleable", function(){
|
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||||
return this.definition().unmangleable();
|
return this.definition().unmangleable(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
// property accessors are not mangleable
|
||||||
|
AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// labels are always mangleable
|
// labels are always mangleable
|
||||||
@@ -347,22 +362,44 @@ AST_Symbol.DEFMETHOD("global", function(){
|
|||||||
return this.definition().global;
|
return this.definition().global;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_LoopControl.DEFMETHOD("target", function(){
|
AST_Var.DEFMETHOD("has_const_pragma", function() {
|
||||||
if (this.label) return this.label.definition().label_target;
|
var comments_before = this.start && this.start.comments_before;
|
||||||
return this.loopcontrol_target;
|
var lastComment = comments_before && comments_before[comments_before.length - 1];
|
||||||
|
return lastComment && /@const\b/.test(lastComment.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||||
|
return defaults(options, {
|
||||||
|
except : [],
|
||||||
|
eval : false,
|
||||||
|
sort : false,
|
||||||
|
toplevel : false,
|
||||||
|
screw_ie8 : false,
|
||||||
|
keep_fnames : false
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||||
options = defaults(options, {
|
options = this._default_mangler_options(options);
|
||||||
sort : false,
|
|
||||||
except : []
|
// Never mangle arguments
|
||||||
});
|
options.except.push('arguments');
|
||||||
|
|
||||||
// We only need to mangle declaration nodes. Special logic wired
|
// We only need to mangle declaration nodes. Special logic wired
|
||||||
// into the code generator will display the mangled name if it's
|
// into the code generator will display the mangled name if it's
|
||||||
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
||||||
// the AST_SymbolDeclaration that it points to).
|
// the AST_SymbolDeclaration that it points to).
|
||||||
var lname = -1;
|
var lname = -1;
|
||||||
var to_mangle = [];
|
var to_mangle = [];
|
||||||
|
|
||||||
|
if (options.cache) {
|
||||||
|
this.globals.each(function(symbol){
|
||||||
|
if (options.except.indexOf(symbol.name) < 0) {
|
||||||
|
to_mangle.push(symbol);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_LabeledStatement) {
|
if (node instanceof AST_LabeledStatement) {
|
||||||
// lname is incremented when we get to the AST_Label
|
// lname is incremented when we get to the AST_Label
|
||||||
@@ -372,17 +409,16 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||||||
return true; // don't descend again in TreeWalker
|
return true; // don't descend again in TreeWalker
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
var p = tw.parent();
|
var p = tw.parent(), a = [];
|
||||||
var is_setget = p instanceof AST_ObjectSetter || p instanceof AST_ObjectGetter;
|
node.variables.each(function(symbol){
|
||||||
var a = node.variables;
|
|
||||||
for (var i in a) if (HOP(a, i)) {
|
|
||||||
var symbol = a[i];
|
|
||||||
if (!(is_setget && symbol instanceof AST_SymbolLambda)) {
|
|
||||||
if (options.except.indexOf(symbol.name) < 0) {
|
if (options.except.indexOf(symbol.name) < 0) {
|
||||||
to_mangle.push(symbol);
|
a.push(symbol);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
if (options.sort) a.sort(function(a, b){
|
||||||
|
return b.references.length - a.references.length;
|
||||||
|
});
|
||||||
|
to_mangle.push.apply(to_mangle, a);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Label) {
|
if (node instanceof AST_Label) {
|
||||||
@@ -391,17 +427,21 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||||||
node.mangled_name = name;
|
node.mangled_name = name;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
|
||||||
|
to_mangle.push(node.definition());
|
||||||
|
return;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.walk(tw);
|
this.walk(tw);
|
||||||
|
|
||||||
if (options.sort) to_mangle = mergeSort(to_mangle, function(a, b){
|
|
||||||
return b.references.length - a.references.length;
|
|
||||||
});
|
|
||||||
|
|
||||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
to_mangle.forEach(function(def){ def.mangle(options) });
|
||||||
|
|
||||||
|
if (options.cache) {
|
||||||
|
options.cache.cname = this.cname;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){
|
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
||||||
|
options = this._default_mangler_options(options);
|
||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
if (node instanceof AST_Constant)
|
if (node instanceof AST_Constant)
|
||||||
base54.consider(node.print_to_string());
|
base54.consider(node.print_to_string());
|
||||||
@@ -459,7 +499,7 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){
|
|||||||
base54.consider("catch");
|
base54.consider("catch");
|
||||||
else if (node instanceof AST_Finally)
|
else if (node instanceof AST_Finally)
|
||||||
base54.consider("finally");
|
base54.consider("finally");
|
||||||
else if (node instanceof AST_Symbol && node.unmangleable())
|
else if (node instanceof AST_Symbol && node.unmangleable(options))
|
||||||
base54.consider(node.name);
|
base54.consider(node.name);
|
||||||
else if (node instanceof AST_Unary || node instanceof AST_Binary)
|
else if (node instanceof AST_Unary || node instanceof AST_Binary)
|
||||||
base54.consider(node.operator);
|
base54.consider(node.operator);
|
||||||
@@ -467,21 +507,21 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){
|
|||||||
base54.consider(node.property);
|
base54.consider(node.property);
|
||||||
});
|
});
|
||||||
this.walk(tw);
|
this.walk(tw);
|
||||||
|
base54.sort();
|
||||||
});
|
});
|
||||||
|
|
||||||
var base54 = (function() {
|
var base54 = (function() {
|
||||||
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
|
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
|
||||||
var chars, frequency;
|
var chars, frequency;
|
||||||
function reset() {
|
function reset() {
|
||||||
frequency = {};
|
frequency = Object.create(null);
|
||||||
chars = string.split("");
|
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
|
||||||
chars.map(function(ch){ frequency[ch] = 0 });
|
chars.forEach(function(ch){ frequency[ch] = 0 });
|
||||||
}
|
}
|
||||||
base54.consider = function(str){
|
base54.consider = function(str){
|
||||||
for (var i = str.length; --i >= 0;) {
|
for (var i = str.length; --i >= 0;) {
|
||||||
var ch = str.charAt(i);
|
var code = str.charCodeAt(i);
|
||||||
if (string.indexOf(ch) >= 0)
|
if (code in frequency) ++frequency[code];
|
||||||
++frequency[ch];
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
base54.sort = function() {
|
base54.sort = function() {
|
||||||
@@ -497,8 +537,10 @@ var base54 = (function() {
|
|||||||
base54.freq = function(){ return frequency };
|
base54.freq = function(){ return frequency };
|
||||||
function base54(num) {
|
function base54(num) {
|
||||||
var ret = "", base = 54;
|
var ret = "", base = 54;
|
||||||
|
num++;
|
||||||
do {
|
do {
|
||||||
ret += chars[num % base];
|
num--;
|
||||||
|
ret += String.fromCharCode(chars[num % base]);
|
||||||
num = Math.floor(num / base);
|
num = Math.floor(num / base);
|
||||||
base = 64;
|
base = 64;
|
||||||
} while (num > 0);
|
} while (num > 0);
|
||||||
@@ -524,8 +566,9 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
|
|||||||
// XXX: this also warns about JS standard names,
|
// XXX: this also warns about JS standard names,
|
||||||
// i.e. Object, Array, parseInt etc. Should add a list of
|
// i.e. Object, Array, parseInt etc. Should add a list of
|
||||||
// exceptions.
|
// exceptions.
|
||||||
AST_Node.warn("Undeclared symbol: {name} [{line},{col}]", {
|
AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
|
||||||
name: node.name,
|
name: node.name,
|
||||||
|
file: node.start.file,
|
||||||
line: node.start.line,
|
line: node.start.line,
|
||||||
col: node.start.col
|
col: node.start.col
|
||||||
});
|
});
|
||||||
@@ -540,9 +583,10 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
|
|||||||
if (sym
|
if (sym
|
||||||
&& (sym.undeclared()
|
&& (sym.undeclared()
|
||||||
|| (sym.global() && sym.scope !== sym.definition().scope))) {
|
|| (sym.global() && sym.scope !== sym.definition().scope))) {
|
||||||
AST_Node.warn("{msg}: {name} [{line},{col}]", {
|
AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
|
||||||
msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
|
msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
|
||||||
name: sym.name,
|
name: sym.name,
|
||||||
|
file: sym.start.file,
|
||||||
line: sym.start.line,
|
line: sym.start.line,
|
||||||
col: sym.start.col
|
col: sym.start.col
|
||||||
});
|
});
|
||||||
@@ -552,14 +596,16 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
|
|||||||
&& node instanceof AST_SymbolRef
|
&& node instanceof AST_SymbolRef
|
||||||
&& node.undeclared()
|
&& node.undeclared()
|
||||||
&& node.name == "eval") {
|
&& node.name == "eval") {
|
||||||
AST_Node.warn("Eval is used [{line},{col}]", node.start);
|
AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
|
||||||
}
|
}
|
||||||
if (options.unreferenced
|
if (options.unreferenced
|
||||||
&& node instanceof AST_SymbolDeclaration
|
&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
|
||||||
|
&& !(node instanceof AST_SymbolCatch)
|
||||||
&& node.unreferenced()) {
|
&& node.unreferenced()) {
|
||||||
AST_Node.warn("{type} {name} is declared but not referenced [{line},{col}]", {
|
AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
|
||||||
type: node instanceof AST_Label ? "Label" : "Symbol",
|
type: node instanceof AST_Label ? "Label" : "Symbol",
|
||||||
name: node.name,
|
name: node.name,
|
||||||
|
file: node.start.file,
|
||||||
line: node.start.line,
|
line: node.start.line,
|
||||||
col: node.start.col
|
col: node.start.col
|
||||||
});
|
});
|
||||||
@@ -567,8 +613,9 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
|
|||||||
if (options.func_arguments
|
if (options.func_arguments
|
||||||
&& node instanceof AST_Lambda
|
&& node instanceof AST_Lambda
|
||||||
&& node.uses_arguments) {
|
&& node.uses_arguments) {
|
||||||
AST_Node.warn("arguments used in function {name} [{line},{col}]", {
|
AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
|
||||||
name: node.name ? node.name.name : "anonymous",
|
name: node.name ? node.name.name : "anonymous",
|
||||||
|
file: node.start.file,
|
||||||
line: node.start.line,
|
line: node.start.line,
|
||||||
col: node.start.col
|
col: node.start.col
|
||||||
});
|
});
|
||||||
@@ -576,8 +623,10 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
|
|||||||
if (options.nested_defuns
|
if (options.nested_defuns
|
||||||
&& node instanceof AST_Defun
|
&& node instanceof AST_Defun
|
||||||
&& !(tw.parent() instanceof AST_Scope)) {
|
&& !(tw.parent() instanceof AST_Scope)) {
|
||||||
AST_Node.warn("Function {name} declared in nested statement [{line},{col}]", {
|
AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
|
||||||
name: node.name.name,
|
name: node.name.name,
|
||||||
|
type: tw.parent().TYPE,
|
||||||
|
file: node.start.file,
|
||||||
line: node.start.line,
|
line: node.start.line,
|
||||||
col: node.start.col
|
col: node.start.col
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ function SourceMap(options) {
|
|||||||
file : null,
|
file : null,
|
||||||
root : null,
|
root : null,
|
||||||
orig : null,
|
orig : null,
|
||||||
|
|
||||||
|
orig_line_diff : 0,
|
||||||
|
dest_line_diff : 0,
|
||||||
});
|
});
|
||||||
var generator = new MOZ_SourceMap.SourceMapGenerator({
|
var generator = new MOZ_SourceMap.SourceMapGenerator({
|
||||||
file : options.file,
|
file : options.file,
|
||||||
@@ -61,14 +64,17 @@ function SourceMap(options) {
|
|||||||
line: orig_line,
|
line: orig_line,
|
||||||
column: orig_col
|
column: orig_col
|
||||||
});
|
});
|
||||||
|
if (info.source === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
source = info.source;
|
source = info.source;
|
||||||
orig_line = info.line;
|
orig_line = info.line;
|
||||||
orig_col = info.column;
|
orig_col = info.column;
|
||||||
name = info.name;
|
name = info.name || name;
|
||||||
}
|
}
|
||||||
generator.addMapping({
|
generator.addMapping({
|
||||||
generated : { line: gen_line, column: gen_col },
|
generated : { line: gen_line + options.dest_line_diff, column: gen_col },
|
||||||
original : { line: orig_line, column: orig_col },
|
original : { line: orig_line + options.orig_line_diff, column: orig_col },
|
||||||
source : source,
|
source : source,
|
||||||
name : name
|
name : name
|
||||||
});
|
});
|
||||||
@@ -76,6 +82,6 @@ function SourceMap(options) {
|
|||||||
return {
|
return {
|
||||||
add : add,
|
add : add,
|
||||||
get : function() { return generator },
|
get : function() { return generator },
|
||||||
toString : function() { return generator.toString() }
|
toString : function() { return JSON.stringify(generator.toJSON()); }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Tree transformer helpers.
|
// Tree transformer helpers.
|
||||||
// XXX: eventually I should refactor the compressor to use this infrastructure.
|
|
||||||
|
|
||||||
function TreeTransformer(before, after) {
|
function TreeTransformer(before, after) {
|
||||||
TreeWalker.call(this);
|
TreeWalker.call(this);
|
||||||
@@ -59,19 +58,19 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
node.DEFMETHOD("transform", function(tw, in_list){
|
node.DEFMETHOD("transform", function(tw, in_list){
|
||||||
var x, y;
|
var x, y;
|
||||||
tw.push(this);
|
tw.push(this);
|
||||||
x = tw.before(this, descend, in_list);
|
if (tw.before) x = tw.before(this, descend, in_list);
|
||||||
if (x === undefined) {
|
if (x === undefined) {
|
||||||
if (!tw.after) {
|
if (!tw.after) {
|
||||||
x = this;
|
x = this;
|
||||||
descend(x, tw);
|
descend(x, tw);
|
||||||
} else {
|
} else {
|
||||||
x = this.clone();
|
tw.stack[tw.stack.length - 1] = x = this;
|
||||||
descend(x, tw);
|
descend(x, tw);
|
||||||
y = tw.after(x, in_list);
|
y = tw.after(x, in_list);
|
||||||
if (y !== undefined) x = y;
|
if (y !== undefined) x = y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tw.pop();
|
tw.pop(this);
|
||||||
return x;
|
return x;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -93,10 +92,6 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
self.body = self.body.transform(tw);
|
self.body = self.body.transform(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
_(AST_BlockStatement, function(self, tw){
|
|
||||||
self.body = do_list(self.body, tw);
|
|
||||||
});
|
|
||||||
|
|
||||||
_(AST_Block, function(self, tw){
|
_(AST_Block, function(self, tw){
|
||||||
self.body = do_list(self.body, tw);
|
self.body = do_list(self.body, tw);
|
||||||
});
|
});
|
||||||
@@ -164,6 +159,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
});
|
});
|
||||||
|
|
||||||
_(AST_VarDef, function(self, tw){
|
_(AST_VarDef, function(self, tw){
|
||||||
|
self.name = self.name.transform(tw);
|
||||||
if (self.value) self.value = self.value.transform(tw);
|
if (self.value) self.value = self.value.transform(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
140
lib/utils.js
140
lib/utils.js
@@ -43,21 +43,8 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function curry(f) {
|
|
||||||
var args = slice(arguments, 1);
|
|
||||||
return function() { return f.apply(this, args.concat(slice(arguments))); };
|
|
||||||
};
|
|
||||||
|
|
||||||
function prog1(ret) {
|
|
||||||
if (ret instanceof Function)
|
|
||||||
ret = ret();
|
|
||||||
for (var i = 1, n = arguments.length; --n > 0; ++i)
|
|
||||||
arguments[i]();
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
function array_to_hash(a) {
|
function array_to_hash(a) {
|
||||||
var ret = {};
|
var ret = Object.create(null);
|
||||||
for (var i = 0; i < a.length; ++i)
|
for (var i = 0; i < a.length; ++i)
|
||||||
ret[a[i]] = true;
|
ret[a[i]] = true;
|
||||||
return ret;
|
return ret;
|
||||||
@@ -85,10 +72,6 @@ function find_if(func, array) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function HOP(obj, prop) {
|
|
||||||
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
||||||
};
|
|
||||||
|
|
||||||
function repeat_string(str, i) {
|
function repeat_string(str, i) {
|
||||||
if (i <= 0) return "";
|
if (i <= 0) return "";
|
||||||
if (i == 1) return str;
|
if (i == 1) return str;
|
||||||
@@ -99,27 +82,36 @@ function repeat_string(str, i) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function DefaultsError(msg, defs) {
|
function DefaultsError(msg, defs) {
|
||||||
|
Error.call(this, msg);
|
||||||
this.msg = msg;
|
this.msg = msg;
|
||||||
this.defs = defs;
|
this.defs = defs;
|
||||||
};
|
};
|
||||||
|
DefaultsError.prototype = Object.create(Error.prototype);
|
||||||
|
DefaultsError.prototype.constructor = DefaultsError;
|
||||||
|
|
||||||
|
DefaultsError.croak = function(msg, defs) {
|
||||||
|
throw new DefaultsError(msg, defs);
|
||||||
|
};
|
||||||
|
|
||||||
function defaults(args, defs, croak) {
|
function defaults(args, defs, croak) {
|
||||||
if (args === true)
|
if (args === true)
|
||||||
args = {};
|
args = {};
|
||||||
var ret = args || {};
|
var ret = args || {};
|
||||||
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i))
|
if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
|
||||||
throw new DefaultsError("`" + i + "` is not a supported option", defs);
|
DefaultsError.croak("`" + i + "` is not a supported option", defs);
|
||||||
for (var i in defs) if (HOP(defs, i)) {
|
for (var i in defs) if (defs.hasOwnProperty(i)) {
|
||||||
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
|
ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
function merge(obj, ext) {
|
function merge(obj, ext) {
|
||||||
for (var i in ext) if (HOP(ext, i)) {
|
var count = 0;
|
||||||
|
for (var i in ext) if (ext.hasOwnProperty(i)) {
|
||||||
obj[i] = ext[i];
|
obj[i] = ext[i];
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
return obj;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
function noop() {};
|
function noop() {};
|
||||||
@@ -158,7 +150,7 @@ var MAP = (function(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (i in a) if (HOP(a, i)) if (doit()) break;
|
for (i in a) if (a.hasOwnProperty(i)) if (doit()) break;
|
||||||
}
|
}
|
||||||
return top.concat(ret);
|
return top.concat(ret);
|
||||||
};
|
};
|
||||||
@@ -183,6 +175,12 @@ function string_template(text, props) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function remove(array, el) {
|
||||||
|
for (var i = array.length; --i >= 0;) {
|
||||||
|
if (array[i] === el) array.splice(i, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function mergeSort(array, cmp) {
|
function mergeSort(array, cmp) {
|
||||||
if (array.length < 2) return array.slice();
|
if (array.length < 2) return array.slice();
|
||||||
function merge(a, b) {
|
function merge(a, b) {
|
||||||
@@ -218,3 +216,95 @@ function set_intersection(a, b) {
|
|||||||
return b.indexOf(el) >= 0;
|
return b.indexOf(el) >= 0;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// this function is taken from Acorn [1], written by Marijn Haverbeke
|
||||||
|
// [1] https://github.com/marijnh/acorn
|
||||||
|
function makePredicate(words) {
|
||||||
|
if (!(words instanceof Array)) words = words.split(" ");
|
||||||
|
var f = "", cats = [];
|
||||||
|
out: for (var i = 0; i < words.length; ++i) {
|
||||||
|
for (var j = 0; j < cats.length; ++j)
|
||||||
|
if (cats[j][0].length == words[i].length) {
|
||||||
|
cats[j].push(words[i]);
|
||||||
|
continue out;
|
||||||
|
}
|
||||||
|
cats.push([words[i]]);
|
||||||
|
}
|
||||||
|
function compareTo(arr) {
|
||||||
|
if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
|
||||||
|
f += "switch(str){";
|
||||||
|
for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
|
||||||
|
f += "return true}return false;";
|
||||||
|
}
|
||||||
|
// When there are more than three length categories, an outer
|
||||||
|
// switch first dispatches on the lengths, to save on comparisons.
|
||||||
|
if (cats.length > 3) {
|
||||||
|
cats.sort(function(a, b) {return b.length - a.length;});
|
||||||
|
f += "switch(str.length){";
|
||||||
|
for (var i = 0; i < cats.length; ++i) {
|
||||||
|
var cat = cats[i];
|
||||||
|
f += "case " + cat[0].length + ":";
|
||||||
|
compareTo(cat);
|
||||||
|
}
|
||||||
|
f += "}";
|
||||||
|
// Otherwise, simply generate a flat `switch` statement.
|
||||||
|
} else {
|
||||||
|
compareTo(words);
|
||||||
|
}
|
||||||
|
return new Function("str", f);
|
||||||
|
};
|
||||||
|
|
||||||
|
function all(array, predicate) {
|
||||||
|
for (var i = array.length; --i >= 0;)
|
||||||
|
if (!predicate(array[i]))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
function Dictionary() {
|
||||||
|
this._values = Object.create(null);
|
||||||
|
this._size = 0;
|
||||||
|
};
|
||||||
|
Dictionary.prototype = {
|
||||||
|
set: function(key, val) {
|
||||||
|
if (!this.has(key)) ++this._size;
|
||||||
|
this._values["$" + key] = val;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
add: function(key, val) {
|
||||||
|
if (this.has(key)) {
|
||||||
|
this.get(key).push(val);
|
||||||
|
} else {
|
||||||
|
this.set(key, [ val ]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
get: function(key) { return this._values["$" + key] },
|
||||||
|
del: function(key) {
|
||||||
|
if (this.has(key)) {
|
||||||
|
--this._size;
|
||||||
|
delete this._values["$" + key];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
has: function(key) { return ("$" + key) in this._values },
|
||||||
|
each: function(f) {
|
||||||
|
for (var i in this._values)
|
||||||
|
f(this._values[i], i.substr(1));
|
||||||
|
},
|
||||||
|
size: function() {
|
||||||
|
return this._size;
|
||||||
|
},
|
||||||
|
map: function(f) {
|
||||||
|
var ret = [];
|
||||||
|
for (var i in this._values)
|
||||||
|
ret.push(f(this._values[i], i.substr(1)));
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
toObject: function() { return this._values }
|
||||||
|
};
|
||||||
|
Dictionary.fromObject = function(obj) {
|
||||||
|
var dict = new Dictionary();
|
||||||
|
dict._size = merge(dict._values, obj);
|
||||||
|
return dict;
|
||||||
|
};
|
||||||
|
|||||||
128
npm-shrinkwrap.json
generated
Normal file
128
npm-shrinkwrap.json
generated
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
{
|
||||||
|
"name": "uglify-js",
|
||||||
|
"version": "2.4.24",
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"from": "abbrev@>=1.0.0 <2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz"
|
||||||
|
},
|
||||||
|
"amdefine": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"from": "amdefine@>=0.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
|
||||||
|
},
|
||||||
|
"async": {
|
||||||
|
"version": "0.2.10",
|
||||||
|
"from": "async@>=0.2.6 <0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
|
||||||
|
},
|
||||||
|
"camelcase": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"from": "camelcase@>=1.0.2 <2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
|
||||||
|
},
|
||||||
|
"decamelize": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"from": "decamelize@>=1.0.0 <2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz"
|
||||||
|
},
|
||||||
|
"deep-is": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"from": "deep-is@>=0.1.2 <0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz"
|
||||||
|
},
|
||||||
|
"esprima": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"from": "esprima@>=1.1.1 <1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz"
|
||||||
|
},
|
||||||
|
"estraverse": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"from": "estraverse@>=1.5.1 <1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz"
|
||||||
|
},
|
||||||
|
"esutils": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"from": "esutils@>=1.0.0 <1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz"
|
||||||
|
},
|
||||||
|
"fast-levenshtein": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"from": "fast-levenshtein@>=1.0.0 <1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz"
|
||||||
|
},
|
||||||
|
"levn": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"from": "levn@>=0.2.5 <0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz"
|
||||||
|
},
|
||||||
|
"nopt": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"from": "nopt@>=2.1.2 <2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz"
|
||||||
|
},
|
||||||
|
"optionator": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"from": "optionator@>=0.5.0 <0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz"
|
||||||
|
},
|
||||||
|
"prelude-ls": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"from": "prelude-ls@>=1.1.1 <1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz"
|
||||||
|
},
|
||||||
|
"reflect": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"from": "git://github.com/zaach/reflect.js.git",
|
||||||
|
"resolved": "git://github.com/zaach/reflect.js.git#286bcd79661c96ecc404357d3c0e35fdb54a6967"
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"from": "source-map@>=0.5.1 <0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.1.tgz"
|
||||||
|
},
|
||||||
|
"type-check": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"from": "type-check@>=0.3.1 <0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.1.tgz"
|
||||||
|
},
|
||||||
|
"uglify-js": {
|
||||||
|
"version": "2.4.24",
|
||||||
|
"from": "git://github.com/mishoo/UglifyJS2.git",
|
||||||
|
"resolved": "git://github.com/mishoo/UglifyJS2.git#2a06c7758e24a64740473c8031eafbb7fefa213f",
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.1.34",
|
||||||
|
"from": "source-map@0.1.34",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uglify-to-browserify": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"from": "uglify-to-browserify@>=1.0.0 <1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz"
|
||||||
|
},
|
||||||
|
"window-size": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"from": "window-size@0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
|
||||||
|
},
|
||||||
|
"wordwrap": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"from": "wordwrap@0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
|
||||||
|
},
|
||||||
|
"yargs": {
|
||||||
|
"version": "3.10.0",
|
||||||
|
"from": "yargs@>=3.10.0 <3.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz"
|
||||||
|
},
|
||||||
|
"zeparser": {
|
||||||
|
"version": "0.0.7",
|
||||||
|
"from": "git://github.com/qfox/ZeParser.git",
|
||||||
|
"resolved": "git://github.com/qfox/ZeParser.git#c99240c5ba7054c467733800ff38265958a2dda9"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
package.json
61
package.json
@@ -1,25 +1,54 @@
|
|||||||
{
|
{
|
||||||
"name": "uglify-js2",
|
"name": "uglify-js",
|
||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"main": "tools/node.js",
|
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||||
"version": "2.0.0",
|
"license": "BSD-2-Clause",
|
||||||
"engines": { "node" : ">=0.4.0" },
|
"version": "2.6.2",
|
||||||
"maintainers": [{
|
"engines": {
|
||||||
"name": "Mihai Bazon",
|
"node": ">=0.8.0"
|
||||||
"email": "mihai.bazon@gmail.com",
|
},
|
||||||
"web": "http://lisperator.net/"
|
"maintainers": [
|
||||||
}],
|
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
|
||||||
"repositories": [{
|
],
|
||||||
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mishoo/UglifyJS2.git"
|
"url": "https://github.com/mishoo/UglifyJS2.git"
|
||||||
}],
|
|
||||||
"dependencies": {
|
|
||||||
"source-map" : "*",
|
|
||||||
"optimist" : "*"
|
|
||||||
},
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/mishoo/UglifyJS2/issues"
|
||||||
|
},
|
||||||
|
"main": "tools/node.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"uglifyjs2" : "bin/uglifyjs2"
|
"uglifyjs": "bin/uglifyjs"
|
||||||
},
|
},
|
||||||
"scripts": {"test": "node test/run-tests.js"}
|
"files": [
|
||||||
|
"bin",
|
||||||
|
"lib",
|
||||||
|
"tools",
|
||||||
|
"LICENSE"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"async": "~0.2.6",
|
||||||
|
"source-map": "~0.5.1",
|
||||||
|
"uglify-to-browserify": "~1.0.0",
|
||||||
|
"yargs": "~3.10.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"acorn": "~0.6.0",
|
||||||
|
"escodegen": "~1.3.3",
|
||||||
|
"esfuzz": "~0.3.1",
|
||||||
|
"estraverse": "~1.5.1",
|
||||||
|
"mocha": "~2.3.4"
|
||||||
|
},
|
||||||
|
"browserify": {
|
||||||
|
"transform": [
|
||||||
|
"uglify-to-browserify"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
|
||||||
|
"test": "node test/run-tests.js"
|
||||||
|
},
|
||||||
|
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
|
||||||
}
|
}
|
||||||
|
|||||||
67
test/compress/angular-inject.js
vendored
Normal file
67
test/compress/angular-inject.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
ng_inject_defun: {
|
||||||
|
options = {
|
||||||
|
angular: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
/*@ngInject*/
|
||||||
|
function Controller(dependency) {
|
||||||
|
return dependency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function Controller(dependency) {
|
||||||
|
return dependency;
|
||||||
|
}
|
||||||
|
Controller.$inject=['dependency']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ng_inject_assignment: {
|
||||||
|
options = {
|
||||||
|
angular: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
/*@ngInject*/
|
||||||
|
var Controller = function(dependency) {
|
||||||
|
return dependency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var Controller = function(dependency) {
|
||||||
|
return dependency;
|
||||||
|
}
|
||||||
|
Controller.$inject=['dependency']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ng_inject_inline: {
|
||||||
|
options = {
|
||||||
|
angular: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
angular.module('a').
|
||||||
|
factory('b',
|
||||||
|
/*@ngInject*/
|
||||||
|
function(dependency) {
|
||||||
|
return dependency;
|
||||||
|
}).
|
||||||
|
directive('c',
|
||||||
|
/*@ngInject*/
|
||||||
|
function(anotherDependency) {
|
||||||
|
return anotherDependency;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
angular.module('a').
|
||||||
|
factory('b',[
|
||||||
|
'dependency',
|
||||||
|
function(dependency) {
|
||||||
|
return dependency;
|
||||||
|
}]).
|
||||||
|
directive('c',[
|
||||||
|
'anotherDependency',
|
||||||
|
function(anotherDependency) {
|
||||||
|
return anotherDependency;
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
}
|
||||||
74
test/compress/arrays.js
Normal file
74
test/compress/arrays.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
holes_and_undefined: {
|
||||||
|
input: {
|
||||||
|
w = [1,,];
|
||||||
|
x = [1, 2, undefined];
|
||||||
|
y = [1, , 2, ];
|
||||||
|
z = [1, undefined, 3];
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
w=[1,,];
|
||||||
|
x=[1,2,void 0];
|
||||||
|
y=[1,,2];
|
||||||
|
z=[1,void 0,3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_join: {
|
||||||
|
options = {
|
||||||
|
unsafe : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a = [ "foo", "bar", "baz" ].join("");
|
||||||
|
var a1 = [ "foo", "bar", "baz" ].join();
|
||||||
|
var b = [ "foo", 1, 2, 3, "bar" ].join("");
|
||||||
|
var c = [ boo(), "foo", 1, 2, 3, "bar", bar() ].join("");
|
||||||
|
var c1 = [ boo(), bar(), "foo", 1, 2, 3, "bar", bar() ].join("");
|
||||||
|
var c2 = [ 1, 2, "foo", "bar", baz() ].join("");
|
||||||
|
var d = [ "foo", 1 + 2 + "bar", "baz" ].join("-");
|
||||||
|
var e = [].join(foo + bar);
|
||||||
|
var f = [].join("");
|
||||||
|
var g = [].join("foo");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "foobarbaz";
|
||||||
|
var a1 = "foo,bar,baz";
|
||||||
|
var b = "foo123bar";
|
||||||
|
var c = boo() + "foo123bar" + bar();
|
||||||
|
var c1 = "" + boo() + bar() + "foo123bar" + bar();
|
||||||
|
var c2 = "12foobar" + baz();
|
||||||
|
var d = "foo-3bar-baz";
|
||||||
|
var e = [].join(foo + bar);
|
||||||
|
var f = "";
|
||||||
|
var g = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_join_2: {
|
||||||
|
options = {
|
||||||
|
unsafe : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a = [ "foo", "bar", boo(), "baz", "x", "y" ].join("");
|
||||||
|
var b = [ "foo", "bar", boo(), "baz", "x", "y" ].join("-");
|
||||||
|
var c = [ "foo", "bar", boo(), "baz", "x", "y" ].join("really-long-separator");
|
||||||
|
var d = [ "foo", "bar", boo(),
|
||||||
|
[ "foo", 1, 2, 3, "bar" ].join("+"),
|
||||||
|
"baz", "x", "y" ].join("-");
|
||||||
|
var e = [ "foo", "bar", boo(),
|
||||||
|
[ "foo", 1, 2, 3, "bar" ].join("+"),
|
||||||
|
"baz", "x", "y" ].join("really-long-separator");
|
||||||
|
var f = [ "str", "str" + variable, "foo", "bar", "moo" + foo ].join("");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "foobar" + boo() + "bazxy";
|
||||||
|
var b = [ "foo-bar", boo(), "baz-x-y" ].join("-");
|
||||||
|
var c = [ "foo", "bar", boo(), "baz", "x", "y" ].join("really-long-separator");
|
||||||
|
var d = [ "foo-bar", boo(), "foo+1+2+3+bar-baz-x-y" ].join("-");
|
||||||
|
var e = [ "foo", "bar", boo(),
|
||||||
|
"foo+1+2+3+bar",
|
||||||
|
"baz", "x", "y" ].join("really-long-separator");
|
||||||
|
var f = "strstr" + variable + "foobarmoo" + foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
106
test/compress/asm.js
Normal file
106
test/compress/asm.js
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
asm_mixed: {
|
||||||
|
options = {
|
||||||
|
sequences : true,
|
||||||
|
properties : true,
|
||||||
|
dead_code : true,
|
||||||
|
drop_debugger : true,
|
||||||
|
conditionals : true,
|
||||||
|
comparisons : true,
|
||||||
|
evaluate : true,
|
||||||
|
booleans : true,
|
||||||
|
loops : true,
|
||||||
|
unused : true,
|
||||||
|
hoist_funs : true,
|
||||||
|
keep_fargs : true,
|
||||||
|
keep_fnames : false,
|
||||||
|
hoist_vars : true,
|
||||||
|
if_return : true,
|
||||||
|
join_vars : true,
|
||||||
|
cascade : true,
|
||||||
|
side_effects : true,
|
||||||
|
negate_iife : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
// adapted from http://asmjs.org/spec/latest/
|
||||||
|
function asm_GeometricMean(stdlib, foreign, buffer) {
|
||||||
|
"use asm";
|
||||||
|
var exp = stdlib.Math.exp;
|
||||||
|
var log = stdlib.Math.log;
|
||||||
|
var values = new stdlib.Float64Array(buffer);
|
||||||
|
function logSum(start, end) {
|
||||||
|
start = start|0;
|
||||||
|
end = end|0;
|
||||||
|
var sum = 0.0, p = 0, q = 0;
|
||||||
|
// asm.js forces byte addressing of the heap by requiring shifting by 3
|
||||||
|
for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {
|
||||||
|
sum = sum + +log(values[p>>3]);
|
||||||
|
}
|
||||||
|
return +sum;
|
||||||
|
}
|
||||||
|
function geometricMean(start, end) {
|
||||||
|
start = start|0;
|
||||||
|
end = end|0;
|
||||||
|
return +exp(+logSum(start, end) / +((end - start)|0));
|
||||||
|
}
|
||||||
|
return { geometricMean: geometricMean };
|
||||||
|
}
|
||||||
|
function no_asm_GeometricMean(stdlib, foreign, buffer) {
|
||||||
|
var exp = stdlib.Math.exp;
|
||||||
|
var log = stdlib.Math.log;
|
||||||
|
var values = new stdlib.Float64Array(buffer);
|
||||||
|
function logSum(start, end) {
|
||||||
|
start = start|0;
|
||||||
|
end = end|0;
|
||||||
|
var sum = 0.0, p = 0, q = 0;
|
||||||
|
// asm.js forces byte addressing of the heap by requiring shifting by 3
|
||||||
|
for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {
|
||||||
|
sum = sum + +log(values[p>>3]);
|
||||||
|
}
|
||||||
|
return +sum;
|
||||||
|
}
|
||||||
|
function geometricMean(start, end) {
|
||||||
|
start = start|0;
|
||||||
|
end = end|0;
|
||||||
|
return +exp(+logSum(start, end) / +((end - start)|0));
|
||||||
|
}
|
||||||
|
return { geometricMean: geometricMean };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function asm_GeometricMean(stdlib, foreign, buffer) {
|
||||||
|
"use asm";
|
||||||
|
var exp = stdlib.Math.exp;
|
||||||
|
var log = stdlib.Math.log;
|
||||||
|
var values = new stdlib.Float64Array(buffer);
|
||||||
|
function logSum(start, end) {
|
||||||
|
start = start | 0;
|
||||||
|
end = end | 0;
|
||||||
|
var sum = 0.0, p = 0, q = 0;
|
||||||
|
for (p = start << 3, q = end << 3; (p | 0) < (q | 0); p = p + 8 | 0) {
|
||||||
|
sum = sum + +log(values[p >> 3]);
|
||||||
|
}
|
||||||
|
return +sum;
|
||||||
|
}
|
||||||
|
function geometricMean(start, end) {
|
||||||
|
start = start | 0;
|
||||||
|
end = end | 0;
|
||||||
|
return +exp(+logSum(start, end) / +(end - start | 0));
|
||||||
|
}
|
||||||
|
return { geometricMean: geometricMean };
|
||||||
|
}
|
||||||
|
function no_asm_GeometricMean(stdlib, foreign, buffer) {
|
||||||
|
function logSum(start, end) {
|
||||||
|
start = 0 | start, end = 0 | end;
|
||||||
|
var sum = 0, p = 0, q = 0;
|
||||||
|
for (p = start << 3, q = end << 3; (0 | q) > (0 | p); p = p + 8 | 0) sum += +log(values[p >> 3]);
|
||||||
|
return +sum;
|
||||||
|
}
|
||||||
|
function geometricMean(start, end) {
|
||||||
|
return start = 0 | start, end = 0 | end, +exp(+logSum(start, end) / +(end - start | 0));
|
||||||
|
}
|
||||||
|
var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
|
||||||
|
return { geometricMean: geometricMean };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1155
test/compress/collapse_vars.js
Normal file
1155
test/compress/collapse_vars.js
Normal file
File diff suppressed because it is too large
Load Diff
22
test/compress/concat-strings.js
Normal file
22
test/compress/concat-strings.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
concat_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a = "foo" + "bar" + x() + "moo" + "foo" + y() + "x" + "y" + "z" + q();
|
||||||
|
var b = "foo" + 1 + x() + 2 + "boo";
|
||||||
|
var c = 1 + x() + 2 + "boo";
|
||||||
|
|
||||||
|
// this CAN'T safely be shortened to 1 + x() + "5boo"
|
||||||
|
var d = 1 + x() + 2 + 3 + "boo";
|
||||||
|
|
||||||
|
var e = 1 + x() + 2 + "X" + 3 + "boo";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "foobar" + x() + "moofoo" + y() + "xyz" + q();
|
||||||
|
var b = "foo1" + x() + "2boo";
|
||||||
|
var c = 1 + x() + 2 + "boo";
|
||||||
|
var d = 1 + x() + 2 + 3 + "boo";
|
||||||
|
var e = 1 + x() + 2 + "X3boo";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,7 @@ ifs_3_should_warn: {
|
|||||||
booleans : true
|
booleans : true
|
||||||
};
|
};
|
||||||
input: {
|
input: {
|
||||||
|
var x, y;
|
||||||
if (x && !(x + "1") && y) { // 1
|
if (x && !(x + "1") && y) { // 1
|
||||||
var qq;
|
var qq;
|
||||||
foo();
|
foo();
|
||||||
@@ -68,6 +69,7 @@ ifs_3_should_warn: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
|
var x, y;
|
||||||
var qq; bar(); // 1
|
var qq; bar(); // 1
|
||||||
var jj; foo(); // 2
|
var jj; foo(); // 2
|
||||||
}
|
}
|
||||||
@@ -84,7 +86,9 @@ ifs_4: {
|
|||||||
x(foo)[10].bar.baz = something_else();
|
x(foo)[10].bar.baz = something_else();
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
x(foo)[10].bar.baz = (foo && bar) ? something() : something_else();
|
foo && bar
|
||||||
|
? x(foo)[10].bar.baz = something()
|
||||||
|
: x(foo)[10].bar.baz = something_else();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +135,7 @@ ifs_6: {
|
|||||||
comparisons: true
|
comparisons: true
|
||||||
};
|
};
|
||||||
input: {
|
input: {
|
||||||
|
var x;
|
||||||
if (!foo && !bar && !baz && !boo) {
|
if (!foo && !bar && !baz && !boo) {
|
||||||
x = 10;
|
x = 10;
|
||||||
} else {
|
} else {
|
||||||
@@ -138,6 +143,672 @@ ifs_6: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
|
var x;
|
||||||
x = foo || bar || baz || boo ? 20 : 10;
|
x = foo || bar || baz || boo ? 20 : 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cond_1: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var do_something; // if undeclared it's assumed to have side-effects
|
||||||
|
if (some_condition()) {
|
||||||
|
do_something(x);
|
||||||
|
} else {
|
||||||
|
do_something(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var do_something;
|
||||||
|
do_something(some_condition() ? x : y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_2: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var x, FooBar;
|
||||||
|
if (some_condition()) {
|
||||||
|
x = new FooBar(1);
|
||||||
|
} else {
|
||||||
|
x = new FooBar(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x, FooBar;
|
||||||
|
x = new FooBar(some_condition() ? 1 : 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_3: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var FooBar;
|
||||||
|
if (some_condition()) {
|
||||||
|
new FooBar(1);
|
||||||
|
} else {
|
||||||
|
FooBar(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var FooBar;
|
||||||
|
some_condition() ? new FooBar(1) : FooBar(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_4: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var do_something;
|
||||||
|
if (some_condition()) {
|
||||||
|
do_something();
|
||||||
|
} else {
|
||||||
|
do_something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var do_something;
|
||||||
|
some_condition(), do_something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_5: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if (some_condition()) {
|
||||||
|
if (some_other_condition()) {
|
||||||
|
do_something();
|
||||||
|
} else {
|
||||||
|
alternate();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alternate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (some_condition()) {
|
||||||
|
if (some_other_condition()) {
|
||||||
|
do_something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
some_condition() && some_other_condition() ? do_something() : alternate();
|
||||||
|
some_condition() && some_other_condition() && do_something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_7: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var x, y, z, a, b;
|
||||||
|
// compress these
|
||||||
|
if (y) {
|
||||||
|
x = 1+1;
|
||||||
|
} else {
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y) {
|
||||||
|
x = 1+1;
|
||||||
|
} else if (z) {
|
||||||
|
x = 2;
|
||||||
|
} else {
|
||||||
|
x = 3-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = y ? 'foo' : 'fo'+'o';
|
||||||
|
|
||||||
|
x = y ? 'foo' : y ? 'foo' : 'fo'+'o';
|
||||||
|
|
||||||
|
// Compress conditions that have side effects
|
||||||
|
if (condition()) {
|
||||||
|
x = 10+10;
|
||||||
|
} else {
|
||||||
|
x = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z) {
|
||||||
|
x = 'fuji';
|
||||||
|
} else if (condition()) {
|
||||||
|
x = 'fu'+'ji';
|
||||||
|
} else {
|
||||||
|
x = 'fuji';
|
||||||
|
}
|
||||||
|
|
||||||
|
x = condition() ? 'foobar' : 'foo'+'bar';
|
||||||
|
|
||||||
|
// don't compress these
|
||||||
|
x = y ? a : b;
|
||||||
|
|
||||||
|
x = y ? 'foo' : 'fo';
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x, y, z, a, b;
|
||||||
|
x = 2;
|
||||||
|
x = 2;
|
||||||
|
x = 'foo';
|
||||||
|
x = 'foo';
|
||||||
|
x = (condition(), 20);
|
||||||
|
x = z ? 'fuji' : (condition(), 'fuji');
|
||||||
|
x = (condition(), 'foobar');
|
||||||
|
x = y ? a : b;
|
||||||
|
x = y ? 'foo' : 'fo';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_7_1: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var x;
|
||||||
|
// access to global should be assumed to have side effects
|
||||||
|
if (y) {
|
||||||
|
x = 1+1;
|
||||||
|
} else {
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x;
|
||||||
|
x = (y, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_8: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true,
|
||||||
|
booleans : false
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
// compress these
|
||||||
|
a = condition ? true : false;
|
||||||
|
a = !condition ? true : false;
|
||||||
|
a = condition() ? true : false;
|
||||||
|
|
||||||
|
a = condition ? !0 : !1;
|
||||||
|
a = !condition ? !null : !2;
|
||||||
|
a = condition() ? !0 : !-3.5;
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = true;
|
||||||
|
} else {
|
||||||
|
a = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = !0;
|
||||||
|
} else {
|
||||||
|
a = !1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = condition ? false : true;
|
||||||
|
a = !condition ? false : true;
|
||||||
|
a = condition() ? false : true;
|
||||||
|
|
||||||
|
a = condition ? !3 : !0;
|
||||||
|
a = !condition ? !2 : !0;
|
||||||
|
a = condition() ? !1 : !0;
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = false;
|
||||||
|
} else {
|
||||||
|
a = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = !1;
|
||||||
|
} else {
|
||||||
|
a = !0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't compress these
|
||||||
|
a = condition ? 1 : false;
|
||||||
|
a = !condition ? true : 0;
|
||||||
|
a = condition ? 1 : 0;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition();
|
||||||
|
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition();
|
||||||
|
|
||||||
|
a = !!condition;
|
||||||
|
a = !!condition;
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition();
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition();
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !condition;
|
||||||
|
|
||||||
|
a = condition ? 1 : false;
|
||||||
|
a = condition ? 0 : true;
|
||||||
|
a = condition ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_8b: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true,
|
||||||
|
booleans : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
// compress these
|
||||||
|
a = condition ? true : false;
|
||||||
|
a = !condition ? true : false;
|
||||||
|
a = condition() ? true : false;
|
||||||
|
|
||||||
|
a = condition ? !0 : !1;
|
||||||
|
a = !condition ? !null : !2;
|
||||||
|
a = condition() ? !0 : !-3.5;
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = true;
|
||||||
|
} else {
|
||||||
|
a = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = !0;
|
||||||
|
} else {
|
||||||
|
a = !1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = condition ? false : true;
|
||||||
|
a = !condition ? false : true;
|
||||||
|
a = condition() ? false : true;
|
||||||
|
|
||||||
|
a = condition ? !3 : !0;
|
||||||
|
a = !condition ? !2 : !0;
|
||||||
|
a = condition() ? !1 : !0;
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = false;
|
||||||
|
} else {
|
||||||
|
a = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = !1;
|
||||||
|
} else {
|
||||||
|
a = !0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = condition ? 1 : false;
|
||||||
|
a = !condition ? true : 0;
|
||||||
|
a = condition ? 1 : 0;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition();
|
||||||
|
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition();
|
||||||
|
|
||||||
|
a = !!condition;
|
||||||
|
a = !!condition;
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition();
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition();
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !condition;
|
||||||
|
|
||||||
|
a = condition ? 1 : !1;
|
||||||
|
a = condition ? 0 : !0;
|
||||||
|
a = condition ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_8c: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : false,
|
||||||
|
booleans : false
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
// compress these
|
||||||
|
a = condition ? true : false;
|
||||||
|
a = !condition ? true : false;
|
||||||
|
a = condition() ? true : false;
|
||||||
|
|
||||||
|
a = condition ? !0 : !1;
|
||||||
|
a = !condition ? !null : !2;
|
||||||
|
a = condition() ? !0 : !-3.5;
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = true;
|
||||||
|
} else {
|
||||||
|
a = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = !0;
|
||||||
|
} else {
|
||||||
|
a = !1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = condition ? false : true;
|
||||||
|
a = !condition ? false : true;
|
||||||
|
a = condition() ? false : true;
|
||||||
|
|
||||||
|
a = condition ? !3 : !0;
|
||||||
|
a = !condition ? !2 : !0;
|
||||||
|
a = condition() ? !1 : !0;
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = false;
|
||||||
|
} else {
|
||||||
|
a = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
a = !1;
|
||||||
|
} else {
|
||||||
|
a = !0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = condition ? 1 : false;
|
||||||
|
a = !condition ? true : 0;
|
||||||
|
a = condition ? 1 : 0;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition();
|
||||||
|
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition;
|
||||||
|
a = condition() ? !0 : !-3.5;
|
||||||
|
|
||||||
|
a = !!condition;
|
||||||
|
a = !!condition;
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition();
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !!condition;
|
||||||
|
a = !condition();
|
||||||
|
|
||||||
|
a = !condition;
|
||||||
|
a = !condition;
|
||||||
|
|
||||||
|
a = condition ? 1 : false;
|
||||||
|
a = condition ? 0 : true;
|
||||||
|
a = condition ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conditional_and: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
// compress these
|
||||||
|
|
||||||
|
a = true && condition;
|
||||||
|
a = 1 && console.log("a");
|
||||||
|
a = 2 * 3 && 2 * condition;
|
||||||
|
a = 5 == 5 && condition + 3;
|
||||||
|
a = "string" && 4 - condition;
|
||||||
|
a = 5 + "" && condition / 5;
|
||||||
|
a = -4.5 && 6 << condition;
|
||||||
|
a = 6 && 7;
|
||||||
|
|
||||||
|
a = false && condition;
|
||||||
|
a = NaN && console.log("b");
|
||||||
|
a = 0 && console.log("c");
|
||||||
|
a = undefined && 2 * condition;
|
||||||
|
a = null && condition + 3;
|
||||||
|
a = 2 * 3 - 6 && 4 - condition;
|
||||||
|
a = 10 == 7 && condition / 5;
|
||||||
|
a = !"string" && 6 % condition;
|
||||||
|
a = 0 && 7;
|
||||||
|
|
||||||
|
// don't compress these
|
||||||
|
|
||||||
|
a = condition && true;
|
||||||
|
a = console.log("a") && 2;
|
||||||
|
a = 4 - condition && "string";
|
||||||
|
a = 6 << condition && -4.5;
|
||||||
|
|
||||||
|
a = condition && false;
|
||||||
|
a = console.log("b") && NaN;
|
||||||
|
a = console.log("c") && 0;
|
||||||
|
a = 2 * condition && undefined;
|
||||||
|
a = condition + 3 && null;
|
||||||
|
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
|
||||||
|
a = condition;
|
||||||
|
a = console.log("a");
|
||||||
|
a = 2 * condition;
|
||||||
|
a = condition + 3;
|
||||||
|
a = 4 - condition;
|
||||||
|
a = condition / 5;
|
||||||
|
a = 6 << condition;
|
||||||
|
a = 7;
|
||||||
|
|
||||||
|
a = false;
|
||||||
|
a = NaN;
|
||||||
|
a = 0;
|
||||||
|
a = void 0;
|
||||||
|
a = null;
|
||||||
|
a = 0;
|
||||||
|
a = false;
|
||||||
|
a = false;
|
||||||
|
a = 0;
|
||||||
|
|
||||||
|
a = condition && true;
|
||||||
|
a = console.log("a") && 2;
|
||||||
|
a = 4 - condition && "string";
|
||||||
|
a = 6 << condition && -4.5;
|
||||||
|
|
||||||
|
a = condition && false;
|
||||||
|
a = console.log("b") && NaN;
|
||||||
|
a = console.log("c") && 0;
|
||||||
|
a = 2 * condition && void 0;
|
||||||
|
a = condition + 3 && null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conditional_or: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
// compress these
|
||||||
|
|
||||||
|
a = true || condition;
|
||||||
|
a = 1 || console.log("a");
|
||||||
|
a = 2 * 3 || 2 * condition;
|
||||||
|
a = 5 == 5 || condition + 3;
|
||||||
|
a = "string" || 4 - condition;
|
||||||
|
a = 5 + "" || condition / 5;
|
||||||
|
a = -4.5 || 6 << condition;
|
||||||
|
a = 6 || 7;
|
||||||
|
|
||||||
|
a = false || condition;
|
||||||
|
a = 0 || console.log("b");
|
||||||
|
a = NaN || console.log("c");
|
||||||
|
a = undefined || 2 * condition;
|
||||||
|
a = null || condition + 3;
|
||||||
|
a = 2 * 3 - 6 || 4 - condition;
|
||||||
|
a = 10 == 7 || condition / 5;
|
||||||
|
a = !"string" || 6 % condition;
|
||||||
|
a = null || 7;
|
||||||
|
|
||||||
|
a = console.log(undefined && condition || null);
|
||||||
|
a = console.log(undefined || condition && null);
|
||||||
|
|
||||||
|
// don't compress these
|
||||||
|
|
||||||
|
a = condition || true;
|
||||||
|
a = console.log("a") || 2;
|
||||||
|
a = 4 - condition || "string";
|
||||||
|
a = 6 << condition || -4.5;
|
||||||
|
|
||||||
|
a = condition || false;
|
||||||
|
a = console.log("b") || NaN;
|
||||||
|
a = console.log("c") || 0;
|
||||||
|
a = 2 * condition || undefined;
|
||||||
|
a = condition + 3 || null;
|
||||||
|
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
|
||||||
|
a = true;
|
||||||
|
a = 1;
|
||||||
|
a = 6;
|
||||||
|
a = true;
|
||||||
|
a = "string";
|
||||||
|
a = "5";
|
||||||
|
a = -4.5;
|
||||||
|
a = 6;
|
||||||
|
|
||||||
|
a = condition;
|
||||||
|
a = console.log("b");
|
||||||
|
a = console.log("c");
|
||||||
|
a = 2 * condition;
|
||||||
|
a = condition + 3;
|
||||||
|
a = 4 - condition;
|
||||||
|
a = condition / 5;
|
||||||
|
a = 6 % condition;
|
||||||
|
a = 7;
|
||||||
|
|
||||||
|
a = console.log(null);
|
||||||
|
a = console.log(condition && null);
|
||||||
|
|
||||||
|
a = condition || true;
|
||||||
|
a = console.log("a") || 2;
|
||||||
|
a = 4 - condition || "string";
|
||||||
|
a = 6 << condition || -4.5;
|
||||||
|
|
||||||
|
a = condition || false;
|
||||||
|
a = console.log("b") || NaN;
|
||||||
|
a = console.log("c") || 0;
|
||||||
|
a = 2 * condition || void 0;
|
||||||
|
a = condition + 3 || null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trivial_boolean_ternary_expressions : {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true,
|
||||||
|
booleans : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
f('foo' in m ? true : false);
|
||||||
|
f('foo' in m ? false : true);
|
||||||
|
|
||||||
|
f(g ? true : false);
|
||||||
|
f(foo() ? true : false);
|
||||||
|
f("bar" ? true : false);
|
||||||
|
f(5 ? true : false);
|
||||||
|
f(5.7 ? true : false);
|
||||||
|
f(x - y ? true : false);
|
||||||
|
|
||||||
|
f(x == y ? true : false);
|
||||||
|
f(x === y ? !0 : !1);
|
||||||
|
f(x < y ? !0 : false);
|
||||||
|
f(x <= y ? true : false);
|
||||||
|
f(x > y ? true : !1);
|
||||||
|
f(x >= y ? !0 : !1);
|
||||||
|
|
||||||
|
f(g ? false : true);
|
||||||
|
f(foo() ? false : true);
|
||||||
|
f("bar" ? false : true);
|
||||||
|
f(5 ? false : true);
|
||||||
|
f(5.7 ? false : true);
|
||||||
|
f(x - y ? false : true);
|
||||||
|
|
||||||
|
f(x == y ? !1 : !0);
|
||||||
|
f(x === y ? false : true);
|
||||||
|
|
||||||
|
f(x < y ? false : true);
|
||||||
|
f(x <= y ? false : !0);
|
||||||
|
f(x > y ? !1 : true);
|
||||||
|
f(x >= y ? !1 : !0);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
f('foo' in m);
|
||||||
|
f(!('foo' in m));
|
||||||
|
|
||||||
|
f(!!g);
|
||||||
|
f(!!foo());
|
||||||
|
f(!0);
|
||||||
|
f(!0);
|
||||||
|
f(!0);
|
||||||
|
f(!!(x - y));
|
||||||
|
|
||||||
|
f(x == y);
|
||||||
|
f(x === y);
|
||||||
|
f(x < y);
|
||||||
|
f(x <= y);
|
||||||
|
f(x > y);
|
||||||
|
f(x >= y);
|
||||||
|
|
||||||
|
f(!g);
|
||||||
|
f(!foo());
|
||||||
|
f(!1);
|
||||||
|
f(!1);
|
||||||
|
f(!1);
|
||||||
|
f(!(x - y));
|
||||||
|
|
||||||
|
f(x != y);
|
||||||
|
f(x !== y);
|
||||||
|
|
||||||
|
f(!(x < y));
|
||||||
|
f(!(x <= y));
|
||||||
|
f(!(x > y));
|
||||||
|
f(!(x >= y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ dead_code_constant_boolean_should_warn_more: {
|
|||||||
var foo;
|
var foo;
|
||||||
function bar() {}
|
function bar() {}
|
||||||
}
|
}
|
||||||
for (var x = 10; x && (y || x) && (!typeof x); ++x) {
|
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
|
||||||
asdf();
|
asdf();
|
||||||
foo();
|
foo();
|
||||||
var moo;
|
var moo;
|
||||||
@@ -83,7 +83,124 @@ dead_code_constant_boolean_should_warn_more: {
|
|||||||
function bar() {}
|
function bar() {}
|
||||||
// nothing for the while
|
// nothing for the while
|
||||||
// as for the for, it should keep:
|
// as for the for, it should keep:
|
||||||
var x = 10;
|
var x = 10, y;
|
||||||
var moo;
|
var moo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dead_code_const_declaration: {
|
||||||
|
options = {
|
||||||
|
dead_code : true,
|
||||||
|
loops : true,
|
||||||
|
booleans : true,
|
||||||
|
conditionals : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var unused;
|
||||||
|
const CONST_FOO = false;
|
||||||
|
if (CONST_FOO) {
|
||||||
|
console.log("unreachable");
|
||||||
|
var moo;
|
||||||
|
function bar() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var unused;
|
||||||
|
const CONST_FOO = !1;
|
||||||
|
var moo;
|
||||||
|
function bar() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dead_code_const_annotation: {
|
||||||
|
options = {
|
||||||
|
dead_code : true,
|
||||||
|
loops : true,
|
||||||
|
booleans : true,
|
||||||
|
conditionals : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var unused;
|
||||||
|
/** @const */ var CONST_FOO_ANN = false;
|
||||||
|
if (CONST_FOO_ANN) {
|
||||||
|
console.log("unreachable");
|
||||||
|
var moo;
|
||||||
|
function bar() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var unused;
|
||||||
|
var CONST_FOO_ANN = !1;
|
||||||
|
var moo;
|
||||||
|
function bar() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dead_code_const_annotation_regex: {
|
||||||
|
options = {
|
||||||
|
dead_code : true,
|
||||||
|
loops : true,
|
||||||
|
booleans : true,
|
||||||
|
conditionals : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var unused;
|
||||||
|
// @constraint this shouldn't be a constant
|
||||||
|
var CONST_FOO_ANN = false;
|
||||||
|
if (CONST_FOO_ANN) {
|
||||||
|
console.log("reachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var unused;
|
||||||
|
var CONST_FOO_ANN = !1;
|
||||||
|
CONST_FOO_ANN && console.log('reachable');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dead_code_const_annotation_complex_scope: {
|
||||||
|
options = {
|
||||||
|
dead_code : true,
|
||||||
|
loops : true,
|
||||||
|
booleans : true,
|
||||||
|
conditionals : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var unused_var;
|
||||||
|
/** @const */ var test = 'test';
|
||||||
|
// @const
|
||||||
|
var CONST_FOO_ANN = false;
|
||||||
|
var unused_var_2;
|
||||||
|
if (CONST_FOO_ANN) {
|
||||||
|
console.log("unreachable");
|
||||||
|
var moo;
|
||||||
|
function bar() {}
|
||||||
|
}
|
||||||
|
if (test === 'test') {
|
||||||
|
var beef = 'good';
|
||||||
|
/** @const */ var meat = 'beef';
|
||||||
|
var pork = 'bad';
|
||||||
|
if (meat === 'pork') {
|
||||||
|
console.log('also unreachable');
|
||||||
|
} else if (pork === 'good') {
|
||||||
|
console.log('reached, not const');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var unused_var;
|
||||||
|
var test = 'test';
|
||||||
|
var CONST_FOO_ANN = !1;
|
||||||
|
var unused_var_2;
|
||||||
|
var moo;
|
||||||
|
function bar() {}
|
||||||
|
var beef = 'good';
|
||||||
|
var meat = 'beef';
|
||||||
|
var pork = 'bad';
|
||||||
|
'good' === pork && console.log('reached, not const');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
24
test/compress/drop-console.js
Normal file
24
test/compress/drop-console.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
drop_console_1: {
|
||||||
|
options = {};
|
||||||
|
input: {
|
||||||
|
console.log('foo');
|
||||||
|
console.log.apply(console, arguments);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log('foo');
|
||||||
|
console.log.apply(console, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_console_1: {
|
||||||
|
options = { drop_console: true };
|
||||||
|
input: {
|
||||||
|
console.log('foo');
|
||||||
|
console.log.apply(console, arguments);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
// with regular compression these will be stripped out as well
|
||||||
|
void 0;
|
||||||
|
void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
179
test/compress/drop-unused.js
Normal file
179
test/compress/drop-unused.js
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
unused_funarg_1: {
|
||||||
|
options = { unused: true, keep_fargs: false };
|
||||||
|
input: {
|
||||||
|
function f(a, b, c, d, e) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_funarg_2: {
|
||||||
|
options = { unused: true, keep_fargs: false };
|
||||||
|
input: {
|
||||||
|
function f(a, b, c, d, e) {
|
||||||
|
return a + c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a, b, c) {
|
||||||
|
return a + c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_nested_function: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function f(x, y) {
|
||||||
|
function g() {
|
||||||
|
something();
|
||||||
|
}
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
function f(x, y) {
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_circular_references_1: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function f(x, y) {
|
||||||
|
// circular reference
|
||||||
|
function g() {
|
||||||
|
return h();
|
||||||
|
}
|
||||||
|
function h() {
|
||||||
|
return g();
|
||||||
|
}
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
function f(x, y) {
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_circular_references_2: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function f(x, y) {
|
||||||
|
var foo = 1, bar = baz, baz = foo + bar, qwe = moo();
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
function f(x, y) {
|
||||||
|
moo(); // keeps side effect
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_circular_references_3: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function f(x, y) {
|
||||||
|
var g = function() { return h() };
|
||||||
|
var h = function() { return g() };
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
function f(x, y) {
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_keep_setter_arg: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
var x = {
|
||||||
|
_foo: null,
|
||||||
|
set foo(val) {
|
||||||
|
},
|
||||||
|
get foo() {
|
||||||
|
return this._foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x = {
|
||||||
|
_foo: null,
|
||||||
|
set foo(val) {
|
||||||
|
},
|
||||||
|
get foo() {
|
||||||
|
return this._foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_var_in_catch: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {
|
||||||
|
var x = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
used_var_in_catch: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {
|
||||||
|
var x = 10;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {
|
||||||
|
var x = 10;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_fnames: {
|
||||||
|
options = { unused: true, keep_fnames: true, unsafe: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
return function bar(baz) {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
return function bar(baz) {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
test/compress/html_comments.js
Normal file
71
test/compress/html_comments.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
html_comment_in_expression: {
|
||||||
|
input: {
|
||||||
|
function f(a, b, x, y) { return a < !--b && x-- > y; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b,x,y){return a< !--b&&x-- >y}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_less_than: {
|
||||||
|
input: {
|
||||||
|
function f(a, b) { return a < !--b; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b){return a< !--b}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_left_shift: {
|
||||||
|
input: {
|
||||||
|
function f(a, b) { return a << !--b; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b){return a<< !--b}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_right_shift: {
|
||||||
|
input: {
|
||||||
|
function f(a, b) { return a-- >> b; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b){return a-- >>b}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_zero_fill_right_shift: {
|
||||||
|
input: {
|
||||||
|
function f(a, b) { return a-- >>> b; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b){return a-- >>>b}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_greater_than: {
|
||||||
|
input: {
|
||||||
|
function f(a, b) { return a-- > b; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b){return a-- >b}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_greater_than_or_equal: {
|
||||||
|
input: {
|
||||||
|
function f(a, b) { return a-- >= b; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b){return a-- >=b}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_right_shift_assign: {
|
||||||
|
input: {
|
||||||
|
// Note: illegal javascript
|
||||||
|
function f(a, b) { return a-- >>= b; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b){return a-- >>=b}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_zero_fill_right_shift_assign: {
|
||||||
|
input: {
|
||||||
|
// Note: illegal javascript
|
||||||
|
function f(a, b) { return a-- >>>= b; }
|
||||||
|
}
|
||||||
|
expect_exact: "function f(a,b){return a-- >>>=b}";
|
||||||
|
}
|
||||||
|
|
||||||
|
html_comment_in_string_literal: {
|
||||||
|
input: {
|
||||||
|
function f() { return "<!--HTML-->comment in<!--string literal-->"; }
|
||||||
|
}
|
||||||
|
expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}';
|
||||||
|
}
|
||||||
25
test/compress/issue-105.js
Normal file
25
test/compress/issue-105.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
typeof_eq_undefined: {
|
||||||
|
options = {
|
||||||
|
comparisons: true
|
||||||
|
};
|
||||||
|
input: { a = typeof b.c != "undefined" }
|
||||||
|
expect: { a = "undefined" != typeof b.c }
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_eq_undefined_unsafe: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
unsafe: true
|
||||||
|
};
|
||||||
|
input: { a = typeof b.c != "undefined" }
|
||||||
|
expect: { a = void 0 !== b.c }
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_eq_undefined_unsafe2: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
unsafe: true
|
||||||
|
};
|
||||||
|
input: { a = "undefined" != typeof b.c }
|
||||||
|
expect: { a = void 0 !== b.c }
|
||||||
|
}
|
||||||
58
test/compress/issue-12.js
Normal file
58
test/compress/issue-12.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
keep_name_of_getter: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: { a = { get foo () {} } }
|
||||||
|
expect: { a = { get foo () {} } }
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_name_of_setter: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: { a = { set foo () {} } }
|
||||||
|
expect: { a = { set foo () {} } }
|
||||||
|
}
|
||||||
|
|
||||||
|
setter_with_operator_keys: {
|
||||||
|
input: {
|
||||||
|
var tokenCodes = {
|
||||||
|
get instanceof(){
|
||||||
|
return test0;
|
||||||
|
},
|
||||||
|
set instanceof(value){
|
||||||
|
test0 = value;
|
||||||
|
},
|
||||||
|
set typeof(value){
|
||||||
|
test1 = value;
|
||||||
|
},
|
||||||
|
get typeof(){
|
||||||
|
return test1;
|
||||||
|
},
|
||||||
|
set else(value){
|
||||||
|
test2 = value;
|
||||||
|
},
|
||||||
|
get else(){
|
||||||
|
return test2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var tokenCodes = {
|
||||||
|
get instanceof(){
|
||||||
|
return test0;
|
||||||
|
},
|
||||||
|
set instanceof(value){
|
||||||
|
test0 = value;
|
||||||
|
},
|
||||||
|
set typeof(value){
|
||||||
|
test1 = value;
|
||||||
|
},
|
||||||
|
get typeof(){
|
||||||
|
return test1;
|
||||||
|
},
|
||||||
|
set else(value){
|
||||||
|
test2 = value;
|
||||||
|
},
|
||||||
|
get else(){
|
||||||
|
return test2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
24
test/compress/issue-126.js
Normal file
24
test/compress/issue-126.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
concatenate_rhs_strings: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
foo(bar() + 123 + "Hello" + "World");
|
||||||
|
foo(bar() + (123 + "Hello") + "World");
|
||||||
|
foo((bar() + 123) + "Hello" + "World");
|
||||||
|
foo(bar() + 123 + "Hello" + "World" + ("Foo" + "Bar"));
|
||||||
|
foo("Foo" + "Bar" + bar() + 123 + "Hello" + "World" + ("Foo" + "Bar"));
|
||||||
|
foo("Hello" + bar() + 123 + "World");
|
||||||
|
foo(bar() + 'Foo' + (10 + parseInt('10')));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo(bar() + 123 + "HelloWorld");
|
||||||
|
foo(bar() + "123HelloWorld");
|
||||||
|
foo((bar() + 123) + "HelloWorld");
|
||||||
|
foo(bar() + 123 + "HelloWorldFooBar");
|
||||||
|
foo("FooBar" + bar() + "123HelloWorldFooBar");
|
||||||
|
foo("Hello" + bar() + "123World");
|
||||||
|
foo(bar() + 'Foo' + (10 + parseInt('10')));
|
||||||
|
}
|
||||||
|
}
|
||||||
48
test/compress/issue-143.js
Normal file
48
test/compress/issue-143.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* There was an incorrect sort behaviour documented in issue #143:
|
||||||
|
* (x = f(…)) <= x → x >= (x = f(…))
|
||||||
|
*
|
||||||
|
* For example, let the equation be:
|
||||||
|
* (a = parseInt('100')) <= a
|
||||||
|
*
|
||||||
|
* If a was an integer and has the value of 99,
|
||||||
|
* (a = parseInt('100')) <= a → 100 <= 100 → true
|
||||||
|
*
|
||||||
|
* When transformed incorrectly:
|
||||||
|
* a >= (a = parseInt('100')) → 99 >= 100 → false
|
||||||
|
*/
|
||||||
|
|
||||||
|
tranformation_sort_order_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) == a }
|
||||||
|
expect: { (a = parseInt('100')) == a }
|
||||||
|
}
|
||||||
|
|
||||||
|
tranformation_sort_order_unequal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) != a }
|
||||||
|
expect: { (a = parseInt('100')) != a }
|
||||||
|
}
|
||||||
|
|
||||||
|
tranformation_sort_order_lesser_or_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) <= a }
|
||||||
|
expect: { (a = parseInt('100')) <= a }
|
||||||
|
}
|
||||||
|
tranformation_sort_order_greater_or_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) >= a }
|
||||||
|
expect: { (a = parseInt('100')) >= a }
|
||||||
|
}
|
||||||
11
test/compress/issue-208.js
Normal file
11
test/compress/issue-208.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
do_not_update_lhs: {
|
||||||
|
options = { global_defs: { DEBUG: false } };
|
||||||
|
input: { DEBUG = false; }
|
||||||
|
expect: { DEBUG = false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
do_update_rhs: {
|
||||||
|
options = { global_defs: { DEBUG: false } };
|
||||||
|
input: { MY_DEBUG = DEBUG; }
|
||||||
|
expect: { MY_DEBUG = false; }
|
||||||
|
}
|
||||||
17
test/compress/issue-22.js
Normal file
17
test/compress/issue-22.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
return_with_no_value_in_if_body: {
|
||||||
|
options = { conditionals: true };
|
||||||
|
input: {
|
||||||
|
function foo(bar) {
|
||||||
|
if (bar) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo (bar) {
|
||||||
|
return bar ? void 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
test/compress/issue-267.js
Normal file
11
test/compress/issue-267.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
issue_267: {
|
||||||
|
options = { comparisons: true };
|
||||||
|
input: {
|
||||||
|
x = a % b / b * c * 2;
|
||||||
|
x = a % b * 2
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
x = a % b / b * c * 2;
|
||||||
|
x = a % b * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
test/compress/issue-269.js
Normal file
66
test/compress/issue-269.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
issue_269_1: {
|
||||||
|
options = {unsafe: true};
|
||||||
|
input: {
|
||||||
|
f(
|
||||||
|
String(x),
|
||||||
|
Number(x),
|
||||||
|
Boolean(x),
|
||||||
|
|
||||||
|
String(),
|
||||||
|
Number(),
|
||||||
|
Boolean()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
f(
|
||||||
|
x + '', +x, !!x,
|
||||||
|
'', 0, false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_269_dangers: {
|
||||||
|
options = {unsafe: true};
|
||||||
|
input: {
|
||||||
|
f(
|
||||||
|
String(x, x),
|
||||||
|
Number(x, x),
|
||||||
|
Boolean(x, x)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
f(String(x, x), Number(x, x), Boolean(x, x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_269_in_scope: {
|
||||||
|
options = {unsafe: true};
|
||||||
|
input: {
|
||||||
|
var String, Number, Boolean;
|
||||||
|
f(
|
||||||
|
String(x),
|
||||||
|
Number(x, x),
|
||||||
|
Boolean(x)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var String, Number, Boolean;
|
||||||
|
f(String(x), Number(x, x), Boolean(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_concat: {
|
||||||
|
options = {unsafe: true};
|
||||||
|
input: {
|
||||||
|
f(
|
||||||
|
String(x + 'str'),
|
||||||
|
String('str' + x)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
f(
|
||||||
|
x + 'str',
|
||||||
|
'str' + x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
test/compress/issue-44.js
Normal file
31
test/compress/issue-44.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
issue_44_valid_ast_1: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function a(b) {
|
||||||
|
for (var i = 0, e = b.qoo(); ; i++) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function a(b) {
|
||||||
|
var i = 0;
|
||||||
|
for (b.qoo(); ; i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_44_valid_ast_2: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function a(b) {
|
||||||
|
if (foo) for (var i = 0, e = b.qoo(); ; i++) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function a(b) {
|
||||||
|
if (foo) {
|
||||||
|
var i = 0;
|
||||||
|
for (b.qoo(); ; i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
test/compress/issue-59.js
Normal file
30
test/compress/issue-59.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
keep_continue: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
while (a) {
|
||||||
|
if (b) {
|
||||||
|
switch (true) {
|
||||||
|
case c():
|
||||||
|
d();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
while (a) {
|
||||||
|
if (b) {
|
||||||
|
switch (true) {
|
||||||
|
case c():
|
||||||
|
d();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
test/compress/issue-597.js
Normal file
25
test/compress/issue-597.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
NaN_and_Infinity_must_have_parens: {
|
||||||
|
options = {};
|
||||||
|
input: {
|
||||||
|
Infinity.toString();
|
||||||
|
NaN.toString();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(1/0).toString();
|
||||||
|
NaN.toString(); // transformation to 0/0 dropped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: {
|
||||||
|
options = {};
|
||||||
|
input: {
|
||||||
|
var Infinity, NaN;
|
||||||
|
Infinity.toString();
|
||||||
|
NaN.toString();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var Infinity, NaN;
|
||||||
|
Infinity.toString();
|
||||||
|
NaN.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
21
test/compress/issue-611.js
Normal file
21
test/compress/issue-611.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
issue_611: {
|
||||||
|
options = {
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
define(function() {
|
||||||
|
function fn() {}
|
||||||
|
if (fn()) {
|
||||||
|
fn();
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
define(function() {
|
||||||
|
function fn(){}
|
||||||
|
if (fn()) return void fn();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
22
test/compress/issue-637.js
Normal file
22
test/compress/issue-637.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
wrongly_optimized: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
booleans: true,
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
function func() {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
if (func() || true) {
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function func() {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
// TODO: optimize to `func(), bar()`
|
||||||
|
(func(), 0) || bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
37
test/compress/issue-747.js
Normal file
37
test/compress/issue-747.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
dont_reuse_prop: {
|
||||||
|
mangle_props = {
|
||||||
|
regex: /asd/
|
||||||
|
};
|
||||||
|
|
||||||
|
input: {
|
||||||
|
var obj = {};
|
||||||
|
obj.a = 123;
|
||||||
|
obj.asd = 256;
|
||||||
|
console.log(obj.a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var obj = {};
|
||||||
|
obj.a = 123;
|
||||||
|
obj.b = 256;
|
||||||
|
console.log(obj.a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unmangleable_props_should_always_be_reserved: {
|
||||||
|
mangle_props = {
|
||||||
|
regex: /asd/
|
||||||
|
};
|
||||||
|
|
||||||
|
input: {
|
||||||
|
var obj = {};
|
||||||
|
obj.asd = 256;
|
||||||
|
obj.a = 123;
|
||||||
|
console.log(obj.a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var obj = {};
|
||||||
|
obj.b = 256;
|
||||||
|
obj.a = 123;
|
||||||
|
console.log(obj.a);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
test/compress/issue-751.js
Normal file
29
test/compress/issue-751.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
negate_booleans_1: {
|
||||||
|
options = {
|
||||||
|
comparisons: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var a = !a || !b || !c || !d || !e || !f;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = !(a && b && c && d && e && f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_booleans_2: {
|
||||||
|
options = {
|
||||||
|
comparisons: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var match = !x && // should not touch this one
|
||||||
|
(!z || c) &&
|
||||||
|
(!k || d) &&
|
||||||
|
the_stuff();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var match = !x &&
|
||||||
|
(!z || c) &&
|
||||||
|
(!k || d) &&
|
||||||
|
the_stuff();
|
||||||
|
}
|
||||||
|
}
|
||||||
27
test/compress/issue-782.js
Normal file
27
test/compress/issue-782.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
remove_redundant_sequence_items: {
|
||||||
|
options = { side_effects: true };
|
||||||
|
input: {
|
||||||
|
(0, 1, eval)();
|
||||||
|
(0, 1, logThis)();
|
||||||
|
(0, 1, _decorators.logThis)();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(0, eval)();
|
||||||
|
logThis();
|
||||||
|
(0, _decorators.logThis)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dont_remove_this_binding_sequence: {
|
||||||
|
options = { side_effects: true };
|
||||||
|
input: {
|
||||||
|
(0, eval)();
|
||||||
|
(0, logThis)();
|
||||||
|
(0, _decorators.logThis)();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(0, eval)();
|
||||||
|
logThis();
|
||||||
|
(0, _decorators.logThis)();
|
||||||
|
}
|
||||||
|
}
|
||||||
32
test/compress/issue-892.js
Normal file
32
test/compress/issue-892.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
dont_mangle_arguments: {
|
||||||
|
mangle = {
|
||||||
|
};
|
||||||
|
options = {
|
||||||
|
sequences : true,
|
||||||
|
properties : true,
|
||||||
|
dead_code : true,
|
||||||
|
drop_debugger : true,
|
||||||
|
conditionals : true,
|
||||||
|
comparisons : true,
|
||||||
|
evaluate : true,
|
||||||
|
booleans : true,
|
||||||
|
loops : true,
|
||||||
|
unused : true,
|
||||||
|
hoist_funs : true,
|
||||||
|
keep_fargs : true,
|
||||||
|
keep_fnames : false,
|
||||||
|
hoist_vars : true,
|
||||||
|
if_return : true,
|
||||||
|
join_vars : true,
|
||||||
|
cascade : true,
|
||||||
|
side_effects : true,
|
||||||
|
negate_iife : false
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){
|
||||||
|
var arguments = arguments, not_arguments = 9;
|
||||||
|
console.log(not_arguments, arguments);
|
||||||
|
})(5,6,7);
|
||||||
|
}
|
||||||
|
expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);"
|
||||||
|
}
|
||||||
20
test/compress/issue-913.js
Normal file
20
test/compress/issue-913.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
keep_var_for_in: {
|
||||||
|
options = {
|
||||||
|
hoist_vars: true,
|
||||||
|
unused: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(obj){
|
||||||
|
var foo = 5;
|
||||||
|
for (var i in obj)
|
||||||
|
return foo;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(obj){
|
||||||
|
var i, foo = 5;
|
||||||
|
for (i in obj)
|
||||||
|
return foo;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
96
test/compress/issue-973.js
Normal file
96
test/compress/issue-973.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
this_binding_conditionals: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(1 && a)();
|
||||||
|
(0 || a)();
|
||||||
|
(0 || 1 && a)();
|
||||||
|
(1 ? a : 0)();
|
||||||
|
|
||||||
|
(1 && a.b)();
|
||||||
|
(0 || a.b)();
|
||||||
|
(0 || 1 && a.b)();
|
||||||
|
(1 ? a.b : 0)();
|
||||||
|
|
||||||
|
(1 && a[b])();
|
||||||
|
(0 || a[b])();
|
||||||
|
(0 || 1 && a[b])();
|
||||||
|
(1 ? a[b] : 0)();
|
||||||
|
|
||||||
|
(1 && eval)();
|
||||||
|
(0 || eval)();
|
||||||
|
(0 || 1 && eval)();
|
||||||
|
(1 ? eval : 0)();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a();
|
||||||
|
a();
|
||||||
|
a();
|
||||||
|
a();
|
||||||
|
|
||||||
|
(0, a.b)();
|
||||||
|
(0, a.b)();
|
||||||
|
(0, a.b)();
|
||||||
|
(0, a.b)();
|
||||||
|
|
||||||
|
(0, a[b])();
|
||||||
|
(0, a[b])();
|
||||||
|
(0, a[b])();
|
||||||
|
(0, a[b])();
|
||||||
|
|
||||||
|
(0, eval)();
|
||||||
|
(0, eval)();
|
||||||
|
(0, eval)();
|
||||||
|
(0, eval)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this_binding_collapse_vars: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var c = a; c();
|
||||||
|
var d = a.b; d();
|
||||||
|
var e = eval; e();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a();
|
||||||
|
(0, a.b)();
|
||||||
|
(0, eval)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this_binding_side_effects: {
|
||||||
|
options = {
|
||||||
|
side_effects : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function (foo) {
|
||||||
|
(0, foo)();
|
||||||
|
(0, foo.bar)();
|
||||||
|
(0, eval)('console.log(foo);');
|
||||||
|
}());
|
||||||
|
(function (foo) {
|
||||||
|
var eval = console;
|
||||||
|
(0, foo)();
|
||||||
|
(0, foo.bar)();
|
||||||
|
(0, eval)('console.log(foo);');
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function (foo) {
|
||||||
|
foo();
|
||||||
|
(0, foo.bar)();
|
||||||
|
(0, eval)('console.log(foo);');
|
||||||
|
}());
|
||||||
|
(function (foo) {
|
||||||
|
var eval = console;
|
||||||
|
foo();
|
||||||
|
(0, foo.bar)();
|
||||||
|
(0, eval)('console.log(foo);');
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
}
|
||||||
88
test/compress/issue-976.js
Normal file
88
test/compress/issue-976.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
eval_collapse_vars: {
|
||||||
|
options = {
|
||||||
|
collapse_vars:true, sequences:false, properties:true, dead_code:true, conditionals:true,
|
||||||
|
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
|
||||||
|
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
function f1() {
|
||||||
|
var e = 7;
|
||||||
|
var s = "abcdef";
|
||||||
|
var i = 2;
|
||||||
|
var eval = console.log.bind(console);
|
||||||
|
var x = s.charAt(i++);
|
||||||
|
var y = s.charAt(i++);
|
||||||
|
var z = s.charAt(i++);
|
||||||
|
eval(x, y, z, e);
|
||||||
|
}
|
||||||
|
function p1() { var a = foo(), b = bar(), eval = baz(); return a + b + eval; }
|
||||||
|
function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
|
||||||
|
(function f2(eval) {
|
||||||
|
var a = 2;
|
||||||
|
console.log(a - 5);
|
||||||
|
eval("console.log(a);");
|
||||||
|
})(eval);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f1() {
|
||||||
|
var e = 7,
|
||||||
|
s = "abcdef",
|
||||||
|
i = 2,
|
||||||
|
eval = console.log.bind(console),
|
||||||
|
x = s.charAt(i++),
|
||||||
|
y = s.charAt(i++),
|
||||||
|
z = s.charAt(i++);
|
||||||
|
eval(x, y, z, e);
|
||||||
|
}
|
||||||
|
function p1() { return foo() + bar() + baz(); }
|
||||||
|
function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
|
||||||
|
(function f2(eval) {
|
||||||
|
var a = 2;
|
||||||
|
console.log(a - 5);
|
||||||
|
eval("console.log(a);");
|
||||||
|
})(eval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_unused: {
|
||||||
|
options = { unused: true, keep_fargs: false };
|
||||||
|
input: {
|
||||||
|
function f1(a, eval, c, d, e) {
|
||||||
|
return a('c') + eval;
|
||||||
|
}
|
||||||
|
function f2(a, b, c, d, e) {
|
||||||
|
return a + eval('c');
|
||||||
|
}
|
||||||
|
function f3(a, eval, c, d, e) {
|
||||||
|
return a + eval('c');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f1(a, eval) {
|
||||||
|
return a('c') + eval;
|
||||||
|
}
|
||||||
|
function f2(a, b, c, d, e) {
|
||||||
|
return a + eval('c');
|
||||||
|
}
|
||||||
|
function f3(a, eval, c, d, e) {
|
||||||
|
return a + eval('c');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_mangle: {
|
||||||
|
mangle = {
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
function f1(a, eval, c, d, e) {
|
||||||
|
return a('c') + eval;
|
||||||
|
}
|
||||||
|
function f2(a, b, c, d, e) {
|
||||||
|
return a + eval('c');
|
||||||
|
}
|
||||||
|
function f3(a, eval, c, d, e) {
|
||||||
|
return a + eval('c');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: 'function f1(n,c,e,a,o){return n("c")+c}function f2(a,b,c,d,e){return a+eval("c")}function f3(a,eval,c,d,e){return a+eval("c")}'
|
||||||
|
}
|
||||||
89
test/compress/issue-979.js
Normal file
89
test/compress/issue-979.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
issue979_reported: {
|
||||||
|
options = {
|
||||||
|
sequences:true, properties:true, dead_code:true, conditionals:true,
|
||||||
|
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
|
||||||
|
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f1() {
|
||||||
|
if (a == 1 || b == 2) {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function f2() {
|
||||||
|
if (!(a == 1 || b == 2)) {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f1() {
|
||||||
|
1!=a&&2!=b||foo();
|
||||||
|
}
|
||||||
|
function f2() {
|
||||||
|
1!=a&&2!=b||foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue979_test_negated_is_best: {
|
||||||
|
options = {
|
||||||
|
sequences:true, properties:true, dead_code:true, conditionals:true,
|
||||||
|
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
|
||||||
|
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f3() {
|
||||||
|
if (a == 1 | b == 2) {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function f4() {
|
||||||
|
if (!(a == 1 | b == 2)) {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function f5() {
|
||||||
|
if (a == 1 && b == 2) {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function f6() {
|
||||||
|
if (!(a == 1 && b == 2)) {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function f7() {
|
||||||
|
if (a == 1 || b == 2) {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f3() {
|
||||||
|
1==a|2==b&&foo();
|
||||||
|
}
|
||||||
|
function f4() {
|
||||||
|
1==a|2==b&&foo();
|
||||||
|
}
|
||||||
|
function f5() {
|
||||||
|
1==a&&2==b&&foo();
|
||||||
|
}
|
||||||
|
function f6() {
|
||||||
|
1!=a||2!=b||foo();
|
||||||
|
}
|
||||||
|
function f7() {
|
||||||
|
return 1!=a&&2!=b?bar():void foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
163
test/compress/labels.js
Normal file
163
test/compress/labels.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
labels_1: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: {
|
||||||
|
if (foo) break out;
|
||||||
|
console.log("bar");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
foo || console.log("bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_2: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: {
|
||||||
|
if (foo) print("stuff");
|
||||||
|
else break out;
|
||||||
|
console.log("here");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
if (foo) {
|
||||||
|
print("stuff");
|
||||||
|
console.log("here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_3: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
for (var i = 0; i < 5; ++i) {
|
||||||
|
if (i < 3) continue;
|
||||||
|
console.log(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
for (var i = 0; i < 5; ++i)
|
||||||
|
i < 3 || console.log(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_4: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: for (var i = 0; i < 5; ++i) {
|
||||||
|
if (i < 3) continue out;
|
||||||
|
console.log(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
for (var i = 0; i < 5; ++i)
|
||||||
|
i < 3 || console.log(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_5: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
// should keep the break-s in the following
|
||||||
|
input: {
|
||||||
|
while (foo) {
|
||||||
|
if (bar) break;
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
out: while (foo) {
|
||||||
|
if (bar) break out;
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
while (foo) {
|
||||||
|
if (bar) break;
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
out: while (foo) {
|
||||||
|
if (bar) break out;
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_6: {
|
||||||
|
input: {
|
||||||
|
out: break out;
|
||||||
|
};
|
||||||
|
expect: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_7: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_8: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_9: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
continue out;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_10: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
break out;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
out: while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
break out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
147
test/compress/loops.js
Normal file
147
test/compress/loops.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
while_becomes_for: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
while (foo()) bar();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; foo(); ) bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_1: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;;)
|
||||||
|
if (foo()) break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; !foo(););
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_2: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();)
|
||||||
|
if (foo()) break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && !foo(););
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_3: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) break;
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && !foo();) {
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_4: {
|
||||||
|
options = { loops: true, sequences: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
if (foo()) break;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && (x(), y(), !foo());) z(), k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_1: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;;) if (foo()) bar(); else break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; foo(); ) bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_2: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && foo();) baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_3: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && foo();) {
|
||||||
|
baz();
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_4: {
|
||||||
|
options = { loops: true, sequences: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && (x(), y(), foo());) baz(), z(), k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_do_while_with_semicolon: {
|
||||||
|
options = { loops: false };
|
||||||
|
input: {
|
||||||
|
do {
|
||||||
|
x();
|
||||||
|
} while (false);y()
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
do x(); while (false);y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_do_while_without_semicolon: {
|
||||||
|
options = { loops: false };
|
||||||
|
input: {
|
||||||
|
do {
|
||||||
|
x();
|
||||||
|
} while (false)y()
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
do x(); while (false);y();
|
||||||
|
}
|
||||||
|
}
|
||||||
76
test/compress/negate-iife.js
Normal file
76
test/compress/negate-iife.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
negate_iife_1: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ stuff() })();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ stuff() }();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_2: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return {} })().x = 10; // should not transform this one
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(){ return {} })().x = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_3: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return true })() ? console.log(true) : console.log(false);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? console.log(false) : console.log(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_3: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
sequences: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return true })() ? console.log(true) : console.log(false);
|
||||||
|
(function(){
|
||||||
|
console.log("something");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? console.log(false) : console.log(true), function(){
|
||||||
|
console.log("something");
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_4: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
sequences: true,
|
||||||
|
conditionals: true,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if ((function(){ return true })()) {
|
||||||
|
foo(true);
|
||||||
|
} else {
|
||||||
|
bar(false);
|
||||||
|
}
|
||||||
|
(function(){
|
||||||
|
console.log("something");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? bar(false) : foo(true), function(){
|
||||||
|
console.log("something");
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
test/compress/new.js
Normal file
12
test/compress/new.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
new_statement: {
|
||||||
|
input: {
|
||||||
|
new x(1);
|
||||||
|
new x(1)(2);
|
||||||
|
new x(1)(2)(3);
|
||||||
|
new new x(1);
|
||||||
|
new new x(1)(2);
|
||||||
|
new (new x(1))(2);
|
||||||
|
(new new x(1))(2);
|
||||||
|
}
|
||||||
|
expect_exact: "new x(1);new x(1)(2);new x(1)(2)(3);new new x(1);new new x(1)(2);new new x(1)(2);(new new x(1))(2);"
|
||||||
|
}
|
||||||
@@ -17,9 +17,58 @@ dot_properties: {
|
|||||||
input: {
|
input: {
|
||||||
a["foo"] = "bar";
|
a["foo"] = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
a.foo = "bar";
|
a.foo = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dot_properties_es5: {
|
||||||
|
options = {
|
||||||
|
properties: true,
|
||||||
|
screw_ie8: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
a["foo"] = "bar";
|
||||||
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a.foo = "bar";
|
||||||
|
a.if = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate_length: {
|
||||||
|
options = {
|
||||||
|
properties: true,
|
||||||
|
unsafe: true,
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
a = "foo".length;
|
||||||
|
a = ("foo" + "bar")["len" + "gth"];
|
||||||
|
a = b.length;
|
||||||
|
a = ("foo" + b).length;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a = 3;
|
||||||
|
a = 6;
|
||||||
|
a = b.length;
|
||||||
|
a = ("foo" + b).length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
124
test/compress/return_undefined.js
Normal file
124
test/compress/return_undefined.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
return_undefined: {
|
||||||
|
options = {
|
||||||
|
sequences : false,
|
||||||
|
if_return : true,
|
||||||
|
evaluate : true,
|
||||||
|
dead_code : true,
|
||||||
|
conditionals : true,
|
||||||
|
comparisons : true,
|
||||||
|
booleans : true,
|
||||||
|
unused : true,
|
||||||
|
side_effects : true,
|
||||||
|
properties : true,
|
||||||
|
drop_debugger : true,
|
||||||
|
loops : true,
|
||||||
|
hoist_funs : true,
|
||||||
|
keep_fargs : true,
|
||||||
|
keep_fnames : false,
|
||||||
|
hoist_vars : true,
|
||||||
|
join_vars : true,
|
||||||
|
cascade : true,
|
||||||
|
negate_iife : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
function f0() {
|
||||||
|
}
|
||||||
|
function f1() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
function f2() {
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
function f3() {
|
||||||
|
return void 123;
|
||||||
|
}
|
||||||
|
function f4() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
function f5(a, b) {
|
||||||
|
console.log(a, b);
|
||||||
|
baz(a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
function f6(a, b) {
|
||||||
|
console.log(a, b);
|
||||||
|
if (a) {
|
||||||
|
foo(b);
|
||||||
|
baz(a);
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
function f7(a, b) {
|
||||||
|
console.log(a, b);
|
||||||
|
if (a) {
|
||||||
|
foo(b);
|
||||||
|
baz(a);
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
function f8(a, b) {
|
||||||
|
foo(a);
|
||||||
|
bar(b);
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
function f9(a, b) {
|
||||||
|
foo(a);
|
||||||
|
bar(b);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
function f10() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function f11() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function f12() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f0() {}
|
||||||
|
function f1() {}
|
||||||
|
function f2() {}
|
||||||
|
function f3() {}
|
||||||
|
function f4() {}
|
||||||
|
function f5(a, b) {
|
||||||
|
console.log(a, b);
|
||||||
|
baz(a);
|
||||||
|
}
|
||||||
|
function f6(a, b) {
|
||||||
|
console.log(a, b);
|
||||||
|
if (a) {
|
||||||
|
foo(b);
|
||||||
|
baz(a);
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function f7(a, b) {
|
||||||
|
console.log(a, b);
|
||||||
|
if (!a)
|
||||||
|
return a + b;
|
||||||
|
foo(b);
|
||||||
|
baz(a);
|
||||||
|
}
|
||||||
|
function f8(a, b) {
|
||||||
|
foo(a);
|
||||||
|
bar(b);
|
||||||
|
}
|
||||||
|
function f9(a, b) {
|
||||||
|
foo(a);
|
||||||
|
bar(b);
|
||||||
|
}
|
||||||
|
function f10() {
|
||||||
|
return !1;
|
||||||
|
}
|
||||||
|
function f11() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function f12() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
test/compress/screw-ie8.js
Normal file
18
test/compress/screw-ie8.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
do_screw: {
|
||||||
|
options = { screw_ie8: true };
|
||||||
|
beautify = {
|
||||||
|
screw_ie8: true,
|
||||||
|
ascii_only: true
|
||||||
|
};
|
||||||
|
|
||||||
|
input: f("\v");
|
||||||
|
expect_exact: 'f("\\v");';
|
||||||
|
}
|
||||||
|
|
||||||
|
dont_screw: {
|
||||||
|
options = { screw_ie8: false };
|
||||||
|
beautify = { screw_ie8: false, ascii_only: true };
|
||||||
|
|
||||||
|
input: f("\v");
|
||||||
|
expect_exact: 'f("\\x0B");';
|
||||||
|
}
|
||||||
@@ -87,3 +87,85 @@ make_sequences_4: {
|
|||||||
with (x = 5, obj);
|
with (x = 5, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lift_sequences_1: {
|
||||||
|
options = { sequences: true };
|
||||||
|
input: {
|
||||||
|
var foo, x, y, bar;
|
||||||
|
foo = !(x(), y(), bar());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var foo, x, y, bar;
|
||||||
|
x(), y(), foo = !bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lift_sequences_2: {
|
||||||
|
options = { sequences: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
var foo, bar;
|
||||||
|
foo.x = (foo = {}, 10);
|
||||||
|
bar = (bar = {}, 10);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var foo, bar;
|
||||||
|
foo.x = (foo = {}, 10),
|
||||||
|
bar = {}, bar = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lift_sequences_3: {
|
||||||
|
options = { sequences: true, conditionals: true };
|
||||||
|
input: {
|
||||||
|
var x, foo, bar, baz;
|
||||||
|
x = (foo(), bar(), baz()) ? 10 : 20;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x, foo, bar, baz;
|
||||||
|
foo(), bar(), x = baz() ? 10 : 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lift_sequences_4: {
|
||||||
|
options = { side_effects: true };
|
||||||
|
input: {
|
||||||
|
var x, foo, bar, baz;
|
||||||
|
x = (foo, bar, baz);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x, foo, bar, baz;
|
||||||
|
x = baz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for_sequences: {
|
||||||
|
options = { sequences: true };
|
||||||
|
input: {
|
||||||
|
// 1
|
||||||
|
foo();
|
||||||
|
bar();
|
||||||
|
for (; false;);
|
||||||
|
// 2
|
||||||
|
foo();
|
||||||
|
bar();
|
||||||
|
for (x = 5; false;);
|
||||||
|
// 3
|
||||||
|
x = (foo in bar);
|
||||||
|
for (; false;);
|
||||||
|
// 4
|
||||||
|
x = (foo in bar);
|
||||||
|
for (y = 5; false;);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
// 1
|
||||||
|
for (foo(), bar(); false;);
|
||||||
|
// 2
|
||||||
|
for (foo(), bar(), x = 5; false;);
|
||||||
|
// 3
|
||||||
|
x = (foo in bar);
|
||||||
|
for (; false;);
|
||||||
|
// 4
|
||||||
|
x = (foo in bar);
|
||||||
|
for (y = 5; false;);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
260
test/compress/switch.js
Normal file
260
test/compress/switch.js
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
constant_switch_1: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (1+1) {
|
||||||
|
case 1: foo(); break;
|
||||||
|
case 1+1: bar(); break;
|
||||||
|
case 1+1+1: baz(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_2: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (1) {
|
||||||
|
case 1: foo();
|
||||||
|
case 1+1: bar(); break;
|
||||||
|
case 1+1+1: baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo();
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_3: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (10) {
|
||||||
|
case 1: foo();
|
||||||
|
case 1+1: bar(); break;
|
||||||
|
case 1+1+1: baz();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_4: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (2) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break;
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
bar();
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_5: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break;
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
// the break inside the if ruins our job
|
||||||
|
// we can still get rid of irrelevant cases.
|
||||||
|
switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break;
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
// XXX: we could optimize this better by inventing an outer
|
||||||
|
// labeled block, but that's kinda tricky.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_6: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
OUT: {
|
||||||
|
foo();
|
||||||
|
switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break OUT;
|
||||||
|
y();
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
OUT: {
|
||||||
|
foo();
|
||||||
|
x();
|
||||||
|
if (foo) break OUT;
|
||||||
|
y();
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_7: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
OUT: {
|
||||||
|
foo();
|
||||||
|
switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break OUT;
|
||||||
|
for (var x = 0; x < 10; x++) {
|
||||||
|
if (x > 5) break; // this break refers to the for, not to the switch; thus it
|
||||||
|
// shouldn't ruin our optimization
|
||||||
|
console.log(x);
|
||||||
|
}
|
||||||
|
y();
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
OUT: {
|
||||||
|
foo();
|
||||||
|
x();
|
||||||
|
if (foo) break OUT;
|
||||||
|
for (var x = 0; x < 10; x++) {
|
||||||
|
if (x > 5) break;
|
||||||
|
console.log(x);
|
||||||
|
}
|
||||||
|
y();
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_8: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
OUT: switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
for (;;) break OUT;
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
OUT: {
|
||||||
|
x();
|
||||||
|
for (;;) break OUT;
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_9: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
OUT: switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
for (;;) if (foo) break OUT;
|
||||||
|
y();
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
OUT: {
|
||||||
|
x();
|
||||||
|
for (;;) if (foo) break OUT;
|
||||||
|
y();
|
||||||
|
bar();
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_default_1: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_default_2: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz(); break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_default: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
something();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
test/compress/typeof.js
Normal file
25
test/compress/typeof.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
typeof_evaluation: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
a = typeof 1;
|
||||||
|
b = typeof 'test';
|
||||||
|
c = typeof [];
|
||||||
|
d = typeof {};
|
||||||
|
e = typeof /./;
|
||||||
|
f = typeof false;
|
||||||
|
g = typeof function(){};
|
||||||
|
h = typeof undefined;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a='number';
|
||||||
|
b='string';
|
||||||
|
c=typeof[];
|
||||||
|
d=typeof{};
|
||||||
|
e=typeof/./;
|
||||||
|
f='boolean';
|
||||||
|
g='function';
|
||||||
|
h='undefined';
|
||||||
|
}
|
||||||
|
}
|
||||||
17
test/compress/unicode.js
Normal file
17
test/compress/unicode.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
unicode_parse_variables: {
|
||||||
|
options = {};
|
||||||
|
input: {
|
||||||
|
var a = {};
|
||||||
|
a.你好 = 456;
|
||||||
|
|
||||||
|
var ↂωↂ = 123;
|
||||||
|
var l০ = 3; // 2nd char is a unicode digit
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = {};
|
||||||
|
a.你好 = 456;
|
||||||
|
|
||||||
|
var ↂωↂ = 123;
|
||||||
|
var l০ = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
test/mocha.js
Normal file
29
test/mocha.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
var Mocha = require('mocha'),
|
||||||
|
fs = require('fs'),
|
||||||
|
path = require('path');
|
||||||
|
|
||||||
|
// Instantiate a Mocha instance.
|
||||||
|
var mocha = new Mocha({});
|
||||||
|
|
||||||
|
var testDir = __dirname + '/mocha/';
|
||||||
|
|
||||||
|
// Add each .js file to the mocha instance
|
||||||
|
fs.readdirSync(testDir).filter(function(file){
|
||||||
|
// Only keep the .js files
|
||||||
|
return file.substr(-3) === '.js';
|
||||||
|
|
||||||
|
}).forEach(function(file){
|
||||||
|
mocha.addFile(
|
||||||
|
path.join(testDir, file)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
mocha.run(function(failures) {
|
||||||
|
if (failures !== 0) {
|
||||||
|
process.on('exit', function () {
|
||||||
|
process.exit(failures);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
22
test/mocha/arguments.js
Normal file
22
test/mocha/arguments.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
var UglifyJS = require('../../');
|
||||||
|
var assert = require("assert");
|
||||||
|
|
||||||
|
describe("arguments", function() {
|
||||||
|
it("Should known that arguments in functions are local scoped", function() {
|
||||||
|
var ast = UglifyJS.parse("var arguments; var f = function() {arguments.length}");
|
||||||
|
ast.figure_out_scope();
|
||||||
|
|
||||||
|
// Test scope of `var arguments`
|
||||||
|
assert.strictEqual(ast.find_variable("arguments").global, true);
|
||||||
|
|
||||||
|
// Select arguments symbol in function
|
||||||
|
var symbol = ast.body[1].definitions[0].value.find_variable("arguments");
|
||||||
|
|
||||||
|
assert.strictEqual(symbol.global, false);
|
||||||
|
assert.strictEqual(symbol.scope, ast. // From ast
|
||||||
|
body[1]. // Select 2nd statement (equals to `var f ...`)
|
||||||
|
definitions[0]. // First definition of selected statement
|
||||||
|
value // Select function as scope
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
45
test/mocha/comment-filter.js
Normal file
45
test/mocha/comment-filter.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
var UglifyJS = require('../../');
|
||||||
|
var assert = require("assert");
|
||||||
|
|
||||||
|
describe("comment filters", function() {
|
||||||
|
it("Should be able to filter comments by passing regex", function() {
|
||||||
|
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
|
||||||
|
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should be able to filter comments by passing a function", function() {
|
||||||
|
var ast = UglifyJS.parse("/*TEST 123*/\n//An other comment\n//8 chars.");
|
||||||
|
var f = function(node, comment) {
|
||||||
|
return comment.value.length === 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should be able to get the comment and comment type when using a function", function() {
|
||||||
|
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
|
||||||
|
var f = function(node, comment) {
|
||||||
|
return comment.type == "comment1" || comment.type == "comment3";
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should be able to filter comments by passing a boolean", function() {
|
||||||
|
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
|
||||||
|
|
||||||
|
assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
|
||||||
|
assert.strictEqual(ast.print_to_string({comments: false}), "");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should never be able to filter comment5 (shebangs)", function() {
|
||||||
|
var ast = UglifyJS.parse("#!Random comment\n//test1\n/*test2*/");
|
||||||
|
var f = function(node, comment) {
|
||||||
|
assert.strictEqual(comment.type === "comment5", false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/\n");
|
||||||
|
});
|
||||||
|
});
|
||||||
89
test/mocha/getter-setter.js
Normal file
89
test/mocha/getter-setter.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
var UglifyJS = require('../../');
|
||||||
|
var assert = require("assert");
|
||||||
|
|
||||||
|
describe("Getters and setters", function() {
|
||||||
|
it("Should not accept operator symbols as getter/setter name", function() {
|
||||||
|
var illegalOperators = [
|
||||||
|
"++",
|
||||||
|
"--",
|
||||||
|
"+",
|
||||||
|
"-",
|
||||||
|
"!",
|
||||||
|
"~",
|
||||||
|
"&",
|
||||||
|
"|",
|
||||||
|
"^",
|
||||||
|
"*",
|
||||||
|
"/",
|
||||||
|
"%",
|
||||||
|
">>",
|
||||||
|
"<<",
|
||||||
|
">>>",
|
||||||
|
"<",
|
||||||
|
">",
|
||||||
|
"<=",
|
||||||
|
">=",
|
||||||
|
"==",
|
||||||
|
"===",
|
||||||
|
"!=",
|
||||||
|
"!==",
|
||||||
|
"?",
|
||||||
|
"=",
|
||||||
|
"+=",
|
||||||
|
"-=",
|
||||||
|
"/=",
|
||||||
|
"*=",
|
||||||
|
"%=",
|
||||||
|
">>=",
|
||||||
|
"<<=",
|
||||||
|
">>>=",
|
||||||
|
"|=",
|
||||||
|
"^=",
|
||||||
|
"&=",
|
||||||
|
"&&",
|
||||||
|
"||"
|
||||||
|
];
|
||||||
|
var generator = function() {
|
||||||
|
var results = [];
|
||||||
|
|
||||||
|
for (var i in illegalOperators) {
|
||||||
|
results.push({
|
||||||
|
code: "var obj = { get " + illegalOperators[i] + "() { return test; }};",
|
||||||
|
operator: illegalOperators[i],
|
||||||
|
method: "get"
|
||||||
|
});
|
||||||
|
results.push({
|
||||||
|
code: "var obj = { set " + illegalOperators[i] + "(value) { test = value}};",
|
||||||
|
operator: illegalOperators[i],
|
||||||
|
method: "set"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
var testCase = function(data) {
|
||||||
|
return function() {
|
||||||
|
UglifyJS.parse(data.code);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var fail = function(data) {
|
||||||
|
return function (e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||||
|
e.message === "Invalid getter/setter name: " + data.operator;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var errorMessage = function(data) {
|
||||||
|
return "Expected but didn't get a syntax error while parsing following line:\n" + data.code;
|
||||||
|
};
|
||||||
|
|
||||||
|
var tests = generator();
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
var test = tests[i];
|
||||||
|
assert.throws(testCase(test), fail(test), errorMessage(test));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
34
test/mocha/string-literal.js
Normal file
34
test/mocha/string-literal.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
var UglifyJS = require('../../');
|
||||||
|
var assert = require("assert");
|
||||||
|
|
||||||
|
describe("String literals", function() {
|
||||||
|
it("Should throw syntax error if a string literal contains a newline", function() {
|
||||||
|
var inputs = [
|
||||||
|
"'\n'",
|
||||||
|
"'\r'",
|
||||||
|
'"\r\n"',
|
||||||
|
"'\u2028'",
|
||||||
|
'"\u2029"'
|
||||||
|
];
|
||||||
|
|
||||||
|
var test = function(input) {
|
||||||
|
return function() {
|
||||||
|
var ast = UglifyJS.parse(input);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var error = function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||||
|
e.message === "Unterminated string constant";
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var input in inputs) {
|
||||||
|
assert.throws(test(inputs[input]), error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not throw syntax error if a string has a line continuation", function() {
|
||||||
|
var output = UglifyJS.parse('var a = "a\\\nb";').print_to_string();
|
||||||
|
assert.equal(output, 'var a="ab";');
|
||||||
|
});
|
||||||
|
});
|
||||||
103
test/mozilla-ast.js
Normal file
103
test/mozilla-ast.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// Testing UglifyJS <-> SpiderMonkey AST conversion
|
||||||
|
// through generative testing.
|
||||||
|
|
||||||
|
var UglifyJS = require(".."),
|
||||||
|
escodegen = require("escodegen"),
|
||||||
|
esfuzz = require("esfuzz"),
|
||||||
|
estraverse = require("estraverse"),
|
||||||
|
prefix = Array(20).join("\b") + " ";
|
||||||
|
|
||||||
|
// Normalizes input AST for UglifyJS in order to get correct comparison.
|
||||||
|
|
||||||
|
function normalizeInput(ast) {
|
||||||
|
return estraverse.replace(ast, {
|
||||||
|
enter: function(node, parent) {
|
||||||
|
switch (node.type) {
|
||||||
|
// Internally mark all the properties with semi-standard type "Property".
|
||||||
|
case "ObjectExpression":
|
||||||
|
node.properties.forEach(function (property) {
|
||||||
|
property.type = "Property";
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Since UglifyJS doesn"t recognize different types of property keys,
|
||||||
|
// decision on SpiderMonkey node type is based on check whether key
|
||||||
|
// can be valid identifier or not - so we do in input AST.
|
||||||
|
case "Property":
|
||||||
|
var key = node.key;
|
||||||
|
if (key.type === "Literal" && typeof key.value === "string" && UglifyJS.is_identifier(key.value)) {
|
||||||
|
node.key = {
|
||||||
|
type: "Identifier",
|
||||||
|
name: key.value
|
||||||
|
};
|
||||||
|
} else if (key.type === "Identifier" && !UglifyJS.is_identifier(key.name)) {
|
||||||
|
node.key = {
|
||||||
|
type: "Literal",
|
||||||
|
value: key.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// UglifyJS internally flattens all the expression sequences - either
|
||||||
|
// to one element (if sequence contains only one element) or flat list.
|
||||||
|
case "SequenceExpression":
|
||||||
|
node.expressions = node.expressions.reduce(function flatten(list, expr) {
|
||||||
|
return list.concat(expr.type === "SequenceExpression" ? expr.expressions.reduce(flatten, []) : [expr]);
|
||||||
|
}, []);
|
||||||
|
if (node.expressions.length === 1) {
|
||||||
|
return node.expressions[0];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function(options) {
|
||||||
|
console.log("--- UglifyJS <-> Mozilla AST conversion");
|
||||||
|
|
||||||
|
for (var counter = 0; counter < options.iterations; counter++) {
|
||||||
|
process.stdout.write(prefix + counter + "/" + options.iterations);
|
||||||
|
|
||||||
|
var ast1 = normalizeInput(esfuzz.generate({
|
||||||
|
maxDepth: options.maxDepth
|
||||||
|
}));
|
||||||
|
|
||||||
|
var ast2 =
|
||||||
|
UglifyJS
|
||||||
|
.AST_Node
|
||||||
|
.from_mozilla_ast(ast1)
|
||||||
|
.to_mozilla_ast();
|
||||||
|
|
||||||
|
var astPair = [
|
||||||
|
{name: 'expected', value: ast1},
|
||||||
|
{name: 'actual', value: ast2}
|
||||||
|
];
|
||||||
|
|
||||||
|
var jsPair = astPair.map(function(item) {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
value: escodegen.generate(item.value)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (jsPair[0].value !== jsPair[1].value) {
|
||||||
|
var fs = require("fs");
|
||||||
|
var acorn = require("acorn");
|
||||||
|
|
||||||
|
fs.existsSync("tmp") || fs.mkdirSync("tmp");
|
||||||
|
|
||||||
|
jsPair.forEach(function (item) {
|
||||||
|
var fileName = "tmp/dump_" + item.name;
|
||||||
|
var ast = acorn.parse(item.value);
|
||||||
|
fs.writeFileSync(fileName + ".js", item.value);
|
||||||
|
fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdout.write("\n");
|
||||||
|
throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n");
|
||||||
|
};
|
||||||
@@ -4,11 +4,29 @@ var U = require("../tools/node");
|
|||||||
var path = require("path");
|
var path = require("path");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var assert = require("assert");
|
var assert = require("assert");
|
||||||
var sys = require("util");
|
|
||||||
|
|
||||||
var tests_dir = path.dirname(module.filename);
|
var tests_dir = path.dirname(module.filename);
|
||||||
|
var failures = 0;
|
||||||
|
var failed_files = {};
|
||||||
|
|
||||||
run_compress_tests();
|
run_compress_tests();
|
||||||
|
if (failures) {
|
||||||
|
console.error("\n!!! Failed " + failures + " test cases.");
|
||||||
|
console.error("!!! " + Object.keys(failed_files).join(", "));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var mocha_tests = require("./mocha.js");
|
||||||
|
mocha_tests();
|
||||||
|
|
||||||
|
var run_sourcemaps_tests = require('./sourcemaps');
|
||||||
|
run_sourcemaps_tests();
|
||||||
|
|
||||||
|
var run_ast_conversion_tests = require("./mozilla-ast");
|
||||||
|
|
||||||
|
run_ast_conversion_tests({
|
||||||
|
iterations: 1000
|
||||||
|
});
|
||||||
|
|
||||||
/* -----[ utils ]----- */
|
/* -----[ utils ]----- */
|
||||||
|
|
||||||
@@ -18,7 +36,7 @@ function tmpl() {
|
|||||||
|
|
||||||
function log() {
|
function log() {
|
||||||
var txt = tmpl.apply(this, arguments);
|
var txt = tmpl.apply(this, arguments);
|
||||||
sys.puts(txt);
|
console.log("%s", txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
function log_directory(dir) {
|
function log_directory(dir) {
|
||||||
@@ -37,6 +55,12 @@ function find_test_files(dir) {
|
|||||||
var files = fs.readdirSync(dir).filter(function(name){
|
var files = fs.readdirSync(dir).filter(function(name){
|
||||||
return /\.js$/i.test(name);
|
return /\.js$/i.test(name);
|
||||||
});
|
});
|
||||||
|
if (process.argv.length > 2) {
|
||||||
|
var x = process.argv.slice(2);
|
||||||
|
files = files.filter(function(f){
|
||||||
|
return x.indexOf(f) >= 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,18 +85,37 @@ function run_compress_tests() {
|
|||||||
log_start_file(file);
|
log_start_file(file);
|
||||||
function test_case(test) {
|
function test_case(test) {
|
||||||
log_test(test.name);
|
log_test(test.name);
|
||||||
var cmp = new U.Compressor(test.options || {}, true);
|
var options = U.defaults(test.options, {
|
||||||
var expect = make_code(as_toplevel(test.expect), false);
|
warnings: false
|
||||||
|
});
|
||||||
|
var cmp = new U.Compressor(options, true);
|
||||||
|
var output_options = test.beautify || {};
|
||||||
|
var expect;
|
||||||
|
if (test.expect) {
|
||||||
|
expect = make_code(as_toplevel(test.expect), output_options);
|
||||||
|
} else {
|
||||||
|
expect = test.expect_exact;
|
||||||
|
}
|
||||||
var input = as_toplevel(test.input);
|
var input = as_toplevel(test.input);
|
||||||
|
var input_code = make_code(test.input, { beautify: true });
|
||||||
|
if (test.mangle_props) {
|
||||||
|
input = U.mangle_properties(input, test.mangle_props);
|
||||||
|
}
|
||||||
var output = input.transform(cmp);
|
var output = input.transform(cmp);
|
||||||
output.figure_out_scope();
|
output.figure_out_scope();
|
||||||
output = make_code(output, false);
|
if (test.mangle) {
|
||||||
|
output.compute_char_frequency(test.mangle);
|
||||||
|
output.mangle_names(test.mangle);
|
||||||
|
}
|
||||||
|
output = make_code(output, output_options);
|
||||||
if (expect != output) {
|
if (expect != output) {
|
||||||
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
||||||
input: make_code(test.input),
|
input: input_code,
|
||||||
output: output,
|
output: output,
|
||||||
expected: expect
|
expected: expect
|
||||||
});
|
});
|
||||||
|
failures++;
|
||||||
|
failed_files[file] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var tests = parse_test(path.resolve(dir, file));
|
var tests = parse_test(path.resolve(dir, file));
|
||||||
@@ -108,7 +151,7 @@ function parse_test(file) {
|
|||||||
file: file,
|
file: file,
|
||||||
line: node.start.line,
|
line: node.start.line,
|
||||||
col: node.start.col,
|
col: node.start.col,
|
||||||
code: make_code(node, false)
|
code: make_code(node, { beautify: false })
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +168,7 @@ function parse_test(file) {
|
|||||||
}
|
}
|
||||||
if (node instanceof U.AST_LabeledStatement) {
|
if (node instanceof U.AST_LabeledStatement) {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
node.label.name == "input" || node.label.name == "expect",
|
node.label.name == "input" || node.label.name == "expect" || node.label.name == "expect_exact",
|
||||||
tmpl("Unsupported label {name} [{line},{col}]", {
|
tmpl("Unsupported label {name} [{line},{col}]", {
|
||||||
name: node.label.name,
|
name: node.label.name,
|
||||||
line: node.label.start.line,
|
line: node.label.start.line,
|
||||||
@@ -137,7 +180,16 @@ function parse_test(file) {
|
|||||||
if (stat.body.length == 1) stat = stat.body[0];
|
if (stat.body.length == 1) stat = stat.body[0];
|
||||||
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement();
|
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement();
|
||||||
}
|
}
|
||||||
|
if (node.label.name === "expect_exact") {
|
||||||
|
if (!(stat.TYPE === "SimpleStatement" && stat.body.TYPE === "String")) {
|
||||||
|
throw new Error(
|
||||||
|
"The value of the expect_exact clause should be a string, " +
|
||||||
|
"like `expect_exact: \"some.exact.javascript;\"`");
|
||||||
|
}
|
||||||
|
test[node.label.name] = stat.body.start.value
|
||||||
|
} else {
|
||||||
test[node.label.name] = stat;
|
test[node.label.name] = stat;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -146,15 +198,15 @@ function parse_test(file) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function make_code(ast, beautify) {
|
function make_code(ast, options) {
|
||||||
if (arguments.length == 1) beautify = true;
|
options.inline_script = true;
|
||||||
var stream = U.OutputStream({ beautify: beautify });
|
var stream = U.OutputStream(options);
|
||||||
ast.print(stream);
|
ast.print(stream);
|
||||||
return stream.get();
|
return stream.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
function evaluate(code) {
|
function evaluate(code) {
|
||||||
if (code instanceof U.AST_Node)
|
if (code instanceof U.AST_Node)
|
||||||
code = make_code(code);
|
code = make_code(code, { beautify: true });
|
||||||
return new Function("return(" + code + ")")();
|
return new Function("return(" + code + ")")();
|
||||||
}
|
}
|
||||||
|
|||||||
40
test/sourcemaps.js
Normal file
40
test/sourcemaps.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
var UglifyJS = require("..");
|
||||||
|
var ok = require("assert");
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
console.log("--- Sourcemaps tests");
|
||||||
|
|
||||||
|
var basic = source_map([
|
||||||
|
'var x = 1 + 1;'
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
|
ok.equal(basic.version, 3);
|
||||||
|
ok.deepEqual(basic.names, ['x']);
|
||||||
|
|
||||||
|
var issue836 = source_map([
|
||||||
|
"({",
|
||||||
|
" get enabled() {",
|
||||||
|
" return 3;",
|
||||||
|
" },",
|
||||||
|
" set enabled(x) {",
|
||||||
|
" ;",
|
||||||
|
" }",
|
||||||
|
"});",
|
||||||
|
].join("\n"));
|
||||||
|
|
||||||
|
ok.deepEqual(issue836.names, ['enabled', 'x']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function source_map(js) {
|
||||||
|
var source_map = UglifyJS.SourceMap();
|
||||||
|
var stream = UglifyJS.OutputStream({ source_map: source_map });
|
||||||
|
var parsed = UglifyJS.parse(js);
|
||||||
|
parsed.print(stream);
|
||||||
|
return JSON.parse(source_map.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run standalone
|
||||||
|
if (module.parent === null) {
|
||||||
|
module.exports();
|
||||||
|
}
|
||||||
|
|
||||||
5603
tools/domprops.json
Normal file
5603
tools/domprops.json
Normal file
File diff suppressed because it is too large
Load Diff
18
tools/exports.js
Normal file
18
tools/exports.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
exports["Compressor"] = Compressor;
|
||||||
|
exports["DefaultsError"] = DefaultsError;
|
||||||
|
exports["Dictionary"] = Dictionary;
|
||||||
|
exports["JS_Parse_Error"] = JS_Parse_Error;
|
||||||
|
exports["MAP"] = MAP;
|
||||||
|
exports["OutputStream"] = OutputStream;
|
||||||
|
exports["SourceMap"] = SourceMap;
|
||||||
|
exports["TreeTransformer"] = TreeTransformer;
|
||||||
|
exports["TreeWalker"] = TreeWalker;
|
||||||
|
exports["base54"] = base54;
|
||||||
|
exports["defaults"] = defaults;
|
||||||
|
exports["mangle_properties"] = mangle_properties;
|
||||||
|
exports["merge"] = merge;
|
||||||
|
exports["parse"] = parse;
|
||||||
|
exports["push_uniq"] = push_uniq;
|
||||||
|
exports["string_template"] = string_template;
|
||||||
|
exports["is_identifier"] = is_identifier;
|
||||||
|
exports["SymbolDef"] = SymbolDef;
|
||||||
282
tools/node.js
282
tools/node.js
@@ -1,57 +1,247 @@
|
|||||||
var save_stderr = process.stderr;
|
var path = require("path");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
|
|
||||||
// discard annoying NodeJS warning ("path.existsSync is now called `fs.existsSync`.")
|
var FILES = exports.FILES = [
|
||||||
var devnull = fs.createWriteStream("/dev/null");
|
"../lib/utils.js",
|
||||||
process.__defineGetter__("stderr", function(){
|
"../lib/ast.js",
|
||||||
return devnull;
|
"../lib/parse.js",
|
||||||
|
"../lib/transform.js",
|
||||||
|
"../lib/scope.js",
|
||||||
|
"../lib/output.js",
|
||||||
|
"../lib/compress.js",
|
||||||
|
"../lib/sourcemap.js",
|
||||||
|
"../lib/mozilla-ast.js",
|
||||||
|
"../lib/propmangle.js",
|
||||||
|
"./exports.js",
|
||||||
|
].map(function(file){
|
||||||
|
return fs.realpathSync(path.join(path.dirname(__filename), file));
|
||||||
});
|
});
|
||||||
|
|
||||||
var vm = require("vm");
|
var UglifyJS = exports;
|
||||||
var sys = require("util");
|
|
||||||
var path = require("path");
|
|
||||||
|
|
||||||
var UglifyJS = vm.createContext({
|
new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
|
||||||
sys : sys,
|
return fs.readFileSync(file, "utf8");
|
||||||
console : console,
|
}).join("\n\n"))(
|
||||||
|
require("source-map"),
|
||||||
MOZ_SourceMap : require("source-map")
|
UglifyJS
|
||||||
});
|
);
|
||||||
|
|
||||||
process.__defineGetter__("stderr", function(){
|
|
||||||
return save_stderr;
|
|
||||||
});
|
|
||||||
|
|
||||||
function load_global(file) {
|
|
||||||
file = path.resolve(path.dirname(module.filename), file);
|
|
||||||
try {
|
|
||||||
var code = fs.readFileSync(file, "utf8");
|
|
||||||
return vm.runInContext(code, UglifyJS, file);
|
|
||||||
} catch(ex) {
|
|
||||||
// XXX: in case of a syntax error, the message is kinda
|
|
||||||
// useless. (no location information).
|
|
||||||
sys.debug("ERROR in file: " + file + " / " + ex);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
load_global("../lib/utils.js");
|
|
||||||
load_global("../lib/ast.js");
|
|
||||||
load_global("../lib/parse.js");
|
|
||||||
load_global("../lib/transform.js");
|
|
||||||
load_global("../lib/scope.js");
|
|
||||||
load_global("../lib/output.js");
|
|
||||||
load_global("../lib/compress.js");
|
|
||||||
load_global("../lib/sourcemap.js");
|
|
||||||
load_global("../lib/mozilla-ast.js");
|
|
||||||
|
|
||||||
UglifyJS.AST_Node.warn_function = function(txt) {
|
UglifyJS.AST_Node.warn_function = function(txt) {
|
||||||
sys.error("WARN: " + txt);
|
console.error("WARN: %s", txt);
|
||||||
};
|
};
|
||||||
|
|
||||||
// XXX: perhaps we shouldn't export everything but heck, I'm lazy.
|
exports.minify = function(files, options) {
|
||||||
for (var i in UglifyJS) {
|
options = UglifyJS.defaults(options, {
|
||||||
if (UglifyJS.hasOwnProperty(i)) {
|
spidermonkey : false,
|
||||||
exports[i] = UglifyJS[i];
|
outSourceMap : null,
|
||||||
|
sourceRoot : null,
|
||||||
|
inSourceMap : null,
|
||||||
|
fromString : false,
|
||||||
|
warnings : false,
|
||||||
|
mangle : {},
|
||||||
|
mangleProperties : false,
|
||||||
|
nameCache : null,
|
||||||
|
output : null,
|
||||||
|
compress : {},
|
||||||
|
parse : {}
|
||||||
|
});
|
||||||
|
UglifyJS.base54.reset();
|
||||||
|
|
||||||
|
// 1. parse
|
||||||
|
var toplevel = null,
|
||||||
|
sourcesContent = {};
|
||||||
|
|
||||||
|
if (options.spidermonkey) {
|
||||||
|
toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
|
||||||
|
} else {
|
||||||
|
if (typeof files == "string")
|
||||||
|
files = [ files ];
|
||||||
|
files.forEach(function(file, i){
|
||||||
|
var code = options.fromString
|
||||||
|
? file
|
||||||
|
: fs.readFileSync(file, "utf8");
|
||||||
|
sourcesContent[file] = code;
|
||||||
|
toplevel = UglifyJS.parse(code, {
|
||||||
|
filename: options.fromString ? i : file,
|
||||||
|
toplevel: toplevel,
|
||||||
|
bare_returns: options.parse ? options.parse.bare_returns : undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (options.wrap) {
|
||||||
|
toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. compress
|
||||||
|
if (options.compress) {
|
||||||
|
var compress = { warnings: options.warnings };
|
||||||
|
UglifyJS.merge(compress, options.compress);
|
||||||
|
toplevel.figure_out_scope();
|
||||||
|
var sq = UglifyJS.Compressor(compress);
|
||||||
|
toplevel = toplevel.transform(sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. mangle properties
|
||||||
|
if (options.mangleProperties || options.nameCache) {
|
||||||
|
options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props");
|
||||||
|
toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties);
|
||||||
|
UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. mangle
|
||||||
|
if (options.mangle) {
|
||||||
|
toplevel.figure_out_scope(options.mangle);
|
||||||
|
toplevel.compute_char_frequency(options.mangle);
|
||||||
|
toplevel.mangle_names(options.mangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. output
|
||||||
|
var inMap = options.inSourceMap;
|
||||||
|
var output = {};
|
||||||
|
if (typeof options.inSourceMap == "string") {
|
||||||
|
inMap = fs.readFileSync(options.inSourceMap, "utf8");
|
||||||
|
}
|
||||||
|
if (options.outSourceMap) {
|
||||||
|
output.source_map = UglifyJS.SourceMap({
|
||||||
|
file: options.outSourceMap,
|
||||||
|
orig: inMap,
|
||||||
|
root: options.sourceRoot
|
||||||
|
});
|
||||||
|
if (options.sourceMapIncludeSources) {
|
||||||
|
for (var file in sourcesContent) {
|
||||||
|
if (sourcesContent.hasOwnProperty(file)) {
|
||||||
|
output.source_map.get().setSourceContent(file, sourcesContent[file]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (options.output) {
|
||||||
|
UglifyJS.merge(output, options.output);
|
||||||
|
}
|
||||||
|
var stream = UglifyJS.OutputStream(output);
|
||||||
|
toplevel.print(stream);
|
||||||
|
|
||||||
|
if (options.outSourceMap && "string" === typeof options.outSourceMap) {
|
||||||
|
stream += "\n//# sourceMappingURL=" + options.outSourceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
var source_map = output.source_map;
|
||||||
|
if (source_map) {
|
||||||
|
source_map = source_map + "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
code : stream + "",
|
||||||
|
map : source_map
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// exports.describe_ast = function() {
|
||||||
|
// function doitem(ctor) {
|
||||||
|
// var sub = {};
|
||||||
|
// ctor.SUBCLASSES.forEach(function(ctor){
|
||||||
|
// sub[ctor.TYPE] = doitem(ctor);
|
||||||
|
// });
|
||||||
|
// var ret = {};
|
||||||
|
// if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
|
||||||
|
// if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
|
||||||
|
// return ret;
|
||||||
|
// }
|
||||||
|
// return doitem(UglifyJS.AST_Node).sub;
|
||||||
|
// }
|
||||||
|
|
||||||
|
exports.describe_ast = function() {
|
||||||
|
var out = UglifyJS.OutputStream({ beautify: true });
|
||||||
|
function doitem(ctor) {
|
||||||
|
out.print("AST_" + ctor.TYPE);
|
||||||
|
var props = ctor.SELF_PROPS.filter(function(prop){
|
||||||
|
return !/^\$/.test(prop);
|
||||||
|
});
|
||||||
|
if (props.length > 0) {
|
||||||
|
out.space();
|
||||||
|
out.with_parens(function(){
|
||||||
|
props.forEach(function(prop, i){
|
||||||
|
if (i) out.space();
|
||||||
|
out.print(prop);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (ctor.documentation) {
|
||||||
|
out.space();
|
||||||
|
out.print_string(ctor.documentation);
|
||||||
|
}
|
||||||
|
if (ctor.SUBCLASSES.length > 0) {
|
||||||
|
out.space();
|
||||||
|
out.with_block(function(){
|
||||||
|
ctor.SUBCLASSES.forEach(function(ctor, i){
|
||||||
|
out.indent();
|
||||||
|
doitem(ctor);
|
||||||
|
out.newline();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
doitem(UglifyJS.AST_Node);
|
||||||
|
return out + "";
|
||||||
|
};
|
||||||
|
|
||||||
|
function readReservedFile(filename, reserved) {
|
||||||
|
if (!reserved) {
|
||||||
|
reserved = { vars: [], props: [] };
|
||||||
|
}
|
||||||
|
var data = fs.readFileSync(filename, "utf8");
|
||||||
|
data = JSON.parse(data);
|
||||||
|
if (data.vars) {
|
||||||
|
data.vars.forEach(function(name){
|
||||||
|
UglifyJS.push_uniq(reserved.vars, name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.props) {
|
||||||
|
data.props.forEach(function(name){
|
||||||
|
UglifyJS.push_uniq(reserved.props, name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.readReservedFile = readReservedFile;
|
||||||
|
|
||||||
|
exports.readDefaultReservedFile = function(reserved) {
|
||||||
|
return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.readNameCache = function(filename, key) {
|
||||||
|
var cache = null;
|
||||||
|
if (filename) {
|
||||||
|
try {
|
||||||
|
var cache = fs.readFileSync(filename, "utf8");
|
||||||
|
cache = JSON.parse(cache)[key];
|
||||||
|
if (!cache) throw "init";
|
||||||
|
cache.props = UglifyJS.Dictionary.fromObject(cache.props);
|
||||||
|
} catch(ex) {
|
||||||
|
cache = {
|
||||||
|
cname: -1,
|
||||||
|
props: new UglifyJS.Dictionary()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.writeNameCache = function(filename, key, cache) {
|
||||||
|
if (filename) {
|
||||||
|
var data;
|
||||||
|
try {
|
||||||
|
data = fs.readFileSync(filename, "utf8");
|
||||||
|
data = JSON.parse(data);
|
||||||
|
} catch(ex) {
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
data[key] = {
|
||||||
|
cname: cache.cname,
|
||||||
|
props: cache.props.toObject()
|
||||||
|
};
|
||||||
|
fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
61
tools/props.html
Normal file
61
tools/props.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>(function(){
|
||||||
|
var props = {};
|
||||||
|
|
||||||
|
function addObject(obj) {
|
||||||
|
if (obj == null) return;
|
||||||
|
try {
|
||||||
|
Object.getOwnPropertyNames(obj).forEach(add);
|
||||||
|
} catch(ex) {}
|
||||||
|
if (obj.prototype) {
|
||||||
|
Object.getOwnPropertyNames(obj.prototype).forEach(add);
|
||||||
|
}
|
||||||
|
if (typeof obj == "function") {
|
||||||
|
try {
|
||||||
|
Object.getOwnPropertyNames(new obj).forEach(add);
|
||||||
|
} catch(ex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(name) {
|
||||||
|
props[name] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.getOwnPropertyNames(window).forEach(function(thing){
|
||||||
|
addObject(window[thing]);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
addObject(new Event("click"));
|
||||||
|
addObject(new Event("contextmenu"));
|
||||||
|
addObject(new Event("mouseup"));
|
||||||
|
addObject(new Event("mousedown"));
|
||||||
|
addObject(new Event("keydown"));
|
||||||
|
addObject(new Event("keypress"));
|
||||||
|
addObject(new Event("keyup"));
|
||||||
|
} catch(ex) {}
|
||||||
|
|
||||||
|
var ta = document.createElement("textarea");
|
||||||
|
ta.style.width = "100%";
|
||||||
|
ta.style.height = "20em";
|
||||||
|
ta.style.boxSizing = "border-box";
|
||||||
|
<!-- ta.value = Object.keys(props).sort(cmp).map(function(name){ -->
|
||||||
|
<!-- return JSON.stringify(name); -->
|
||||||
|
<!-- }).join(",\n"); -->
|
||||||
|
ta.value = JSON.stringify({
|
||||||
|
vars: [],
|
||||||
|
props: Object.keys(props).sort(cmp)
|
||||||
|
}, null, 2);
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
|
||||||
|
function cmp(a, b) {
|
||||||
|
a = a.toLowerCase();
|
||||||
|
b = b.toLowerCase();
|
||||||
|
return a < b ? -1 : a > b ? 1 : 0;
|
||||||
|
}
|
||||||
|
})();</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user