Compare commits

..

148 Commits

Author SHA1 Message Date
Alex Lam S.L
dfe4f6c6de v3.1.4 2017-10-16 02:44:17 +08:00
Alex Lam S.L
a09c8ad666 update dependency (#2362)
- source-map@0.6.1
2017-10-16 02:41:22 +08:00
Alex Lam S.L
ec598c351b fix-ups for #2356 (#2360) 2017-10-15 22:33:55 +08:00
Alex Lam S.L
eba0f93bc0 more tests for #2351 (#2357) 2017-10-12 02:58:25 +08:00
Roger Peppe
99800d4aa9 update README to include defaults (#2356)
fixes #2353
2017-10-12 02:56:02 +08:00
Tim Malone
70d56c951a Update README.md - sourceMappingURL directive note (#2355)
Moves this README note to underneath the 'url' rather than 'root' option.
2017-10-11 19:48:43 +08:00
Alex Lam S.L
b810e2f8da perform reduce_vars on safe literals (#2351)
- constant expression
- single reference
- same scope
- not across loop body
2017-10-09 12:25:06 +08:00
Alex Lam S.L
1abe14296e collapse a.b whenever safe (#2350) 2017-10-08 13:17:48 +08:00
Alex Lam S.L
6920e898d1 v3.1.3 2017-10-01 12:36:07 +08:00
Alex Lam S.L
dd71639264 enhance reduce_vars for AST_Accessor (#2339)
fixes #2336
2017-10-01 03:01:50 +08:00
Alex Lam S.L
2dcc552ce0 trap invalid use of reserved words (#2338)
fixes #2337
2017-10-01 02:10:41 +08:00
Alex Lam S.L
55387e8fd0 v3.1.2 2017-09-24 02:02:04 +08:00
kzc
7e3e9da860 fix "use asm" numeric output (#2328)
fixes #2324
2017-09-21 04:42:40 +08:00
Alex Lam S.L
00f509405b suppress collapse_vars of this into "use strict" (#2326)
fixes #2319
2017-09-20 05:23:20 +08:00
Alex Lam S.L
aceb0af36b v3.1.1 2017-09-17 04:36:27 +08:00
Alex Lam S.L
4f0953f7e9 handle LHS side-effects on cascade & collapse_vars (#2314)
fixes #2313
2017-09-16 11:45:19 +08:00
Alex Lam S.L
182a47bfb1 improve source mapping (#2312)
fixes #2310
2017-09-15 12:46:48 +08:00
Alex Lam S.L
cd27f4ec38 v3.1.0 2017-09-10 15:17:24 +08:00
Mateusz Burzyński
8158b1bdcf Testing all leading comments against being PURE comments (#2305) 2017-09-10 02:08:15 +08:00
Alex Lam S.L
aacf3edc68 extend unsafe on pure global functions (#2303) 2017-09-07 22:08:34 +08:00
kzc
8b89072190 add Date and other known globals to unsafe compress option (#2302) 2017-09-07 02:44:26 +08:00
Alex Lam S.L
395a17ccda fix collapse_vars on default function argument (#2299)
Avoid collision with local variable `undefined` under certain corner cases.

fixes #2298
2017-09-04 02:32:33 +08:00
Alex Lam S.L
3f355866cf correctly count declarations after hoist_vars (#2297)
fixes #2295
2017-09-03 17:23:31 +08:00
David Šanda
71d52f147d Fix CLI example for mangle reserved list of names (#2294) 2017-08-31 00:55:32 +08:00
David Šanda
eb7adaa6fc Fix CLI source-maps examples (#2291)
fixes #2284
2017-08-29 23:49:20 +08:00
Alex Lam S.L
e5cf7972ea fix unused patching of AST_For.init blocks (#2289)
fixes #2288
2017-08-29 01:10:04 +08:00
Alex Lam S.L
f81ff10a9b v3.0.28 2017-08-20 00:27:01 +08:00
Erik Desjardins
16d40915b4 don't escape null characters as \0 when followed by any digit (#2273)
fixes #2272
2017-08-14 12:30:08 +08:00
Alex Lam S.L
e7c21e87e3 fix ie8 mangling of top-level AST_SymbolCatch (#2263)
fixes #2254
2017-08-01 02:38:32 +08:00
Alex Lam S.L
c4c2ef44d0 v3.0.27 2017-07-30 01:50:42 +08:00
Alex Lam S.L
a845897758 improve mangle.properties (#2261)
- include dead code when `keep_quoted`
- unify `keep_quoted` & `reserved`
- make `test/run-tests.js` consistent with `minify()`

fixes #2256
2017-07-29 23:02:04 +08:00
kzc
32ea2c5530 issue template: describe acceptable JS input (#2255) 2017-07-27 21:38:36 +08:00
Alex Lam S.L
bc61deeca9 v3.0.26 2017-07-23 12:39:36 +08:00
Alex Lam S.L
6a5e74b44e unescape surrogate pairs only (#2246)
fixes #2242
2017-07-23 12:38:21 +08:00
Alex Lam S.L
54446341ee update dependencies (#2241)
- acorn@5.1.1
- commander@2.11.0
- mocha@3.4.2
2017-07-16 16:20:40 +08:00
Alex Lam S.L
4e12a6f740 v3.0.25 2017-07-16 11:05:53 +08:00
Alex Lam S.L
b35dfc2599 reject malformed CLI parameters (#2239)
fixes #2237
2017-07-15 23:50:27 +08:00
Alex Lam S.L
9e1da9235e ensure ie8 works with mangled properties (#2238)
fixes #2234
2017-07-15 22:50:59 +08:00
Alex Lam S.L
a5ffe2c23f drop unused builtin globals under unsafe (#2236)
fixes #2233
2017-07-15 15:16:11 +08:00
Alex Lam S.L
9282e7b0c6 fix unsafe evaluate of Object static methods (#2232)
fixes #2231
2017-07-14 19:52:01 +08:00
Alex Lam S.L
5229cb2b1b drop unused compound assignments (#2230)
fixes #2226
2017-07-14 00:39:34 +08:00
Alex Lam S.L
458e3e15f0 enhance passes (#2229)
- remove hardcoded upper limit
- continue based on node count reduction
- emit verbose statistics

fixes #2226
2017-07-13 02:18:59 +08:00
Alex Lam S.L
c615a1e80a fix gzip stream in test/benchmark.js (#2228) 2017-07-12 02:55:57 +08:00
Alex Lam S.L
10a938cb79 enhance source mapping on IIFEs (#2224)
fixes #2213
2017-07-11 02:34:28 +08:00
Alex Lam S.L
4956ad311b benchmark gzipped output (#2220) 2017-07-09 01:44:59 +08:00
kzc
145874e504 docs: update benchmarks using node 8, add babili (#2218) 2017-07-09 01:06:15 +08:00
Alex Lam S.L
bd7be07c38 v3.0.24 2017-07-08 12:53:20 +08:00
Alex Lam S.L
71ee91e716 handle duplicate argument names in collapse_vars (#2215) 2017-07-08 04:42:35 +08:00
Alex Lam S.L
4f70d2e28c inlining of static methods & constants (#2211)
- guard by `unsafe`
- support `Array`, `Math`, `Number`, `Object` & `String`

fixes #2207
2017-07-07 05:35:32 +08:00
Alex Lam S.L
4b6ca5e742 inline property access of object literal (#2209)
- only if property value is side-effect-free
- guard by `unsafe`

fixes #2208
2017-07-06 21:51:58 +08:00
Alex Lam S.L
9306da3c58 suppress collapse_vars of this as call argument (#2204)
fixes #2203
2017-07-06 01:03:52 +08:00
Alex Lam S.L
1ac25fc032 improve compress granularity through typeofs (#2201)
fixes #2198
2017-07-05 19:20:33 +08:00
Alex Lam S.L
5f046c724b minor clean-ups to evaluate (#2197) 2017-07-03 18:52:39 +08:00
Alex Lam S.L
af0262b7e5 improve parenthesis emission (#2196)
- eliminate `throw` usages
- suppress extraneous parenthesis
  - `new function() {foo.bar()}.baz`
  - `for (function() { "foo" in bar; };;);`
2017-07-03 04:17:37 +08:00
Alex Lam S.L
6b3aeff1d8 clean up TreeWalker.pop() (#2195)
Remove superfluous parameter.
2017-07-03 03:23:38 +08:00
Alex Lam S.L
20e4f8277f refactor throw usage within compress (#2193)
Eliminate exceptional constructs from normal control flow.
2017-07-03 02:10:56 +08:00
kzc
f3a487a368 document fast mangle-only minify mode (#2194) 2017-07-03 01:37:04 +08:00
Alex Lam S.L
2dde41615a v3.0.23 2017-07-02 17:24:22 +08:00
Alex Lam S.L
8b69a3d18e drop argument value after collapse_vars (#2190) 2017-07-02 04:28:11 +08:00
Alex Lam S.L
d40950b741 improve inline efficiency (#2188)
... by teaching `collapse_vars` some new tricks.

fixes #2187
2017-07-02 01:05:14 +08:00
Alex Lam S.L
7659ea1d2e v3.0.22 2017-06-30 11:18:34 +08:00
Alex Lam S.L
bdeadffbf5 improve usability of name cache under minify() (#2176)
fixes #2174
2017-06-29 12:48:34 +08:00
Alex Lam S.L
5e6f26445f v3.0.21 2017-06-29 00:49:06 +08:00
Alex Lam S.L
f0a99125ee improve unsafe_Func (#2171)
- minimise disturbance to `compute_char_frequency()`
- remove extraneous quotation marks
2017-06-27 23:53:42 +08:00
Alex Lam S.L
1e4de2e6d3 parse @global_defs as expressions (#2169)
- let parser rejects non-conformant input
- eliminate need for extraneous parenthesis
2017-06-27 10:31:19 +08:00
Alex Lam S.L
8b4dcd8f3e v3.0.20 2017-06-25 15:05:05 +08:00
Alex Lam S.L
285401ced8 more tests for #2158 (#2160) 2017-06-25 14:21:48 +08:00
Alex Lam S.L
9db4c42380 fix cascade & collapse on property access of constants (#2158) 2017-06-24 21:30:06 +08:00
Alex Lam S.L
94e5e00c03 refactor compute_char_frequency() (#2152)
- minimise maintenance when updating AST
- maximise code sharing between `master` & `harmony`
2017-06-23 20:05:31 +08:00
Alex Lam S.L
dc6bcaa18e synchronise mangle.properties for minify() & test/compress (#2151) 2017-06-23 15:53:13 +08:00
Alex Lam S.L
d58b184835 refactor Compressor.toplevel (#2149) 2017-06-23 13:11:40 +08:00
Alex Lam S.L
b3a57ff019 minimise reduce_vars cloning overhead (#2148) 2017-06-23 06:59:53 +08:00
Alex Lam S.L
3d5bc08185 fix reduce_vars on this (#2145)
fixes #2140
2017-06-23 04:44:57 +08:00
Alex Lam S.L
0692435f01 fix for-in loop parsing (#2144) 2017-06-23 04:14:30 +08:00
Alex Lam S.L
f67a6b0e43 v3.0.19 2017-06-22 03:24:22 +08:00
Alex Lam S.L
343ea326c2 ensure mangling works if catch reuses a scope variable (#2123)
fixes #2120
2017-06-20 02:14:05 +08:00
Alex Lam S.L
1c150c632f v3.0.18 2017-06-18 15:01:20 +08:00
Alex Lam S.L
0a0f4f5591 make defensive copies when inline (#2116)
fixes #2114
2017-06-17 14:32:37 +08:00
Alex Lam S.L
931daa85bf fix loss of context in collapse_vars & cascade (#2112)
fixes #2110
2017-06-16 21:18:43 +08:00
Alex Lam S.L
00e4f7b3c1 in-place tigten_body() (#2111)
By reducing copies of `AST_Node` arrays, we should be able to reduce:
- memory pressure
- potential bugs caused by multiple references in AST
- duplicated executions of `OPT()`
2017-06-16 19:19:54 +08:00
Alex Lam S.L
11e63bc335 correctly determine scope of AST_This (#2109)
fixes #2107
2017-06-16 14:54:46 +08:00
Alex Lam S.L
33405bb24b enforce inline scope restriction (#2106)
fixes #2105
2017-06-16 03:21:38 +08:00
Alex Lam S.L
57dc4fb32f v3.0.17 2017-06-15 18:59:37 +08:00
Alex Lam S.L
b85a358deb suppress inline of this (#2103)
fixes #2101
2017-06-15 12:14:16 +08:00
Alex Lam S.L
43697958f3 avoid intermittent test time-out failures (#2100) 2017-06-15 04:47:57 +08:00
Alex Lam S.L
3f961bbba0 compute uses_arguments correctly in figure_out_scope() (#2099)
fixes #2097
2017-06-15 03:28:26 +08:00
Alex Lam S.L
0a1e523cd5 fix parsing of expect_stdout (#2096)
fixes #2095
2017-06-15 01:00:03 +08:00
Alex Lam S.L
4231f7323e v3.0.16 2017-06-14 16:45:09 +08:00
kzc
da2de350c3 add comment about quote_style and gzip (#2092) 2017-06-14 12:23:03 +08:00
Alex Lam S.L
41beae4dd7 cache web assets between CI runs (#2089)
- skip `test/jetstream.js` for `node@0.12`
2017-06-14 11:53:10 +08:00
Ziad El Khoury Hanna
82db9188ac fix CLI parsing of --source-map content (#2088)
fixes #2082
2017-06-13 16:30:46 +08:00
Alex Lam S.L
3dc9e140e4 add Node.js 8 to Travis CI (#2086)
- explicitly terminate `test/jetstream.js` upon completion
- log verbose output from `test/benchmark.js` & `test/jetstream.js`
- remove obsolete workaround for Travis CI
2017-06-13 06:21:16 +08:00
Alex Lam S.L
fed0096556 allow expect_stdout to specify Error (#2087) 2017-06-13 04:57:26 +08:00
Alex Lam S.L
2bdc8802dd fix variable accounting in inline (#2085)
fixes #2084
2017-06-13 01:40:14 +08:00
Alex Lam S.L
5ef7cb372a suppress false positives for-in loops (#2080)
fixes #2079
2017-06-10 13:55:17 +08:00
Alex Lam S.L
4ad7b1dae4 fix portability of sandbox.run_code() on Node.js 0.1x (#2078) 2017-06-10 01:08:58 +08:00
Alex Lam S.L
9186859cb7 fix non-string parameters (#2076)
`Stream.write()` is not as versatile as `console.log()`
2017-06-10 00:11:40 +08:00
Alex Lam S.L
47c0713747 report test/ufuzz.js failures in process.stderr (#2074) 2017-06-09 15:56:28 +08:00
Alex Lam S.L
293c566d6c marshal mangle[.properties].reserved from non-Array values (#2072) 2017-06-09 04:29:12 +08:00
Alex Lam S.L
9c306406f1 fix iteration over object with inherited properties (#2068)
fixes #2055
2017-06-08 03:27:03 +08:00
Alex Lam S.L
9db0695b10 fix cascade on multi-branch evaluations (#2067)
Partially reverts #2059 as this has better coverage and performance.

fixes #2062
2017-06-07 19:52:01 +08:00
Alex Lam S.L
f2af093402 fix CLI output corruption (#2061)
Using `console.error()` & `console.log()` result in inconsistent formatting across Node.js versions.

Avoid this issue by directly writing to `process.stderr` & `process.stdout` instead.

Miscellaneous
- prettify invalid option listing
2017-06-07 04:25:32 +08:00
Alex Lam S.L
b9ad53d1ab fix inline handling of AST_Call.args (#2059) 2017-06-06 22:55:25 +08:00
Alex Lam S.L
b0eab71470 implement test/jetstream.js --debug (#2058) 2017-06-06 19:28:12 +08:00
Alex Lam S.L
3493a182b2 implement function inlining (#2053)
- empty body
- single `AST_Return`
- single `AST_SimpleStatement`
- avoid `/*#__PURE__*/`

Miscellaneous
- enhance single-use function substitution

fixes #281
2017-06-06 05:49:53 +08:00
Alex Lam S.L
27c5284d3d workaround webkit parsing error (#2056)
apply `webkit` to jetstream tests
2017-06-06 04:06:42 +08:00
Alex Lam S.L
540220b91b fix AST_Function scope invariance (#2052)
improve function name hack in `run_code()`
2017-06-04 19:27:43 +08:00
Alex Lam S.L
84634da4b5 add tests for AST_SymbolAccessor (#2049) 2017-06-03 16:08:10 +08:00
Alex Lam S.L
1743621889 clean up lib/parse.js (#2047)
- remove unused definitions
- replace `array_to_hash()`
2017-06-03 14:00:59 +08:00
kzc
f330ab743a better document behavior of unsafe_Func (#2043) 2017-06-02 12:07:17 +08:00
Alex Lam S.L
4377e932ca v3.0.15 2017-06-01 18:12:38 +08:00
Alex Lam S.L
bac14ba881 fix non-identifier getter/setter name (#2041)
fixes #2040
2017-06-01 18:11:16 +08:00
Alex Lam S.L
ec095ed647 whitelist unsafe evaluate candidates (#2039)
- all arguments may accept constant values
- return constant value
- free of side effects
- available & identical across locales and runtime environments
2017-06-01 04:33:05 +08:00
Alex Lam S.L
17e73121fa enhance unsafe evaluate (#2037) 2017-06-01 00:56:28 +08:00
kzc
f71e8fd948 reformat mangle options section of README (#2036) 2017-05-31 21:52:43 +08:00
Alex Lam S.L
3e62faa64f v3.0.14 2017-05-31 11:34:51 +08:00
Alex Lam S.L
e9645e017f introduce unsafe_Func (#2033)
Separate flag for #203 functionality.
2017-05-31 03:38:00 +08:00
Alex Lam S.L
55b5f2a8aa widen CLI parse error code fragment displayed (#2032)
fixes #2030
2017-05-31 01:56:52 +08:00
Alex Lam S.L
4e0a22e5c8 v3.0.13 2017-05-29 10:52:13 +08:00
Alex Lam S.L
1aa38051fb better fix for #512 & #2010 (#2019)
- remove duplicated functionalities
- fix similar issue with `else`
2017-05-29 10:51:41 +08:00
Alex Lam S.L
e62b879b48 display default values in --help options (#2018) 2017-05-28 22:57:20 +08:00
Alex Lam S.L
c6c9f4f5a8 implement --help options (#2017) 2017-05-28 18:21:44 +08:00
Alex Lam S.L
fec14379f6 improve CLI usability (#2016)
Report supported options upon invalid option syntax.

fixes #1883
2017-05-28 04:09:40 +08:00
Alex Lam S.L
79131cd647 extend node_version range on applicable tests (#2015) 2017-05-27 22:18:28 +08:00
Alex Lam S.L
c3f14a1481 v3.0.12 2017-05-27 18:08:09 +08:00
Alex Lam S.L
7b13159cda fix hoist_funs on block-scoped function under "use strict" (#2013)
Technically not part of ES5, but commonly used code exists in the wild.
2017-05-27 17:44:59 +08:00
Alex Lam S.L
95094b9c22 fix if_return on AST_Defun (#2010)
Previous fiix for #1052 perturbs declaration order of functions which leads to incorrect behaviour under "use strict".
2017-05-27 13:41:49 +08:00
kzc
1ff8e9dd38 clarify what --mangle-props does (#2012) 2017-05-27 13:17:30 +08:00
kzc
78309a293d better document mangle properties options (#2009) 2017-05-27 02:28:43 +08:00
kzc
695e182d59 fix and expand --mangle-props documentation (#2008)
fixes #2007
2017-05-27 01:25:51 +08:00
Alex Lam S.L
dc33facfcb fix dead_code on block-scoped function under "use strict" (#2006)
Technically not part of ES5, but commonly used code exists in the wild.
2017-05-26 16:08:51 +08:00
Alex Lam S.L
c70fb60384 clean up lib/scope.js (#2003)
fixes #2004
2017-05-26 03:58:35 +08:00
Alex Lam S.L
793d61499b report timing breakdown (#2000)
fix corner cases with `sourceMap`

fixes #1998
2017-05-25 07:15:55 +08:00
Alex Lam S.L
a277fe168d ensure new line after describe_ast() (#1999) 2017-05-25 02:32:36 +08:00
Alex Lam S.L
7d3b941e6e reinstate describe_ast() on CLI (#1996)
fixes #1995
2017-05-24 02:30:09 +08:00
Alex Lam S.L
e95052a423 v3.0.11 2017-05-23 22:26:59 +08:00
Alex Lam S.L
e667f0acb8 fix source map offset (#1993)
Account for whitespace insertions.

fixes #505
fixes #890
2017-05-23 20:25:48 +08:00
kzc
69ac794bc8 add another minify() options example (#1988) 2017-05-22 12:19:07 +08:00
Alex Lam S.L
efdb65913b improve usability of global_defs in minify() (#1987)
Use `@key` to `parse()` string value as `AST_Node`.

fixes #1986
2017-05-22 01:38:43 +08:00
kzc
a1dedeb3ce more refinement of minify() documentation (#1983) 2017-05-21 04:55:03 +08:00
Alex Lam S.L
d3c4a8e9e7 v3.0.10 2017-05-21 01:30:17 +08:00
kzc
7e164aba8f add "es5" to package.json keywords (#1980) 2017-05-20 22:09:50 +08:00
kzc
22aedef849 document minify() option toplevel (#1979) 2017-05-20 22:09:21 +08:00
Alex Lam S.L
58fae7dc07 enhance if_return to handle return void... (#1977)
fixes #512
2017-05-20 15:58:46 +08:00
kzc
5bf8d7e949 document 3.x minify() does not throw errors (#1975) 2017-05-20 10:49:35 +08:00
kzc
1df9d06f4a document minify warnings and add an error example (#1973) 2017-05-19 17:20:21 +08:00
Alex Lam S.L
3408fc9d32 v3.0.9 2017-05-19 09:35:26 +08:00
Alex Lam S.L
eae26756f1 introduce unsafe_regexp (#1970)
fixes #1964
2017-05-19 09:06:29 +08:00
119 changed files with 6453 additions and 10099 deletions

View File

@@ -8,7 +8,14 @@
**Uglify version (`uglifyjs -V`)**
**JavaScript input** <!-- ideally as small as possible -->
**JavaScript input**
<!--
A complete parsable JS program exhibiting the issue with
UglifyJS alone - without third party tools or libraries.
Ideally the input should be as small as possible.
Post a link to a gist if necessary.
-->
**The `uglifyjs` CLI command executed or `minify()` options used.**

View File

@@ -4,8 +4,11 @@ node_js:
- "0.12"
- "4"
- "6"
- "8"
env:
- UGLIFYJS_TEST_ALL=1
matrix:
fast_finish: true
sudo: false
cache:
directories: tmp

571
README.md
View File

@@ -1,11 +1,14 @@
uglify-es
=========
UglifyJS 3
==========
**uglify-es** is an ECMAScript 2015 parser, minifier, compressor and beautifier toolkit.
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
#### Note:
- **The `uglify-es` API and CLI is compatible with `uglify-js@3.x`.**
- **`uglify-es` is not backwards compatible with the `uglify-js@2.x` API and CLI.**
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- `uglify-js` only supports ECMAScript 5 (ES5).
- Those wishing to minify
ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
Install
-------
@@ -15,11 +18,11 @@ First make sure you have installed the latest version of [node.js](http://nodejs
From NPM for use as a command line app:
npm install uglify-es -g
npm install uglify-js -g
From NPM for programmatic use:
npm install uglify-es
npm install uglify-js
# Command line usage
@@ -42,6 +45,7 @@ a double dash to prevent input files being used as option arguments:
```
-h, --help Print usage information.
`--help options` for details on available options.
-V, --version Print version number.
-p, --parse <options> Specify parser options:
`acorn` Use Acorn for parsing.
@@ -107,7 +111,7 @@ a double dash to prevent input files being used as option arguments:
By default UglifyJS will not try to be IE-proof.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--name-cache File to hold mangled name mappings.
--name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options:
`base` Path to compute relative paths from input files.
@@ -123,8 +127,8 @@ a double dash to prevent input files being used as option arguments:
the source map.
`url` If specified, path to the source map to append in
`//# sourceMappingURL`.
--stats Display operations run time on STDERR.
--toplevel Compress and/or mangle variables in toplevel scope.
--timings Display operations run time on STDERR.
--toplevel Compress and/or mangle variables in top level scope.
--verbose Print diagnostic messages.
--warn Print warning messages.
--wrap <name> Embed everything in a big function, making the
@@ -146,19 +150,19 @@ debugging your compressed JavaScript. To get a source map, pass
Additional options:
- `--source-map filename=<NAME>` to specify the name of the source map.
- `--source-map "filename='<NAME>'"` to specify the name of the source map.
- `--source-map root=<URL>` to pass the URL where the original files can be found.
- `--source-map "root='<URL>'"` to pass the URL where the original files can be found.
- `--source-map "url='<URL>'"` to specify the URL where the source map can be found.
Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the
`//# sourceMappingURL=` directive.
- `--source-map url=<URL>` to specify the URL where the source map can be found.
For example:
uglifyjs js/file1.js js/file2.js \
-o foo.min.js -c -m \
--source-map root="http://foo.com/src",url=foo.min.js.map
--source-map "root='http://foo.com/src',url='foo.min.js.map'"
The above will compress and mangle `file1.js` and `file2.js`, will drop the
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
@@ -177,8 +181,8 @@ CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript →
compressed JS by mapping every token in the compiled JS to its original
location.
To use this feature pass `--source-map content="/path/to/input/source.map"`
or `--source-map content=inline` if the source map is included inline with
To use this feature pass `--source-map "content='/path/to/input/source.map'"`
or `--source-map "content=inline"` if the source map is included inline with
the sources.
## CLI compress options
@@ -199,40 +203,68 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `toplevel` mangle names declared in the toplevel scope (disabled by
default).
- `toplevel` (default `false`) -- mangle names declared in the top level scope.
- `eval` mangle names visible in scopes where `eval` or `with` are used
(disabled by default).
- `eval` (default `false`) -- mangle names visible in scopes where `eval` or `with` are used.
When mangling is enabled but you want to prevent certain names from being
mangled, you can declare those names with `--mangle reserved` — pass a
comma-separated list of names. For example:
uglifyjs ... -m reserved=[$,require,exports]
uglifyjs ... -m reserved=['$','require','exports']
to prevent the `require`, `exports` and `$` names from being changed.
### CLI 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:
**Note:** THIS WILL PROBABLY BREAK YOUR CODE. Mangling property names
is a separate step, different from variable name mangling. Pass
`--mangle-props` to enable it. It will mangle all properties in the
input code with the exception of built in DOM properties and properties
in core javascript classes. For example:
```javascript
// example.js
var x = {
foo: 1
baz_: 0,
foo_: 1,
calc: function() {
return this.foo_ + this.baz_;
}
};
x.bar = 2;
x["baz"] = 3;
x[condition ? "moo" : "boo"] = 4;
console.log(x.something());
x.bar_ = 2;
x["baz_"] = 3;
console.log(x.calc());
```
Mangle all properties (except for javascript `builtins`):
```bash
$ uglifyjs example.js -c -m --mangle-props
```
```javascript
var x={o:0,_:1,l:function(){return this._+this.o}};x.t=2,x.o=3,console.log(x.l());
```
Mangle all properties except for `reserved` properties:
```bash
$ uglifyjs example.js -c -m --mangle-props reserved=[foo_,bar_]
```
```javascript
var x={o:0,foo_:1,_:function(){return this.foo_+this.o}};x.bar_=2,x.o=3,console.log(x._());
```
Mangle all properties matching a `regex`:
```bash
$ uglifyjs example.js -c -m --mangle-props regex=/_$/
```
```javascript
var x={o:0,_:1,calc:function(){return this._+this.o}};x.l=2,x.o=3,console.log(x.calc());
```
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
with single characters, while `something()` will be left as is.
Combining mangle properties options:
```bash
$ uglifyjs example.js -c -m --mangle-props regex=/_$/,reserved=[bar_]
```
```javascript
var x={o:0,_:1,calc:function(){return this._+this.o}};x.bar_=2,x.o=3,console.log(x.calc());
```
In order for this to be of any use, we avoid mangling standard JS names by
default (`--mangle-props builtins` to override).
@@ -241,7 +273,7 @@ A default exclusion file is provided in `tools/domprops.json` which should
cover most standard JS and DOM properties defined in various browsers. Pass
`--mangle-props domprops` to disable this feature.
You can also use a regular expression to define which property names should be
A regular expression can be used to define which property names should be
mangled. For example, `--mangle-props regex=/^_/` will only mangle property
names that start with an underscore.
@@ -269,9 +301,20 @@ Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
so that it is not mangled throughout the entire script even when used in an
unquoted style (`o.foo`). Example:
```javascript
// stuff.js
var o = {
"foo": 1,
bar: 3
};
o.foo += o.bar;
console.log(o.foo);
```
```bash
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props keep_quoted -mc
var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo);
$ uglifyjs stuff.js --mangle-props keep_quoted -c -m
```
```javascript
var o={foo:1,o:3};o.foo+=o.o,console.log(o.foo);
```
### Debugging property name mangling
@@ -282,6 +325,13 @@ would mangle to `o._$foo$_` with this option. This allows property mangling
of a large codebase while still being able to debug the code and identify
where mangling is breaking things.
```bash
$ uglifyjs stuff.js --mangle-props debug -c -m
```
```javascript
var o={_$foo$_:1,_$bar$_:3};o._$foo$_+=o._$bar$_,console.log(o._$foo$_);
```
You can also pass a custom suffix using `--mangle-props debug=XYZ`. This would then
mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a
script to identify how a property got mangled. One technique is to pass a
@@ -295,30 +345,137 @@ identify mistakes like writing mangled keys to storage.
Assuming installation via NPM, you can load UglifyJS in your application
like this:
```javascript
var UglifyJS = require("uglify-es");
var UglifyJS = require("uglify-js");
```
There is a single high level minification function, `minify(files, options)`, which will
performs all the steps in a configurable manner.
Example:
There is a single high level function, **`minify(code, options)`**,
which will perform all minification [phases](#minify-options) in a configurable
manner. By default `minify()` will enable the options [`compress`](#compress-options)
and [`mangle`](#mangle-options). Example:
```javascript
var result = UglifyJS.minify("var b = function() {};");
console.log(result.code); // minified output
console.log(result.error); // runtime error
var code = "function add(first, second) { return first + second; }";
var result = UglifyJS.minify(code);
console.log(result.error); // runtime error, or `undefined` if no error
console.log(result.code); // minified output: function add(n,d){return n+d}
```
You can also compress multiple files:
You can `minify` more than one JavaScript file at a time by using an object
for the first argument where the keys are file names and the values are source
code:
```javascript
var result = UglifyJS.minify({
"file1.js": "var a = function() {};",
"file2.js": "var b = function() {};"
});
var code = {
"file1.js": "function add(first, second) { return first + second; }",
"file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var result = UglifyJS.minify(code);
console.log(result.code);
// function add(d,n){return d+n}console.log(add(3,7));
```
The `toplevel` option:
```javascript
var code = {
"file1.js": "function add(first, second) { return first + second; }",
"file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = { toplevel: true };
var result = UglifyJS.minify(code, options);
console.log(result.code);
// console.log(3+7);
```
The `nameCache` option:
```javascript
var options = {
mangle: {
toplevel: true,
},
nameCache: {}
};
var result1 = UglifyJS.minify({
"file1.js": "function add(first, second) { return first + second; }"
}, options);
var result2 = UglifyJS.minify({
"file2.js": "console.log(add(1 + 2, 3 + 4));"
}, options);
console.log(result1.code);
// function n(n,r){return n+r}
console.log(result2.code);
// console.log(n(3,7));
```
You may persist the name cache to the file system in the following way:
```javascript
var cacheFileName = "/tmp/cache.json";
var options = {
mangle: {
properties: true,
},
nameCache: JSON.parse(fs.readFileSync(cacheFileName, "utf8"))
};
fs.writeFileSync("part1.js", UglifyJS.minify({
"file1.js": fs.readFileSync("file1.js", "utf8"),
"file2.js": fs.readFileSync("file2.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync("part2.js", UglifyJS.minify({
"file3.js": fs.readFileSync("file3.js", "utf8"),
"file4.js": fs.readFileSync("file4.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync(cacheFileName, JSON.stringify(options.nameCache), "utf8");
```
An example of a combination of `minify()` options:
```javascript
var code = {
"file1.js": "function add(first, second) { return first + second; }",
"file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = {
toplevel: true,
compress: {
global_defs: {
"@console.log": "alert"
},
passes: 2
},
output: {
beautify: false,
preamble: "/* uglified */"
}
};
var result = UglifyJS.minify(code, options);
console.log(result.code);
// /* uglified */
// alert(10);"
```
To produce warnings:
```javascript
var code = "function f(){ var u; return 2 + 3; }";
var options = { warnings: true };
var result = UglifyJS.minify(code, options);
console.log(result.error); // runtime error, `undefined` in this case
console.log(result.warnings); // [ 'Dropping unused variable u [0:1,18]' ]
console.log(result.code); // function f(){return 5}
```
An error example:
```javascript
var result = UglifyJS.minify({"foo.js" : "if (0) else console.log(1);"});
console.log(JSON.stringify(result.error));
// {"message":"Unexpected token: keyword (else)","filename":"foo.js","line":1,"col":7,"pos":7}
```
Note: unlike `uglify-js@2.x`, the `3.x` API does not throw errors. To
achieve a similar effect one could do the following:
```javascript
var result = UglifyJS.minify(code, options);
if (result.error) throw result.error;
```
## Minify options
- `warnings` (default `false`) — pass `true` to display compressor warnings.
- `warnings` (default `false`) — pass `true` to return compressor warnings
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
- `parse` (default `{}`) — pass an object if you wish to specify some
additional [parse options](#parse-options).
@@ -342,9 +499,19 @@ console.log(result.code);
- `toplevel` (default `false`) - set to `true` if you wish to enable top level
variable and function name mangling and to drop unused variables and functions.
- `nameCache` (default `null`) - pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
a read/write property. `minify()` will read the name cache state of this
object and update it during minification so that it may be
reused or externally persisted by the user.
- `ie8` (default `false`) - set to `true` to support IE8.
## Minify option structure
- `keep_fnames` (default: `false`) - pass `true` to prevent discarding or mangling
of function names. Useful for code relying on `Function.prototype.name`.
## Minify options structure
```javascript
{
@@ -368,6 +535,7 @@ console.log(result.code);
sourceMap: {
// source map options
},
nameCache: null, // or specify a name cache object
toplevel: false,
ie8: false,
}
@@ -423,99 +591,78 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Parse options
- `bare_returns` (default `false`) -- support top level `return` statements
- `html5_comments` (default `true`)
- `shebang` (default `true`) -- support `#!command` as the first line
## Compress options
- `sequences` (default: true) -- join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
to disable. The smallest `sequences` length is `2`. A `sequences` value of `1`
is grandfathered to be equivalent to `true` and as such means `200`. On rare
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `booleans` (default: `true`) -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c`
- `properties` -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `cascade` (default: `true`) -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `dead_code` -- remove unreachable code
- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables - side
effects permitting.
- `drop_debugger` -- remove `debugger;` statements
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: false) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`,
or `valueOf`. This could cause change in execution order after operands in the
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `conditionals` -- apply optimizations for `if`-s and conditional
expressions
- `comparisons` -- apply certain optimizations to binary nodes, for example:
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes, for example:
`!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `evaluate` -- attempt to evaluate constant expressions
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
expressions
- `booleans` -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c`
- `dead_code` (default: `true`) -- remove unreachable code
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition
- `drop_console` (default: `false`) -- default `false`. Pass `true` to discard calls to
`console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `unused` -- drop unreferenced functions and variables (simple direct variable
assignments do not count as references unless set to `"keep_assign"`)
- `drop_debugger` (default: `true`) -- remove `debugger;` statements
- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the toplevel scope (`false` by default, `true` to drop both unreferenced
functions and variables)
- `evaluate` (default: `true`) -- attempt to evaluate constant expressions
- `top_retain` -- prevent specific toplevel functions and variables from `unused`
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`)
- `expression` (default: `false`) -- default `false`. Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `hoist_funs` -- hoist function declarations
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
- `hoist_funs` (default: `true`) -- hoist function declarations
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general)
- `if_return` -- optimizations for if/return and if/continue
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
- `join_vars` -- join consecutive `var` statements
- `inline` (default: `true`) -- embed simple functions
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `join_vars` (default: `true`) -- join consecutive `var` statements
- `collapse_vars` -- Collapse single-use `var` and `const` definitions
when possible.
- `keep_fargs` (default: `true`) -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
- `reduce_vars` -- Improve optimization on variables assigned with and
used as constant values.
- `keep_infinity` (default: `false`) -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `warnings` -- display warnings when dropping unreachable code or unused
declarations etc.
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition
- `negate_iife` -- negate "Immediately-Called Function Expressions"
- `negate_iife` (default: `true`) -- 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.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `passes` (default: `1`) -- The maximum number of times to run compress.
In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time.
- `pure_funcs` -- default `null`. You can pass an array of names and
- `properties` (default: `true`) -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `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
@@ -526,47 +673,79 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
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. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `pure_getters` (default: `"strict"`) -- 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.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `expression` -- default `false`. Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
used as constant values.
- `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
- `sequences` (default: `true`) -- join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
to disable. The smallest `sequences` length is `2`. A `sequences` value of `1`
is grandfathered to be equivalent to `true` and as such means `200`. On rare
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `keep_fnames` -- default `false`. Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `side_effects` (default: `true`) -- default `true`. Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `passes` -- default `1`. Number of times to run compress with a maximum of 3.
In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time.
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the top level scope (`false` by default, `true` to drop both unreferenced
functions and variables)
- `side_effects` -- default `true`. Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo()`;
- `top_retain` (default: `null`) -- prevent specific toplevel functions and variables from `unused`
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`)
- `typeofs` (default: `true`) -- default `true`. Transforms `typeof foo == "undefined"` into
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues.
- `unsafe_comps` (default: `false`) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`,
or `valueOf`. This could cause change in execution order after operands in the
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `unsafe_Func` (default: `false`) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: `false`) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_proto` (default: `false`) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple direct variable
assignments do not count as references unless set to `"keep_assign"`)
- `warnings` (default: `false`) -- display warnings when dropping unreachable code or unused
declarations etc.
## Mangle options
- `reserved` - pass an array of identifiers that should be excluded from mangling
- `reserved` (default `[]`). Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` mangle names declared in the toplevel scope (disabled by
default).
- `toplevel` (default `false`). Pass `true` to mangle names declared in the
top level scope.
- `eval` mangle names visible in scopes where eval or with are used
(disabled by default).
- `keep_fnames` -- default `false`. Pass `true` to not mangle
function names. Useful for code relying on `Function.prototype.name`.
See also: the `keep_fnames` [compress option](#compress-options).
- `eval` (default `false`). Pass `true` to mangle names visible in scopes
where `eval` or `with` are used.
Examples:
@@ -592,10 +771,19 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options
- `regex` — Pass a RegExp to only mangle certain names
- `keep_quoted` — Only mangle unquoted property names
- `debug` — Mangle names with the original name still present. Defaults to `false`.
Pass an empty string to enable, or a non-empty string to set the suffix.
- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
- `debug` (default: `false`) -— Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `keep_quoted` (default: `false`) -— Only mangle unquoted property names.
- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property
names matching the regular expression.
- `reserved` (default: `[]`) -- Do not mangle property names listed in the
`reserved` array.
## Output options
@@ -605,51 +793,64 @@ can pass additional arguments that control the code output:
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `beautify` (default `true`) -- whether to actually beautify the output.
Passing `-b` will set this to true, but you might need to pass `-b` even
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
- `comments` (default `false`) -- pass `true` or `"all"` to preserve all
comments, `"some"` to preserve some comments, a regular expression string
(e.g. `/^!/`) or a function.
- `ecma` (default `5`) -- set output printing mode. This will only change the
output in direct control of the beautifier. Non-compatible features in the
abstract syntax tree will still be outputted as is.
- `indent_level` (default 4)
- `indent_start` (default 0) -- prefix all lines by that many spaces
- `indent_level` (default `4`)
- `indent_start` (default `0`) -- prefix all lines by that many spaces
- `inline_script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
- `max_line_len` (default `false`) -- maximum line length (for uglified code)
- `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.
- `preserve_line` (default `false`) -- pass `true` to preserve lines, but it
only works if `beautify` is set to `false`.
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
objects
- `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.
more double quotes in the string itself. `0` is best for gzip size.
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `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).
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
- `width` (default 80) -- only takes effect when beautification is on, this
- `width` (default `80`) -- only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
function expressions. See
[#640](https://github.com/mishoo/UglifyJS2/issues/640) for more details.
@@ -695,7 +896,6 @@ when this flag is on:
- `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)
@@ -722,8 +922,8 @@ 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
`build/defines.js` file with the following:
```javascript
const DEBUG = false;
const PRODUCTION = true;
var DEBUG = false;
var PRODUCTION = true;
// etc.
```
@@ -743,7 +943,7 @@ You can also use conditional compilation via the programmatic API. With the diff
property name is `global_defs` and is a compressor property:
```javascript
var result = uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
var result = UglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
compress: {
dead_code: true,
global_defs: {
@@ -753,6 +953,32 @@ var result = uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
});
```
To replace an identifier with an arbitrary non-constant expression it is
necessary to prefix the `global_defs` key with `"@"` to instruct UglifyJS
to parse the value as an expression:
```javascript
UglifyJS.minify("alert('hello');", {
compress: {
global_defs: {
"@alert": "console.log"
}
}
}).code;
// returns: 'console.log("hello");'
```
Otherwise it would be replaced as string literal:
```javascript
UglifyJS.minify("alert('hello');", {
compress: {
global_defs: {
"alert": "console.log"
}
}
}).code;
// returns: '"console.log"("hello");'
```
### Using native Uglify AST with `minify()`
```javascript
// example: parse only, produce native Uglify AST
@@ -822,3 +1048,30 @@ in total it's a bit more than just using UglifyJS's own parser.
[acorn]: https://github.com/ternjs/acorn
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
### Uglify Fast Minify Mode
It's not well known, but whitespace removal and symbol mangling accounts
for 95% of the size reduction in minified code for most javascript - not
elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to
[`butternut`](https://www.npmjs.com/package/butternut):
| d3.js | minify size | gzip size | minify time (seconds) |
| --- | ---: | ---: | ---: |
| original | 451,131 | 108,733 | - |
| uglify-js@3.0.24 mangle=false, compress=false | 316,600 | 85,245 | 0.70 |
| uglify-js@3.0.24 mangle=true, compress=false | 220,216 | 72,730 | 1.13 |
| butternut@0.4.6 | 217,568 | 72,738 | 1.41 |
| uglify-js@3.0.24 mangle=true, compress=true | 212,511 | 71,560 | 3.36 |
| babili@0.1.4 | 210,713 | 72,140 | 12.64 |
To enable fast minify mode from the CLI use:
```
uglifyjs file.js -m
```
To enable fast minify mode with the API use:
```js
UglifyJS.minify(code, { compress: false, mangle: true });
```

View File

@@ -21,14 +21,25 @@ var options = {
compress: false,
mangle: false
};
program.version(info.name + ' ' + info.version);
program.version(info.name + " " + info.version);
program.parseArgv = program.parse;
program.parse = undefined;
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true));
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true));
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast;
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() {
var text = [];
var options = UglifyJS.default_options();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
text.push(format_object(options[option]));
text.push("");
}
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js());
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js());
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js());
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js());
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js());
program.option("-o, --output <file>", "Output file (default STDOUT).");
program.option("--comments [filter]", "Preserve copyright comments in the output.");
program.option("--config-file <file>", "Read minify() options from JSON file.");
@@ -38,7 +49,7 @@ program.option("--keep-fnames", "Do not mangle/drop function names. Useful for c
program.option("--name-cache <file>", "File to hold mangled name mappings.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
program.option("--stats", "Display operations run time on STDERR.")
program.option("--timings", "Display operations run time on STDERR.")
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
program.option("--verbose", "Print diagnostic messages.");
program.option("--warn", "Print warning messages.");
@@ -95,17 +106,8 @@ if (program.mangleProps) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps;
}
var cache;
if (program.nameCache) {
cache = JSON.parse(read_file(program.nameCache, "{}"));
if (options.mangle) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.cache = to_cache("vars");
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
options.mangle.properties.cache = to_cache("props");
}
}
options.nameCache = JSON.parse(read_file(program.nameCache, "{}"));
}
if (program.output == "ast") {
options.output = {
@@ -114,10 +116,10 @@ if (program.output == "ast") {
};
}
if (program.parse) {
if (program.parse.acorn || program.parse.spidermonkey) {
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser");
} else {
if (!program.parse.acorn && !program.parse.spidermonkey) {
options.parse = program.parse;
} else if (program.sourceMap && program.sourceMap.content == "inline") {
fatal("ERROR: inline source map only works with built-in parser");
}
}
var convert_path = function(name) {
@@ -139,7 +141,7 @@ if (program.verbose) {
}
if (program.self) {
if (program.args.length) {
console.error("WARN: Ignoring input files since --self was passed");
print_error("WARN: Ignoring input files since --self was passed");
}
if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) {
@@ -169,9 +171,9 @@ function convert_ast(fn) {
function run() {
UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg);
print_error("WARN: " + msg);
};
if (program.stats) program.stats = Date.now();
if (program.timings) options.timings = true;
try {
if (program.parse) {
if (program.parse.acorn) {
@@ -198,7 +200,7 @@ function run() {
if (result.error) {
var ex = result.error;
if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/);
var line = lines[ex.line - 1];
@@ -207,21 +209,22 @@ function run() {
col = line.length;
}
if (line) {
if (col > 40) {
line = line.slice(col - 40);
col = 40;
var limit = 70;
if (col > limit) {
line = line.slice(col - limit);
col = limit;
}
console.error(line.slice(0, 80));
console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
print_error(line.slice(0, 80));
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
}
}
if (ex.defs) {
console.error("Supported options:");
console.error(ex.defs);
print_error("Supported options:");
print_error(format_object(ex.defs));
}
fatal(ex);
} else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) {
print(JSON.stringify(result.ast, function(key, value) {
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
@@ -237,7 +240,7 @@ function run() {
return value;
}, 2));
} else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, {
print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
@@ -251,19 +254,19 @@ function run() {
fs.writeFileSync(program.output + ".map", result.map);
}
} else {
console.log(result.code);
print(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
}
if (result.timings) for (var phase in result.timings) {
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
}
if (program.stats) console.error("Elapsed:", Date.now() - program.stats);
}
function fatal(message) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
console.error(message);
print_error(message);
process.exit(1);
}
@@ -307,7 +310,7 @@ function read_file(path, default_value) {
}
}
function parse_js(flag, constants) {
function parse_js(flag) {
return function(value, options) {
options = options || {};
try {
@@ -325,7 +328,7 @@ function parse_js(flag, constants) {
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string();
var value = node.right;
if (!constants) {
if (flag) {
options[name] = value;
} else if (value instanceof UglifyJS.AST_Array) {
options[name] = value.elements.map(to_string);
@@ -348,37 +351,51 @@ function parse_js(flag, constants) {
}
}));
} catch(ex) {
fatal("Error parsing arguments for '" + flag + "': " + value);
if (flag) {
fatal("Error parsing arguments for '" + flag + "': " + value);
} else {
options[value] = null;
}
}
return options;
}
}
function parse_source_map() {
var parse = parse_js("sourceMap", true);
var parse = parse_js();
return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap;
var hasContent = options && "content" in options;
var settings = parse(value, options);
if (!hasContent && settings.content && settings.content != "inline") {
console.error("INFO: Using input source map:", settings.content);
print_error("INFO: Using input source map: " + settings.content);
settings.content = read_file(settings.content, settings.content);
}
return settings;
}
}
function to_cache(key) {
if (cache[key]) {
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
} else {
cache[key] = {
cname: -1,
props: new UglifyJS.Dictionary()
};
}
return cache[key];
}
function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
}
function format_object(obj) {
var lines = [];
var padding = "";
Object.keys(obj).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(obj[name]) ];
}).forEach(function(tokens) {
lines.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
return lines.join("\n");
}
function print_error(msg) {
process.stderr.write(msg);
process.stderr.write("\n");
}
function print(txt) {
process.stdout.write(txt);
process.stdout.write("\n");
}

View File

@@ -134,11 +134,10 @@ var AST_Debugger = DEFNODE("Debugger", null, {
$documentation: "Represents a debugger statement",
}, AST_Statement);
var AST_Directive = DEFNODE("Directive", "value scope quote", {
var AST_Directive = DEFNODE("Directive", "value quote", {
$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);
@@ -157,7 +156,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
function walk_body(node, visitor) {
var body = node.body;
if (body instanceof AST_Node) {
if (body instanceof AST_Statement) {
body._walk(visitor);
}
else for (var i = 0, len = body.length; i < len; i++) {
@@ -284,10 +283,6 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", {
}
}, AST_IterationStatement);
var AST_ForOf = DEFNODE("ForOf", null, {
$documentation: "A `for ... of` statement",
}, AST_ForIn);
var AST_With = DEFNODE("With", "expression", {
$documentation: "A `with` statement",
$propdoc: {
@@ -303,10 +298,9 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", {
$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",
@@ -315,13 +309,6 @@ var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_
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)",
},
get_defun_scope: function () {
var self = this;
while (self.is_block_scope() && self.parent_scope) {
self = self.parent_scope;
}
return self;
}
}, AST_Block);
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
@@ -342,111 +329,13 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
}
}, AST_Scope);
var AST_Expansion = DEFNODE("Expansion", "expression", {
$documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list",
$propdoc: {
expression: "AST_Symbol the thing to be expanded"
},
_walk: function(visitor) {
var self = this;
return visitor._visit(this, function(){
self.expression.walk(visitor);
});
}
});
var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
$documentation: "A set of arrow function parameters or a sequence expression. This is used because when the parser sees a \"(\" it could be the start of a seq, or the start of a parameter list of an arrow function.",
$propdoc: {
expressions: "[AST_Expression|AST_Destructuring|AST_Expansion*] array of expressions or argument names or destructurings."
},
as_params: function (croak) {
// We don't want anything which doesn't belong in a destructuring
var root = this;
return this.expressions.map(function to_fun_args(ex, _, __, default_seen_above) {
var insert_default = function(ex, default_value) {
if (default_value) {
return new AST_DefaultAssign({
start: ex.start,
left: ex,
operator: "=",
right: default_value,
end: default_value.end
});
}
return ex;
}
if (ex instanceof AST_Object) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: false,
names: ex.properties.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_ObjectKeyVal) {
if (ex.key instanceof AST_SymbolRef) {
ex.key = to_fun_args(ex.key, 0, [ex.key]);
}
ex.value = to_fun_args(ex.value, 0, [ex.key]);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Hole) {
return ex;
} else if (ex instanceof AST_Destructuring) {
ex.names = ex.names.map(to_fun_args);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_SymbolRef) {
return insert_default(new AST_SymbolFunarg({
name: ex.name,
start: ex.start,
end: ex.end
}), default_seen_above);
} else if (ex instanceof AST_Expansion) {
ex.expression = to_fun_args(ex.expression);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Array) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: true,
names: ex.elements.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_Assign) {
return insert_default(to_fun_args(ex.left, undefined, undefined, ex.right), default_seen_above);
} else if (ex instanceof AST_DefaultAssign) {
ex.left = to_fun_args(ex.left, 0, [ex.left]);
return ex;
} else {
croak("Invalid function parameter", ex.start.line, ex.start.col);
}
});
},
as_expr: function() {
var exprs = this.expressions;
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
expressions: exprs
});
}
});
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", {
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
$documentation: "Base class for functions",
$propdoc: {
is_generator: "[boolean] is generatorFn or not",
name: "[AST_SymbolDeclaration?] the name of this function",
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments",
argnames: "[AST_SymbolFunarg*] array of function arguments",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
},
args_as_names: function () {
var out = [];
for (var i = 0; i < this.argnames.length; i++) {
if (this.argnames[i] instanceof AST_Destructuring) {
out = out.concat(this.argnames[i].all_symbols());
} else {
out.push(this.argnames[i]);
}
}
return out;
},
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.name) this.name._walk(visitor);
@@ -467,78 +356,10 @@ var AST_Function = DEFNODE("Function", null, {
$documentation: "A function expression"
}, AST_Lambda);
var AST_Arrow = DEFNODE("Arrow", null, {
$documentation: "An ES6 Arrow function ((a) => b)"
}, AST_Lambda);
var AST_Defun = DEFNODE("Defun", null, {
$documentation: "A function definition"
}, AST_Lambda);
/* -----[ DESTRUCTURING ]----- */
var AST_Destructuring = DEFNODE("Destructuring", "names is_array", {
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
$propdoc: {
"names": "[AST_Node*] Array of properties or elements",
"is_array": "[Boolean] Whether the destructuring represents an object or array"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.names.forEach(function(name){
name._walk(visitor);
});
});
},
all_symbols: function() {
var out = [];
this.walk(new TreeWalker(function (node) {
if (node instanceof AST_Symbol) {
out.push(node);
}
if (node instanceof AST_Expansion) {
out.push(node.expression);
}
}));
return out;
}
});
var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_string prefix", {
$documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`",
$propdoc: {
template_string: "[AST_TemplateString] The template string",
prefix: "[AST_SymbolRef|AST_PropAccess] The prefix, which can be a symbol such as `foo` or a dotted expression such as `String.raw`."
},
_walk: function(visitor) {
this.prefix._walk(visitor);
this.template_string._walk(visitor);
}
})
var AST_TemplateString = DEFNODE("TemplateString", "segments", {
$documentation: "A template string literal",
$propdoc: {
segments: "[AST_TemplateSegment|AST_Expression]* One or more segments, starting with AST_TemplateSegment. AST_Expression may follow AST_TemplateSegment, but each AST_Expression must be followed by AST_TemplateSegment."
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.segments.forEach(function(seg, i){
if (i % 2 !== 0) {
seg._walk(visitor);
}
});
});
}
});
var AST_TemplateSegment = DEFNODE("TemplateSegment", "value raw", {
$documentation: "A segment of a template string literal",
$propdoc: {
value: "Content of the segment",
raw: "Raw content of the segment"
}
});
/* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, {
@@ -658,7 +479,7 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
var AST_Catch = DEFNODE("Catch", "argname", {
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
$propdoc: {
argname: "[AST_SymbolCatch|AST_Destructuring|AST_Expansion|AST_DefaultAssign] symbol for the exception"
argname: "[AST_SymbolCatch] symbol for the exception"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
@@ -672,10 +493,10 @@ var AST_Finally = DEFNODE("Finally", null, {
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
}, AST_Block);
/* -----[ VAR/CONST ]----- */
/* -----[ VAR ]----- */
var AST_Definitions = DEFNODE("Definitions", "definitions", {
$documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
$documentation: "Base class for `var` nodes (variable declarations/initializations)",
$propdoc: {
definitions: "[AST_VarDef*] array of variable definitions"
},
@@ -693,75 +514,10 @@ var AST_Var = DEFNODE("Var", null, {
$documentation: "A `var` statement"
}, AST_Definitions);
var AST_Let = DEFNODE("Let", null, {
$documentation: "A `let` statement"
}, AST_Definitions);
var AST_Const = DEFNODE("Const", null, {
$documentation: "A `const` statement"
}, AST_Definitions);
var AST_NameImport = DEFNODE("NameImport", "foreign_name name", {
$documentation: "The part of the import statement that imports names from a module.",
$propdoc: {
foreign_name: "[AST_SymbolImportForeign] The name being imported (as specified in the module)",
name: "[AST_SymbolImport] The name as it becomes available to this module."
},
_walk: function (visitor) {
return visitor._visit(this, function() {
this.foreign_name._walk(visitor);
this.name._walk(visitor);
});
}
})
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
$documentation: "An `import` statement",
$propdoc: {
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
imported_names: "[AST_NameImport*] The names of non-default imported variables",
module_name: "[AST_String] String literal describing where this module came from",
},
_walk: function(visitor) {
return visitor._visit(this, function() {
if (this.imported_name) {
this.imported_name._walk(visitor);
}
if (this.imported_names) {
this.imported_names.forEach(function (name_import) {
name_import._walk(visitor);
});
}
this.module_name._walk(visitor);
});
}
});
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
$documentation: "An `export` statement",
$propdoc: {
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
exported_value: "[AST_Node?] An exported value",
exported_names: "[AST_NameImport*?] List of exported names",
module_name: "[AST_String?] Name of the file to load exports from",
is_default: "[Boolean] Whether this is the default exported value of this module"
},
_walk: function (visitor) {
visitor._visit(this, function () {
if (this.exported_definition) {
this.exported_definition._walk(visitor);
}
if (this.exported_value) {
this.exported_value._walk(visitor);
}
});
}
}, AST_Statement);
var AST_VarDef = DEFNODE("VarDef", "name value", {
$documentation: "A variable declaration; only appears in a AST_Definitions node",
$propdoc: {
name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable",
name: "[AST_SymbolVar] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer"
},
_walk: function(visitor) {
@@ -892,10 +648,6 @@ var AST_Assign = DEFNODE("Assign", null, {
$documentation: "An assignment expression — `a = b + 5`",
}, AST_Binary);
var AST_DefaultAssign = DEFNODE("DefaultAssign", null, {
$documentation: "A default assignment expression like in `(a = 3) => a`"
}, AST_Binary);
/* -----[ LITERALS ]----- */
var AST_Array = DEFNODE("Array", "elements", {
@@ -931,13 +683,11 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties",
$propdoc: {
key: "[string|AST_Node] the property name converted to a string for ObjectKeyVal. For setters, getters and computed property this is an arbitrary AST_Node",
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an AST_SymbolAccessor.",
value: "[AST_Node] property value. For setters and getters this is an AST_Accessor."
},
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.key instanceof AST_Node)
this.key._walk(visitor);
this.value._walk(visitor);
});
}
@@ -950,94 +700,35 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
}
}, AST_ObjectProperty);
var AST_ObjectSetter = DEFNODE("ObjectSetter", "quote static", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this is a static setter (classes only)"
},
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
$documentation: "An object setter property",
}, AST_ObjectProperty);
var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this is a static getter (classes only)"
},
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
$documentation: "An object getter property",
}, AST_ObjectProperty);
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this method is static (classes only)",
is_generator: "[boolean] is generatorFn or not",
},
$documentation: "An ES6 concise method inside an object or class"
}, AST_ObjectProperty);
var AST_Class = DEFNODE("Class", "name extends properties", {
$propdoc: {
name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.",
extends: "[AST_Node]? optional parent class",
properties: "[AST_ObjectProperty*] array of properties"
},
$documentation: "An ES6 class",
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.name) {
this.name._walk(visitor);
}
if (this.extends) {
this.extends._walk(visitor);
}
this.properties.forEach(function(prop){
prop._walk(visitor);
});
});
},
}, AST_Scope);
var AST_DefClass = DEFNODE("DefClass", null, {
$documentation: "A class definition",
}, AST_Class);
var AST_ClassExpression = DEFNODE("ClassExpression", null, {
$documentation: "A class expression."
}, AST_Class);
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_NewTarget = DEFNODE("NewTarget", null, {
$documentation: "A reference to new.target"
});
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
$documentation: "The name of a property accessor (setter/getter function)"
}, AST_Symbol);
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, function name or argument, symbol in catch)",
}, AST_Symbol);
var AST_SymbolVar = DEFNODE("SymbolVar", null, {
$documentation: "Symbol defining a variable",
}, AST_SymbolDeclaration);
var AST_SymbolBlockDeclaration = DEFNODE("SymbolBlockDeclaration", null, {
$documentation: "Base class for block-scoped declaration symbols"
}, AST_SymbolDeclaration);
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
$documentation: "A constant declaration"
}, AST_SymbolBlockDeclaration);
var AST_SymbolLet = DEFNODE("SymbolLet", null, {
$documentation: "A block-scoped `let` declaration"
}, AST_SymbolBlockDeclaration);
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
$documentation: "Symbol naming a function argument",
}, AST_SymbolVar);
@@ -1046,33 +737,13 @@ var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
$documentation: "Symbol defining a function",
}, AST_SymbolDeclaration);
var AST_SymbolMethod = DEFNODE("SymbolMethod", null, {
$documentation: "Symbol in an object defining a method",
}, AST_Symbol);
var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
$documentation: "Symbol naming a function expression",
}, AST_SymbolDeclaration);
var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
$documentation: "Symbol naming a class's name in a class declaration. Lexically scoped to its containing scope, and accessible within the class."
}, AST_SymbolBlockDeclaration);
var AST_SymbolClass = DEFNODE("SymbolClass", null, {
$documentation: "Symbol naming a class's name. Lexically scoped to the class."
}, AST_SymbolDeclaration);
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
$documentation: "Symbol naming the exception in catch",
}, AST_SymbolBlockDeclaration);
var AST_SymbolImport = DEFNODE("SymbolImport", null, {
$documentation: "Symbol refering to an imported name",
}, AST_SymbolBlockDeclaration);
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
$documentation: "A symbol imported from a module, but it is defined in the other module, and its real name is irrelevant for this module's purposes",
}, AST_Symbol);
}, AST_SymbolDeclaration);
var AST_Label = DEFNODE("Label", "references", {
$documentation: "Symbol naming a label (declaration)",
@@ -1097,10 +768,6 @@ var AST_This = DEFNODE("This", null, {
$documentation: "The `this` symbol",
}, AST_Symbol);
var AST_Super = DEFNODE("Super", null, {
$documentation: "The `super` symbol",
}, AST_Symbol);
var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants",
getValue: function() {
@@ -1174,21 +841,6 @@ var AST_True = DEFNODE("True", null, {
value: true
}, AST_Boolean);
/* -----[ Yield ]----- */
var AST_Yield = DEFNODE("Yield", "expression is_star", {
$documentation: "A `yield` statement",
$propdoc: {
expression: "[AST_Node?] the value returned or thrown by this statement; could be null (representing undefined) but only when is_star is set to false",
is_star: "[Boolean] Whether this is a yield or yield* statement"
},
_walk: function(visitor) {
return visitor._visit(this, this.expression && function(){
this.expression._walk(visitor);
});
}
});
/* -----[ TreeWalker ]----- */
function TreeWalker(callback) {
@@ -1205,7 +857,7 @@ TreeWalker.prototype = {
if (!ret && descend) {
descend.call(node);
}
this.pop(node);
this.pop();
return ret;
},
parent: function(n) {
@@ -1216,17 +868,11 @@ TreeWalker.prototype = {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node;
} else if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
if (!this.directives["use strict"]) {
this.directives["use strict"] = node;
}
}
this.stack.push(node);
},
pop: function(node) {
this.stack.pop();
if (node instanceof AST_Lambda || node instanceof AST_Class) {
pop: function() {
if (this.stack.pop() instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives);
}
},
@@ -1244,7 +890,7 @@ TreeWalker.prototype = {
var dir = this.directives[type];
if (dir) return dir;
var node = this.stack[this.stack.length - 1];
if (node instanceof AST_Scope && node.body) {
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;

File diff suppressed because it is too large Load Diff

View File

@@ -27,40 +27,73 @@ function set_shorthand(name, options, keys) {
}
}
function init_cache(cache) {
if (!cache) return;
if (!("cname" in cache)) cache.cname = -1;
if (!("props" in cache)) {
cache.props = new Dictionary();
} else if (!(cache.props instanceof Dictionary)) {
cache.props = Dictionary.fromObject(cache.props);
}
}
function to_json(cache) {
return {
cname: cache.cname,
props: cache.props.toObject()
};
}
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
if (typeof files == "string") {
files = [ files ];
}
options = defaults(options, {
compress: {},
ie8: false,
keep_fnames: false,
mangle: {},
nameCache: null,
output: {},
parse: {},
sourceMap: false,
timings: false,
toplevel: false,
warnings: false,
wrap: false,
}, true);
var timings = options.timings && {
start: Date.now()
};
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]);
var quoted_props;
if (options.mangle) {
options.mangle = defaults(options.mangle, {
cache: null,
cache: options.nameCache && (options.nameCache.vars || {}),
eval: false,
ie8: false,
keep_classnames: false,
keep_fnames: false,
properties: false,
reserved: [],
safari10: false,
toplevel: false,
}, true);
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") {
options.mangle.properties = {};
}
if (options.mangle.properties.keep_quoted) {
quoted_props = options.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
options.mangle.properties.reserved = quoted_props;
}
if (options.nameCache && !("cache" in options.mangle.properties)) {
options.mangle.properties.cache = options.nameCache.props || {};
}
}
init_cache(options.mangle.cache);
init_cache(options.mangle.properties.cache);
}
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
@@ -77,13 +110,17 @@ function minify(files, options) {
warnings.push(warning);
};
}
if (timings) timings.parse = Date.now();
var toplevel;
if (files instanceof AST_Toplevel) {
toplevel = files;
} else {
if (typeof files == "string") {
files = [ files ];
}
options.parse = options.parse || {};
options.parse.toplevel = null;
for (var name in files) {
for (var name in files) if (HOP(files, name)) {
options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") {
@@ -94,22 +131,29 @@ function minify(files, options) {
}
toplevel = options.parse.toplevel;
}
if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props);
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (options.compress) {
toplevel.figure_out_scope(options.mangle);
toplevel = new Compressor(options.compress).compress(toplevel);
}
if (timings) timings.scope1 = Date.now();
if (options.compress) toplevel.figure_out_scope(options.mangle);
if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) {
toplevel.figure_out_scope(options.mangle);
base54.reset();
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
if (options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
}
if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (timings) timings.output = Date.now();
var result = {};
if (options.output.ast) {
result.ast = toplevel;
@@ -125,7 +169,9 @@ function minify(files, options) {
root: options.sourceMap.root
});
if (options.sourceMap.includeSources) {
for (var name in files) {
if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) if (HOP(files, name)) {
options.output.source_map.get().setSourceContent(name, files[name]);
}
}
@@ -144,6 +190,24 @@ function minify(files, options) {
}
}
}
if (options.nameCache && options.mangle) {
if (options.mangle.cache) options.nameCache.vars = to_json(options.mangle.cache);
if (options.mangle.properties && options.mangle.properties.cache) {
options.nameCache.props = to_json(options.mangle.properties.cache);
}
}
if (timings) {
timings.end = Date.now();
result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2),
compress: 1e-3 * (timings.scope2 - timings.compress),
mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),
total: 1e-3 * (timings.end - timings.start)
}
}
if (warnings.length) {
result.warnings = warnings;
}

View File

@@ -118,7 +118,7 @@
value : from_moz(M.value)
};
if (M.kind == "init") return new AST_ObjectKeyVal(args);
args.key = new AST_SymbolMethod({
args.key = new AST_SymbolAccessor({
name: args.key
});
args.value = new AST_Accessor(args.value);
@@ -168,7 +168,7 @@
});
},
VariableDeclaration: function(M) {
return new (M.kind === "const" ? AST_Const : AST_Var)({
return new AST_Var({
start : my_start_token(M),
end : my_end_token(M),
definitions : M.declarations.map(from_moz)
@@ -204,7 +204,7 @@
Identifier: function(M) {
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
return new ( p.type == "LabeledStatement" ? AST_Label
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
: p.type == "VariableDeclarator" && p.id === M ? AST_SymbolVar
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
: p.type == "CatchClause" ? AST_SymbolCatch
@@ -324,7 +324,7 @@
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
return {
type: "VariableDeclaration",
kind: M instanceof AST_Const ? "const" : "var",
kind: "var",
declarations: M.definitions.map(to_moz)
};
});
@@ -381,7 +381,7 @@
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
var key = {
type: "Literal",
value: M.key instanceof AST_SymbolMethod ? M.key.name : M.key
value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key
};
var kind;
if (M instanceof AST_ObjectKeyVal) {

View File

@@ -54,11 +54,9 @@ function OutputStream(options) {
options = defaults(options, {
ascii_only : false,
ascii_identifiers: undefined,
beautify : false,
bracketize : false,
comments : false,
ecma : 5,
ie8 : false,
indent_level : 4,
indent_start : 0,
@@ -71,18 +69,12 @@ function OutputStream(options) {
quote_style : 0,
semicolons : true,
shebang : true,
shorthand : undefined,
source_map : null,
webkit : false,
width : 80,
wrap_iife : false,
}, true);
if (typeof options.ascii_identifiers === 'undefined')
options.ascii_identifiers = options.ascii_only;
if (options.shorthand === undefined)
options.shorthand = options.ecma > 5;
// Convert comment option to RegExp if neccessary and set up comments filter
var comment_filter = return_false; // Default case, throw all comments away
if (options.comments) {
@@ -117,20 +109,10 @@ function OutputStream(options) {
var current_pos = 0;
var OUTPUT = "";
function to_ascii(str, identifier) {
return str.replace(/[\ud800-\udbff][\udc00-\udfff]|[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
var code = get_full_char_code(ch, 0).toString(16);
if ((identifier && code.length === 1 && options.ecma >= 6) || code.length > 4) {
if (options.ecma < 6) {
if (identifier) {
return ch; // no \u{} support
}
return "\\u" + ch.charCodeAt(0).toString(16) + "\\u"
+ ch.charCodeAt(1).toString(16);
}
return "\\u{" + code + "}";
} else if (code.length <= 2 && !identifier) {
var to_utf8 = options.ascii_only ? function(str, identifier) {
return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
var code = ch.charCodeAt(0).toString(16);
if (code.length <= 2 && !identifier) {
while (code.length < 2) code = "0" + code;
return "\\x" + code;
} else {
@@ -138,6 +120,12 @@ function OutputStream(options) {
return "\\u" + code;
}
});
} : function(str) {
return str.replace(/[\ud800-\udbff](?![\udc00-\udfff])/g, function(ch) {
return "\\u" + ch.charCodeAt(0).toString(16);
}).replace(/(^|[^\ud800-\udbff])([\udc00-\udfff])/g, function(match, prefix, ch) {
return prefix + "\\u" + ch.charCodeAt(0).toString(16);
});
};
function make_string(str, quote) {
@@ -158,7 +146,7 @@ function OutputStream(options) {
case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff";
case "\0":
return /[0-7]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
return /[0-9]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
}
return s;
});
@@ -168,11 +156,7 @@ function OutputStream(options) {
function quote_double() {
return '"' + str.replace(/\x22/g, '\\"') + '"';
}
function quote_template() {
return '`' + str.replace(/`/g, '\\`') + '`';
}
if (options.ascii_only) str = to_ascii(str);
if (quote === "`") return quote_template();
str = to_utf8(str);
switch (options.quote_style) {
case 1:
return quote_single();
@@ -197,8 +181,7 @@ function OutputStream(options) {
function make_name(name) {
name = name.toString();
if (options.ascii_identifiers)
name = to_ascii(name, true);
name = to_utf8(name, true);
return name;
};
@@ -212,12 +195,43 @@ function OutputStream(options) {
var might_need_semicolon = false;
var might_add_newline = 0;
var last = "";
var mapping_token, mapping_name, mappings = options.source_map && [];
var do_add_mapping = mappings ? function() {
mappings.forEach(function(mapping) {
try {
options.source_map.add(
mapping.token.file,
mapping.line, mapping.col,
mapping.token.line, mapping.token.col,
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: mapping.token.file,
line: mapping.token.line,
col: mapping.token.col,
cline: mapping.line,
ccol: mapping.col,
name: mapping.name || ""
})
}
});
mappings = [];
} : noop;
var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) {
if (might_add_newline) {
var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline);
if (mappings) {
var delta = right.length - current_col;
mappings.forEach(function(mapping) {
mapping.line++;
mapping.col += delta;
});
}
OUTPUT = left + "\n" + right;
current_line++;
current_pos++;
@@ -227,15 +241,18 @@ function OutputStream(options) {
AST_Node.warn("Output exceeds {max_line_len} characters", options);
}
}
might_add_newline = 0;
if (might_add_newline) {
might_add_newline = 0;
do_add_mapping();
}
} : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , .");
function print(str) {
str = String(str);
var ch = get_full_char(str, 0);
var prev = get_full_char(last, last.length - 1);
var ch = str.charAt(0);
var prev = last.charAt(last.length - 1);
if (might_need_semicolon) {
might_need_semicolon = false;
@@ -287,6 +304,18 @@ function OutputStream(options) {
}
might_need_space = false;
}
if (mapping_token) {
mappings.push({
token: mapping_token,
name: mapping_name,
line: current_line,
col: current_col
});
mapping_token = false;
if (!might_add_newline) do_add_mapping();
}
OUTPUT += str;
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1;
@@ -299,10 +328,6 @@ function OutputStream(options) {
last = str;
};
var star = function(){
print("*");
}
var space = options.beautify ? function() {
print(" ");
} : function() {
@@ -385,24 +410,9 @@ function OutputStream(options) {
space();
};
var add_mapping = options.source_map ? function(token, name) {
try {
if (token) options.source_map.add(
token.file || "?",
current_line, current_col,
token.line, token.col,
(!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 || ""
})
}
var add_mapping = mappings ? function(token, name) {
mapping_token = token;
mapping_name = name;
} : noop;
function get() {
@@ -422,14 +432,13 @@ function OutputStream(options) {
should_break : function() { return options.width && this.current_width() >= options.width },
newline : newline,
print : print,
star : star,
space : space,
comma : comma,
colon : colon,
last : function() { return last },
semicolon : semicolon,
force_semicolon : force_semicolon,
to_ascii : to_ascii,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
@@ -442,10 +451,6 @@ function OutputStream(options) {
}
print(encoded);
},
print_template_string_chars: function(str) {
var encoded = encode_string(str, '`').replace(/\${/g, "\\${");
return print(encoded.substr(1, encoded.length - 2));
},
encode_string : encode_string,
next_indent : next_indent,
with_indent : with_indent,
@@ -477,13 +482,17 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
};
var use_asm = false;
var in_directive = false;
var active_scope = null;
var use_asm = null;
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) {
use_asm = true;
var self = this, generator = self._codegen;
if (self instanceof AST_Scope) {
active_scope = self;
}
else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
use_asm = active_scope;
}
function doit() {
self.add_comments(stream);
@@ -497,10 +506,11 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
if (self === use_asm) {
use_asm = null;
}
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
AST_Node.DEFMETHOD("print_to_string", function(options){
var s = OutputStream(options);
@@ -598,6 +608,13 @@ function OutputStream(options) {
return true;
}
if (output.option('webkit')) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) {
return true;
}
}
if (output.option('wrap_iife')) {
var p = output.parent();
return p instanceof AST_Call && p.expression === this;
@@ -606,11 +623,6 @@ function OutputStream(options) {
return false;
});
PARENS(AST_Arrow, function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this;
});
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
PARENS(AST_Object, function(output){
@@ -620,28 +632,20 @@ function OutputStream(options) {
PARENS(AST_Unary, function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this
|| p instanceof AST_Call && p.expression === this
|| p instanceof AST_Binary
&& p.operator === "**"
&& this instanceof AST_UnaryPrefix
&& p.left === this
&& this.operator !== "++"
&& this.operator !== "--";
|| p instanceof AST_Call && p.expression === this;
});
PARENS(AST_Sequence, function(output){
var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|| 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_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_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
* ==> 20 (side effect, set a := 10 and b := 20) */
|| p instanceof AST_Arrow // x => (x, x)
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|| 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_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_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
* ==> 20 (side effect, set a := 10 and b := 20) */
;
});
@@ -668,24 +672,6 @@ function OutputStream(options) {
}
});
PARENS(AST_Yield, function(output){
var p = output.parent();
// (yield 1) + (yield 2)
// a = yield 3
if (p instanceof AST_Binary && p.operator !== "=")
return true;
// (yield 1) ? yield 2 : yield 3
if (p instanceof AST_Conditional && p.condition === this)
return true;
// -(yield 4)
if (p instanceof AST_Unary)
return true;
// (yield x).foo
// (yield x)['foo']
if (p instanceof AST_PropAccess && p.expression === this)
return true;
});
PARENS(AST_PropAccess, function(output){
var p = output.parent();
if (p instanceof AST_New && p.expression === this) {
@@ -695,14 +681,15 @@ function OutputStream(options) {
// 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;
}
var parens = false;
this.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Call) {
parens = true;
return true;
}
}));
return parens;
}
});
@@ -755,9 +742,6 @@ function OutputStream(options) {
// (a = foo)["prop"] —or— (a = foo).prop
if (p instanceof AST_PropAccess && p.expression === this)
return true;
// ({a, b} = {a: 1, b: 2}), a destructuring assignment
if (this instanceof AST_Assign && this.left instanceof AST_Destructuring && this.left.is_array === false)
return true;
});
/* -----[ PRINTERS ]----- */
@@ -766,28 +750,6 @@ function OutputStream(options) {
output.print_string(self.value, self.quote);
output.semicolon();
});
DEFPRINT(AST_Expansion, function (self, output) {
output.print('...');
self.expression.print(output);
});
DEFPRINT(AST_Destructuring, function (self, output) {
output.print(self.is_array ? "[" : "{");
var first = true;
var len = self.names.length;
self.names.forEach(function (name, i) {
if (first) first = false; else { output.comma(); output.space(); }
name.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 && name instanceof AST_Hole)
output.comma();
})
output.print(self.is_array ? "]" : "}");
});
DEFPRINT(AST_Debugger, function(self, output){
output.print("debugger");
output.semicolon();
@@ -912,11 +874,7 @@ function OutputStream(options) {
output.with_parens(function(){
self.init.print(output);
output.space();
if (self instanceof AST_ForOf) {
output.print("of");
} else {
output.print("in");
}
output.print("in");
output.space();
self.object.print(output);
});
@@ -938,19 +896,10 @@ function OutputStream(options) {
var self = this;
if (!nokeyword) {
output.print("function");
if (this.is_generator) {
output.star();
}
if (self.name) {
output.space();
}
}
if (self.name instanceof AST_Symbol) {
if (self.name) {
output.space();
self.name.print(output);
} else if (nokeyword && self.name instanceof AST_Node) {
output.with_square(function() {
self.name.print(output); // Computed method name
});
}
output.with_parens(function(){
self.argnames.forEach(function(arg, i){
@@ -965,56 +914,6 @@ function OutputStream(options) {
self._do_print(output);
});
DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
self.prefix.print(output);
self.template_string.print(output);
});
DEFPRINT(AST_TemplateString, function(self, output) {
var is_tagged = output.parent() instanceof AST_PrefixedTemplateString;
output.print("`");
for (var i = 0; i < self.segments.length; i++) {
if (!(self.segments[i] instanceof AST_TemplateSegment)) {
output.print("${");
self.segments[i].print(output);
output.print("}");
} else if (is_tagged) {
output.print(self.segments[i].raw);
} else {
output.print_template_string_chars(self.segments[i].value);
}
}
output.print("`");
});
AST_Arrow.DEFMETHOD("_do_print", function(output){
var self = this;
var parent = output.parent();
var needs_parens = parent instanceof AST_Binary ||
parent instanceof AST_Unary ||
(parent instanceof AST_Call && self === parent.expression);
if (needs_parens) { output.print("(") }
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
self.argnames[0].print(output);
} else {
output.with_parens(function(){
self.argnames.forEach(function(arg, i){
if (i) output.comma();
arg.print(output);
});
});
}
output.space();
output.print('=>');
output.space();
if (self.body instanceof AST_Node) {
this.body.print(output);
} else {
print_bracketed(this.body, output);
}
if (needs_parens) { output.print(")") }
});
/* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
@@ -1031,17 +930,6 @@ function OutputStream(options) {
self._do_print(output, "throw");
});
/* -----[ yield ]----- */
DEFPRINT(AST_Yield, function(self, output){
var star = self.is_star ? "*" : "";
output.print("yield" + star);
if (self.expression) {
output.space();
self.expression.print(output);
}
});
/* -----[ loop control ]----- */
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
@@ -1190,122 +1078,22 @@ function OutputStream(options) {
if (!avoid_semicolon)
output.semicolon();
});
DEFPRINT(AST_Let, function(self, output){
self._do_print(output, "let");
});
DEFPRINT(AST_Var, function(self, output){
self._do_print(output, "var");
});
DEFPRINT(AST_Const, function(self, output){
self._do_print(output, "const");
});
DEFPRINT(AST_Import, function(self, output) {
output.print("import");
output.space();
if (self.imported_name) {
self.imported_name.print(output);
}
if (self.imported_name && self.imported_names) {
output.print(",");
output.space();
}
if (self.imported_names) {
if (self.imported_names.length === 1 && self.imported_names[0].foreign_name.name === "*") {
self.imported_names[0].print(output);
} else {
output.print("{");
self.imported_names.forEach(function (name_import, i) {
output.space();
name_import.print(output);
if (i < self.imported_names.length - 1) {
output.print(",");
output.space();
}
});
output.space();
output.print("}");
}
}
if (self.imported_name || self.imported_names) {
output.space();
output.print("from")
output.space();
}
self.module_name.print(output);
output.semicolon();
});
DEFPRINT(AST_NameImport, function(self, output) {
var definition = self.name.definition();
var names_are_different =
(definition && definition.mangled_name || self.name.name) !==
self.foreign_name.name;
if (names_are_different) {
output.print(self.foreign_name.name);
output.space();
output.print("as");
output.space();
self.name.print(output);
} else {
self.name.print(output);
}
});
DEFPRINT(AST_Export, function(self, output) {
output.print("export");
output.space();
if (self.is_default) {
output.print("default");
output.space();
}
if (self.exported_names) {
output.space();
if (self.exported_names.length === 1 && self.exported_names[0].name.name === "*") {
self.exported_names[0].print(output);
} else {
output.print("{");
self.exported_names.forEach(function (name_import, i) {
output.space();
name_import.print(output);
if (i < self.exported_names.length - 1) {
output.print(",");
output.space();
}
});
output.space();
output.print("}");
}
output.space();
}
else if (self.exported_value) {
self.exported_value.print(output);
} else if (self.exported_definition) {
self.exported_definition.print(output);
}
if (self.module_name) {
output.space();
output.print("from");
output.space();
self.module_name.print(output);
}
output.semicolon();
});
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);
}
var parens = false;
// need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60
if (noin) node.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
parens = true;
return true;
}
}));
node.print(output, parens);
};
DEFPRINT(AST_VarDef, function(self, output){
@@ -1325,6 +1113,9 @@ function OutputStream(options) {
self.expression.print(output);
if (self instanceof AST_New && !need_constructor_parens(self, output))
return;
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start);
}
output.with_parens(function(){
self.args.forEach(function(expr, i){
if (i) output.comma();
@@ -1364,15 +1155,23 @@ function OutputStream(options) {
DEFPRINT(AST_Dot, function(self, output){
var expr = self.expression;
expr.print(output);
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
var prop = self.property;
if (output.option("ie8") && RESERVED_WORDS(prop)) {
output.print("[");
output.add_mapping(self.end);
output.print_string(prop);
output.print("]");
} else {
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
}
output.print(".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
output.print_name(prop);
}
output.print(".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
output.print_name(self.property);
});
DEFPRINT(AST_Sub, function(self, output){
self.expression.print(output);
@@ -1463,49 +1262,8 @@ function OutputStream(options) {
});
else output.print("{}");
});
DEFPRINT(AST_Class, function(self, output){
output.print("class");
output.space();
if (self.name) {
self.name.print(output);
output.space();
}
if (self.extends) {
var parens = (
!(self.extends instanceof AST_SymbolRef)
&& !(self.extends instanceof AST_PropAccess)
&& !(self.extends instanceof AST_ClassExpression)
&& !(self.extends instanceof AST_Function)
);
output.print("extends");
if (parens) {
output.print("(");
} else {
output.space();
}
self.extends.print(output);
if (parens) {
output.print(")");
} else {
output.space();
}
}
if (self.properties.length > 0) output.with_block(function(){
self.properties.forEach(function(prop, i){
if (i) {
output.newline();
}
output.indent();
prop.print(output);
});
output.newline();
});
else output.print("{}");
});
DEFPRINT(AST_NewTarget, function(self, output) {
output.print("new.target");
});
AST_ObjectProperty.DEFMETHOD("print_property_name", function(key, quote, output) {
function print_property_name(key, quote, output) {
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
@@ -1522,98 +1280,33 @@ function OutputStream(options) {
} else {
output.print_string(key, quote);
}
});
}
DEFPRINT(AST_ObjectKeyVal, function(self, output){
function get_name(self) {
var def = self.definition();
return def ? def.mangled_name || def.name : self.name;
}
var allowShortHand = output.option("shorthand");
if (allowShortHand &&
self.value instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self.value) === self.key
) {
self.print_property_name(self.key, self.quote, output);
} else if (allowShortHand &&
self.value instanceof AST_DefaultAssign &&
self.value.left instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self.value.left) === self.key
) {
self.print_property_name(self.key, self.quote, output);
output.print("=");
self.value.right.print(output);
} else {
if (!(self.key instanceof AST_Node)) {
self.print_property_name(self.key, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
output.colon();
self.value.print(output);
}
print_property_name(self.key, self.quote, output);
output.colon();
self.value.print(output);
});
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) {
if (self.static) {
output.print("static");
output.space();
}
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
output.print(type);
output.space();
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
print_property_name(this.key.name, this.quote, output);
this.value._do_print(output, true);
});
DEFPRINT(AST_ObjectSetter, function(self, output){
self._print_getter_setter("set", self, output);
self._print_getter_setter("set", output);
});
DEFPRINT(AST_ObjectGetter, function(self, output){
self._print_getter_setter("get", self, output);
self._print_getter_setter("get", output);
});
DEFPRINT(AST_ConciseMethod, function(self, output){
if (self.static) {
output.print("static");
output.space();
}
if (self.is_generator) {
output.print("*");
}
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
});
AST_Symbol.DEFMETHOD("_do_print", function(output){
var def = this.definition();
output.print_name(def ? def.mangled_name || def.name : this.name);
});
DEFPRINT(AST_Symbol, function (self, output) {
self._do_print(output);
});
DEFPRINT(AST_SymbolDeclaration, function(self, output){
self._do_print(output);
DEFPRINT(AST_Symbol, function(self, output){
var def = self.definition();
output.print_name(def ? def.mangled_name || def.name : self.name);
});
DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_This, function(self, output){
output.print("this");
});
DEFPRINT(AST_Super, function(self, output){
output.print("super");
});
DEFPRINT(AST_Constant, function(self, output){
output.print(self.getValue());
});
@@ -1634,9 +1327,7 @@ function OutputStream(options) {
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
}
if (output.option("ascii_only")) {
str = output.to_ascii(str);
}
str = output.to_utf8(str);
output.print(str);
var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)

File diff suppressed because one or more lines are too long

View File

@@ -44,16 +44,6 @@
"use strict";
function find_builtins(reserved) {
// Compatibility fix for some standard defined globals not defined on every js environment
var new_globals = ["Symbol", "Map", "Promise", "Proxy", "Reflect", "Set", "WeakMap", "WeakSet"];
var objects = {};
var global_ref = typeof global === "object" ? global : self;
new_globals.forEach(function (new_global) {
objects[new_global] = global_ref[new_global] || new Function();
});
// NaN will be included due to Number.NaN
[
"null",
@@ -65,15 +55,7 @@ function find_builtins(reserved) {
].forEach(add);
[ Object, Array, Function, Number,
String, Boolean, Error, Math,
Date, RegExp, objects.Symbol, ArrayBuffer,
DataView, decodeURI, decodeURIComponent,
encodeURI, encodeURIComponent, eval, EvalError,
Float32Array, Float64Array, Int8Array, Int16Array,
Int32Array, isFinite, isNaN, JSON, objects.Map, parseFloat,
parseInt, objects.Promise, objects.Proxy, RangeError, ReferenceError,
objects.Reflect, objects.Set, SyntaxError, TypeError, Uint8Array,
Uint8ClampedArray, Uint16Array, Uint32Array, URIError,
objects.WeakMap, objects.WeakSet
Date, RegExp
].forEach(function(ctor){
Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) {
@@ -85,6 +67,34 @@ function find_builtins(reserved) {
}
}
function reserve_quoted_keys(ast, reserved) {
function add(name) {
push_uniq(reserved, name);
}
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) {
add(node.key);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
}
}));
}
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.expressions[node.expressions.length - 1], add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
}
return true;
}));
}
function mangle_properties(ast, options) {
options = defaults(options, {
builtins: false,
@@ -94,9 +104,10 @@ function mangle_properties(ast, options) {
only_cache: false,
regex: null,
reserved: null,
});
}, true);
var reserved = options.reserved || [];
var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved);
var cache = options.cache;
@@ -108,7 +119,6 @@ function mangle_properties(ast, options) {
}
var regex = options.regex;
var keep_quoted = options.keep_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
@@ -121,12 +131,11 @@ function mangle_properties(ast, options) {
var names_to_mangle = [];
var unmangleable = [];
var to_keep = {};
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
add(node.key, keep_quoted && node.quote);
add(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
@@ -136,18 +145,14 @@ function mangle_properties(ast, options) {
add(node.property);
}
else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted);
}
else if (node instanceof AST_ConciseMethod) {
add(node.name.name);
addStrings(node.property, add);
}
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) {
if (!(keep_quoted && node.quote))
node.key = mangle(node.key);
node.key = mangle(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter
@@ -156,27 +161,9 @@ function mangle_properties(ast, options) {
else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
if (!keep_quoted)
node.property = mangleStrings(node.property);
else if (!options.keep_quoted && node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
}
else if (node instanceof AST_ConciseMethod) {
if (should_mangle(node.name.name)) {
node.name.name = mangle(node.name.name);
}
}
// 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
@@ -192,19 +179,13 @@ function mangle_properties(ast, options) {
}
function should_mangle(name) {
if (keep_quoted && name in to_keep) return false;
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, keep) {
if (keep) {
to_keep[name] = true;
return;
}
function add(name) {
if (can_mangle(name))
push_uniq(names_to_mangle, name);
@@ -224,19 +205,16 @@ function mangle_properties(ast, options) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) {
if (can_mangle(debug_mangled)) {
mangled = debug_mangled;
}
}
// either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) {
// Note: `can_mangle()` does not check if the name collides with the `to_keep` set
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this
// check so we don't collide with a quoted name.
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || keep_quoted && mangled in to_keep);
} while (!can_mangle(mangled));
}
cache.props.set(name, mangled);
@@ -244,32 +222,6 @@ function mangle_properties(ast, options) {
return mangled;
}
function addStrings(node, keep) {
var out = {};
try {
(function walk(node){
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Sequence) {
walk(node.expressions[node.expressions.length - 1]);
return true;
}
if (node instanceof AST_String) {
add(node.value, keep);
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_Sequence) {

View File

@@ -49,9 +49,7 @@ function SymbolDef(scope, index, orig) {
this.scope = scope;
this.references = [];
this.global = false;
this.export = false;
this.mangled_name = null;
this.object_destructuring_arg = false;
this.undeclared = false;
this.index = index;
this.id = SymbolDef.next_id++;
@@ -64,17 +62,11 @@ SymbolDef.prototype = {
if (!options) options = {};
return (this.global && !options.toplevel)
|| this.export
|| this.object_destructuring_arg
|| 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))
|| this.orig[0] instanceof AST_SymbolMethod
|| (options.keep_classnames
&& (this.orig[0] instanceof AST_SymbolClass
|| this.orig[0] instanceof AST_SymbolDefClass));
|| this.orig[0] instanceof AST_SymbolDefun));
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
@@ -87,7 +79,7 @@ SymbolDef.prototype = {
if (options.ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
@@ -95,6 +87,9 @@ SymbolDef.prototype = {
cache.set(this.name, this.mangled_name);
}
}
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
}
};
@@ -102,7 +97,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, {
cache: null,
ie8: false,
safari10: false,
});
// pass 1: setup scope chaining and handle definitions
@@ -110,35 +104,15 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var in_destructuring = null;
var in_export = false;
var in_block = 0;
var for_scopes = [];
var tw = new TreeWalker(function(node, descend){
if (node.is_block_scope()) {
if (node instanceof AST_Catch) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
if (!(node instanceof AST_Scope)) {
scope.uses_with = save_scope.uses_with;
scope.uses_eval = save_scope.uses_eval;
scope.directives = save_scope.directives;
}
if (options.safari10) {
if (node instanceof AST_For || node instanceof AST_ForIn) {
for_scopes.push(scope);
}
}
descend();
scope = save_scope;
return true;
}
if (node instanceof AST_Destructuring && node.is_array === false) {
in_destructuring = node; // These don't nest
descend();
in_destructuring = null;
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
@@ -152,22 +126,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Export) {
in_export = true;
descend();
in_export = false;
return true;
}
if (node instanceof AST_BlockStatement
|| node instanceof AST_Switch
|| node instanceof AST_Try
|| node instanceof AST_Catch
|| node instanceof AST_Finally) {
in_block++;
descend();
in_block--;
return true;
}
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
@@ -186,15 +144,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_SymbolFunarg) {
node.object_destructuring_arg = !!in_destructuring;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node, in_export, in_block);
defun.def_function(node);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
@@ -202,28 +157,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
var parent_lambda = defun.parent_scope;
while (parent_lambda.is_block_scope()) {
parent_lambda = parent_lambda.parent_scope;
}
(node.scope = parent_lambda).def_function(node, in_export, in_block);
(node.scope = defun.parent_scope).def_function(node);
}
else if (node instanceof AST_SymbolClass) {
defun.def_variable(node, in_export, in_block);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node, in_export, in_block);
}
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
(node.scope = defun.parent_scope).def_function(node, in_export, in_block);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export, in_block);
def.destructuring = in_destructuring;
else if (node instanceof AST_SymbolVar) {
defun.def_variable(node);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
@@ -234,7 +171,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node, in_export, in_block).defun = defun;
scope.def_variable(node).defun = defun;
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
@@ -249,24 +186,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
self.walk(tw);
// pass 2: find back references and eval
var func = null;
var cls = null;
var globals = self.globals = new Dictionary();
self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Lambda) {
var prev_func = func;
func = node;
descend();
func = prev_func;
return true;
}
if (node instanceof AST_Class) {
var prev_cls = cls;
cls = node;
descend();
cls = prev_cls;
return true;
}
if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node);
return true;
@@ -279,16 +200,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
}
var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) {
sym = self.def_global(node);
} else if (sym.scope instanceof AST_Lambda && name == "arguments") {
sym.scope.uses_arguments = true;
}
node.thedef = sym;
node.reference(options);
return true;
}
// ensure mangling works if catch reuses a scope variable
var def;
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
var s = node.scope;
while (s) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
s = s.parent_scope;
}
}
});
self.walk(tw);
@@ -305,24 +235,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
ref.reference(options);
});
node.thedef = def;
node.reference(options);
return true;
}
}));
}
// pass 4: add symbol definitions to loop scopes
// Safari/Webkit bug workaround - loop init let variable shadowing argument.
// https://github.com/mishoo/UglifyJS2/issues/1753
// https://bugs.webkit.org/show_bug.cgi?id=171041
if (options.safari10) {
for (var i = 0; i < for_scopes.length; i++) {
var scope = for_scopes[i];
scope.parent_scope.variables.each(function(def) {
push_uniq(scope.enclosed, def);
});
}
}
if (options.cache) {
this.cname = options.cache.cname;
}
@@ -351,27 +269,10 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
this.cname = -1; // the current index for mangling functions/variables
});
AST_Node.DEFMETHOD("is_block_scope", function(){
return false; // Behaviour will be overridden by AST_Block
});
AST_Block.DEFMETHOD("is_block_scope", function(){
return (
!(this instanceof AST_Lambda) &&
!(this instanceof AST_Toplevel) &&
!(this instanceof AST_Class) &&
!(this instanceof AST_SwitchBranch)
);
});
AST_IterationStatement.DEFMETHOD("is_block_scope", function(){
return true;
});
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false;
this.def_variable(new AST_SymbolConst({
this.def_variable(new AST_SymbolFunarg({
name: "arguments",
start: this.start,
end: this.end
@@ -404,24 +305,16 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("def_function", function(symbol, in_export, in_block){
this.functions.set(symbol.name, this.def_variable(symbol, in_export, in_block));
AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol));
});
AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export, in_block){
AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def;
if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def);
def.object_destructuring_arg = symbol.object_destructuring_arg;
if (in_export) {
def.export = true;
}
if (in_block && symbol instanceof AST_SymbolBlockDeclaration) {
def.global = false;
} else {
def.global = !this.parent_scope;
}
def.global = !this.parent_scope;
} else {
def = this.variables.get(symbol.name);
def.orig.push(symbol);
@@ -470,31 +363,17 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
AST_Symbol.DEFMETHOD("unmangleable", function(options){
var def = this.definition();
return def && def.unmangleable(options);
return !def || def.unmangleable(options);
});
// labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", function(){
return false;
});
AST_Label.DEFMETHOD("unmangleable", return_false);
AST_Symbol.DEFMETHOD("unreferenced", function(){
return this.definition().references.length == 0
&& !(this.scope.uses_eval || this.scope.uses_with);
});
AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared;
});
AST_LabelRef.DEFMETHOD("undeclared", function(){
return false;
});
AST_Label.DEFMETHOD("undeclared", function(){
return false;
});
AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef;
});
@@ -504,14 +383,15 @@ AST_Symbol.DEFMETHOD("global", function(){
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
options = defaults(options, {
eval : false,
ie8 : false,
keep_classnames: false,
keep_fnames : false,
reserved : [],
toplevel : false,
});
if (!Array.isArray(options.reserved)) options.reserved = [];
return options;
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
@@ -559,19 +439,13 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.mangled_name = name;
return true;
}
var mangle_with_block_scope =
(!options.ie8 && node instanceof AST_SymbolCatch) ||
node instanceof AST_SymbolBlockDeclaration;
if (mangle_with_block_scope) {
if (!options.ie8 && node instanceof AST_SymbolCatch) {
to_mangle.push(node.definition());
return;
}
});
this.walk(tw);
to_mangle.forEach(function(def){
if (def.destructuring && !def.destructuring.is_array) return;
def.mangle(options);
});
to_mangle.forEach(function(def){ def.mangle(options) });
if (options.cache) {
options.cache.cname = this.cname;
@@ -580,111 +454,69 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
var tw = new TreeWalker(function(node){
if (node instanceof AST_Constant)
base54.consider(node.print_to_string());
else if (node instanceof AST_Return)
base54.consider("return");
else if (node instanceof AST_Throw)
base54.consider("throw");
else if (node instanceof AST_Continue)
base54.consider("continue");
else if (node instanceof AST_Break)
base54.consider("break");
else if (node instanceof AST_Debugger)
base54.consider("debugger");
else if (node instanceof AST_Directive)
base54.consider(node.value);
else if (node instanceof AST_While)
base54.consider("while");
else if (node instanceof AST_Do)
base54.consider("do while");
else if (node instanceof AST_If) {
base54.consider("if");
if (node.alternative) base54.consider("else");
}
else if (node instanceof AST_Var)
base54.consider("var");
else if (node instanceof AST_Const)
base54.consider("const");
else if (node instanceof AST_Lambda)
base54.consider("function");
else if (node instanceof AST_For)
base54.consider("for");
else if (node instanceof AST_ForIn)
base54.consider("for in");
else if (node instanceof AST_Switch)
base54.consider("switch");
else if (node instanceof AST_Case)
base54.consider("case");
else if (node instanceof AST_Default)
base54.consider("default");
else if (node instanceof AST_With)
base54.consider("with");
else if (node instanceof AST_ObjectSetter)
base54.consider("set" + (typeof node.key === "string" ? node.key : ""));
else if (node instanceof AST_ObjectGetter)
base54.consider("get" + (typeof node.key === "string" ? node.key : ""));
else if (node instanceof AST_ObjectKeyVal && typeof node.key === "string")
base54.consider(node.key);
else if (node instanceof AST_ConciseMethod && typeof node.key === "string")
base54.consider(node.key);
else if (node instanceof AST_New)
base54.consider("new");
else if (node instanceof AST_This)
base54.consider("this");
else if (node instanceof AST_Super)
base54.consider("super");
else if (node instanceof AST_Try)
base54.consider("try");
else if (node instanceof AST_Catch)
base54.consider("catch");
else if (node instanceof AST_Finally)
base54.consider("finally");
else if (node instanceof AST_Yield)
base54.consider("yield");
else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)
base54.consider(node.operator);
else if (node instanceof AST_Dot)
base54.consider(node.property);
});
this.walk(tw);
try {
AST_Node.prototype.print = function(stream, force_parens) {
this._print(stream, force_parens);
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
base54.consider(this.name, -1);
} else if (options.properties) {
if (this instanceof AST_Dot) {
base54.consider(this.property, -1);
} else if (this instanceof AST_Sub) {
skip_string(this.property);
}
}
};
base54.consider(this.print_to_string(), 1);
} finally {
AST_Node.prototype.print = AST_Node.prototype._print;
}
base54.sort();
function skip_string(node) {
if (node instanceof AST_String) {
base54.consider(node.value, -1);
} else if (node instanceof AST_Conditional) {
skip_string(node.consequent);
skip_string(node.alternative);
} else if (node instanceof AST_Sequence) {
skip_string(node.expressions[node.expressions.length - 1]);
}
}
});
var base54 = (function() {
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
var leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split("");
var digits = "0123456789".split("");
var chars, frequency;
function reset() {
frequency = Object.create(null);
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
chars.forEach(function(ch){ frequency[ch] = 0 });
leading.forEach(function(ch) {
frequency[ch] = 0;
});
digits.forEach(function(ch) {
frequency[ch] = 0;
});
}
base54.consider = function(str){
base54.consider = function(str, delta) {
for (var i = str.length; --i >= 0;) {
var code = str.charCodeAt(i);
if (code in frequency) ++frequency[code];
frequency[str[i]] += delta;
}
};
function compare(a, b) {
return frequency[b] - frequency[a];
}
base54.sort = function() {
chars = mergeSort(chars, function(a, b){
if (is_digit(a) && !is_digit(b)) return 1;
if (is_digit(b) && !is_digit(a)) return -1;
return frequency[b] - frequency[a];
});
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
};
base54.reset = reset;
reset();
base54.get = function(){ return chars };
base54.freq = function(){ return frequency };
function base54(num) {
var ret = "", base = 54;
num++;
do {
num--;
ret += String.fromCharCode(chars[num % base]);
ret += chars[num % base];
num = Math.floor(num / base);
base = 64;
} while (num > 0);

View File

@@ -70,7 +70,7 @@ TreeTransformer.prototype = new TreeWalker;
if (y !== undefined) x = y;
}
}
tw.pop(this);
tw.pop();
return x;
});
};
@@ -163,18 +163,10 @@ TreeTransformer.prototype = new TreeWalker;
if (self.value) self.value = self.value.transform(tw);
});
_(AST_Destructuring, function(self, tw) {
self.names = do_list(self.names, tw);
});
_(AST_Lambda, function(self, tw){
if (self.name) self.name = self.name.transform(tw);
self.argnames = do_list(self.argnames, tw);
if (self.body instanceof AST_Node) {
self.body = self.body.transform(tw);
} else {
self.body = do_list(self.body, tw);
}
self.body = do_list(self.body, tw);
});
_(AST_Call, function(self, tw){
@@ -195,10 +187,6 @@ TreeTransformer.prototype = new TreeWalker;
self.property = self.property.transform(tw);
});
_(AST_Yield, function(self, tw){
if (self.expression) self.expression = self.expression.transform(tw);
});
_(AST_Unary, function(self, tw){
self.expression = self.expression.transform(tw);
});
@@ -223,36 +211,7 @@ TreeTransformer.prototype = new TreeWalker;
});
_(AST_ObjectProperty, function(self, tw){
if (self.key instanceof AST_Node) {
self.key = self.key.transform(tw);
}
self.value = self.value.transform(tw);
});
_(AST_Class, function(self, tw){
if (self.name) self.name = self.name.transform(tw);
if (self.extends) self.extends = self.extends.transform(tw);
self.properties = do_list(self.properties, tw);
});
_(AST_Expansion, function(self, tw){
self.expression = self.expression.transform(tw);
});
_(AST_Export, function(self, tw){
if (self.exported_value) self.exported_value = self.exported_value.transform(tw);
});
_(AST_TemplateString, function(self, tw) {
for (var i = 0; i < self.segments.length; i++) {
if (!(self.segments[i] instanceof AST_TemplateSegment)) {
self.segments[i] = self.segments[i].transform(tw);
}
}
});
_(AST_PrefixedTemplateString, function(self, tw) {
self.template_string = self.template_string.transform(tw);
});
})();

View File

@@ -43,13 +43,6 @@
"use strict";
function array_to_hash(a) {
var ret = Object.create(null);
for (var i = 0; i < a.length; ++i)
ret[a[i]] = true;
return ret;
};
function slice(a, start) {
return Array.prototype.slice.call(a, start || 0);
};

View File

@@ -1,10 +1,10 @@
{
"name": "uglify-es",
"name": "uglify-js",
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.0.8",
"version": "3.1.4",
"engines": {
"node": ">=0.8.0"
},
@@ -29,16 +29,16 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.9.0",
"source-map": "~0.5.1"
"commander": "~2.11.0",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~5.0.3",
"mocha": "~2.3.4",
"semver": "~5.3.0"
"acorn": "~5.1.1",
"mocha": "~3.5.1",
"semver": "~5.4.1"
},
"scripts": {
"test": "node test/run-tests.js"
},
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
"keywords": ["uglify", "uglify-js", "minify", "minifier", "es5"]
}

View File

@@ -4,12 +4,14 @@
"use strict";
var createHash = require("crypto").createHash;
var fetch = require("./fetch");
var fork = require("child_process").fork;
var zlib = require("zlib");
var args = process.argv.slice(2);
if (!args.length) {
args.push("-mc");
}
args.push("--stats");
args.push("--timings");
var urls = [
"https://code.jquery.com/jquery-3.2.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js",
@@ -29,14 +31,10 @@ function done() {
var info = results[url];
console.log();
console.log(url);
var elapsed = 0;
console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) {
elapsed += 1e-3 * parseInt(time);
return "";
}));
console.log("Run-time:", elapsed.toFixed(3), "s");
console.log(info.log);
console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes");
console.log("GZipped: ", info.gzip, "bytes");
console.log("SHA1 sum:", info.sha1);
if (info.code) {
failures.push(url);
@@ -55,15 +53,21 @@ urls.forEach(function(url) {
results[url] = {
input: 0,
output: 0,
gzip: 0,
log: ""
};
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
fetch(url, function(err, res) {
if (err) throw err;
var uglifyjs = fork("bin/uglifyjs", args, { silent: true });
res.on("data", function(data) {
results[url].input += data.length;
}).pipe(uglifyjs.stdin);
uglifyjs.stdout.on("data", function(data) {
results[url].output += data.length;
}).pipe(zlib.createGzip({
level: zlib.Z_BEST_COMPRESSION
})).on("data", function(data) {
results[url].gzip += data.length;
}).pipe(createHash("sha1")).on("data", function(data) {
results[url].sha1 = data.toString("hex");
done();

View File

@@ -1,5 +1,3 @@
// NOTE trailing comma doesn't contribute to length of an array
// That also means the array changes length if previous element is a hole too and got cut off
holes_and_undefined: {
input: {
w = [1,,];
@@ -93,79 +91,6 @@ constant_join_2: {
}
}
spread_with_variable_as_last_element: {
input: {
var values = [4, 5, 6];
var a = [1, 2, 3, ...values];
}
expect: {
var values = [4, 5, 6];
var a = [1, 2, 3, ...values];
}
}
spread_with_variable_in_middle: {
input: {
var values = [4, 5, 6];
var a = [1, 2, 3, ...values, 7,,,];
}
expect: {
var values = [4, 5, 6];
var a = [1, 2, 3, ...values, 7,,,];
}
}
spread_with_variable_at_front: {
input: {
var values = [1, 2, 3];
var a = [...values, 4, 5, 6];
}
expect: {
var values = [1, 2, 3];
var a = [...values, 4, 5, 6];
}
}
spread_with_variable_at_front_after_elisions: {
input: {
var values = [1, 2, 3];
var a = [,,,...values, 4, 5, 6];
}
expect: {
var values = [1, 2, 3];
var a = [,,,...values, 4, 5, 6];
}
}
spread_with_array_at_end: {
input: {
var a = [1, 2, ...[4, 5, 6]];
}
expect: {
var a = [1, 2, ...[4, 5, 6]];
}
}
spread_with_logical_expression_at_end: {
options = { evaluate: true }
input: {
var a = [1, 2, 3, ...[2+2]]
}
expect: {
var a = [1, 2, 3, ...[4]]
}
}
spread_with_logical_expression_at_middle: {
options = { evaluate: true }
input: {
var a = [1, 1, ...[1+1, 1+2, 2+3], 8]
}
expect: {
var a = [1, 1, ...[2, 3, 5], 8]
}
}
constant_join_3: {
options = {
unsafe: true,

View File

@@ -1,137 +0,0 @@
arrow_functions_without_body: {
input: {
var a1 = () => 42;
var a2 = (p) => p;
var a3 = p => p;
var a4 = (...p) => p;
var a5 = (b, c) => b + c;
var a6 = (b, ...c) => b + c[0];
var a7 = (...b) => b.join();
}
expect: {
var a1 = () => 42;
var a2 = (p) => p;
var a3 = p => p;
var a4 = (...p) => p;
var a5 = (b, c) => b + c;
var a6 = (b, ...c) => b + c[0];
var a7 = (...b) => b.join();
}
}
arrow_functions_with_body: {
input: {
var a1 = () => {
var a = 42 * Math.random();
return a;
};
var a2 = (p) => {
var a = Math.random() * p;
return a;
};
var a3 = p => {
var a = Math.random() * p;
return a;
};
var a4 = (...p) => {
var a = Math.random() * p;
return a;
};
var a5 = (b, c) => {
var result = b * c + b / c;
return result
};
var a6 = (b, ...c) => {
var result = b;
for (var i = 0; i < c.length; i++)
result += c[i];
return result
};
var a7 = (...b) => {
b.join();
}
}
expect: {
var a1 = () => {
var a = 42 * Math.random();
return a;
};
var a2 = (p) => {
var a = Math.random() * p;
return a;
};
var a3 = p => {
var a = Math.random() * p;
return a;
};
var a4 = (...p) => {
var a = Math.random() * p;
return a;
};
var a5 = (b, c) => {
var result = b * c + b / c;
return result
};
var a6 = (b, ...c) => {
var result = b;
for (var i = 0; i < c.length; i++)
result += c[i];
return result
};
var a7 = (...b) => {
b.join();
};
}
}
arrow_function_with_single_parameter_with_default: {
input: {
var foo = (a = 0) => doSomething(a);
}
expect_exact: "var foo=(a=0)=>doSomething(a);"
}
arrow_binding_pattern: {
input: {
var foo = ([]) => "foo";
var bar = ({}) => "bar";
var with_default = (foo = "default") => foo;
var object_with_default = ({foo = "default", bar: baz = "default"}) => foo;
var array_after_spread = (...[foo]) => foo;
var array_after_spread = (...{foo}) => foo;
var computed = ({ [compute()]: x }) => {};
var array_hole = ([, , ...x] = [1, 2]) => {};
var object_trailing_elision = ({foo,}) => {};
var spread_empty_array = (...[]) => "foo";
var spread_empty_object = (...{}) => "foo";
}
expect: {
var foo = ([]) => "foo";
var bar = ({}) => "bar";
var with_default = (foo = "default") => foo;
var object_with_default = ({foo = "default", bar: baz = "default"}) => foo;
var array_after_spread = (...[foo]) => foo;
var array_after_spread = (...{foo}) => foo;
var computed = ({ [compute()]: x }) => {};
var array_hole = ([, , ...x] = [1, 2]) => {};
var object_trailing_elision = ({foo,}) => {};
var spread_empty_array = (...[]) => "foo";
var spread_empty_object = (...{}) => "foo";
}
}
arrow_binding_pattern_strict: {
input: {
var foo = ([,]) => "foo";
}
expect_exact: 'var foo=([,])=>"foo";'
}
arrow_with_regexp: {
input: {
num => /\d{11,14}/.test( num )
}
expect: {
num => /\d{11,14}/.test( num )
}
}

View File

@@ -13,7 +13,7 @@ ascii_only_true: {
"\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff";
}
}
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0e\\x0f"+"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f"+\' !"# ... }~\\x7f\\x80\\x81 ... \\xfe\\xff\\u0fff\\uffff\'}'
expect_exact: 'function f(){return"\\x000\\x001\\x007\\x008\\0"+"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0e\\x0f"+"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f"+\' !"# ... }~\\x7f\\x80\\x81 ... \\xfe\\xff\\u0fff\\uffff\'}'
}
ascii_only_false: {
@@ -31,5 +31,5 @@ ascii_only_false: {
"\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff";
}
}
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}'
expect_exact: 'function f(){return"\\x000\\x001\\x007\\x008\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}'
}

View File

@@ -104,3 +104,65 @@ asm_mixed: {
}
}
asm_toplevel: {
options = {}
input: {
"use asm";
0.0;
function f() {
0.0;
(function(){
0.0;
});
}
0.0;
}
expect_exact: '"use asm";0.0;function f(){0.0;(function(){0.0})}0.0;'
}
asm_function_expression: {
options = {}
input: {
0.0;
var a = function() {
"use asm";
0.0;
}
function f() {
0.0;
return function(){
"use asm";
0.0;
}
0.0;
}
0.0;
}
expect_exact: '0;var a=function(){"use asm";0.0};function f(){0;return function(){"use asm";0.0};0}0;'
}
asm_nested_functions: {
options = {}
input: {
0.0;
function a() {
"use asm";
0.0;
}
0.0;
function b() {
0.0;
function c(){
"use asm";
0.0;
}
0.0;
function d(){
0.0;
}
0.0;
}
0.0;
}
expect_exact: '0;function a(){"use asm";0.0}0;function b(){0;function c(){"use asm";0.0}0;function d(){0}0}0;'
}

View File

@@ -1,177 +0,0 @@
let_statement: {
input: {
let x = 6;
}
expect_exact: "let x=6;"
}
do_not_hoist_let: {
options = {
hoist_vars: true,
};
input: {
function x() {
if (FOO) {
let let1;
let let2;
var var1;
var var2;
}
}
}
expect: {
function x() {
var var1, var2;
if (FOO) {
let let1;
let let2;
}
}
}
}
do_not_remove_anon_blocks_if_they_have_decls: {
input: {
function x() {
{
let x;
}
{
var x;
}
{
const y;
class Zee {};
}
}
{
let y;
}
{
var y;
}
}
expect: {
function x(){
{
let x
}
var x;
{
const y;
class Zee {}
}
}
{
let y
}
var y;
}
}
remove_unused_in_global_block: {
options = {
unused: true,
}
input: {
{
let x;
const y;
class Zee {};
var w;
}
let ex;
const why;
class Zed {};
var wut;
console.log(x, y, Zee);
}
expect: {
var w;
let ex;
const why;
class Zed {};
var wut;
console.log(x, y, Zee);
}
}
regression_block_scope_resolves: {
mangle = { };
options = {
dead_code: false
};
input: {
(function () {
if(1) {
let x;
const y;
class Zee {};
}
if(1) {
let ex;
const why;
class Zi {};
}
console.log(x, y, Zee, ex, why, Zi);
}());
}
expect: {
(function () {
if (1) {
let o;
const n;
class c {};
}
if (1) {
let o;
const n;
class c {};
}
console.log(x, y, Zee, ex, why, Zi);
}());
}
}
switch_block_scope_mangler: {
mangle = {}
input: {
var fn = function(code) {
switch (code) {
case 1:
let apple = code + 1;
let dog = code + 4;
console.log(apple, dog);
break;
case 2:
let banana = code + 2;
console.log(banana);
break;
default:
let cat = code + 3;
console.log(cat);
}
};
}
expect: {
var fn = function(o) {
switch (o) {
case 1:
let e = o + 1
let c = o + 4;
console.log(e, c);
break;
case 2:
let l = o + 2;
console.log(l);
break;
default:
let a = o + 3;
console.log(a);
}
};
}
}

View File

@@ -583,8 +583,8 @@ collapse_vars_assignment: {
return a = a;
}
function f1(c) {
const a = 3 / c;
const b = 1 - a;
var a = 3 / c;
var b = 1 - a;
return b;
}
function f2(c) {
@@ -724,10 +724,10 @@ collapse_vars_misc1: {
return t;
}
function f1(x) { var y = 5 - x; return y; }
function f2(x) { const z = foo(), y = z / (5 - x); return y; }
function f2(x) { var z = foo(), y = z / (5 - x); return y; }
function f3(x) { var z = foo(), y = (5 - x) / z; return y; }
function f4(x) { var z = foo(), y = (5 - u) / z; return y; }
function f5(x) { const z = foo(), y = (5 - window.x) / z; return y; }
function f5(x) { var z = foo(), y = (5 - window.x) / z; return y; }
function f6() { var b = window.a * window.z; return b && zap(); }
function f7() { var b = window.a * window.z; return b + b; }
function f8() { var b = window.a * window.z; var c = b + 5; return b + c; }
@@ -744,7 +744,7 @@ collapse_vars_misc1: {
function f2(x) { return foo() / (5 - x) }
function f3(x) { return (5 - x) / foo() }
function f4(x) { var z = foo(); return (5 - u) / z }
function f5(x) { const z = foo(); return (5 - window.x) / z }
function f5(x) { var z = foo(); return (5 - window.x) / z }
function f6() { return window.a * window.z && zap() }
function f7() { var b = window.a * window.z; return b + b }
function f8() { var b = window.a * window.z; return b + (b + 5) }
@@ -863,7 +863,7 @@ collapse_vars_unary: {
input: {
function f0(o, p) {
var x = o[p];
delete x;
return delete x;
}
function f1(n) {
var k = !!n;
@@ -893,7 +893,7 @@ collapse_vars_unary: {
expect: {
function f0(o, p) {
var x = o[p];
delete x;
return delete x;
}
function f1(n) {
return n > +!!n
@@ -1146,7 +1146,7 @@ collapse_vars_constants: {
function f3(x) {
var b = x.prop;
sideeffect1();
return b + -9;
return b + (function() { return -9; })();
}
}
}
@@ -1268,22 +1268,21 @@ collapse_vars_short_circuited_conditions: {
collapse_vars_regexp: {
options = {
collapse_vars: true,
loops: false,
sequences: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
unused: true,
hoist_funs: true,
keep_fargs: true,
cascade: true,
collapse_vars: true,
comparisons: true,
conditionals: true,
dead_code: true,
evaluate: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
hoist_funs: true,
keep_fargs: true,
loops: false,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
@@ -1292,12 +1291,12 @@ collapse_vars_regexp: {
return [rx, k];
}
function f2() {
var rx = /[abc123]+/;
var rx = /ab*/g;
return function(s) {
return rx.exec(s);
};
}
(function(){
(function() {
var result;
var s = 'acdabcdeabbb';
var rx = /ab*/g;
@@ -1305,22 +1304,35 @@ collapse_vars_regexp: {
console.log(result[0]);
}
})();
(function() {
var result;
var s = 'acdabcdeabbb';
var rx = f2();
while (result = rx(s)) {
console.log(result[0]);
}
})();
}
expect: {
function f1() {
return [/[A-Z]+/, 9];
}
function f2() {
var rx = /[abc123]+/;
var rx = /ab*/g;
return function(s) {
return rx.exec(s);
};
}
(function(){
(function() {
var result, rx = /ab*/g;
while (result = rx.exec("acdabcdeabbb"))
console.log(result[0]);
})();
(function() {
var result, rx = f2();
while (result = rx("acdabcdeabbb"))
console.log(result[0]);
})();
}
expect_stdout: true
}
@@ -1328,7 +1340,6 @@ collapse_vars_regexp: {
issue_1537: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var k = '';
@@ -1340,104 +1351,6 @@ issue_1537: {
}
}
issue_1537_for_of: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var k = '';
for (k of {prop: 'val'}){}
}
expect: {
var k = '';
for (k of {prop: 'val'});
}
}
issue_1537_destructuring_1: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = 1, y = 2;
[x] = [y];
}
expect: {
var x = 1, y = 2;
[x] = [y];
}
}
issue_1537_destructuring_2: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = foo();
[x] = [1];
}
expect: {
var x = foo();
[x] = [1];
}
}
issue_1537_destructuring_3: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = Math.random();
({p: x = 9} = {v: 1});
}
expect: {
var x = Math.random();
({p: x = 9} = {v: 1});
}
}
issue_1537_destructuring_for_in: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = 1, y = 2;
(function() {
for ([[x], y] in a);
})();
}
expect: {
var x = 1, y = 2;
(function() {
for ([[x], y] in a);
})();
}
}
issue_1537_destructuring_for_of: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = 1, y = 2;
(function() {
for ([[x], y] of a);
})();
}
expect: {
var x = 1, y = 2;
(function() {
for ([[x], y] of a);
})();
}
}
issue_1562: {
options = {
collapse_vars: true,
@@ -1933,9 +1846,9 @@ issue_1858: {
}
expect: {
console.log(function(x) {
var a = {}, b = a.b = x;
var a = {}, b = a.b = 1;
return a.b + b;
}(1));
}());
}
expect_stdout: "2"
}
@@ -2077,10 +1990,10 @@ chained_3: {
}
expect: {
console.log(function(a, b) {
var c = a, c = b;
var c = 1, c = b;
b++;
return c;
}(1, 2));
}(0, 2));
}
expect_stdout: "2"
}
@@ -2203,10 +2116,11 @@ unused_orig: {
expect: {
var a = 1;
console.log(function(b) {
var a;
var c = b;
for (var d in c)
return --b + (a = c[0]);
for (var d in c) {
var a = c[0];
return --b + a;
}
a && a.NaN;
}([2]), a);
}
@@ -2285,48 +2199,404 @@ compound_assignment: {
expect_stdout: "4"
}
reassign_const_1: {
issue_2187_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f() {
const a = 1;
a = 2;
return a;
}
console.log(f());
var a = 1;
!function(foo) {
foo();
var a = 2;
console.log(a);
}(function() {
console.log(a);
});
}
expect: {
function f() {
const a = 1;
a = 2;
return a;
}
console.log(f());
var a = 1;
!function(foo) {
foo();
var a = 2;
console.log(a);
}(function() {
console.log(a);
});
}
expect_stdout: true
expect_stdout: [
"1",
"2",
]
}
reassign_const_2: {
issue_2187_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var b = 1;
console.log(function(a) {
return a && ++b;
}(b--));
}
expect: {
var b = 1;
console.log(function(a) {
return b-- && ++b;
}());
}
expect_stdout: "1"
}
issue_2187_3: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
var b = 1;
console.log(function(a) {
return a && ++b;
}(b--));
}
expect: {
var b = 1;
console.log(b-- && ++b);
}
expect_stdout: "1"
}
issue_2203_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return function(c) {
return c.a;
}((String, (Object, this)));
}
}.b());
}
expect: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return function(c) {
return c.a;
}((String, (Object, this)));
}
}.b());
}
expect_stdout: "PASS"
}
issue_2203_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
a = "PASS";
console.log({
a: "FAIL",
b: function() {
return function(c) {
return c.a;
}((String, (Object, function() {
return this;
}())));
}
}.b());
}
expect: {
a = "PASS";
console.log({
a: "FAIL",
b: function() {
return function(c) {
return (String, (Object, function() {
return this;
}())).a;
}();
}
}.b());
}
expect_stdout: "PASS"
}
duplicate_argname: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f() { return "PASS"; }
console.log(function(a, a) {
f++;
return a;
}("FAIL", f()));
}
expect: {
function f() { return "PASS"; }
console.log(function(a, a) {
f++;
return a;
}("FAIL", f()));
}
expect_stdout: "PASS"
}
issue_2298: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
function f() {
var a = undefined;
var undefined = a++;
try {
!function g(b) {
b[1] = "foo";
}();
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
f();
}();
}
expect: {
!function() {
(function() {
var a = undefined;
var undefined = a++;
try {
!function(b) {
(void 0)[1] = "foo";
}();
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
})();
}();
}
expect_stdout: "PASS"
}
issue_2313_1: {
options = {
collapse_vars: true,
conditionals: true,
}
input: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
if (this.c) console.log(a, b);
}
}
foo.d();
}
expect: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
this.c && console.log(a, b);
}
}
foo.d();
}
expect_stdout: "2 1"
}
issue_2313_2: {
options = {
collapse_vars: true,
}
input: {
function f() {
const a = 1;
++a;
return a;
}
console.log(f());
var c = 0;
!function a() {
a && c++;
var a = 0;
a && c++;
}();
console.log(c);
}
expect: {
function f() {
const a = 1;
++a;
return a;
}
console.log(f());
var c = 0;
!function a() {
a && c++;
var a = 0;
a && c++;
}();
console.log(c);
}
expect_stdout: true
expect_stdout: "0"
}
issue_2319_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function(a) {
return a;
}(!function() {
return this;
}()));
}
expect: {
console.log(function(a) {
return !function() {
return this;
}();
}());
}
expect_stdout: "false"
}
issue_2319_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function(a) {
"use strict";
return a;
}(!function() {
return this;
}()));
}
expect: {
console.log(function(a) {
"use strict";
return a;
}(!function() {
return this;
}()));
}
expect_stdout: "false"
}
issue_2319_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
"use strict";
console.log(function(a) {
return a;
}(!function() {
return this;
}()));
}
expect: {
"use strict";
console.log(function(a) {
return !function() {
return this;
}();
}());
}
expect_stdout: "true"
}
prop_side_effects_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log({
bar: function() {
return 2;
}
}.bar());
}
expect_stdout: [
"1",
"2",
]
}
prop_side_effects_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log(2);
}
expect_stdout: [
"1",
"2",
]
}

View File

@@ -21,7 +21,7 @@ concat_1: {
var c = 1 + x() + 2 + "boo";
var d = 1 + x() + 2 + 3 + "boo";
var e = 1 + x() + 2 + "X3boo";
var f = "\x00360\08\0";
var f = "\x00360\x008\0";
}
}

View File

@@ -1,166 +0,0 @@
issue_1191: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function foo(rot) {
const rotTol = 5;
if (rot < -rotTol || rot > rotTol)
bar();
baz();
}
}
expect: {
function foo(rot) {
(rot < -5 || rot > 5) && bar();
baz();
}
}
}
issue_1194: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function f1() {const a = "X"; return a + a;}
function f2() {const aa = "X"; return aa + aa;}
function f3() {const aaa = "X"; return aaa + aaa;}
}
expect: {
function f1(){return"XX"}
function f2(){return"XX"}
function f3(){return"XX"}
}
}
issue_1396: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function foo(a) {
const VALUE = 1;
console.log(2 | VALUE);
console.log(VALUE + 1);
console.log(VALUE);
console.log(a & VALUE);
}
function bar() {
const s = "01234567890123456789";
console.log(s + s + s + s + s);
const CONSTANT = "abc";
console.log(CONSTANT + CONSTANT + CONSTANT + CONSTANT + CONSTANT);
}
}
expect: {
function foo(a) {
console.log(3);
console.log(2);
console.log(1);
console.log(1 & a);
}
function bar() {
const s = "01234567890123456789";
console.log(s + s + s + s + s);
console.log("abcabcabcabcabc");
}
}
}
unused_regexp_literal: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
}
input: {
function f(){ var a = /b/; }
}
expect: {
function f(){}
}
}
regexp_literal_not_const: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
(function(){
var result;
const s = 'acdabcdeabbb';
const REGEXP_LITERAL = /ab*/g;
while (result = REGEXP_LITERAL.exec(s)) {
console.log(result[0]);
}
})();
}
expect: {
(function() {
var result;
const REGEXP_LITERAL = /ab*/g;
while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]);
})();
}
expect_stdout: true
}

View File

@@ -31,7 +31,7 @@ dead_code_2_should_warn: {
function f() {
g();
x = 10;
throw "foo";
throw new Error("foo");
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
@@ -46,16 +46,60 @@ dead_code_2_should_warn: {
})();
}
}
f();
}
expect: {
function f() {
g();
x = 10;
throw "foo";
throw new Error("foo");
var x;
function g(){};
}
f();
}
expect_stdout: true
node_version: "<=4"
}
dead_code_2_should_warn_strict: {
options = {
dead_code: true
};
input: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
if (x) {
y();
var x;
function g(){};
// but nested declarations should not be kept.
(function(){
var q;
function y(){};
})();
}
}
f();
}
expect: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
var x;
}
f();
}
expect_stdout: true
node_version: ">=4"
}
dead_code_constant_boolean_should_warn_more: {
@@ -78,6 +122,7 @@ dead_code_constant_boolean_should_warn_more: {
foo();
var moo;
}
bar();
}
expect: {
var foo;
@@ -86,155 +131,46 @@ dead_code_constant_boolean_should_warn_more: {
// as for the for, it should keep:
var x = 10, y;
var moo;
bar();
}
expect_stdout: true
node_version: "<=4"
}
dead_code_block_decls_die: {
options = {
dead_code : true,
conditionals : true,
booleans : true,
evaluate : true
};
input: {
if (0) {
let foo = 6;
const bar = 12;
class Baz {};
var qux;
}
console.log(foo, bar, Baz);
}
expect: {
var qux;
console.log(foo, bar, Baz);
}
}
dead_code_const_declaration: {
dead_code_constant_boolean_should_warn_more_strict: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
side_effects : true,
};
input: {
var unused;
const CONST_FOO = false;
if (CONST_FOO) {
"use strict";
while (!((foo && bar) || (x + "0"))) {
console.log("unreachable");
var moo;
var foo;
function bar() {}
}
}
expect: {
var unused;
const CONST_FOO = !1;
var moo;
function bar() {}
}
expect_stdout: true
}
dead_code_const_annotation: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
toplevel : true,
};
input: {
var unused;
/** @const */ var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("unreachable");
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
function bar() {}
}
bar();
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
"use strict";
var foo;
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo;
function bar() {}
}
expect_stdout: true
}
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');
}
expect_stdout: true
}
dead_code_const_annotation_complex_scope: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
toplevel : 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';
bar();
}
expect_stdout: true
node_version: ">=4"
}
try_catch_finally: {
@@ -294,3 +230,186 @@ accessor: {
}
expect: {}
}
issue_2233_1: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
Array.isArray;
Boolean;
console.log;
Date;
decodeURI;
decodeURIComponent;
encodeURI;
encodeURIComponent;
Error.name;
escape;
eval;
EvalError;
Function.length;
isFinite;
isNaN;
JSON;
Math.random;
Number.isNaN;
parseFloat;
parseInt;
RegExp;
Object.defineProperty;
String.fromCharCode;
RangeError;
ReferenceError;
SyntaxError;
TypeError;
unescape;
URIError;
}
expect: {}
expect_stdout: true
}
global_timeout_and_interval_symbols: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
// These global symbols do not exist in the test sandbox
// and must be tested separately.
clearInterval;
clearTimeout;
setInterval;
setTimeout;
}
expect: {}
}
issue_2233_2: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
var RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Number.isNaN;
}
}
}
issue_2233_3: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
UndeclaredGlobal;
}
}
global_fns: {
options = {
side_effects: true,
unsafe: true,
}
input: {
Boolean(1, 2);
decodeURI(1, 2);
decodeURIComponent(1, 2);
Date(1, 2);
encodeURI(1, 2);
encodeURIComponent(1, 2);
Error(1, 2);
escape(1, 2);
EvalError(1, 2);
isFinite(1, 2);
isNaN(1, 2);
Number(1, 2);
Object(1, 2);
parseFloat(1, 2);
parseInt(1, 2);
RangeError(1, 2);
ReferenceError(1, 2);
String(1, 2);
SyntaxError(1, 2);
TypeError(1, 2);
unescape(1, 2);
URIError(1, 2);
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect: {
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect_stdout: [
"SyntaxError",
"SyntaxError",
"RangeError",
]
}

View File

@@ -1,332 +0,0 @@
destructuring_arrays: {
input: {
{const [aa, bb] = cc;}
{const [aa, [bb, cc]] = dd;}
{let [aa, bb] = cc;}
{let [aa, [bb, cc]] = dd;}
var [aa, bb] = cc;
var [aa, [bb, cc]] = dd;
var [,[,,,,,],,,zz,] = xx; // Trailing comma
var [,,zzz,,] = xxx; // Trailing comma after hole
}
expect: {
{const [aa, bb] = cc;}
{const [aa, [bb, cc]] = dd;}
{let [aa, bb] = cc;}
{let [aa, [bb, cc]] = dd;}
var [aa, bb] = cc;
var [aa, [bb, cc]] = dd;
var [,[,,,,,],,,zz] = xx;
var [,,zzz,,] = xxx;
}
}
destructuring_arrays_holes: {
input: {
var [,,,,] = a;
var [,,b,] = c;
var [d,,] = e;
}
expect_exact: "var[,,,,]=a;var[,,b]=c;var[d,,]=e;"
}
destructuring_objects: {
input: {
{const {aa, bb} = {aa:1, bb:2};}
{const {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
{let {aa, bb} = {aa:1, bb:2};}
{let {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
var {aa, bb} = {aa:1, bb:2};
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
}
expect: {
{const {aa, bb} = {aa:1, bb:2};}
{const {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
{let {aa, bb} = {aa:1, bb:2};}
{let {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
var {aa, bb} = {aa:1, bb:2};
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
}
}
destructuring_objects_trailing_elision: {
beautify = {
ecma: 6
}
input: {
var {cc,} = foo;
}
expect_exact: "var{cc}=foo;"
}
nested_destructuring_objects: {
beautify = {
ecma: 6
}
input: {
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
}
expect_exact: 'const[{a},b]=c;let[{a},b]=c;var[{a},b]=c;';
}
destructuring_constdef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (const [x,y] in pairs);
for (const [a] = 0;;);
for (const {c} of cees);
}
expect_exact: "for(const[x,y]in pairs);for(const[a]=0;;);for(const{c}of cees);"
}
destructuring_letdef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (let [x,y] in pairs);
for (let [a] = 0;;);
for (let {c} of cees);
}
expect_exact: "for(let[x,y]in pairs);for(let[a]=0;;);for(let{c}of cees);"
}
destructuring_vardef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (var [x,y] in pairs);
for (var [a] = 0;;);
for (var {c} of cees);
}
expect_exact: "for(var[x,y]in pairs);for(var[a]=0;;);for(var{c}of cees);"
}
destructuring_expressions: {
beautify = {
ecma: 6
}
input: {
({a, b});
[{a}];
f({x});
}
expect_exact: "({a,b});[{a}];f({x});"
}
destructuring_remove_unused_1: {
options = {
unused: true
}
input: {
function a() {
var unused = "foo";
var a = [1];
var [b] = a;
f(b);
}
function b() {
var unused = "foo";
var a = {b: 1};
var {b} = a;
f(b);
}
function c() {
var unused = "foo";
var a = [[1]];
var [[b]] = a;
f(b);
}
function d() {
var unused = "foo";
var a = {b: {b:1}};
var {b:{b}} = a;
f(b);
}
function e() {
var unused = "foo";
var a = [1, 2, 3, 4, 5];
var x = [[1, 2, 3]];
var y = {h: 1};
var [b, ...c] = a;
var [...[e, f]] = x;
var [...{g: h}] = y;
f(b, c, e, f, g);
}
}
expect: {
function a() {
var a = [1];
var [b] = a;
f(b);
}
function b() {
var a = {b: 1};
var {b} = a;
f(b);
}
function c() {
var a = [[1]];
var [[b]] = a;
f(b);
}
function d() {
var a = {b: {b:1}};
var {b:{b}} = a;
f(b);
}
function e() {
var a = [1, 2, 3, 4, 5];
var x = [[1, 2, 3]];
var y = {h: 1};
var [b, ...c] = a;
var [...[e, f]] = x;
var [...{g: h}] = y;
f(b, c, e, f, g);
}
}
}
destructuring_remove_unused_2: {
options = {
unused: true
}
input: {
function a() {
var unused = "foo";
var a = [,,1];
var [b] = a;
f(b);
}
function b() {
var unused = "foo";
var a = [{a: [1]}];
var [{b: a}] = a;
f(b);
}
}
expect: {
function a() {
var a = [,,1];
var [b] = a;
f(b);
}
function b() {
var a = [{a: [1]}];
var [{b: a}] = a;
f(b);
}
}
}
object_destructuring_may_need_parentheses: {
beautify = {
ecma: 6
}
input: {
({a, b} = {a: 1, b: 2});
}
expect_exact: "({a,b}={a:1,b:2});"
}
destructuring_with_undefined_as_default_assignment: {
options = {
evaluate: true
}
input: {
[foo = undefined] = bar;
[foo = void 0] = bar;
}
expect: {
[foo] = bar;
[foo] = bar;
}
}
destructuring_dont_evaluate_with_undefined_as_default_assignment: {
options = {
evaluate: false
}
input: {
[foo = undefined] = bar;
}
expect: {
[foo = void 0] = bar;
}
}
reduce_vars: {
options = {
reduce_vars: true,
}
input: {
{const [aa, [bb, cc]] = dd;}
{let [aa, [bb, cc]] = dd;}
var [aa, [bb, cc]] = dd;
[aa, [bb, cc]] = dd;
{const {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
{let {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}});
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
[{a},b] = c;
for (const [x,y] in pairs);
for (let [x,y] in pairs);
for (var [x,y] in pairs);
for ([x,y] in pairs);
}
expect: {
{const [aa, [bb, cc]] = dd;}
{let [aa, [bb, cc]] = dd;}
var [aa, [bb, cc]] = dd;
[aa, [bb, cc]] = dd;
{const {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
{let {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}});
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
[{a},b] = c;
for (const [x,y] in pairs);
for (let [x,y] in pairs);
for (var [x,y] in pairs);
for ([x,y] in pairs);
}
}
unused: {
options = {
unused: true,
}
input: {
let { foo: [, , ...a] } = { foo: [1, 2, 3, 4], bar: 5 };
console.log(a);
}
expect: {
let { foo: [, , ...a] } = { foo: [1, 2, 3, 4], bar: 5 };
console.log(a);
}
}
issue_1886: {
options = {
collapse_vars: true,
}
input: {
let [a] = [1];
console.log(a);
}
expect: {
let [a] = [1];
console.log(a);
}
expect_exact: "1"
}

View File

@@ -1,10 +0,0 @@
class_directives_compression: {
input: {
class foo {
foo() {
"use strict";
}
}
}
expect_exact: "class foo{foo(){}}"
}

View File

@@ -164,87 +164,6 @@ used_var_in_catch: {
}
}
unused_block_decls_in_catch: {
options = { unused: true };
input: {
function foo() {
try {
foo();
} catch(ex) {
let x = 10;
const y = 10;
class Zee {};
}
}
}
expect: {
function foo() {
try {
foo();
} catch(ex) {}
}
}
}
used_block_decls_in_catch: {
options = { unused: true };
input: {
function foo() {
try {
foo();
} catch(ex) {
let x = 10;
const y = 10;
class Zee {};
}
console.log(x, y, Zee);
}
}
expect: {
function foo() {
try {
foo();
} catch(ex) {}
console.log(x, y, Zee);
}
}
}
unused_block_decls: {
options = { unused: true };
input: {
function foo() {
{
const x;
}
{
let y;
}
console.log(x, y);
}
}
expect: {
function foo() {
console.log(x, y);
}
}
}
unused_keep_harmony_destructuring: {
options = { unused: true };
input: {
function foo() {
var {x, y} = foo;
var a = foo;
}
}
expect: {
function foo() {
var {x, y} = foo;
}
}
}
keep_fnames: {
options = { unused: true, keep_fnames: true, unsafe: true };
input: {
@@ -730,37 +649,6 @@ drop_value: {
}
}
const_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
const b = 2;
return 1 + b;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
expect: {
function f() {
return 3;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
}
issue_1539: {
options = {
cascade: true,
@@ -863,11 +751,11 @@ issue_1583: {
expect: {
function m(t) {
(function(e) {
t = (function() {
t = function() {
return (function(a) {
return a;
})(function(a) {});
})();
return function(a) {};
})();
}();
})();
}
}
@@ -897,10 +785,6 @@ issue_1709: {
var x = 1;
return x;
}(),
function y() {
const y = 2;
return y;
}(),
function z() {
function z() {}
return z;
@@ -913,10 +797,6 @@ issue_1709: {
var x = 1;
return x;
}(),
function() {
const y = 2;
return y;
}(),
function() {
function z() {}
return z;
@@ -1210,6 +1090,7 @@ var_catch_toplevel: {
a--;
try {
a++;
x();
} catch(a) {
if (a) var a;
var a = 10;
@@ -1219,9 +1100,8 @@ var_catch_toplevel: {
}
expect: {
!function() {
a--;
try {
a++;
x();
} catch(a) {
var a;
}
@@ -1229,7 +1109,97 @@ var_catch_toplevel: {
}
}
reassign_const: {
issue_2105: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
return bar;
} );
});
}
expect: {
(function() {
var quux = function() {
console.log("PASS");
};
return {
prop: function() {
console.log;
quux();
}
};
})().prop();
}
expect_stdout: "PASS"
}
issue_2226_1: {
options = {
side_effects: true,
unused: true,
}
input: {
function f1() {
var a = b;
a += c;
}
function f2(a) {
a <<= b;
}
function f3(a) {
--a;
}
function f4() {
var a = b;
return a *= c;
}
function f5(a) {
x(a /= b);
}
}
expect: {
function f1() {
b;
c;
}
function f2(a) {
b;
}
function f3(a) {
0;
}
function f4() {
var a = b;
return a *= c;
}
function f5(a) {
x(a /= b);
}
}
}
issue_2226_2: {
options = {
cascade: true,
sequences: true,
@@ -1237,19 +1207,57 @@ reassign_const: {
unused: true,
}
input: {
function f() {
const a = 1;
a = 2;
console.log(function(a, b) {
a += b;
return a;
}
console.log(f());
}(1, 2));
}
expect: {
function f() {
const a = 1;
return a = 2, a;
}
console.log(f());
console.log(function(a, b) {
return a += b;
}(1, 2));
}
expect_stdout: "3"
}
issue_2226_3: {
options = {
collapse_vars: true,
side_effects: true,
unused: true,
}
input: {
console.log(function(a, b) {
a += b;
return a;
}(1, 2));
}
expect: {
console.log(function(a, b) {
return a += 2;
}(1));
}
expect_stdout: "3"
}
issue_2288: {
options = {
unused: true,
}
beautify = {
beautify: true,
}
input: {
function foo(o) {
for (var j = o.a, i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
}
}
expect: {
function foo(o) {
o.a;
for (i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
}
}
expect_stdout: true
}

View File

@@ -224,100 +224,6 @@ positive_zero: {
expect_stdout: true
}
pow: {
options = { evaluate: true }
input: {
var a = 5 ** 3;
}
expect: {
var a = 125;
}
}
pow_sequence: {
options = {
evaluate: true
}
input: {
var a = 2 ** 3 ** 2;
}
expect: {
var a = 512;
}
}
pow_mixed: {
options = {
evaluate: true
}
input: {
var a = 5 + 2 ** 3 + 5;
var b = 5 * 3 ** 2;
var c = 5 ** 3 * 2;
var d = 5 ** +3;
}
expect: {
var a = 18;
var b = 45;
var c = 250;
var d = 125;
}
}
pow_with_right_side_evaluating_to_unary: {
options = {
evaluate: true
}
input: {
var a = (4 - 7) ** foo;
var b = ++bar ** 3;
var c = --baz ** 2;
}
expect_exact: "var a=(-3)**foo;var b=++bar**3;var c=--baz**2;"
}
pow_with_number_constants: {
options = {
evaluate: true
}
input: {
var a = 5 ** NaN;
/* NaN exponent results to NaN */
var b = 42 ** +0;
/* +0 exponent results to NaN */
var c = 42 ** -0;
/* -0 exponent results to NaN */
var d = NaN ** 1;
/* NaN with non-zero exponent is NaN */
var e = 2 ** Infinity;
/* abs(base) > 1 with Infinity as exponent is Infinity */
var f = 2 ** -Infinity;
/* abs(base) > 1 with -Infinity as exponent is +0 */
var g = (-7) ** (0.5);
var h = 2324334 ** 34343443;
var i = (-2324334) ** 34343443;
var j = 2 ** (-3);
var k = 2.0 ** -3;
var l = 2.0 ** (5 - 7);
var m = 3 ** -10; // Result will be 0.000016935087808430286, which is too long
}
expect: {
var a = NaN;
var b = 1;
var c = 1;
var d = NaN;
var e = 1/0;
var f = 0;
var g = NaN;
var h = 1/0;
var i = -1/0;
var j = .125;
var k = .125;
var l = .25;
var m = 3 ** -10;
}
}
unsafe_constant: {
options = {
evaluate : true,
@@ -344,22 +250,26 @@ unsafe_constant: {
unsafe_object: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o = { a: 1 };
console.log(
({a:1}) + 1,
({a:1}).a + 1,
({a:1}).b + 1,
({a:1}).a.b + 1
o + 1,
o.a + 1,
o.b + 1,
o.a.b + 1
);
}
expect: {
var o = { a: 1 };
console.log(
({a:1}) + 1,
o + 1,
2,
({a:1}).b + 1,
o.b + 1,
1..b + 1
);
}
@@ -368,22 +278,26 @@ unsafe_object: {
unsafe_object_nested: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o = { a: { b: 1 } };
console.log(
({a:{b:1}}) + 1,
({a:{b:1}}).a + 1,
({a:{b:1}}).b + 1,
({a:{b:1}}).a.b + 1
o + 1,
o.a + 1,
o.b + 1,
o.a.b + 1
);
}
expect: {
var o = { a: { b: 1 } };
console.log(
({a:{b:1}}) + 1,
({a:{b:1}}).a + 1,
({a:{b:1}}).b + 1,
o + 1,
o.a + 1,
o.b + 1,
2
);
}
@@ -392,21 +306,25 @@ unsafe_object_nested: {
unsafe_object_complex: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o = { a: { b: 1 }, b: 1 };
console.log(
({a:{b:1},b:1}) + 1,
({a:{b:1},b:1}).a + 1,
({a:{b:1},b:1}).b + 1,
({a:{b:1},b:1}).a.b + 1
o + 1,
o.a + 1,
o.b + 1,
o.a.b + 1
);
}
expect: {
var o = { a: { b: 1 }, b: 1 };
console.log(
({a:{b:1},b:1}) + 1,
({a:{b:1},b:1}).a + 1,
o + 1,
o.a + 1,
2,
2
);
@@ -416,22 +334,26 @@ unsafe_object_complex: {
unsafe_object_repeated: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o = { a: { b: 1 }, a: 1 };
console.log(
({a:{b:1},a:1}) + 1,
({a:{b:1},a:1}).a + 1,
({a:{b:1},a:1}).b + 1,
({a:{b:1},a:1}).a.b + 1
o + 1,
o.a + 1,
o.b + 1,
o.a.b + 1
);
}
expect: {
var o = { a: { b: 1 }, a: 1 };
console.log(
({a:{b:1},a:1}) + 1,
o + 1,
2,
({a:{b:1},a:1}).b + 1,
o.b + 1,
1..b + 1
);
}
@@ -480,9 +402,9 @@ unsafe_function: {
expect: {
console.log(
({a:{b:1},b:function(){}}) + 1,
({a:{b:1},b:function(){}}).a + 1,
({a:{b:1},b:function(){}}).b + 1,
({a:{b:1},b:function(){}}).a.b + 1
({b:function(){}}, {b:1}) + 1,
({a:{b:1}}, function(){}) + 1,
({b:function(){}}, {b:1}).b + 1
);
}
expect_stdout: true
@@ -730,25 +652,27 @@ unsafe_prototype_function: {
var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2];
var g = ({valueOf: 0}).valueOf();
var h = "" + ({toString: 0});
var g = ({}, 0)();
var h = ({}, 0)();
}
}
call_args: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
const a = 1;
var a = 1;
console.log(a);
+function(a) {
return a;
}(a);
}
expect: {
const a = 1;
var a = 1;
console.log(1);
+(1, 1);
}
@@ -758,19 +682,20 @@ call_args: {
call_args_drop_param: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
const a = 1;
var a = 1;
console.log(a);
+function(a) {
return a;
}(a, b);
}
expect: {
const a = 1;
console.log(1);
+(b, 1);
}
@@ -873,13 +798,15 @@ unsafe_charAt_noop: {
input: {
console.log(
s.charAt(0),
"string".charAt(x)
"string".charAt(x),
(typeof x).charAt()
);
}
expect: {
console.log(
s.charAt(0),
"string".charAt(x)
"string".charAt(x),
(typeof x)[0]
);
}
}
@@ -1083,3 +1010,188 @@ Infinity_NaN_undefined_LHS: {
"}",
]
}
issue_1964_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: false,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect_stdout: [
"\\s",
"b",
]
}
issue_1964_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
console.log(long_variable_name.source);
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
console.log(/\s/.source);
return "a b c".split(/\s/)[1];
}
console.log(f());
}
expect_stdout: [
"\\s",
"b",
]
}
array_slice_index: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([1,2,3].slice(1)[1]);
}
expect: {
console.log(3);
}
expect_stdout: "3"
}
string_charCodeAt: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("foo".charCodeAt("bar".length));
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}
issue_2207_1: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(String.fromCharCode(65));
console.log(Math.max(3, 6, 2, 7, 3, 4));
console.log(Math.cos(1.2345));
console.log(Math.cos(1.2345) - Math.sin(4.321));
console.log(Math.pow(Math.PI, Math.E - Math.LN10));
}
expect: {
console.log("A");
console.log(7);
console.log(Math.cos(1.2345));
console.log(1.2543732512566947);
console.log(1.6093984514472044);
}
expect_stdout: true
}
issue_2207_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect_stdout: true
}
issue_2207_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);
console.log(Number.NaN);
console.log(Number.NEGATIVE_INFINITY);
console.log(Number.POSITIVE_INFINITY);
}
expect: {
console.log(Number.MAX_VALUE);
console.log(5e-324);
console.log(NaN);
console.log(-1/0);
console.log(1/0);
}
expect_stdout: true
}
issue_2231_1: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Object.keys(void 0));
}
expect: {
console.log(Object.keys(void 0));
}
expect_stdout: true
}
issue_2231_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Object.getOwnPropertyNames(null));
}
expect: {
console.log(Object.getOwnPropertyNames(null));
}
expect_stdout: true
}

View File

@@ -1,28 +0,0 @@
expand_arguments: {
input: {
func(a, ...rest);
func(...all);
}
expect_exact: "func(a,...rest);func(...all);"
}
expand_expression_arguments: {
input: {
f(...a.b);
f(...a.b());
f(...(a));
f(...(a.b));
f(...a[i]);
}
expect_exact: "f(...a.b);f(...a.b());f(...a);f(...a.b);f(...a[i]);"
}
expand_parameters: {
input: {
(function (a, ...b){});
(function (...args){});
}
expect_exact: "(function(a,...b){});(function(...args){});"
}

View File

@@ -1,52 +0,0 @@
pow: {
input: {
var a = 2 ** 7;
var b = 3;
b **= 2;
}
expect: {
var a = 2 ** 7;
var b = 3;
b **= 2;
}
}
pow_with_number_constants: {
input: {
var a = 5 ** NaN;
var b = 42 ** +0;
var c = 42 ** -0;
var d = NaN ** 1;
var e = 2 ** Infinity;
var f = 2 ** -Infinity;
}
expect: {
var a = 5 ** NaN;
var b = 42 ** +0;
var c = 42 ** -0;
var d = NaN ** 1;
var e = 2 ** (1/0);
var f = 2 ** (-1/0);
}
}
pow_with_parentheses: {
input: {
var g = (-7) ** (0.5);
var h = 2324334 ** 34343443;
var i = (-2324334) ** 34343443;
var j = 2 ** (-3);
var k = 2.0 ** -3;
var l = 2.0 ** (5 - 7);
}
expect_exact: "var g=(-7)**.5;var h=2324334**34343443;var i=(-2324334)**34343443;var j=2**-3;var k=2**-3;var l=2**(5-7);"
}
pow_with_unary_between_brackets: {
input: {
var a = (-(+5)) ** 3;
}
expect: {
var a = (-+5)**3;
}
}

View File

@@ -21,6 +21,7 @@ iifes_returning_constants_keep_fargs_true: {
join_vars : true,
reduce_vars : true,
cascade : true,
inline : true,
}
input: {
(function(){ return -1.23; }());
@@ -56,6 +57,7 @@ iifes_returning_constants_keep_fargs_false: {
join_vars : true,
reduce_vars : true,
cascade : true,
inline : true,
}
input: {
(function(){ return -1.23; }());
@@ -82,6 +84,7 @@ issue_485_crashing_1530: {
conditionals: true,
dead_code: true,
evaluate: true,
inline: true,
}
input: {
(function(a) {
@@ -154,6 +157,7 @@ function_returning_constant_literal: {
evaluate: true,
cascade: true,
unused: true,
inline: true,
}
input: {
function greeter() {
@@ -167,3 +171,340 @@ function_returning_constant_literal: {
}
expect_stdout: "Hello there"
}
hoist_funs: {
options = {
hoist_funs: true,
}
input: {
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
function f() {}
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'function' 'function'",
"2 'function' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'function' 'function'",
]
node_version: "<=4"
}
hoist_funs_strict: {
options = {
hoist_funs: true,
}
input: {
"use strict";
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
"use strict";
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'undefined' 'function'",
"2 'undefined' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'undefined' 'function'",
]
node_version: ">=4"
}
issue_203: {
options = {
keep_fargs: false,
side_effects: true,
unsafe_Func: true,
unused: true,
}
input: {
var m = {};
var fn = Function("require", "module", "exports", "module.exports = 42;");
fn(null, m, m.exports);
console.log(m.exports);
}
expect: {
var m = {};
var fn = Function("n,o", "o.exports=42");
fn(null, m, m.exports);
console.log(m.exports);
}
expect_stdout: "42"
}
no_webkit: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
issue_2084: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
!function(c) {
c = 1 + c;
var c = 0;
function f14(a_1) {
if (c = 1 + c, 0 !== 23..toString())
c = 1 + c, a_1 && (a_1[0] = 0);
}
f14();
}(-1);
}();
console.log(c);
}
expect: {
var c = 0;
!function(c) {
c = 1 + c,
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
}(-1),
console.log(c);
}
expect_stdout: "0"
}
issue_2097: {
options = {
negate_iife: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}
f(1);
}
expect: {
!function() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}(1);
}
expect_stdout: "1"
}
issue_2101: {
options = {
inline: true,
}
input: {
a = {};
console.log(function() {
return function() {
return this.a;
}();
}() === function() {
return a;
}());
}
expect: {
a = {};
console.log(function() {
return this.a;
}() === a);
}
expect_stdout: "true"
}
inner_ref: {
options = {
inline: true,
unused: true,
}
input: {
console.log(function(a) {
return function() {
return a;
}();
}(1), function(a) {
return function(a) {
return a;
}();
}(2));
}
expect: {
console.log(function(a) {
return a;
}(1), function(a) {
return a;
}());
}
expect_stdout: "1 undefined"
}
issue_2107: {
options = {
cascade: true,
collapse_vars: true,
inline: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
c++;
}(c++ + new function() {
this.a = 0;
var a = (c = c + 1) + (c = 1 + c);
return c++ + a;
}());
console.log(c);
}
expect: {
var c = 0;
c++, new function() {
this.a = 0, c = 1 + (c += 1), c++;
}(), c++, console.log(c);
}
expect_stdout: "5"
}
issue_2114_1: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}();
console.log(c);
}
expect_stdout: "2"
}
issue_2114_2: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
passes: 2,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}();
console.log(c);
}
expect_stdout: "2"
}

View File

@@ -37,6 +37,7 @@ object: {
VALUE: 42,
},
},
side_effects: true,
unsafe: true,
}
input: {
@@ -120,7 +121,7 @@ mixed: {
properties: true,
}
input: {
const FOO = { BAR: 0 };
var FOO = { BAR: 0 };
console.log(FOO.BAR);
console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE);
@@ -130,7 +131,7 @@ mixed: {
console.log(CONFIG);
}
expect: {
const FOO = { BAR: 0 };
var FOO = { BAR: 0 };
console.log("moo");
console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE);
@@ -140,9 +141,9 @@ mixed: {
console.log(CONFIG);
}
expect_warnings: [
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:126,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:128,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:130,8]',
]
}
@@ -160,3 +161,38 @@ issue_1801: {
console.log(!0);
}
}
issue_1986: {
options = {
global_defs: {
"@alert": "console.log",
},
}
input: {
alert(42);
}
expect: {
console.log(42);
}
}
issue_2167: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
global_defs: {
"@isDevMode": "function(){}",
},
side_effects: true,
}
input: {
if (isDevMode()) {
greetOverlord();
}
doWork();
}
expect: {
doWork();
}
}

View File

@@ -1,585 +0,0 @@
arrow_function_parens: {
input: {
something && (() => {});
}
expect_exact: "something&&(()=>{});"
}
arrow_function_parens_2: {
input: {
(() => null)();
}
expect_exact: "(()=>null)();"
}
typeof_arrow_functions: {
options = {
evaluate: true
}
input: {
var foo = typeof (x) => null;
}
expect_exact: "var foo=\"function\";"
}
classes: {
input: {
class SomeClass {
constructor() {
};
foo() {};
};
class NoSemi {
constructor(...args) {
}
foo() {}
};
class ChildClass extends SomeClass {};
var asExpression = class AsExpression {};
var nameless = class {};
}
expect_exact: "class SomeClass{constructor(){}foo(){}}class NoSemi{constructor(...args){}foo(){}}class ChildClass extends SomeClass{}var asExpression=class AsExpression{};var nameless=class{};"
}
class_statics: {
input: {
x = class {
static staticMethod() {}
static get foo() {}
static set bar() {}
static() { /* "static" can be a method name! */ }
get() { /* "get" can be a method name! */ }
set() { /* "set" can be a method name! */ }
}
}
expect_exact: "x=class{static staticMethod(){}static get foo(){}static set bar(){}static(){}get(){}set(){}};"
}
class_name_can_be_mangled: {
mangle = { };
input: {
function x() {
class Foo {
}
var class1 = Foo
var class2 = class Bar {}
}
}
expect: {
function x() {
class a { }
var n = a
var r = class a {}
}
}
}
class_name_can_be_preserved: {
mangle = {
keep_classnames: true
}
input: {
function x() {
(class Baz { });
class Foo {};
}
}
expect: {
function x() {
(class Baz { });
class Foo {};
}
}
}
classes_can_have_generators: {
input: {
class Foo {
*bar() {}
static *baz() {}
}
}
expect: {
class Foo {
*bar() {}
static *baz() {}
}
}
}
classes_can_have_computed_generators: {
input: {
class C4 {
*['constructor']() {}
}
}
expect: {
class C4 {
*['constructor']() {}
}
}
}
classes_can_have_computed_static: {
input: {
class C4 {
static ['constructor']() {}
}
}
expect: {
class C4 {
static ['constructor']() {}
}
}
}
class_methods_and_getters_with_keep_quoted_props_enabled: {
beautify = {
quote_style: 3,
keep_quoted_props: true,
}
input: {
class clss {
a() {}
"b"() {}
get c() { return "c"}
get "d"() { return "d"}
set e(a) { doSomething(a); }
set 'f'(a) { doSomething(b); }
static g() {}
static "h"() {}
}
}
expect_exact: 'class clss{a(){}"b"(){}get c(){return"c"}get"d"(){return"d"}set e(a){doSomething(a)}set\'f\'(a){doSomething(b)}static g(){}static"h"(){}}'
}
classes_with_expression_as_expand: {
input: {
class D extends (calls++, C) {}
}
expect_exact: "class D extends(calls++,C){}"
}
new_target: {
input: {
new.target;
new.target.name;
}
expect_exact: "new.target;new.target.name;"
}
number_literals: {
input: {
0b1001;
0B1001;
0o11;
0O11;
}
expect: {
9;
9;
9;
9;
}
}
import_statement: {
input: {
import "mod-name";
import Foo from "bar";
import { Bar, Baz } from 'lel';
import Bar, { Foo } from 'lel';
import { Bar as kex, Baz as food } from 'lel';
}
expect_exact: 'import"mod-name";import Foo from"bar";import{Bar,Baz}from"lel";import Bar,{Foo}from"lel";import{Bar as kex,Baz as food}from"lel";'
}
import_all_statement: {
input: {
import * from 'lel';
import * as Lel from 'lel';
}
expect_exact: 'import*from"lel";import*as Lel from"lel";'
}
export_statement: {
options = {
evaluate: true,
}
input: {
export default 1 + 2;
export var foo = 4;
export let foo = 6;
export const foo = 6;
export function foo() {};
export class foo { };
}
expect_exact: "export default 3;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
}
export_default_object_expression: {
options = {
evaluate: true,
}
input: {
export default {
foo: 1 + 2,
bar() { return 4; },
get baz() { return this.foo; },
};
}
expect_exact: "export default{foo:3,bar(){return 4},get baz(){return this.foo}};"
}
export_default_array: {
options = {
evaluate: true,
}
input: {
export default [ 1 + 2, foo ];
}
expect_exact: "export default[3,foo];"
}
export_default_anon_function: {
options = {
evaluate: true,
}
input: {
export default function(){
console.log(1 + 2);
}
}
expect_exact: "export default function(){console.log(3)};"
}
export_default_anon_class: {
options = {
evaluate: true,
}
input: {
export default class {
foo() { console.log(1 + 2); }
}
}
expect_exact: "export default class{foo(){console.log(3)}};"
}
export_module_statement: {
input: {
export * from "a.js";
export {A} from "a.js";
export {A, B} from "a.js";
export {C};
}
expect_exact: 'export*from"a.js";export{A}from"a.js";export{A,B}from"a.js";export{C};'
}
import_statement_mangling: {
mangle = { toplevel: true };
input: {
import Foo from "foo";
import Bar, {Food} from "lel";
import {What as Whatever} from "lel";
Foo();
Bar();
Food();
Whatever();
}
expect: {
import l from "foo";
import e, {Food as o} from "lel";
import {What as f} from "lel";
l();
e();
o();
f();
}
}
export_statement_mangling: {
mangle = { };
input: {
export var foo = 6;
export function bar() { }
export class Baz { }
bar(foo, Baz)
}
expect: {
export var foo = 6;
export function bar() { }
export class Baz { }
bar(foo, Baz)
}
}
// https://github.com/mishoo/UglifyJS2/issues/1021
regression_for_of_const: {
input: {
for (const x of y) {}
for (const x in y) {}
}
expect: {
for (const x of y);for (const x in y);
}
}
// Fabio: My patches accidentally caused a crash whenever
// there's an extraneous set of parens around an object.
regression_cannot_destructure: {
input: {
var x = ({ x : 3 });
x(({ x: 3 }));
}
expect_exact: "var x={x:3};x({x:3});";
}
regression_cannot_use_of: {
input: {
function of() {
}
var of = "is a valid variable name";
of = { of: "is ok" };
x.of;
of: foo()
}
expect: {
function of(){}
var of="is a valid variable name";
of={of:"is ok"};
x.of;
foo(); /* Label statement missing? No prob. */
}
}
fat_arrow_as_param: {
input: {
foo(x => x);
foo(x => x, y => y);
foo(x => (x, x));
foo(x => (x, x), y => (y, y));
}
expect_exact: "foo(x=>x);foo(x=>x,y=>y);foo(x=>(x,x));foo(x=>(x,x),y=>(y,y));"
}
default_assign: {
options = {
keep_fargs: false,
unused: true,
}
input: {
function f(a, b = 3) {
console.log(a);
}
g = ([[] = 123]) => {};
h = ([[x, y, z] = [4, 5, 6]] = []) => {};
function i([[x, y, z] = [4, 5, 6]] = []) {
console.log(b);
};
}
expect: {
function f(a) {
console.log(a);
}
g = ([[] = 123]) => {};
h = ([[x, y, z] = [4, 5, 6]] = []) => {};
function i([[x, y, z] = [4, 5, 6]] = []) {
console.log(b);
};
}
}
expansion: {
options = {
keep_fargs: false,
unused: true,
}
input: {
function f(a, ...b) {
console.log(a);
}
}
expect: {
function f(a) {
console.log(a);
}
}
}
issue_1613: {
mangle = { toplevel: true };
input: {
const name = 1;
const foo = {
name
};
}
expect_exact: "const n=1;const c={name:n};"
}
format_methods: {
beautify = {
beautify: true,
}
input: {
class A extends B {constructor(a){x()} static s(b,c){y()} run(d,e,f){z()}}
}
expect_exact: [
"class A extends B {",
" constructor(a) {",
" x();",
" }",
" static s(b, c) {",
" y();",
" }",
" run(d, e, f) {",
" z();",
" }",
"}",
]
}
issue_1898: {
options = {
}
mangle = {
}
input: {
class Foo {
bar() {
for (const x of [ 6, 5 ]) {
for (let y of [ 4, 3 ]) {
for (var z of [ 2, 1 ]) {
console.log(x, y, z);
}
}
}
}
}
new Foo().bar();
}
expect: {
class Foo {
bar() {
for (const n of [ 6, 5 ])
for (let r of [ 4, 3 ])
for (var o of [ 2, 1 ])
console.log(n, r, o);
}
}
new Foo().bar();
}
}
issue_1753: {
mangle = { safari10: true };
input: {
class SomeClass {
constructor(props) {
let pickedSets = [];
for (let i = 0; i < 6; i++) {
pickedSets.push({
mainDrawNumbers: [],
extraDrawNumbers: []
});
}
}
}
}
expect: {
class SomeClass {
constructor(r) {
let a = [];
for (let s = 0; s < 6; s++)
a.push({
mainDrawNumbers: [],
extraDrawNumbers: []
});
}
}
}
}
issue_1753_disable: {
mangle = { safari10: false }
input: {
class SomeClass {
constructor(props) {
let pickedSets = [];
for (let i = 0; i < 6; i++) {
pickedSets.push({
mainDrawNumbers: [],
extraDrawNumbers: []
});
}
}
}
}
expect: {
class SomeClass {
constructor(r) {
let a = [];
for (let r = 0; r < 6; r++)
a.push({
mainDrawNumbers: [],
extraDrawNumbers: []
});
}
}
}
}
class_extends: {
options = {
evaluate: true,
}
input: {
function f() {
class foo extends bar {}
class pro extends some.prop {}
class arr extends stuff[1 - 1] {}
class bin extends (a || b) {}
class seq extends (a, b) {}
class ter extends (a ? b : c) {}
class uni extends (!0) {}
}
}
expect_exact: "function f(){class foo extends bar{}class pro extends some.prop{}class arr extends stuff[0]{}class bin extends(a||b){}class seq extends(a,b){}class ter extends(a?b:c){}class uni extends(!0){}}"
}
class_extends_class: {
options = {
}
input: {
class anon extends class {} {}
class named extends class base {} {}
}
expect_exact: "class anon extends class{}{}class named extends class base{}{}"
}
class_extends_function: {
options = {
}
input: {
class anon extends function(){} {}
class named extends function base(){} {}
}
expect_exact: "class anon extends function(){}{}class named extends function base(){}{}"
}
class_extends_regex: {
options = {
}
input: {
function f() {
class rx1 extends (/rx/) {}
// class rx2 extends /rx/ {} // FIXME - parse error
}
}
expect_exact: "function f(){class rx1 extends(/rx/){}}"
}

View File

@@ -1,85 +0,0 @@
hoist_vars: {
options = {
hoist_vars: true
}
input: {
function a() {
bar();
var var1;
var var2;
}
function b(anArg) {
bar();
var var1;
var anArg;
}
}
expect: {
function a() {
var var1, var2; // Vars go up and are joined
bar();
}
function b(anArg) {
var var1;
bar();
// But vars named like arguments go away!
}
}
}
hoist_funs: {
options = {
hoist_funs: true
}
input: {
function a() {
bar();
function foo() {}
}
}
expect: {
function a() {
function foo() {} // Funs go up
bar();
}
}
}
hoist_no_destructurings: {
options = {
hoist_vars: true,
hoist_funs: true
}
input: {
function a([anArg]) {
bar();
var var1;
var anArg; // Because anArg is already declared, this goes away!
}
}
expect: {
function a([anArg]) {
var var1;
bar();
}
}
}
dont_hoist_var_destructurings: {
options = {
hoist_vars: true,
hoist_funs: true
}
input: {
function x() {
// If foo is null or undefined, this should be an exception
var {x,y} = foo;
}
}
expect: {
function x() {
var {x,y} = foo;
}
}
}

View File

@@ -88,3 +88,24 @@ sequences_funs: {
}
}
}
issue_2295: {
options = {
collapse_vars: true,
hoist_vars: true,
}
input: {
function foo(o) {
var a = o.a;
if (a) return a;
var a = 1;
}
}
expect: {
function foo(o) {
var a = o.a;
if (a) return a;
a = 1;
}
}
}

View File

@@ -53,12 +53,3 @@ html_comment_in_string_literal: {
}
expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}';
}
html_comment_after_multiline_comment: {
input: {
var foo; /*
*/--> var bar;
var foobar;
}
expect_exact: "var foo;var foobar;"
}

View File

@@ -302,3 +302,27 @@ issue_1437_conditionals: {
}
}
}
issue_512: {
options = {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
}
input: {
function a() {
if (b()) {
c();
return;
}
throw e;
}
}
expect: {
function a() {
if (!b()) throw e;
c();
}
}
}

View File

@@ -1,8 +0,0 @@
parenthesis_strings_in_parenthesis: {
input: {
var foo = ('(');
a(')');
}
expect_exact: 'var foo="(";a(")");'
}

View File

@@ -71,11 +71,13 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:51,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:48,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:55,21]",
"WARN: pass 0: last_count: Infinity, count: 37",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:53,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]",
"WARN: pass 1: last_count: 37, count: 18",
]
}
@@ -109,10 +111,146 @@ non_hoisted_function_after_return_2b: {
}
expect_warnings: [
// duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:95,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:95,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:97,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:99,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:99,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:103,12]",
]
}
non_hoisted_function_after_return_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar();
not_called1();
} else {
return baz();
not_called2();
}
function bar() { return 7; }
return not_reached;
function UnusedFunction() {}
function baz() { return 8; }
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return x ? bar() : baz();
function bar() { return 7 }
function baz() { return 8 }
}
console.log(foo(0), foo(1));
}
expect_stdout: "8 7"
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:133,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:136,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:139,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:140,21]",
]
}
non_hoisted_function_after_return_2a_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2, warnings: "verbose"
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
var a = not_called(1);
} else {
return bar(2);
var b = not_called(2);
}
var c = bar(3);
function bar(x) { return 7 - x; }
function nope() {}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) {
return 7 - x;
}
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:175,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:175,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:175,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:182,21]",
"WARN: pass 0: last_count: Infinity, count: 48",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:180,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:180,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:183,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:178,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:180,16]",
"WARN: pass 1: last_count: 48, count: 29",
]
}
non_hoisted_function_after_return_2b_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
} else {
return bar(2);
var b;
}
var c = bar(3);
function bar(x) {
return 7 - x;
}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) { return 7 - x; }
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
// duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:229,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:229,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:231,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:235,12]",
]
}

View File

@@ -1,16 +1,3 @@
const_declaration: {
options = {
evaluate: true
};
input: {
const goog = goog || {};
}
expect: {
const goog = goog || {};
}
}
const_pragma: {
options = {
evaluate: true,

View File

@@ -1,30 +0,0 @@
issue_1043: {
options = {
side_effects: true
};
input: {
function* range(start = 0, end = null, step = 1) {
if (end == null) {
end = start;
start = 0;
}
for (let i = start; i < end; i += step) {
yield i;
}
}
}
expect: {
function* range(start = 0, end = null, step = 1) {
if (null == end) {
end = start;
start = 0;
}
for (let i = start; i < end; i += step)
yield i;
}
}
}

View File

@@ -1,9 +0,0 @@
issue_1044: {
options = { evaluate: true, conditionals: true };
input: {
const mixed = Base ? class extends Base {} : class {}
}
expect: {
const mixed = Base ? class extends Base {} : class {}
}
}

View File

@@ -1,90 +1,91 @@
multiple_functions: {
options = { if_return: true, hoist_funs: false };
options = {
hoist_funs: false,
if_return: true,
}
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
function g() {}
} )();
}
expect: {
( function() {
function f() {}
function g() {}
// NOTE: other compression steps will reduce this
// down to just `window`.
if ( window );
function f() {}
function g() {}
} )();
}
}
single_function: {
options = { if_return: true, hoist_funs: false };
options = {
hoist_funs: false,
if_return: true,
}
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
} )();
}
expect: {
( function() {
function f() {}
if ( window );
function f() {}
} )();
}
}
deeply_nested: {
options = { if_return: true, hoist_funs: false };
options = {
hoist_funs: false,
if_return: true,
}
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
function g() {}
if ( !document ) {
return;
}
function h() {}
} )();
}
expect: {
( function() {
function f() {}
function g() {}
function h() {}
// NOTE: other compression steps will reduce this
// down to just `window`.
if ( window )
if (document);
function f() {}
function g() {}
function h() {}
} )();
}
}
not_hoisted_when_already_nested: {
options = { if_return: true, hoist_funs: false };
options = {
hoist_funs: false,
if_return: true,
}
input: {
( function() {
if ( !window ) {
return;
}
if ( foo ) function f() {}
} )();
}
expect: {
@@ -94,3 +95,70 @@ not_hoisted_when_already_nested: {
} )();
}
}
defun_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}
defun_hoist_funs: {
options = {
hoist_funs: true,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
function g() {}
function h() {}
if (window);
}
}
}
defun_else_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (window) function g() {}
else return;
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}

View File

@@ -1,76 +0,0 @@
issue_1212_debug_false: {
options = {
global_defs : { DEBUG: false },
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: {
class foo {
bar() {
if (DEBUG)
console.log("DEV");
else
console.log("PROD");
}
}
new foo().bar();
}
expect: {
class foo{
bar() { console.log("PROD") }
}
(new foo).bar();
}
}
issue_1212_debug_true: {
options = {
global_defs : { DEBUG: true },
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: {
class foo {
bar() {
if (DEBUG)
console.log("DEV");
else
console.log("PROD");
}
}
new foo().bar();
}
expect: {
class foo{
bar() { console.log("DEV") }
}
(new foo).bar();
}
}

View File

@@ -96,6 +96,13 @@ pure_function_calls_toplevel: {
})();
})();
// pure top-level calls will be dropped regardless of the leading comments position
var MyClass = /*#__PURE__*//*@class*/(function(){
function MyClass() {}
MyClass.prototype.method = function() {};
return MyClass;
})();
// comment #__PURE__ comment
bar(), baz(), quux();
a.b(), /* @__PURE__ */ c.d.e(), f.g();
@@ -110,10 +117,12 @@ pure_function_calls_toplevel: {
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:92,37]",
"WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:92,16]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:90,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:100,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:101,31]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:107,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:108,31]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:84,33]",
"WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:100,45]",
"WARN: Dropping unused variable MyClass [test/compress/issue-1261.js:100,12]",
]
}
@@ -148,29 +157,29 @@ should_warn: {
baz();
}
expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,61]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,23]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:128,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:129,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:129,23]",
"WARN: Condition always true [test/compress/issue-1261.js:129,23]",
"WARN: Condition left of || always true [test/compress/issue-1261.js:130,8]",
"WARN: Condition always true [test/compress/issue-1261.js:130,8]",
"WARN: Boolean && always false [test/compress/issue-1261.js:131,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:131,23]",
"WARN: Condition always false [test/compress/issue-1261.js:131,23]",
"WARN: Condition left of && always false [test/compress/issue-1261.js:132,8]",
"WARN: Condition always false [test/compress/issue-1261.js:132,8]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:133,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:133,23]",
"WARN: Condition always true [test/compress/issue-1261.js:133,23]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:134,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:134,31]",
"WARN: Condition always true [test/compress/issue-1261.js:134,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:135,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:136,24]",
"WARN: Condition always true [test/compress/issue-1261.js:136,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,31]",
"WARN: Condition always false [test/compress/issue-1261.js:137,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,61]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,23]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:137,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:138,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:138,23]",
"WARN: Condition always true [test/compress/issue-1261.js:138,23]",
"WARN: Condition left of || always true [test/compress/issue-1261.js:139,8]",
"WARN: Condition always true [test/compress/issue-1261.js:139,8]",
"WARN: Boolean && always false [test/compress/issue-1261.js:140,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:140,23]",
"WARN: Condition always false [test/compress/issue-1261.js:140,23]",
"WARN: Condition left of && always false [test/compress/issue-1261.js:141,8]",
"WARN: Condition always false [test/compress/issue-1261.js:141,8]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:142,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
"WARN: Condition always true [test/compress/issue-1261.js:142,23]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,31]",
"WARN: Condition always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:145,24]",
"WARN: Condition always true [test/compress/issue-1261.js:145,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:146,31]",
"WARN: Condition always false [test/compress/issue-1261.js:146,8]",
]
}

View File

@@ -1,6 +1,8 @@
issue_1321_no_debug: {
mangle_props = {
keep_quoted: true
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
var x = {};
@@ -10,17 +12,19 @@ issue_1321_no_debug: {
}
expect: {
var x = {};
x.b = 1;
x["a"] = 2 * x.b;
console.log(x.b, x["a"]);
x.x = 1;
x["a"] = 2 * x.x;
console.log(x.x, x["a"]);
}
expect_stdout: true
}
issue_1321_debug: {
mangle_props = {
keep_quoted: true,
debug: ""
mangle = {
properties: {
debug: "",
keep_quoted: true,
},
}
input: {
var x = {};
@@ -30,16 +34,18 @@ issue_1321_debug: {
}
expect: {
var x = {};
x.a = 1;
x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]);
x.x = 1;
x["_$foo$_"] = 2 * x.x;
console.log(x.x, x["_$foo$_"]);
}
expect_stdout: true
}
issue_1321_with_quoted: {
mangle_props = {
keep_quoted: false
mangle = {
properties: {
keep_quoted: false,
},
}
input: {
var x = {};
@@ -49,9 +55,9 @@ issue_1321_with_quoted: {
}
expect: {
var x = {};
x.a = 1;
x["b"] = 2 * x.a;
console.log(x.a, x["b"]);
x.x = 1;
x["o"] = 2 * x.x;
console.log(x.x, x["o"]);
}
expect_stdout: true
}

View File

@@ -1,6 +1,7 @@
typeof_eq_undefined: {
options = {
comparisons: true
comparisons: true,
typeofs: true,
}
input: {
var a = typeof b != "undefined";
@@ -24,6 +25,7 @@ typeof_eq_undefined_ie8: {
options = {
comparisons: true,
ie8: true,
typeofs: true,
}
input: {
var a = typeof b != "undefined";
@@ -45,7 +47,8 @@ typeof_eq_undefined_ie8: {
undefined_redefined: {
options = {
comparisons: true
comparisons: true,
typeofs: true,
}
input: {
function f(undefined) {
@@ -58,7 +61,8 @@ undefined_redefined: {
undefined_redefined_mangle: {
options = {
comparisons: true
comparisons: true,
typeofs: true,
}
mangle = {}
input: {

View File

@@ -1,291 +0,0 @@
same_variable_in_multiple_for_loop: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
for (let i = 0; i < 3; i++) {
let a = 100;
console.log(i, a);
for (let i = 0; i < 2; i++) {
console.log(i, a);
let c = 2;
console.log(c);
}
}
}
expect: {
for (let o = 0; o < 3; o++) {
let l = 100;
console.log(o, l);
for (let o = 0; o < 2; o++) {
console.log(o, l);
let c = 2;
console.log(c);
}
}
}
expect_stdout: true
}
same_variable_in_multiple_forOf: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp of test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let tmp of dd) {
console.log(tmp);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o of test) {
console.log(o);
let e;
e = [ "e", "f", "g" ];
for (let o of e)
console.log(o);
}
}
expect_stdout: true
}
same_variable_in_multiple_forIn: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: false,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp in test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let tmp in test) {
console.log(tmp);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o in test) {
console.log(o);
let e;
e = [ "e", "f", "g" ];
for (let o in test)
console.log(o);
}
}
expect_stdout: true
}
different_variable_in_multiple_for_loop: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
for (let i = 0; i < 3; i++) {
let a = 100;
console.log(i, a);
for (let j = 0; j < 2; j++) {
console.log(j, a);
let c = 2;
console.log(c);
}
}
}
expect: {
for (let o = 0; o < 3; o++) {
let l = 100;
console.log(o, l);
for (let o = 0; o < 2; o++) {
console.log(o, l);
let c = 2;
console.log(c);
}
}
}
expect_stdout: true
}
different_variable_in_multiple_forOf: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp of test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let t of dd) {
console.log(t);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o of test) {
console.log(o);
let e;
e = [ "e", "f", "g" ];
for (let o of e)
console.log(o);
}
}
expect_stdout: true
}
different_variable_in_multiple_forIn: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: false,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp in test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let t in test) {
console.log(t);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o in test) {
console.log(o);
let e;
e = [ "e", "f", "g" ];
for (let o in test)
console.log(o);
}
}
expect_stdout: true
}
more_variable_in_multiple_for: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: false,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
for (let a = 9, i = 0; i < 20; i += a) {
let b = a++ + i;
console.log(a, b, i);
for (let k = b, m = b*b, i = 0; i < 10; i++) {
console.log(a, b, m, k, i);
}
}
}
expect: {
for (let o = 9, l = 0; l < 20; l += o) {
let c = o++ + l;
console.log(o, c, l);
for (let l = c, e = c * c, f = 0; f < 10; f++)
console.log(o, c, e, l, f);
}
}
expect_stdout: true
}

View File

@@ -85,15 +85,3 @@ unsafe_undefined: {
}
expect_stdout: true
}
runtime_error: {
input: {
const a = 1;
console.log(a++);
}
expect: {
const a = 1;
console.log(a++);
}
expect_stdout: true
}

View File

@@ -1,5 +1,7 @@
mangle_props: {
mangle_props = {}
mangle = {
properties: true,
}
input: {
var obj = {
undefined: 1,
@@ -54,10 +56,12 @@ mangle_props: {
}
numeric_literal: {
mangle = {
properties: true,
}
beautify = {
beautify: true,
}
mangle_props = {}
input: {
var obj = {
0: 0,
@@ -82,7 +86,7 @@ numeric_literal: {
' 42: 2,',
' "42": 3,',
' 37: 4,',
' a: 5,',
' o: 5,',
' 1e42: 6,',
' b: 7,',
' "1e+42": 8',
@@ -92,7 +96,7 @@ numeric_literal: {
'',
'console.log(obj[42], obj["42"]);',
'',
'console.log(obj[37], obj["a"], obj[37], obj["37"]);',
'console.log(obj[37], obj["o"], obj[37], obj["37"]);',
'',
'console.log(obj[1e42], obj["b"], obj["1e+42"]);',
]
@@ -103,3 +107,139 @@ numeric_literal: {
"8 7 8",
]
}
identifier: {
mangle = {
properties: true,
}
input: {
var obj = {
abstract: 1,
boolean: 2,
byte: 3,
char: 4,
class: 5,
double: 6,
enum: 7,
export: 8,
extends: 9,
final: 10,
float: 11,
goto: 12,
implements: 13,
import: 14,
int: 15,
interface: 16,
let: 17,
long: 18,
native: 19,
package: 20,
private: 21,
protected: 22,
public: 23,
short: 24,
static: 25,
super: 26,
synchronized: 27,
this: 28,
throws: 29,
transient: 30,
volatile: 31,
yield: 32,
false: 33,
null: 34,
true: 35,
break: 36,
case: 37,
catch: 38,
const: 39,
continue: 40,
debugger: 41,
default: 42,
delete: 43,
do: 44,
else: 45,
finally: 46,
for: 47,
function: 48,
if: 49,
in: 50,
instanceof: 51,
new: 52,
return: 53,
switch: 54,
throw: 55,
try: 56,
typeof: 57,
var: 58,
void: 59,
while: 60,
with: 61,
};
}
expect: {
var obj = {
e: 1,
t: 2,
n: 3,
a: 4,
i: 5,
o: 6,
r: 7,
l: 8,
s: 9,
c: 10,
f: 11,
u: 12,
d: 13,
h: 14,
p: 15,
b: 16,
v: 17,
w: 18,
y: 19,
g: 20,
m: 21,
k: 22,
x: 23,
j: 24,
z: 25,
q: 26,
A: 27,
B: 28,
C: 29,
D: 30,
F: 31,
G: 32,
false: 33,
null: 34,
true: 35,
H: 36,
I: 37,
J: 38,
K: 39,
L: 40,
M: 41,
N: 42,
O: 43,
P: 44,
Q: 45,
R: 46,
S: 47,
T: 48,
U: 49,
V: 50,
W: 51,
X: 52,
Y: 53,
Z: 54,
$: 55,
_: 56,
ee: 57,
te: 58,
ne: 59,
ae: 60,
ie: 61,
};
}
}

View File

@@ -1,6 +1,7 @@
unary_prefix: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}

View File

@@ -1,34 +0,0 @@
compress_new_function: {
options = {
unsafe: true
}
input: {
new Function("aa, bb", 'return aa;');
}
expect: {
Function("a", "b", "return a");
}
}
compress_new_function_with_destruct: {
options = {
unsafe: true,
ecma: 6
}
beautify = {
ecma: 6
}
input: {
new Function("aa, [bb]", 'return aa;');
new Function("aa, {bb}", 'return aa;');
new Function("[[aa]], [{bb}]", 'return aa;');
}
expect: {
Function("a", "[b]", "return a");
Function("a", "{bb}", "return a");
Function("[[a]]", "[{bb}]", 'return a');
}
}

View File

@@ -38,7 +38,7 @@ mixed: {
}
}
input: {
const ENV = 3;
var ENV = 3;
var FOO = 4;
f(ENV * 10);
--FOO;
@@ -49,7 +49,7 @@ mixed: {
x = DEBUG;
}
expect: {
const ENV = 3;
var ENV = 3;
var FOO = 4;
f(10);
--FOO;
@@ -60,7 +60,7 @@ mixed: {
x = 0;
}
expect_warnings: [
'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,14]',
'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,12]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]',

493
test/compress/issue-281.js Normal file
View File

@@ -0,0 +1,493 @@
collapse_vars_constants: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function f1(x) {
var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2();
return b + (function() { return d - a * e - c; })();
}
function f2(x) {
var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2();
return b + (function() { return -a * e - c; })();
}
}
expect: {
function f1(x) {
var b = x.prop, d = sideeffect1(), e = sideeffect2();
return b + (d - 4 * e - 5);
}
function f2(x) {
var b = x.prop;
sideeffect1();
return b + (-4 * sideeffect2() - 5);
}
}
}
modified: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f5(1));
}
expect: {
function f5(b) {
var a = b;
return b++ + a;
}
console.log(f5(1));
}
expect_stdout: "2"
}
ref_scope: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
console.log(function() {
var a = 1, b = 2, c = 3;
var a = c++, b = b /= a;
return function() {
return a;
}() + b;
}());
}
expect: {
console.log(function() {
var a = 1, b = 2, c = 3;
b = b /= a = c++;
return a + b;
}());
}
expect_stdout: true
}
safe_undefined: {
options = {
conditionals: true,
if_return: true,
inline: true,
unsafe: false,
unused: true,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}(1)());
}
expect: {
var a, c;
console.log(a ? b : c ? d : void 0);
}
expect_stdout: true
}
negate_iife_3: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_3_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_4: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
expect: {
t ? console.log(true) : console.log(false), void console.log("something");
}
}
negate_iife_5: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
negate_iife_5_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
sequences: true,
};
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
issue_1254_negate_iife_true: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '(void console.log("test"))()()();'
}
negate_iife_issue_1073: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
sequences: true,
unused: true,
};
input: {
new (function(a) {
return function Foo() {
this.x = a;
console.log(this);
};
}(7))();
}
expect: {
new function() {
this.x = 7,
console.log(this);
}();
}
expect_stdout: true
}
issue_1288_side_effects: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
unused: true,
};
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w;
x || (x = {});
y;
}
}
inner_var_for_in_1: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
for (b in (function() {
return x(a, b, c);
})()) {
var c = 3, d = 4;
x(a, b, c, d);
}
x(a, b, c, d);
}
}
expect: {
function f() {
var a = 1, b = 2;
for (b in x(1, b, c)) {
var c = 3, d = 4;
x(1, b, c, d);
}
x(1, b, c, d);
}
}
}
issue_1595_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
g(3);
}
}
issue_1758: {
options = {
inline: true,
sequences: true,
side_effects: true,
}
input: {
console.log(function(c) {
var undefined = 42;
return function() {
c--;
c--, c.toString();
return;
}();
}());
}
expect: {
console.log(function(c) {
var undefined = 42;
return c--, c--, void c.toString();
}());
}
expect_stdout: "undefined"
}
wrap_iife: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
}
wrap_iife_in_expression: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
foo = (function () {
return bar();
})();
}
expect_exact: 'foo=bar();'
}
wrap_iife_in_return_call: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return (function() {
console.log('test')
})();
})()();
}
expect_exact: '(void console.log("test"))();'
}
pure_annotation_1: {
options = {
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function() {
console.log("hello");
}());
}
expect_exact: ""
}
pure_annotation_2: {
options = {
collapse_vars: true,
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function(n) {
console.log("hello", n);
}(42));
}
expect_exact: ""
}
drop_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
++a && a.var, a++;
console.log(a);
}
expect_stdout: "3"
}
keep_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: true,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
++a && a.var, a++;
console.log(a);
}
expect_stdout: "3"
}

View File

@@ -1,37 +1,45 @@
dont_reuse_prop: {
mangle_props = {
regex: /asd/
};
mangle = {
properties: {
regex: /asd/,
},
}
input: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.a = 123;
obj.asd = 256;
console.log(obj.a);
}
expect: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.a = 123;
obj.b = 256;
console.log(obj.a);
}
expect_stdout: "123"
}
unmangleable_props_should_always_be_reserved: {
mangle_props = {
regex: /asd/
};
mangle = {
properties: {
regex: /asd/,
},
}
input: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.asd = 256;
obj.a = 123;
console.log(obj.a);
}
expect: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.b = 256;
obj.a = 123;
console.log(obj.a);
}
}
expect_stdout: "123"
}

View File

@@ -1,9 +0,0 @@
template_strings: {
input: {
foo(
`<span>${contents}</span>`,
`<a href="${url}">${text}</a>`
);
}
expect_exact: "foo(`<span>${contents}</span>`,`<a href=\"${url}\">${text}</a>`);"
}

View File

@@ -1,40 +0,0 @@
only_vars: {
options = { join_vars: true };
input: {
let netmaskBinary = '';
for (let i = 0; i < netmaskBits; ++i) {
netmaskBinary += '1';
}
}
expect: {
let netmaskBinary = '';
for (let i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
}
}
issue_1079_with_vars: {
options = { join_vars: true };
input: {
var netmaskBinary = '';
for (var i = 0; i < netmaskBits; ++i) {
netmaskBinary += '1';
}
}
expect: {
for (var netmaskBinary = '', i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
}
}
issue_1079_with_mixed: {
options = { join_vars: true };
input: {
var netmaskBinary = '';
for (let i = 0; i < netmaskBits; ++i) {
netmaskBinary += '1';
}
}
expect: {
var netmaskBinary = ''
for (let i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
}
}

View File

@@ -146,50 +146,6 @@ parse_do_while_without_semicolon: {
}
}
keep_collapse_const_in_own_block_scope: {
options = {
join_vars: true,
loops: true
}
input: {
var i=2;
const c=5;
while(i--)
console.log(i);
console.log(c);
}
expect: {
var i=2;
const c=5;
for(;i--;)
console.log(i);
console.log(c);
}
expect_stdout: true
}
keep_collapse_const_in_own_block_scope_2: {
options = {
join_vars: true,
loops: true
}
input: {
const c=5;
var i=2; // Moves to loop, while it did not in previous test
while(i--)
console.log(i);
console.log(c);
}
expect: {
const c=5;
for(var i=2;i--;)
console.log(i);
console.log(c);
}
expect_stdout: true
}
evaluate: {
options = {
loops: true,
@@ -480,3 +436,17 @@ do_switch: {
} while (false);
}
}
in_parenthesis_1: {
input: {
for (("foo" in {});0;);
}
expect_exact: 'for(("foo"in{});0;);'
}
in_parenthesis_2: {
input: {
for ((function(){ "foo" in {}; });0;);
}
expect_exact: 'for(function(){"foo"in{}};0;);'
}

View File

@@ -22,7 +22,8 @@ negate_iife_1_off: {
negate_iife_2: {
options = {
negate_iife: true
inline: true,
negate_iife: true,
};
input: {
(function(){ return {} })().x = 10;
@@ -32,6 +33,7 @@ negate_iife_2: {
negate_iife_2_side_effects: {
options = {
inline: true,
negate_iife: true,
side_effects: true,
}
@@ -58,6 +60,7 @@ negate_iife_3_evaluate: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
}
input: {
@@ -100,6 +103,7 @@ negate_iife_3_off_evaluate: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: false,
}
input: {

View File

@@ -83,18 +83,18 @@ new_with_unary_prefix: {
expect_exact: 'var bar=(+new Date).toString(32);';
}
new_with_assignement_expression: {
options = {
evaluate: true
}
dot_parenthesis_1: {
input: {
var a;
new x(a = 5 * 2, b = [1, 2, 3], c = {a: "a", b: "b", cd: "c" + "d"});
new y([a, b] = [3, 4]);
}
expect: {
var a;
new x(a = 10, b = [1, 2, 3], c = {a: "a", b: "b", cd: "cd"});
new y([a, b] = [3, 4]);
console.log(new (Math.random().constructor) instanceof Number);
}
expect_exact: "console.log(new(Math.random().constructor)instanceof Number);"
expect_stdout: "true"
}
dot_parenthesis_2: {
input: {
console.log(typeof new function(){Math.random()}.constructor);
}
expect_exact: "console.log(typeof new function(){Math.random()}.constructor);"
expect_stdout: "function"
}

View File

@@ -1,4 +1,4 @@
eval_let: {
eval_let_6: {
input: {
eval("let a;");
console.log();
@@ -10,3 +10,29 @@ eval_let: {
expect_stdout: ""
node_version: ">=6"
}
eval_let_4: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Block-scoped declarations (let, const, function, class) not yet supported outside strict mode")
node_version: "4"
}
eval_let_0: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Unexpected identifier")
node_version: "<=0.12"
}

View File

@@ -1,512 +0,0 @@
getter_setter: {
input: {
var get = "bar";
var a = {
get,
set: "foo",
get bar() {
return this.get;
},
get 5() {
return "five";
},
get 0xf55() {
return "f five five";
},
get "five"() {
return 5;
},
set one(value) {
this._one = value;
},
set 9(value) {
this._nine = value;
},
set 0b1010(value) {
this._ten = value;
},
set "eleven"(value) {
this._eleven = value;
}
};
var b = {
get() { return "gift"; },
set: function(code) { return "Storing code " + code; }
};
var c = {
["get"]: "foo",
["set"]: "bar"
};
var d = {
get: "foo",
set: "bar"
};
}
expect: {
var get = "bar";
var a = {
get,
set: "foo",
get bar() {
return this.get;
},
get 5() {
return "five";
},
get 0xf55() {
return "f five five";
},
get "five"() {
return 5;
},
set one(value) {
this._one = value;
},
set 9(value) {
this._nine = value;
},
set 0b1010(value) {
this._ten = value;
},
set "eleven"(value) {
this._eleven = value;
}
};
var b = {
get() { return "gift"; },
set: function(code) { return "Storing code " + code; }
};
var c = {
["get"]: "foo",
["set"]: "bar"
};
var d = {
get: "foo",
set: "bar"
};
}
}
getter_setter_mangler: {
mangle = {}
beautify = {
ecma: 6
}
input: {
function f(get,set) {
return {
get,
set,
get g(){},
set s(n){},
c,
a:1,
m(){}
};
}
}
expect_exact: "function f(n,t){return{get:n,set:t,get g(){},set s(n){},c,a:1,m(){}}}"
}
use_shorthand_opportunity: {
beautify = {
ecma: 6
}
input: {
var foo = 123;
var obj = {foo: foo};
}
expect_exact: "var foo=123;var obj={foo};"
}
computed_property_names: {
input: {
obj({ ["x" + "x"]: 6 });
}
expect_exact: 'obj({["x"+"x"]:6});'
}
computed_property_names_evaluated_1: {
options = {
evaluate: true
}
input: {
obj({
[1 + 1]: 2,
["x" + "x"]: 6
});
}
expect_exact: 'obj({[2]:2,["xx"]:6});'
}
computed_property_names_evaluated_2: {
options = {
evaluate: true
}
input: {
var foo = something();
var obj = {
[foo]() {
return "blah";
}
}
}
expect_exact: 'var foo=something();var obj={[foo](){return"blah"}};'
}
shorthand_properties: {
mangle = true;
input: {
(function() {
var prop = 1;
const value = {prop};
return value;
})();
}
expect: {
(function() {
var n = 1;
const r = {prop:n};
return r;
})();
}
}
concise_methods: {
beautify = {
ecma: 6
}
input: {
x = {
foo(a, b) {
return x;
}
}
y = {
foo([{a}]) {
return a;
},
bar(){}
}
}
expect_exact: "x={foo(a,b){return x}};y={foo([{a}]){return a},bar(){}};"
}
concise_methods_with_computed_property: {
options = {
evaluate: true
}
input: {
var foo = {
[Symbol.iterator]() {
return { /* stuff */ }
},
[1 + 2]() {
return 3;
},
["1" + "4"]() {
return 14;
}
}
}
expect: {
var foo = {
[Symbol.iterator]() {
return { /* stuff */ }
},
[3]() {
return 3;
},
["14"]() {
return 14;
}
}
}
}
concise_methods_with_computed_property2: {
options = {
evaluate: true
}
input: {
var foo = {
[[1]](){
return "success";
}
};
doSomething(foo[[1]]());
}
expect_exact: 'var foo={[[1]](){return"success"}};doSomething(foo[[1]]());'
}
concise_methods_with_various_property_names: {
input: {
var get = "bar";
var a = {
bar() {
return this.get;
},
5() {
return "five";
},
0xf55() {
return "f five five";
},
"five"() {
return 5;
},
0b1010(value) {
this._ten = value;
}
};
}
expect: {
var get = "bar";
var a = {
bar() {
return this.get;
},
5() {
return "five";
},
0xf55() {
return "f five five";
},
"five"() {
return 5;
},
0b1010(value) {
this._ten = value;
}
};
}
}
concise_methods_and_mangle_props: {
mangle_props = {
regex: /_/
};
input: {
function x() {
obj = {
_foo() { return 1; }
}
}
}
expect: {
function x() {
obj = {
a() { return 1; }
}
}
}
}
concise_generators: {
beautify = {
ecma: 6
}
input: {
x = {
*foo(a, b) {
return x;
}
}
y = {
*foo([{a}]) {
yield a;
},
bar(){}
}
}
expect_exact: "x={*foo(a,b){return x}};y={*foo([{a}]){yield a},bar(){}};"
}
concise_methods_and_keyword_names: {
input: {
x = {
catch() {},
throw() {}
}
}
expect: {
x={catch(){},throw(){}};
}
}
getter_setter_with_computed_value: {
input: {
class C {
get ['a']() {
return 'A';
}
set ['a'](value) {
do_something(a);
}
}
var x = {
get [a.b]() {
return 42;
}
};
class MyArray extends Array {
get [Symbol.species]() {
return Array;
}
}
}
expect_exact: 'class C{get["a"](){return"A"}set["a"](value){do_something(a)}}var x={get[a.b](){return 42}};class MyArray extends Array{get[Symbol.species](){return Array}}'
}
property_with_operator_value: {
input: {
var foo = {
"*": 1,
get "*"() {
return 2;
},
*"*"() {
return 3;
},
"%": 1,
get "%"() {
return 2;
},
*"%"() {
return 3;
}
}
class bar {
get "*"() {
return 1
}
*"*"() {
return 2;
}
get "%"() {
return 1
}
*"%"() {
return 2;
}
}
}
expect_exact: 'var foo={"*":1,get"*"(){return 2},*"*"(){return 3},"%":1,get"%"(){return 2},*"%"(){return 3}};class bar{get"*"(){return 1}*"*"(){return 2}get"%"(){return 1}*"%"(){return 2}}'
}
property_with_unprintable: {
input: {
var foo = {
"\x00\x01": "foo",
get "\x00\x01"() {
return "bar";
},
set "\x00\x01"(foo) {
save(foo);
},
*"\x00\x01"() {
return "foobar";
}
}
class bar {
get "\x00\x01"() {
return "bar"
}
set "\x00\x01"(foo) {
save(foo);
}
*"\x00\x01"() {
return "foobar";
}
}
}
expect_exact: 'var foo={"\\0\x01":"foo",get"\\0\x01"(){return"bar"},set"\\0\x01"(foo){save(foo)},*"\\0\x01"(){return"foobar"}};class bar{get"\\0\x01"(){return"bar"}set"\\0\x01"(foo){save(foo)}*"\\0\x01"(){return"foobar"}}'
}
property_with_unprintable_ascii_only: {
beautify = {
ascii_only: true,
}
input: {
var foo = {
"\x00\x01": "foo",
get "\x00\x01"() {
return "bar";
},
set "\x00\x01"(foo) {
save(foo);
},
*"\x00\x01"() {
return "foobar";
}
}
class bar {
get "\x00\x01"() {
return "bar"
}
set "\x00\x01"(foo) {
save(foo);
}
*"\x00\x01"() {
return "foobar";
}
}
}
expect_exact: 'var foo={"\\0\\x01":"foo",get"\\0\\x01"(){return"bar"},set"\\0\\x01"(foo){save(foo)},*"\\0\\x01"(){return"foobar"}};class bar{get"\\0\\x01"(){return"bar"}set"\\0\\x01"(foo){save(foo)}*"\\0\\x01"(){return"foobar"}}'
}
property_with_unprintable_ascii_only_static: {
beautify = {
ascii_only: true
}
input: {
class foo {
static get "\x02\x03"() {
return "bar";
}
static set "\x04\x05"(foo) {
save(foo);
}
}
}
expect_exact: 'class foo{static get"\\x02\\x03"(){return"bar"}static set"\\x04\\x05"(foo){save(foo)}}'
}
methods_and_getters_with_keep_quoted_props_enabled: {
beautify = {
quote_style: 3,
keep_quoted_props: true,
}
input: {
var obj = {
a() {},
"b"() {},
get c() { return "c"},
get "d"() { return "d"},
set e(a) { doSomething(a); },
set f(a) { doSomething(b); }
}
}
expect_exact: 'var obj={a(){},"b"(){},get c(){return"c"},get"d"(){return"d"},set e(a){doSomething(a)},set f(a){doSomething(b)}};'
}
allow_assignments_to_property_values: {
input: {
var foo = {123: foo = 123} = {foo: "456"};
}
expect: {
var foo = {123: foo = 123} = {foo: "456"};
}
}
variable_as_computed_property: {
input: {
function getLine(header) {
return {
[header]: {}
};
}
}
expect_exact: "function getLine(header){return{[header]:{}}}"
}

View File

@@ -1,180 +0,0 @@
arrow_functions: {
input: {
(a) => b; // 1 args
(a, b) => c; // n args
() => b; // 0 args
(a) => (b) => c; // func returns func returns func
(a) => ((b) => c); // So these parens are dropped
() => (b,c) => d; // func returns func returns func
a=>{return b;}
a => 'lel'; // Dropping the parens
}
expect_exact: "a=>b;(a,b)=>c;()=>b;a=>b=>c;a=>b=>c;()=>(b,c)=>d;a=>b;a=>\"lel\";"
}
arrow_return: {
input: {
() => {};
() => { return; };
a => { return 1; }
a => { return -b }
a => { return b; var b; }
(x, y) => { return x - y; }
}
expect_exact: "()=>{};()=>{};a=>1;a=>-b;a=>{return b;var b};(x,y)=>x-y;"
}
regression_arrow_functions_and_hoist: {
options = {
hoist_vars: true,
hoist_funs: true
}
input: {
(a) => b;
}
expect_exact: "a=>b;"
}
regression_assign_arrow_functions: {
input: {
oninstall = e => false;
oninstall = () => false;
}
expect: {
oninstall=e=>false;
oninstall=()=>false;
}
}
destructuring_arguments_1: {
input: {
(function ( a ) { });
(function ( [ a ] ) { });
(function ( [ a, b ] ) { });
(function ( [ [ a ] ] ) { });
(function ( [ [ a, b ] ] ) { });
(function ( [ a, [ b ] ] ) { });
(function ( [ [ b ], a ] ) { });
(function ( { a } ) { });
(function ( { a, b } ) { });
(function ( [ { a } ] ) { });
(function ( [ { a, b } ] ) { });
(function ( [ a, { b } ] ) { });
(function ( [ { b }, a ] ) { });
( [ a ] ) => { };
( [ a, b ] ) => { };
( { a } ) => { };
( { a, b, c, d, e } ) => { };
( [ a ] ) => b;
( [ a, b ] ) => c;
( { a } ) => b;
( { a, b } ) => c;
}
expect: {
(function(a){});
(function([a]){});
(function([a,b]){});
(function([[a]]){});
(function([[a,b]]){});
(function([a,[b]]){});
(function([[b],a]){});
(function({a}){});
(function({a,b}){});
(function([{a}]){});
(function([{a,b}]){});
(function([a,{b}]){});
(function([{b},a]){});
([a])=>{};
([a,b])=>{};
({a})=>{};
({a,b,c,d,e})=>{};
([a])=>b;
([a,b])=>c;
({a})=>b;
({a,b})=>c;
}
}
destructuring_arguments_2: {
input: {
(function([]) {});
(function({}) {});
(function([,,,,,]) {});
(function ([a, {b: c}]) {});
(function ([...args]) {});
(function ({x,}) {});
class a { *method({ [thrower()]: x } = {}) {}};
(function(a, b, c, d, [{e: [...f]}]){})(1, 2, 3, 4, [{e: [1, 2, 3]}]);
}
expect: {
(function([]) {});
(function({}) {});
(function([,,,,,]) {});
(function ([a, {b: c}]) {});
(function ([...args]) {});
(function ({x,}) {});
class a { *method({ [thrower()]: x } = {}) {}};
(function(a, b, c, d, [{e: [...f]}]){})(1, 2, 3, 4, [{e: [1, 2, 3]}]);
}
}
destructuring_arguments_3: {
beautify = {
ecma: 6
}
input: {
function fn3({x: {y: {z: {} = 42}}}) {}
const { cover = (function () {}), xCover = (0, function() {}) } = {};
let { cover = (function () {}), xCover = (0, function() {}) } = {};
var { cover = (function () {}), xCover = (0, function() {}) } = {};
}
expect_exact: "function fn3({x:{y:{z:{}=42}}}){}const{cover=function(){},xCover=(0,function(){})}={};let{cover=function(){},xCover=(0,function(){})}={};var{cover=function(){},xCover=(0,function(){})}={};"
}
default_arguments: {
beautify = {
ecma: 6
}
input: {
function x(a = 6) { }
function x(a = (6 + 5)) { }
function x({ foo } = {}, [ bar ] = [ 1 ]) { }
}
expect_exact: "function x(a=6){}function x(a=6+5){}function x({foo}={},[bar]=[1]){}"
}
default_values_in_destructurings: {
beautify = {
ecma: 6
}
input: {
function x({a=(4), b}) {}
function x([b, c=(12)]) {}
var { x = (6), y } = x;
var [ x, y = (6) ] = x;
}
expect_exact: "function x({a=4,b}){}function x([b,c=12]){}var{x=6,y}=x;var[x,y=6]=x;"
}
accept_duplicated_parameters_in_non_strict_without_spread_or_default_assignment: {
input: {
function a(b, b){}
function b({c: test, c: test}){}
}
expect: {
function a(b, b){}
function b({c: test, c: test}){}
}
}

View File

@@ -13,8 +13,10 @@ keep_properties: {
dot_properties: {
options = {
properties: true,
}
beautify = {
ie8: true,
};
}
input: {
a["foo"] = "bar";
a["if"] = "if";
@@ -36,8 +38,10 @@ dot_properties: {
dot_properties_es5: {
options = {
properties: true,
}
beautify = {
ie8: false,
};
}
input: {
a["foo"] = "bar";
a["if"] = "if";
@@ -124,9 +128,11 @@ evaluate_string_length: {
}
mangle_properties: {
mangle_props = {
keep_quoted: false
};
mangle = {
properties: {
keep_quoted: false,
},
}
input: {
a["foo"] = "bar";
a.color = "red";
@@ -137,9 +143,9 @@ mangle_properties: {
expect: {
a["a"] = "bar";
a.b = "red";
x = {c: 10};
a.d(x.c, a.a);
a['d']({b: "blue", a: "baz"});
x = {o: 10};
a.r(x.o, a.a);
a['r']({b: "blue", a: "baz"});
}
}
@@ -147,9 +153,10 @@ mangle_unquoted_properties: {
options = {
properties: false
}
mangle_props = {
builtins: true,
keep_quoted: true
mangle = {
properties: {
keep_quoted: true,
},
}
beautify = {
beautify: false,
@@ -178,24 +185,26 @@ mangle_unquoted_properties: {
function f1() {
a["foo"] = "bar";
a.color = "red";
a.b = 2;
x = {"bar": 10, c: 7};
a.c = 9;
a.r = 2;
x = {"bar": 10, b: 7};
a.b = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, c: 7};
a.c = 9;
a.b = 3;
x = {bar: 10, b: 7};
a.b = 9;
a.r = 3;
}
}
}
mangle_debug: {
mangle_props = {
debug: ""
};
mangle = {
properties: {
debug: "",
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -207,9 +216,11 @@ mangle_debug: {
}
mangle_debug_true: {
mangle_props = {
debug: true
};
mangle = {
properties: {
debug: true,
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -221,9 +232,11 @@ mangle_debug_true: {
}
mangle_debug_suffix: {
mangle_props = {
debug: "XYZ"
};
mangle = {
properties: {
debug: "XYZ",
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -238,11 +251,12 @@ mangle_debug_suffix_keep_quoted: {
options = {
properties: false
}
mangle_props = {
builtins: true,
keep_quoted: true,
debug: "XYZ",
reserved: []
mangle = {
properties: {
debug: "XYZ",
keep_quoted: true,
reserved: [],
},
}
beautify = {
beautify: false,
@@ -557,3 +571,236 @@ native_prototype: {
"".indexOf.call(e, "bar");
}
}
accessor_boolean: {
input: {
var a = 1;
var b = {
get true() {
return a;
},
set false(c) {
a = c;
}
};
console.log(b.true, b.false = 2, b.true);
}
expect_exact: 'var a=1;var b={get true(){return a},set false(c){a=c}};console.log(b.true,b.false=2,b.true);'
expect_stdout: "1 2 2"
}
accessor_get_set: {
input: {
var a = 1;
var b = {
get set() {
return a;
},
set get(c) {
a = c;
}
};
console.log(b.set, b.get = 2, b.set);
}
expect_exact: 'var a=1;var b={get set(){return a},set get(c){a=c}};console.log(b.set,b.get=2,b.set);'
expect_stdout: "1 2 2"
}
accessor_null_undefined: {
input: {
var a = 1;
var b = {
get null() {
return a;
},
set undefined(c) {
a = c;
}
};
console.log(b.null, b.undefined = 2, b.null);
}
expect_exact: 'var a=1;var b={get null(){return a},set undefined(c){a=c}};console.log(b.null,b.undefined=2,b.null);'
expect_stdout: "1 2 2"
}
accessor_number: {
input: {
var a = 1;
var b = {
get 42() {
return a;
},
set 42(c) {
a = c;
}
};
console.log(b[42], b[42] = 2, b[42]);
}
expect_exact: 'var a=1;var b={get 42(){return a},set 42(c){a=c}};console.log(b[42],b[42]=2,b[42]);'
expect_stdout: "1 2 2"
}
accessor_string: {
input: {
var a = 1;
var b = {
get "a-b"() {
return a;
},
set "a-b"(c) {
a = c;
}
};
console.log(b["a-b"], b["a-b"] = 2, b["a-b"]);
}
expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);'
expect_stdout: "1 2 2"
}
accessor_this: {
input: {
var a = 1;
var b = {
get this() {
return a;
},
set this(c) {
a = c;
}
};
console.log(b.this, b.this = 2, b.this);
}
expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);'
expect_stdout: "1 2 2"
}
issue_2208_1: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
p: function() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
issue_2208_2: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
a: 42,
p: function() {
return this.a;
}
}.p());
}
expect: {
console.log({
a: 42,
p: function() {
return this.a;
}
}.p());
}
expect_stdout: "42"
}
issue_2208_3: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
a = 42;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 42;
console.log(function() {
return this.a;
}());
}
expect_stdout: "42"
}
issue_2208_4: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
function foo() {}
console.log({
a: foo(),
p: function() {
return 42;
}
}.p());
}
expect: {
function foo() {}
console.log((foo(), function() {
return 42;
})());
}
expect_stdout: "42"
}
issue_2208_5: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
p: "FAIL",
p: function() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
issue_2256: {
options = {
side_effects: true,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
({ "keep": 1 });
g.keep = g.change;
}
expect: {
g.keep = g.g;
}
}

View File

@@ -178,3 +178,424 @@ impure_getter_2: {
}
expect: {}
}
issue_2110_1: {
options = {
cascade: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
return f.g = function() {
return this;
}, f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
issue_2110_2: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
f.g = function() {
return this;
};
return f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
set_immutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_3: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: true
}
set_immutable_4: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_mutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
if (a.foo += "") console.log("PASS");
else console.log("FAIL");
}();
}
expect_stdout: "PASS"
}
set_mutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
(a.foo += "") ? console.log("PASS") : console.log("FAIL");
}();
}
expect_stdout: "PASS"
}
issue_2313_1: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
return console.log(1), {
y: function() {
return console.log(2), {
z: 0
};
}
};
}
x().y().z++,
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: true,
sequences: true,
side_effects: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
return console.log(1), {
y: function() {
return console.log(2), {
z: 0
};
}
};
}
x().y().z++,
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_3: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: "strict",
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_4: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_5: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
x().y++;
x().y;
}
expect: {
x().y++;
x().y;
}
}
issue_2313_6: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
x().y++;
x().y;
}
expect: {
x().y++;
x();
}
}

View File

@@ -2,6 +2,7 @@ reduce_vars: {
options = {
conditionals : true,
evaluate : true,
inline : true,
global_defs : {
C : 0
},
@@ -171,6 +172,7 @@ unsafe_evaluate: {
options = {
evaluate : true,
reduce_vars : true,
side_effects : true,
unsafe : true,
unused : true
}
@@ -1032,6 +1034,7 @@ defun_inline_2: {
defun_inline_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
@@ -1054,6 +1057,7 @@ defun_inline_3: {
defun_call: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1080,6 +1084,7 @@ defun_call: {
defun_redefine: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1112,6 +1117,7 @@ defun_redefine: {
func_inline: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1138,6 +1144,7 @@ func_inline: {
func_modified: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1311,19 +1318,47 @@ iife_func_side_effects: {
unused: true,
}
input: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) {
return b();
function y() {
console.log("FAIL");
}
return y + b();
})(x(), function() {
return y();
}, z());
}
expect: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) {
return function() {
return y();
}();
})(x(), 0, z());
console.log("FAIL");
} + b();
})(x(), function() {
return y();
}, z());
}
expect_stdout: [
"x",
"z",
"y",
]
}
issue_1595_1: {
@@ -1435,6 +1470,7 @@ issue_1670_1: {
reduce_vars: true,
side_effects: true,
switches: true,
typeofs: true,
unused: true,
}
input: {
@@ -1498,6 +1534,7 @@ issue_1670_3: {
reduce_vars: true,
side_effects: true,
switches: true,
typeofs: true,
unused: true,
}
input: {
@@ -1687,6 +1724,7 @@ redefine_arguments_1: {
redefine_arguments_2: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
side_effects: true,
@@ -1723,6 +1761,7 @@ redefine_arguments_2: {
redefine_arguments_3: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
reduce_vars: true,
@@ -1799,6 +1838,7 @@ redefine_farg_1: {
redefine_farg_2: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
side_effects: true,
@@ -1835,6 +1875,7 @@ redefine_farg_2: {
redefine_farg_3: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
reduce_vars: true,
@@ -1858,10 +1899,7 @@ redefine_farg_3: {
console.log(f([]), g([]), h([]));
}
expect: {
console.log(function(a) {
var a;
return typeof a;
}([]), "number", "undefined");
console.log(typeof [], "number", "undefined");
}
expect_stdout: "object number undefined"
}
@@ -2190,10 +2228,11 @@ issue_1814_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
const a = 42;
var a = 42;
!function() {
var b = a;
!function(a) {
@@ -2202,7 +2241,6 @@ issue_1814_1: {
}();
}
expect: {
const a = 42;
!function() {
!function(a) {
console.log(a++, 42);
@@ -2216,10 +2254,11 @@ issue_1814_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
const a = "32";
var a = "32";
!function() {
var b = a + 1;
!function(a) {
@@ -2228,7 +2267,6 @@ issue_1814_2: {
}();
}
expect: {
const a = "32";
!function() {
!function(a) {
console.log(a++, "321");
@@ -2509,7 +2547,7 @@ issue_1922_2: {
expect_stdout: "1"
}
accessor: {
accessor_1: {
options = {
evaluate: true,
reduce_vars: true,
@@ -2537,3 +2575,322 @@ accessor: {
}
expect_stdout: "1 1"
}
accessor_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var A = 1;
var B = {
get c() {
console.log(A);
}
};
B.c;
}
expect: {
({
get c() {
console.log(1);
}
}).c;
}
expect_stdout: "1"
}
for_in_prop: {
options = {
reduce_vars: true,
}
input: {
var a = {
foo: function() {
for (this.b in [1, 2]);
}
};
a.foo();
console.log(a.b);
}
expect: {
var a = {
foo: function() {
for (this.b in [1, 2]);
}
};
a.foo();
console.log(a.b);
}
expect_stdout: "1"
}
obj_var_1: {
options = {
evaluate: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log({
bar: function() {
return 2;
}
}.bar());
}
expect_stdout: "2"
}
obj_var_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(2);
}
expect_stdout: "2"
}
obj_arg_1: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
function f(obj) {
return obj.bar();
}
console.log(f({
bar: function() {
return C + C;
}
}));
}
expect: {
console.log({
bar: function() {
return 2;
}
}.bar());
}
expect_stdout: "2"
}
obj_arg_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
function f(obj) {
return obj.bar();
}
console.log(f({
bar: function() {
return C + C;
}
}));
}
expect: {
console.log(2);
}
expect_stdout: "2"
}
func_arg_1: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 42;
!function(a) {
console.log(a());
}(function() {
return a;
});
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
func_arg_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 42;
!function(a) {
console.log(a());
}(function(a) {
return a;
});
}
expect: {
console.log(void 0);
}
expect_stdout: "undefined"
}
regex_loop: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(x) {
for (var r, s = "acdabcdeabbb"; r = x().exec(s);)
console.log(r[0]);
}
var a = /ab*/g;
f(function() {
return a;
});
}
expect: {
var a = /ab*/g;
(function(x) {
for (var r, s = "acdabcdeabbb"; r = x().exec(s);)
console.log(r[0]);
})(function() {
return a;
});
}
expect_stdout: true
}
obj_for_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { a: 1 };
for (var i = o.a--; i; i--)
console.log(i);
}
expect: {
for (var i = { a: 1 }.a--; i; i--)
console.log(i);
}
expect_stdout: "1"
}
obj_for_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { a: 1 };
for (var i; i = o.a--;)
console.log(i);
}
expect: {
var o = { a: 1 };
for (var i; i = o.a--;)
console.log(i);
}
expect_stdout: "1"
}
array_forin_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = [ 1, 2, 3 ];
for (var b in a)
console.log(b);
}
expect: {
for (var b in [ 1, 2, 3 ])
console.log(b);
}
expect_stdout: [
"0",
"1",
"2",
]
}
array_forin_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = [];
for (var b in [ 1, 2, 3 ])
a.push(b);
console.log(a.length);
}
expect: {
var a = [];
for (var b in [ 1, 2, 3 ])
a.push(b);
console.log(a.length);
}
expect_stdout: "3"
}

14
test/compress/sandbox.js Normal file
View File

@@ -0,0 +1,14 @@
console_log: {
input: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect_stdout: [
"%% %s",
"% %s",
]
}

View File

@@ -255,3 +255,139 @@ issue_1586_2: {
}
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
}
issue_2120_1: {
mangle = {
ie8: false,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (a) {
if (t) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2120_2: {
mangle = {
ie8: true,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2254_1: {
mangle = {
ie8: false,
}
input: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(s) {
try {
throw "FAIL";
} catch (e) {
return s;
}
}
}
expect: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(e) {
try {
throw "FAIL";
} catch (t) {
return e;
}
}
}
expect_stdout: "PASS"
}
issue_2254_2: {
mangle = {
ie8: true,
}
input: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(s) {
try {
throw "FAIL";
} catch (e) {
return s;
}
}
}
expect: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(t) {
try {
throw "FAIL";
} catch (e) {
return t;
}
}
}
expect_stdout: "PASS"
}

View File

@@ -176,6 +176,11 @@ for_sequences: {
// 4
x = (foo in bar);
for (y = 5; false;);
// 5
x = function() {
foo in bar;
};
for (y = 5; false;);
}
expect: {
// 1
@@ -188,6 +193,10 @@ for_sequences: {
// 4
x = (foo in bar);
for (y = 5; false;);
// 5
for (x = function() {
foo in bar;
}, y = 5; false;);
}
}
@@ -711,26 +720,63 @@ issue_27: {
}
}
reassign_const: {
issue_2062: {
options = {
booleans: true,
cascade: true,
conditionals: true,
side_effects: true,
}
input: {
var a = 1;
if ([ a || a++ + a--, a++ + a--, a && a.var ]);
console.log(a);
}
expect: {
var a = 1;
a || (a++, a--), a++, --a && a.var;
console.log(a);
}
expect_stdout: "1"
}
issue_2313: {
options = {
cascade: true,
sequences: true,
side_effects: true,
}
input: {
function f() {
const a = 1;
a++;
return a;
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
if (this.c) console.log(a, b);
}
}
console.log(f());
foo.d();
}
expect: {
function f() {
const a = 1;
return a++, a;
var a = 0, b = 0;
var foo = {
get c() {
return a++, 42;
},
set c(c) {
b++;
},
d: function() {
if (this.c++, this.c) console.log(a, b);
}
}
console.log(f());
foo.d();
}
expect_stdout: true
expect_stdout: "2 1"
}

View File

@@ -1,9 +0,0 @@
super_can_be_parsed: {
input: {
super(1,2);
super.meth();
}
expect_exact: "super(1,2);super.meth();"
}

View File

@@ -1,402 +0,0 @@
template_strings: {
beautify = {
quote_style: 3
}
input: {
``;
`xx\`x`;
`${ foo + 2 }`;
` foo ${ bar + `baz ${ qux }` }`;
}
expect_exact: "``;`xx\\`x`;`${foo+2}`;` foo ${bar+`baz ${qux}`}`;";
}
template_string_prefixes: {
beautify = {
quote_style: 3
}
input: {
String.raw`foo`;
foo `bar`;
}
expect_exact: "String.raw`foo`;foo`bar`;";
}
template_strings_ascii_only: {
beautify = {
ascii_only: true,
quote_style: 3
}
input: {
var foo = `foo
bar
ↂωↂ`;
var bar = `\``;
}
expect_exact: "var foo=`foo\\n bar\\n \\u2182\\u03c9\\u2182`;var bar=`\\``;"
}
template_strings_without_ascii_only: {
beautify = {
quote_style: 3
}
input: {
var foo = `foo
bar
ↂωↂ`
}
expect_exact: "var foo=`foo\\n bar\\n ↂωↂ`;"
}
template_string_with_constant_expression: {
options = {
evaluate: true
}
beautify = {
quote_style: 3
}
input: {
var foo = `${4 + 4} equals 4 + 4`;
}
expect: {
var foo = `8 equals 4 + 4`;
}
}
template_string_with_predefined_constants: {
options = {
evaluate: true
}
beautify = {
quote_style: 3
}
input: {
var foo = `This is ${undefined}`;
var bar = `This is ${NaN}`;
var baz = `This is ${null}`;
var foofoo = `This is ${Infinity}`;
var foobar = "This is ${1/0}";
var foobaz = 'This is ${1/0}';
var barfoo = "This is ${NaN}";
var bazfoo = "This is ${null}";
var bazbaz = `This is ${1/0}`;
var barbar = `This is ${0/0}`;
var barbar = "This is ${0/0}";
var barber = 'This is ${0/0}';
var a = `${4**11}`; // 8 in template vs 7 chars - 4194304
var b = `${4**12}`; // 8 in template vs 8 chars - 16777216
var c = `${4**14}`; // 8 in template vs 9 chars - 268435456
}
expect: {
var foo = `This is undefined`;
var bar = `This is NaN`;
var baz = `This is null`;
var foofoo = `This is ${1/0}`;
var foobar = "This is ${1/0}";
var foobaz = 'This is ${1/0}';
var barfoo = "This is ${NaN}";
var bazfoo = "This is ${null}";
var bazbaz = `This is ${1/0}`;
var barbar = `This is NaN`;
var barbar = "This is ${0/0}";
var barber = 'This is ${0/0}';
var a = `4194304`;
var b = `16777216`; // Potential for further concatentation
var c = `${4**14}`; // Not worth converting
}
}
template_string_evaluate_with_many_segments: {
options = {
evaluate: true
}
beautify = {
quote_style: 3
}
input: {
var foo = `Hello ${guest()}, welcome to ${location()}${"."}`;
var bar = `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `${1}${foobar()}${2}${foobar()}${3}${foobar()}`;
}
expect: {
var foo = `Hello ${guest()}, welcome to ${location()}.`;
var bar = `1234567890`;
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `1${foobar()}2${foobar()}3${foobar()}`;
}
}
template_string_with_many_segments: {
beautify = {
quote_style: 3
}
input: {
var foo = `Hello ${guest()}, welcome to ${location()}${"."}`;
var bar = `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `${1}${foobar()}${2}${foobar()}${3}${foobar()}`;
}
expect: {
var foo = `Hello ${guest()}, welcome to ${location()}${"."}`;
var bar = `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `${1}${foobar()}${2}${foobar()}${3}${foobar()}`;
}
}
template_string_to_normal_string: {
options = {
evaluate: true
}
beautify = {
quote_style: 0
}
input: {
var foo = `This is ${undefined}`;
var bar = "Decimals " + `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
}
expect: {
var foo = `This is undefined`;
var bar = "Decimals 1234567890";
}
}
template_concattenating_string: {
options = {
evaluate: true
}
beautify = {
quote_style: 3 // Yes, keep quotes
}
input: {
var foo = "Have a nice " + `day. ${`day. ` + `day.`}`;
var bar = "Have a nice " + `${day()}`;
}
expect: {
var foo = "Have a nice day. day. day.";
var bar = "Have a nice " + `${day()}`;
}
}
evaluate_nested_templates: {
options = {
evaluate: true
}
beautify = {
quote_style: 0
}
input: {
var baz = `${`${`${`foo`}`}`}`;
}
expect: {
var baz = `foo`;
}
}
enforce_double_quotes: {
beautify = {
quote_style: 1
}
input: {
var foo = `Hello world`;
var bar = `Hello ${'world'}`;
var baz = `Hello ${world()}`;
}
expect: {
var foo = `Hello world`;
var bar = `Hello ${"world"}`;
var baz = `Hello ${world()}`;
}
}
enforce_single_quotes: {
beautify = {
quote_style: 2
}
input: {
var foo = `Hello world`;
var bar = `Hello ${"world"}`;
var baz = `Hello ${world()}`;
}
expect: {
var foo = `Hello world`;
var bar = `Hello ${'world'}`;
var baz = `Hello ${world()}`;
}
}
enforce_double_quotes_and_evaluate: {
beautify = {
quote_style: 1
}
options = {
evaluate: true
}
input: {
var foo = `Hello world`;
var bar = `Hello ${'world'}`;
var baz = `Hello ${world()}`;
}
expect: {
var foo = `Hello world`;
var bar = `Hello world`;
var baz = `Hello ${world()}`;
}
}
enforce_single_quotes_and_evaluate: {
beautify = {
quote_style: 2
}
options = {
evaluate: true
}
input: {
var foo = `Hello world`;
var bar = `Hello ${"world"}`;
var baz = `Hello ${world()}`;
}
expect: {
var foo = `Hello world`;
var bar = `Hello world`;
var baz = `Hello ${world()}`;
}
}
respect_inline_script: {
beautify = {
inline_script: true,
quote_style: 3
}
input: {
var foo = `</script>${content}`;
var bar = `<!--`;
var baz = `-->`;
}
expect_exact: "var foo=`<\\/script>${content}`;var bar=`\\x3c!--`;var baz=`--\\x3e`;";
}
do_not_optimize_tagged_template_1: {
beautify = {
quote_style: 0
}
options = {
evaluate: true
}
input: {
var foo = tag`Shall not be optimized. ${"But " + "this " + "is " + "fine."}`;
var bar = tag`Don't even mind changing my quotes!`;
}
expect_exact:
'var foo=tag`Shall not be optimized. ${"But this is fine."}`;var bar=tag`Don\'t even mind changing my quotes!`;';
}
do_not_optimize_tagged_template_2: {
options = {
evaluate: true
}
input: {
var foo = tag`test` + " something out";
}
expect_exact: 'var foo=tag`test`+" something out";';
}
keep_raw_content_in_tagged_template: {
options = {
evaluate: true
}
input: {
var foo = tag`\u0020\u{20}\u{00020}\x20\40\040 `;
}
expect_exact: "var foo=tag`\\u0020\\u{20}\\u{00020}\\x20\\40\\040 `;";
}
allow_chained_templates: {
input: {
var foo = tag`a``b``c``d`;
}
expect: {
var foo = tag`a``b``c``d`;
}
}
check_escaped_chars: {
input: {
var foo = `\u0020\u{20}\u{00020}\x20\40\040 `;
}
expect_exact: "var foo=` `;";
}
escape_dollar_curly: {
options = {
evaluate: true
}
input: {
console.log(`\$\{ beep \}`)
console.log(`${1-0}\${2-0}$\{3-0}${4-0}`)
console.log(`$${""}{not an expression}`)
}
expect_exact: "console.log(`\\${ beep }`);console.log(`1\\${2-0}\\${3-0}4`);console.log(`\\${not an expression}`);"
}
template_starting_with_newline: {
options = {
dead_code: true
}
input: {
function foo(e) {
return `
this is a template string!`;
};
}
expect_exact: "function foo(e){return`\\nthis is a template string!`}"
}
template_with_newline: {
options = {
dead_code: true
}
input: {
function foo(e) {
return `yep,
this is a template string!`;
};
}
expect_exact: "function foo(e){return`yep,\\nthis is a template string!`}"
}
template_ending_with_newline: {
options = {
dead_code: true
}
input: {
function foo(e) {
return `this is a template string!
`;
};
}
expect_exact: "function foo(e){return`this is a template string!\\n`}"
}
issue_1856: {
beautify = {
ascii_only: false,
}
input: {
console.log(`\\n\\r\\u2028\\u2029\n\r\u2028\u2029`);
}
expect_exact: "console.log(`\\\\n\\\\r\\\\u2028\\\\u2029\\n\\r\\u2028\\u2029`);"
}
issue_1856_ascii_only: {
beautify = {
ascii_only: true,
}
input: {
console.log(`\\n\\r\\u2028\\u2029\n\r\u2028\u2029`);
}
expect_exact: "console.log(`\\\\n\\\\r\\\\u2028\\\\u2029\\n\\r\\u2028\\u2029`);"
}

View File

@@ -1,12 +0,0 @@
catch_destructuring_with_sequence: {
beautify = {
ecma: 6
}
input: {
try {
throw {};
} catch ({xCover = (0, function() {})} ) {
}
}
expect_exact: "try{throw{}}catch({xCover=(0,function(){})}){}"
}

View File

@@ -16,105 +16,42 @@ unicode_parse_variables: {
}
}
unicode_escaped_identifier: {
beautify = {ecma: 6}
input: {
var \u{61} = "foo";
var \u{10000} = "bar";
issue_2242_1: {
beautify = {
ascii_only: false,
}
expect_exact: 'var a="foo";var \u{10000}="bar";';
input: {
console.log("\ud83d", "\ude00", "\ud83d\ude00", "\ud83d@\ude00");
}
expect_exact: 'console.log("\\ud83d","\\ude00","\ud83d\ude00","\\ud83d@\\ude00");'
}
unicode_identifier_ascii_only: {
beautify = {ascii_only: true, ecma: 6}
input: {
var \u{0061} = "hi";
var bar = "h\u{0065}llo";
var \u{10000} = "testing \u{101111}";
issue_2242_2: {
beautify = {
ascii_only: true,
}
expect_exact: 'var a="hi";var bar="hello";var \\u{10000}="testing \\u{101111}";'
input: {
console.log("\ud83d", "\ude00", "\ud83d\ude00", "\ud83d@\ude00");
}
expect_exact: 'console.log("\\ud83d","\\ude00","\\ud83d\\ude00","\\ud83d@\\ude00");'
}
unicode_string_literals: {
beautify = {ascii_only: true, ecma: 6}
input: {
var a = "6 length unicode character: \u{101111}";
issue_2242_3: {
options = {
evaluate: false,
}
expect_exact: 'var a="6 length unicode character: \\u{101111}";'
input: {
console.log("\ud83d" + "\ude00", "\ud83d" + "@" + "\ude00");
}
expect_exact: 'console.log("\\ud83d"+"\\ude00","\\ud83d"+"@"+"\\ude00");'
}
// Don't escape identifiers below es6 (or in this case double escaped in expect_exact)
unicode_output_es5_surrogates: {
beautify = {ascii_only: true, ecma: 5}
input: {
var \u{10000} = "6 length unicode character: \u{10FFFF}";
issue_2242_4: {
options = {
evaluate: true,
}
expect_exact: 'var \u{10000}="6 length unicode character: \\udbff\\udfff";'
input: {
console.log("\ud83d" + "\ude00", "\ud83d" + "@" + "\ude00");
}
expect_exact: 'console.log("\ud83d\ude00","\\ud83d@\\ude00");'
}
check_escape_style: {
beautify = {ascii_only: true, ecma: 6}
input: {
var a = "\x01";
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
var \u0100 = "\u0100";
var \u1000 = "\u1000";
var \u{10000} = "\u{10000}";
var \u{2f800} = "\u{100000}";
}
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \\u{10000}="\\u{10000}";var \\u{2f800}="\\u{100000}";'
}
// Don't escape identifiers below es6, no escaped identifiers support and no \u{} syntax
check_escape_style_es5: {
beautify = {ascii_only: true, ecma: 5}
input: {
var a = "\x01";
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
var \u0100 = "\u0100";
var \u1000 = "\u1000";
var \u{10000} = "\u{10000}"; // Identifier won't be escaped in es 5.1
var \u{2f800} = "\u{100000}"; // Same
}
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \ud800\udc00="\\ud800\\udc00";var \ud87e\udc00="\\udbc0\\udc00";'
}
ID_continue_with_surrogate_pair: {
beautify = {ascii_only: true, ecma: 6}
input: {
var \u{2f800}\u{2f800}\u{2f800}\u{2f800} = "\u{100000}\u{100000}\u{100000}\u{100000}\u{100000}";
}
expect_exact: 'var \\u{2f800}\\u{2f800}\\u{2f800}\\u{2f800}="\\u{100000}\\u{100000}\\u{100000}\\u{100000}\\u{100000}";'
}
escape_non_escaped_identifier: {
beautify = {ascii_only: true, ecma: 6}
input: {
var µþ = "µþ";
}
expect_exact: 'var \\u00b5\\u00fe="\\xb5\\xfe";'
}
non_escape_2_non_escape: {
beautify = {ascii_only: false, ecma: 6}
input: {
var µþ = "µþ";
}
expect_exact: 'var µþ="µþ";'
}
non_escape_2_half_escape1: {
beautify = {ascii_only: false, ascii_identifiers: true, ecma: 6}
input: {
var µþ = "µþ";
}
expect_exact: 'var \\u00b5\\u00fe="µþ";'
}
non_escape_2_half_escape2: {
beautify = {ascii_only: true, ascii_identifiers: false, ecma: 6}
input: {
var µþ = "µþ";
}
expect_exact: 'var µþ="\\xb5\\xfe";'
}

View File

@@ -1,192 +0,0 @@
generators: {
input: {
function* fn() {};
}
expect_exact: "function*fn(){}"
}
generators_yield: {
input: {
function* fn() {
yield remote();
}
}
expect_exact: "function*fn(){yield remote()}"
}
generators_yield_assign: {
input: {
function* fn() {
var x = {};
x.prop = yield 5;
}
}
expect_exact: "function*fn(){var x={};x.prop=yield 5}"
}
generator_yield_undefined: {
input: {
function* fn() {
yield;
}
}
expect_exact: "function*fn(){yield}"
}
yield_optimize_expression: {
options = {
}
input: {
function* f1() { yield; }
function* f2() { yield undefined; }
function* f3() { yield null; }
function* f4() { yield* undefined; }
}
expect: {
function* f1() { yield }
function* f2() { yield; }
function* f3() { yield null; }
function* f4() { yield* void 0; }
}
}
yield_statements: {
input: {
function* fn() {
var a = (yield 1) + (yield 2);
var b = (yield 3) === (yield 4);
var c = (yield 5) << (yield 6);
var d = yield 7;
var e = (yield 8) ? yield 9 : yield 10;
var f = -(yield 11);
}
}
expect_exact: "function*fn(){var a=(yield 1)+(yield 2);var b=(yield 3)===(yield 4);var c=(yield 5)<<(yield 6);var d=yield 7;var e=(yield 8)?yield 9:yield 10;var f=-(yield 11)}"
}
yield_as_identifier_in_function_in_generator: {
input: {
var g = function*() {
function h() {
yield = 1;
}
};
}
expect: {
var g = function*() {
function h() {
yield = 1;
}
};
}
}
yield_before_punctuators: {
input: {
iter = (function*() {
assignmentResult = [ x = yield ] = value;
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} } // Added return to avoid {} drop
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
}
expect: {
iter = (function*() {
assignmentResult = [ x = yield ] = value;
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} }
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
}
}
yield_as_identifier_outside_strict_mode: {
input: {
import yield from "bar";
yield = 123;
while (true) {
yield:
for(;;) break yield;
foo();
}
while (true)
yield: for(;;) continue yield;
function yield(){}
function foo(...yield){}
try { new Error("") } catch (yield) {}
var yield = "foo";
}
expect: {
import yield from "bar";
yield = 123;
while (true) {
yield:
for(;;) break yield;
foo();
}
while (true)
yield: for(;;) continue yield;
function yield(){}
function foo(...yield){}
try { new Error("") } catch (yield) {}
var yield = "foo";
}
}
empty_generator_as_parameter_with_side_effects: {
options = {
side_effects: true
}
input: {
var GeneratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf(function*() {}())
);
evaluate(GeneratorPrototype);
}
expect_exact: "var GeneratorPrototype=Object.getPrototypeOf(Object.getPrototypeOf(function*(){}()));evaluate(GeneratorPrototype);"
}
empty_generator_as_parameter_without_side_effects: {
options = {
side_effects: false
}
input: {
var GeneratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf(function*() {}())
);
evaluate(GeneratorPrototype);
}
expect_exact: "var GeneratorPrototype=Object.getPrototypeOf(Object.getPrototypeOf(function*(){}()));evaluate(GeneratorPrototype);"
}
yield_dot: {
options = {
}
input: {
function* foo(){
yield x.foo;
(yield x).foo;
yield (yield obj.foo()).bar();
}
}
expect_exact: "function*foo(){yield x.foo;(yield x).foo;yield(yield obj.foo()).bar()}"
}
yield_sub: {
options = {
}
input: {
function* foo(){
yield x['foo'];
(yield x)['foo'];
yield (yield obj.foo())['bar']();
}
}
expect_exact: 'function*foo(){yield x["foo"];(yield x)["foo"];yield(yield obj.foo())["bar"]()}'
}

View File

@@ -8,6 +8,7 @@ exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify;
exports["parse"] = parse;
exports["reserve_quoted_keys"] = reserve_quoted_keys;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;

31
test/fetch.js Normal file
View File

@@ -0,0 +1,31 @@
var fs = require("fs");
var path = require("path");
try {
fs.mkdirSync("./tmp");
} catch (e) {
if (e.code != "EEXIST") throw e;
}
function local(url) {
return path.join("./tmp", encodeURIComponent(url));
}
function read(url) {
return fs.createReadStream(local(url));
}
module.exports = function(url, callback) {
var result = read(url);
result.on("error", function(e) {
if (e.code != "ENOENT") return callback(e);
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
if (res.statusCode !== 200) return callback(res);
res.pipe(fs.createWriteStream(local(url)).on("close", function() {
callback(null, read(url));
}));
});
}).on("open", function() {
callback(null, result);
});
};

View File

@@ -1,8 +0,0 @@
function f() {
const a;
}
function g() {
"use strict";
const a;
}

View File

@@ -0,0 +1,4 @@
var a, b = [1, 2];
for (1, 2, a in b) {
console.log(a, b[a]);
}

View File

@@ -0,0 +1,4 @@
var c = [1, 2];
for (var a, b in c) {
console.log(a, c[a]);
}

View File

@@ -0,0 +1 @@
console.log(x);

View File

@@ -0,0 +1 @@
{"version": 3,"sources": ["index.js"],"mappings": ";"}

View File

@@ -0,0 +1,10 @@
function foo() {
return function() {
console.log("PASS");
};
}
(function() {
var f = foo();
f();
})();

View File

@@ -0,0 +1,5 @@
function test(callback) {
'aaaaaaaaaaaaaaaa';
callback(err, data);
callback(err, data);
}

View File

@@ -0,0 +1,5 @@
function test(a){
"aaaaaaaaaaaaaaaa"
;a(err,data),a(err,data)
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQyxJQUFLQyJ9

View File

@@ -1,2 +1,2 @@
new function(){console.log(3)};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxHQUFyQyxZQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19

View File

@@ -3,7 +3,7 @@
"use strict";
var site = "http://browserbench.org/JetStream/";
var site = "http://browserbench.org/JetStream";
if (typeof phantom == "undefined") {
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
@@ -11,45 +11,69 @@ if (typeof phantom == "undefined") {
stream._handle.setBlocking(true);
});
var args = process.argv.slice(2);
var debug = args.indexOf("--debug");
if (debug >= 0) {
args.splice(debug, 1);
debug = true;
} else {
debug = false;
}
if (!args.length) {
args.push("-mc");
args.push("-mcb", "beautify=false,webkit");
}
args.push("--stats");
args.push("--timings");
var child_process = require("child_process");
try {
require("phantomjs-prebuilt");
} catch(e) {
child_process.execSync("npm install phantomjs-prebuilt@2.1.14");
}
var fetch = require("./fetch");
var http = require("http");
var server = http.createServer(function(request, response) {
request.resume();
var url = decodeURIComponent(request.url.slice(1));
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.indexOf(site) == 0 ? url.slice(site.length) : url, args.join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});
uglifyjs.stderr.on("data", function(data) {
stderr += data;
}).setEncoding("utf8");
uglifyjs.stdout.pipe(response);
http.get(url, function(res) {
res.pipe(uglifyjs.stdin);
});
}).listen().on("listening", function() {
var phantomjs = require("phantomjs-prebuilt");
var program = phantomjs.exec(process.argv[1], server.address().port);
program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr);
program.on("exit", function(code) {
server.close();
if (code) throw new Error("JetStream failed!");
console.log("JetStream completed successfully.");
var url = site + request.url;
fetch(url, function(err, res) {
if (err) throw err;
response.writeHead(200, {
"Content-Type": {
css: "text/css",
js: "application/javascript",
png: "image/png"
}[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8"
});
if (/\.js$/.test(url)) {
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.slice(site.length + 1), args.join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});
uglifyjs.stderr.on("data", function(data) {
stderr += data;
}).setEncoding("utf8");
uglifyjs.stdout.pipe(response);
res.pipe(uglifyjs.stdin);
} else {
res.pipe(response);
}
});
}).listen();
server.on("listening", function() {
var port = server.address().port;
if (debug) {
console.log("http://localhost:" + port + "/");
} else {
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) {
if (error) throw error;
var program = require("phantomjs-prebuilt").exec(process.argv[1], port);
program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr);
program.on("exit", function(code) {
server.close();
if (code) throw new Error("JetStream failed!");
console.log("JetStream completed successfully.");
process.exit(0);
});
});
}
});
server.timeout = 0;
} else {
@@ -63,10 +87,6 @@ if (typeof phantom == "undefined") {
phantom.exit(1);
};
var url = "http://localhost:" + require("system").args[1] + "/";
page.onResourceRequested = function(requestData, networkRequest) {
if (/\.js$/.test(requestData.url))
networkRequest.changeUrl(url + encodeURIComponent(requestData.url));
}
page.onConsoleMessage = function(msg) {
if (/Error:/i.test(msg)) {
console.error(msg);
@@ -77,8 +97,8 @@ if (typeof phantom == "undefined") {
phantom.exit();
}
};
page.open(site, function(status) {
if (status != "success") phantomjs.exit(1);
page.open(url, function(status) {
if (status != "success") phantom.exit(1);
page.evaluate(function() {
JetStream.switchToQuick();
JetStream.start();

View File

@@ -16,8 +16,8 @@ describe("Accessor tokens", function() {
assert.equal(node.start.pos, 12);
assert.equal(node.end.endpos, 46);
assert(node.key instanceof UglifyJS.AST_SymbolMethod);
assert.equal(node.key.start.pos, 12);
assert(node.key instanceof UglifyJS.AST_SymbolAccessor);
assert.equal(node.key.start.pos, 16);
assert.equal(node.key.end.endpos, 22);
assert(node.value instanceof UglifyJS.AST_Accessor);

View File

@@ -27,253 +27,4 @@ describe("arguments", function() {
assert.strictEqual(ast.body[0].body[0].uses_arguments, true);
assert.strictEqual(ast.body[0].body[0].body[0].uses_arguments, false);
});
it("Should parse a function containing default assignment correctly", function() {
var ast = UglifyJS.parse("function foo(a = 123) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].operator, "=");
assert(ast.body[0].argnames[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo(a = a) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].operator, "=");
assert(ast.body[0].argnames[0].right instanceof UglifyJS.AST_SymbolRef);
});
it("Should parse a function containing default assignments in destructuring correctly", function() {
var ast = UglifyJS.parse("function foo([a = 123]) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].operator, "=");
assert(ast.body[0].argnames[0].names[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a = 123}) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
// Property a of first argument
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a: a = 123}) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
// Content destructuring of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
// Property a of first argument
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[0].value.right instanceof UglifyJS.AST_Number);
});
it("Should parse a function containing default assignments in complex destructuring correctly", function() {
var ast = UglifyJS.parse("function foo([a, [b = 123]]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[1].is_array, true);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].argnames[0].names[1].names[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[1].names[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].operator, "=");
assert(ast.body[0].argnames[0].names[1].names[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo([a, {b: c = 123}]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[1].is_array, false);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].argnames[0].names[1].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].key, "b");
assert(ast.body[0].argnames[0].names[1].names[0].value instanceof UglifyJS.AST_DefaultAssign);
// Property b of second argument
assert(ast.body[0].argnames[0].names[1].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[1].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[1].names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a, b: {b = 123}}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[1].key, "b");
assert(ast.body[0].argnames[0].names[1].value instanceof UglifyJS.AST_Destructuring);
// Check content of nested destructuring in first parameter
var content = ast.body[0].argnames[0].names[1].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(content.names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a: {b = 123}}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_Destructuring);
// Check content of nested destructuring
content = ast.body[0].argnames[0].names[0].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(content.names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof UglifyJS.AST_Number);
});
it("Should parse spread correctly", function() {
var ast = UglifyJS.parse("function foo(a, b, ...c){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 3);
// Check parameters
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[2] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[2].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo([a, b, ...c]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
// Check content first parameter
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[2].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo([a, b, [c, ...d]]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
// Check content outer destructuring array
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[2].is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].argnames[0].names[2].names.length, 2);
assert(ast.body[0].argnames[0].names[2].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2].names[1] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[2].names[1].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo({a: [b, ...c]}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
// Check outer destructuring object
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].argnames[0].names[0].value.names.length, 2);
assert(ast.body[0].argnames[0].names[0].value.names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[0].value.names[1] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[0].value.names[1].expression instanceof UglifyJS.AST_SymbolFunarg);
});
});
});

View File

@@ -1,402 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Arrow functions", function() {
it("Should not accept spread tokens on non-last parameters or without arguments parentheses", function() {
var tests = [
"var a = ...a => {return a.join()}",
"var b = (a, ...b, c) => { return a + b.join() + c}",
"var c = (...a, b) => a.join()"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: expand (...)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept holes in object binding patterns, while still allowing a trailing elision", function() {
var tests = [
"f = ({, , ...x} = [1, 2]) => {};"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: punc (,)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept newlines before arrow token", function() {
var tests = [
"f = foo\n=> 'foo';",
"f = (foo, bar)\n=> 'foo';",
"f = ()\n=> 'foo';",
"foo((bar)\n=>'baz';);"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected newline before arrow (=>)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept arrow functions in the middle or end of an expression", function() {
var tests = [
"typeof x => 0",
"0 + x => 0"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: arrow (=>)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should parse a function containing default assignment correctly", function() {
var ast = uglify.parse("var a = (a = 123) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = (a = a) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].right instanceof uglify.AST_SymbolRef);
});
it("Should parse a function containing default assignments in destructuring correctly", function() {
var ast = uglify.parse("var a = ([a = 123]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a = 123}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
// First object element in first argument
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a: a = 123}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
// Content destructuring of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectProperty);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.right instanceof uglify.AST_Number);
});
it("Should parse a function containing default assignments in complex destructuring correctly", function() {
var ast = uglify.parse("var a = ([a, [b = 123]]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].is_array, true);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ([a, {b: c = 123}]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].is_array, false);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].key, "b");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a, b: {b = 123}}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// First argument, property 1
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_SymbolFunarg);
// First argument, property 2
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].key, "b");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].value instanceof uglify.AST_Destructuring);
// Check content of nested destructuring
var content = ast.body[0].definitions[0].value.argnames[0].names[1].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof uglify.AST_ObjectKeyVal);
// Content of first property in nested destructuring
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof uglify.AST_DefaultAssign);
assert(content.names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a: {b = 123}}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_Destructuring);
// Check content of nested destructuring
content = ast.body[0].definitions[0].value.argnames[0].names[0].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof uglify.AST_ObjectKeyVal);
// Check first property of nested destructuring
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof uglify.AST_DefaultAssign);
assert(content.names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof uglify.AST_Number);
});
it("Should parse spread correctly", function() {
var ast = uglify.parse("var a = (a, b, ...c) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 3);
// Check parameters
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[2] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[2].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ([a, b, ...c]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
// Check content first parameter
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ([a, b, [c, ...d]]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
// Check content outer destructuring array
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[2].is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[2].names.length, 2);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[1] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[1].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ({a: [b, ...c]}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
// Check outer destructuring object
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.names.length, 2);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[1] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[1].expression instanceof uglify.AST_SymbolFunarg);
});
it("Should handle arrow function with bind", function() {
function minify(code) {
return uglify.minify(code, {
mangle: false
}).code;
}
assert.strictEqual(
minify(minify("test(((index) => { console.log(this, index); }).bind(this, 1));")),
"test((index=>{console.log(this,index)}).bind(this,1));"
);
assert.strictEqual(
minify(minify('test(((index) => { console.log(this, index); })["bind"](this, 1));')),
"test((index=>{console.log(this,index)}).bind(this,1));"
);
});
});

View File

@@ -1,34 +0,0 @@
var UglifyJS = require("../../");
var assert = require("assert");
describe("builtins", function() {
it ("Should not mangle builtins", function() {
var test = "function foo(something){\n" +
" return [Object,Array,Function,Number,String,Boolean,Error,Math,Date,RegExp,Symbol,Map,Promise,Proxy,Reflect,Set,WeakMap,WeakSet,Float32Array,something];\n" +
"};";
var result = UglifyJS.minify(test, {parse: {bare_returns: true}}).code;
assert.strictEqual(result.indexOf("something"), -1);
assert.notEqual(result.indexOf("Object"), -1);
assert.notEqual(result.indexOf("Array"), -1);
assert.notEqual(result.indexOf("Function"), -1);
assert.notEqual(result.indexOf("Number"), -1);
assert.notEqual(result.indexOf("String"), -1);
assert.notEqual(result.indexOf("Boolean"), -1);
assert.notEqual(result.indexOf("Error"), -1);
assert.notEqual(result.indexOf("Math"), -1);
assert.notEqual(result.indexOf("Date"), -1);
assert.notEqual(result.indexOf("RegExp"), -1);
assert.notEqual(result.indexOf("Symbol"), -1);
assert.notEqual(result.indexOf("Promise"), -1);
assert.notEqual(result.indexOf("Proxy"), -1);
assert.notEqual(result.indexOf("Reflect"), -1);
assert.notEqual(result.indexOf("Set"), -1);
assert.notEqual(result.indexOf("WeakMap"), -1);
assert.notEqual(result.indexOf("WeakSet"), -1);
assert.notEqual(result.indexOf("Map"), -1);
assert.notEqual(result.indexOf("Float32Array"), -1);
});
});

View File

@@ -1,57 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Class", function() {
it("Should not accept spread on non-last parameters in methods", function() {
var tests = [
"class foo { bar(...a, b) { return a.join(b) } }",
"class foo { bar(a, b, ...c, d) { return c.join(a + b) + d } }",
"class foo { *bar(...a, b) { return a.join(b) } }",
"class foo { *bar(a, b, ...c, d) { return c.join(a + b) + d } }"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
/^Unexpected token: /.test(e.message);
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should return the correct token for class methods", function() {
var tests = [
{
code: "class foo{static test(){}}",
token_value_start: "static",
token_value_end: "}"
},
{
code: "class bar{*procedural(){}}",
token_value_start: "*",
token_value_end: "}"
},
{
code: "class foobar{aMethod(){}}",
token_value_start: "aMethod",
token_value_end: "}"
},
{
code: "class foobaz{get something(){}}",
token_value_start: "get",
token_value_end: "}"
}
];
for (var i = 0; i < tests.length; i++) {
var ast = uglify.parse(tests[i].code);
assert.strictEqual(ast.body[0].properties[0].start.value, tests[i].token_value_start);
assert.strictEqual(ast.body[0].properties[0].end.value, tests[i].token_value_end);
}
});
});

View File

@@ -9,7 +9,7 @@ function read(path) {
describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) {
this.timeout(15000);
this.timeout(30000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
@@ -19,7 +19,9 @@ describe("bin/uglifyjs", function () {
eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object');
assert.strictEqual(WrappedUglifyJS.minify("foo([true,,2+3]);").code, "foo([!0,,5]);");
var result = WrappedUglifyJS.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done();
});
@@ -61,7 +63,7 @@ describe("bin/uglifyjs", function () {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n");
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==\n");
done();
});
});
@@ -75,6 +77,23 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("should not consider source map file content as source map file name (issue #2082)", function (done) {
var command = [
uglifyjscmd,
"test/input/issue-2082/sample.js",
"--source-map", "content=test/input/issue-2082/sample.js.map",
"--source-map", "url=inline",
].join(" ");
exec(command, function (err, stdout, stderr) {
if (err) throw err;
var stderrLines = stderr.split('\n');
assert.strictEqual(stderrLines[0], 'INFO: Using input source map: test/input/issue-2082/sample.js.map');
assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}');
done();
});
});
it("Should work with --keep-fnames (mangle only)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
@@ -173,7 +192,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [
"var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==",
"",
].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n");
@@ -379,21 +398,6 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should throw syntax error (const a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/const.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/const.js:7,11",
" const a;",
" ^",
"ERROR: Missing initializer in const declaration"
].join("\n"));
done();
});
});
it("Should throw syntax error (delete x)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/delete.js';
@@ -464,7 +468,7 @@ describe("bin/uglifyjs", function () {
"Parse error at test/input/invalid/try.js:7,18",
" try {} catch (eval) {}",
" ^",
"ERROR: Unexpected eval identifier as parameter inside strict mode"
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
@@ -514,6 +518,36 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should throw syntax error (for-in init)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-in_1.js:2,5",
"for (1, 2, a in b) {",
" ^",
"ERROR: Invalid left-hand side in for..in loop"
].join("\n"));
done();
});
});
it("Should throw syntax error (for-in var)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-in_2.js:2,5",
"for (var a, b in c) {",
" ^",
"ERROR: Only one variable declaration allowed in for..in loop"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) {
var command = [
uglifyjscmd,
@@ -527,7 +561,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [
'"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiWUFBQSxJQUFJQSxLQUFNLFFBQU5BLEtBQU1DLEdBQUEsTUFBSyxPQUFTQSxFQUN4QkMsU0FBUUMsSUFBSUgsSUFBSSJ9",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiYUFBQSxJQUFJQSxJQUFNLFNBQU5BLElBQU1DLEdBQUEsTUFBSyxPQUFTQSxHQUN4QkMsUUFBUUMsSUFBSUgsSUFBSSJ9",
""
].join("\n"));
done();
@@ -539,6 +573,25 @@ describe("bin/uglifyjs", function () {
return JSON.stringify(map).replace(/"/g, '\\"');
}
});
it("Should include function calls in source map", function(done) {
var command = [
uglifyjscmd,
"test/input/issue-2310/input.js",
"-c",
"--source-map", "url=inline",
].join(" ");
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, [
'function foo(){return function(){console.log("PASS")}}foo()();',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMjMxMC9pbnB1dC5qcyJdLCJuYW1lcyI6WyJmb28iLCJjb25zb2xlIiwibG9nIiwiZiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsTUFDTCxPQUFPLFdBQ0hDLFFBQVFDLElBQUksU0FLUkYsS0FDUkcifQ==",
""
].join("\n"));
done();
});
});
it("Should dump AST as JSON", function(done) {
var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast";
exec(command, function (err, stdout) {
@@ -550,4 +603,51 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should print supported options on invalid option syntax", function(done) {
var command = uglifyjscmd + " test/input/comments/filter.js -b ascii-only";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
it("Should work with --mangle reserved=[]", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=[callback]";
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(callback){"aaaaaaaaaaaaaaaa";callback(err,data);callback(err,data)}\n');
done();
});
});
it("Should work with --mangle reserved=false", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=false";
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(a){"aaaaaaaaaaaaaaaa";a(err,data);a(err,data)}\n');
done();
});
});
it("Should fail with --mangle-props reserved=[in]", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js --mangle-props reserved=[in]";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `reserved=\[in]` is not a supported option/.test(stderr), stderr);
done();
});
});
it("Should fail with --define a-b", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js --define a-b";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr, "Error parsing arguments for 'define': a-b\n");
done();
});
});
});

View File

@@ -31,8 +31,7 @@ describe("Comment", function() {
"/*Some comment 2\r\n\r\n\r\n*/\r\n>\n\n\n\n\n\n",
"/*Some comment 3\r\r\r*/\r>\n\n\n\n\n\n",
"/*Some comment 4\u2028\u2028\u2028*/\u2028>\n\n\n\n\n\n",
"/*Some comment 5\u2029\u2029\u2029*/\u2029>\n\n\n\n\n\n",
"/*Some comment 6\udbff\udfff\udbff\udfff\n\n\n*/\n>\n\n\n\n\n"
"/*Some comment 5\u2029\u2029\u2029*/\u2029>\n\n\n\n\n\n"
];
var fail = function(e) {

View File

@@ -1,138 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Destructuring", function() {
it("Should generate similar trees for destructuring in left hand side expressions, definitions, functions and arrow functions", function() {
var patterns = [
"[]",
"{}",
"[a, b, c]",
"{a: b, c: d}",
"{a}",
"{a, b}",
"{a: {}}",
"{a: []}",
"[{}]",
"[[]]",
"{a: {b}}",
// Can't do `a = 123` with lhs expression, so only test in destructuring
"[foo = bar]",
"{a = 123}",
"[{foo: abc = 123}]",
"{foo: [abc = 123]}",
"[...foo]",
"[...{}]",
"[...[]]"
// Can't do `...` because that is an invalid lhs expression, spread in array destructuring should be fine though
];
var types = [
{
name: "lhs",
symbolType: uglify.AST_SymbolRef,
tree: function (ast) {
return ast.body[0].body.left;
},
generate: function (code) {
return "(" + code + " = a)";
}
},
{
name: "var",
symbolType: uglify.AST_SymbolVar,
tree: function (ast) {
return ast.body[0].definitions[0].name;
},
generate: function (code) {
return "var " + code + " = a";
}
},
{
name: "function",
symbolType: uglify.AST_SymbolFunarg,
tree: function (ast) {
return ast.body[0].argnames[0];
},
generate: function (code) {
return "function a(" + code + ") {}";
}
},
{
name: "arrow",
symbolType: uglify.AST_SymbolFunarg,
tree: function (ast) {
return ast.body[0].definitions[0].value.argnames[0];
},
generate: function (code) {
return "var a = (" + code + ") => {}";
}
}
];
var walker = function(type, ref, code, result) {
var w = new uglify.TreeWalker(function(node) {
if (w.parent() instanceof uglify.AST_DefaultAssign &&
w.parent().right === node
) {
return true; // Don't check the content of the default assignment
} else if (node instanceof uglify.AST_Symbol) {
assert(node instanceof type.symbolType,
node.TYPE + " while " + type.symbolType.TYPE + " expected at pos " +
node.start.pos + " in `" + code + "` (" + ref + ")"
);
result.push([
new uglify.AST_Symbol({
start: node.start,
name: node.name,
end: node.end
}),
w.parent()
]);
return;
}
result.push([node, w.parent()]);
});
return w;
};
var getNodeType = function(node) {
return node[0].TYPE + (node[1] ? " " + node[1].TYPE : "");
}
for (var i = 0; i < patterns.length; i++) {
var results = [];
for (var j = 0; j < types.length; j++) {
var code = types[j].generate(patterns[i])
var ast = types[j].tree(
uglify.parse(code)
);
results.push([]);
ast.walk(walker(
types[j],
"`" + patterns[i] + "` on " + types[j].name,
code,
results[j]
));
if (j > 0) {
assert.deepEqual(
results[0].map(getNodeType),
results[j].map(getNodeType),
"AST disagree on " + patterns[i] + " with " + types[j].name
);
}
}
}
});
});

View File

@@ -62,10 +62,10 @@ describe("Directives", function() {
it("Should know which strings are directive and which ones are not", function() {
var test_directive = function(tokenizer, test) {
test.directives.map(function(directive) {
assert.strictEqual(tokenizer.has_directive(directive), true, "Didn't found directive `" + directive + "` at the end of `" + test.input + '`');
assert.strictEqual(tokenizer.has_directive(directive), true, directive + " in " + test.input);
});
test.non_directives.map(function(fake_directive) {
assert.strictEqual(tokenizer.has_directive(fake_directive), false, "Unexpectedly found directive `" + fake_directive + "` at the end of `" + test.input + '`');
assert.strictEqual(tokenizer.has_directive(fake_directive), false, fake_directive + " in " + test.input);
});
}
@@ -156,16 +156,6 @@ describe("Directives", function() {
input: '"use strict";try{"use asm";',
directives: ["use strict"],
non_directives: ["use\nstrict", "use \nstrict", "use asm"]
},
{
input: 'class foo {',
directives: ["use strict"],
non_directives: ["use\nstrict", "use asm"]
},
{
input: 'class foo {}',
directives: [],
non_directives: ["use strict", "use asm", "use\nstrict"]
}
];
@@ -388,44 +378,4 @@ describe("Directives", function() {
);
}
});
it("Should be detect implicit usages of strict mode from tree walker", function() {
var tests = [
{
input: 'class foo {bar(){_check_}}',
directives: ["use strict"],
non_directives: ["use bar"]
},
{
input: 'class foo {bar(){}}_check_',
directives: [],
non_directives: ["use strict", "use bar"]
}
];
var i = 0;
var checked;
var checkWalker = new uglify.TreeWalker(function(node, descend) {
if (node instanceof uglify.AST_Symbol && node.name === "_check_") {
checked = true;
for (var j = 0; j < tests[i].directives.length; j++) {
assert.ok(checkWalker.has_directive(tests[i].directives[j]),
"Did not found directive '" + tests[i].directives[j] + "' in test " + tests[i].input)
}
for (var k = 0; k < tests[i].non_directives.length; k++) {
assert.equal(checkWalker.has_directive(tests[i].non_directives[k]), undefined,
"Found directive '" + tests[i].non_directives[k] + "' in test " + tests[i].input)
}
}
});
for (; i < tests.length; i++) {
// Do tests - iterate the ast in each test - check only when _check_ occurs - fail when no _check_ has been found
checked = false;
var ast = uglify.parse(tests[i].input);
ast.walk(checkWalker);
if (!checked) {
throw "No _check_ symbol found in " + tests[i].input;
}
}
});
});

View File

@@ -1,35 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("EOF", function() {
it("Should test code for at least throwing syntax error when incomplete", function() {
var error = function(e) {
return e instanceof uglify.JS_Parse_Error;
}
var parse = function(test) {
return function() {
uglify.parse(test);
}
}
// Chops off 1 char at a time until limit or start of string is reached
// The passed code must still be valid when unchopped
var test_eol = function(input, chopLimit) {
if (chopLimit === undefined) {
chopLimit = input.length - 1;
}
assert.doesNotThrow(parse(input), "Expected valid code for \n" + input);
for (var i = input.length - 1; chopLimit > 0; chopLimit--, i--) {
var code = input.substr(0, i);
assert.throws(parse(code), error, code);
}
}
test_eol("var \\u1234", 7); // Incomplete identifier
test_eol("'Incomplete string'");
test_eol("/Unterminated regex/");
test_eol("` Unterminated template string`");
test_eol("/* Unfinishing multiline comment */");
});
});

View File

@@ -1,40 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Export", function() {
it ("Should parse export directives", function() {
var inputs = [
['export * from "a.js"', ['*'], "a.js"],
['export {A} from "a.js"', ['A'], "a.js"],
['export {A as X} from "a.js"', ['X'], "a.js"],
['export {A as Foo, B} from "a.js"', ['Foo', 'B'], "a.js"],
['export {A, B} from "a.js"', ['A', 'B'], "a.js"],
];
var test = function(code) {
return uglify.parse(code);
};
var extractNames = function(symbols) {
var ret = [];
for (var i = 0; i < symbols.length; i++) {
ret.push(symbols[i].name.name)
}
return ret;
};
for (var i = 0; i < inputs.length; i++) {
var ast = test(inputs[i][0]);
var names = inputs[i][1];
var filename = inputs[i][2];
assert(ast instanceof uglify.AST_Toplevel);
assert.equal(ast.body.length, 1);
var st = ast.body[0];
assert(st instanceof uglify.AST_Export);
var actualNames = extractNames(st.exported_names);
assert.deepEqual(actualNames, names);
assert.equal(st.module_name.value, filename)
}
})
});

View File

@@ -1,32 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Expression", function() {
it("Should not allow the first exponentiation operator to be prefixed with an unary operator", function() {
var tests = [
"+5 ** 3",
"-5 ** 3",
"~5 ** 3",
"!5 ** 3",
"void 5 ** 3",
"typeof 5 ** 3",
"delete 5 ** 3",
"var a = -(5) ** 3;"
];
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error &&
/^Unexpected token: operator \((?:[!+~-]|void|typeof|delete)\)/.test(e.message);
}
var exec = function(test) {
return function() {
uglify.parse(test);
}
}
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail, tests[i]);
}
});
});

View File

@@ -1,252 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Function", function() {
it ("Should parse binding patterns correctly", function() {
// Function argument nodes are correct
function get_args(args) {
return args.map(function (arg) {
return [arg.TYPE, arg.name];
});
}
// Destructurings as arguments
var destr_fun1 = uglify.parse('(function ({a, b}) {})').body[0].body;
var destr_fun2 = uglify.parse('(function ([a, [b]]) {})').body[0].body;
var destr_fun3 = uglify.parse('({a, b}) => null').body[0].body;
var destr_fun4 = uglify.parse('([a, [b]]) => null').body[0].body;
assert.equal(destr_fun1.argnames.length, 1);
assert.equal(destr_fun2.argnames.length, 1);
assert.equal(destr_fun3.argnames.length, 1);
assert.equal(destr_fun4.argnames.length, 1);
var destruct1 = destr_fun1.argnames[0];
var destruct2 = destr_fun2.argnames[0];
var destruct3 = destr_fun3.argnames[0];
var destruct4 = destr_fun4.argnames[0];
assert(destruct1 instanceof uglify.AST_Destructuring);
assert(destruct2 instanceof uglify.AST_Destructuring);
assert(destruct3 instanceof uglify.AST_Destructuring);
assert(destruct4 instanceof uglify.AST_Destructuring);
assert(destruct2.names[1] instanceof uglify.AST_Destructuring);
assert(destruct4.names[1] instanceof uglify.AST_Destructuring);
assert.equal(destruct1.start.value, '{');
assert.equal(destruct1.end.value, '}');
assert.equal(destruct2.start.value, '[');
assert.equal(destruct2.end.value, ']');
assert.equal(destruct3.start.value, '{');
assert.equal(destruct3.end.value, '}');
assert.equal(destruct4.start.value, '[');
assert.equal(destruct4.end.value, ']');
assert.equal(destruct1.is_array, false);
assert.equal(destruct2.is_array, true);
assert.equal(destruct3.is_array, false);
assert.equal(destruct4.is_array, true);
// destruct 1
assert.deepEqual(
[
destruct1.names[0].TYPE,
destruct1.names[0].key,
destruct1.names[0].value.name
],
['ObjectKeyVal', 'a', 'a']
);
assert.deepEqual(
[
destruct1.names[1].TYPE,
destruct1.names[1].key,
destruct1.names[1].value.name
],
['ObjectKeyVal', 'b', 'b']
);
// destruct 2
assert.deepEqual(
[
destruct2.names[0].TYPE,
destruct2.names[0].name
],
['SymbolFunarg', 'a']
);
assert.deepEqual(
[
destruct2.names[1].names[0].TYPE,
destruct2.names[1].names[0].name
],
['SymbolFunarg', 'b']
);
// destruct 3
assert.strictEqual(typeof destruct3.names[0].key, "string");
assert.strictEqual(destruct3.names[0].key, "a");
assert.strictEqual(destruct3.names[0].value.TYPE, "SymbolFunarg");
assert.strictEqual(destruct3.names[0].value.name, "a");
assert.strictEqual(typeof destruct3.names[1].key, "string");
assert.strictEqual(destruct3.names[1].key, "b");
assert.strictEqual(destruct3.names[1].value.TYPE, "SymbolFunarg");
assert.strictEqual(destruct3.names[1].value.name, "b");
// destruct 4
assert.deepEqual(
[
destruct4.names[0].TYPE,
destruct4.names[0].name
],
['SymbolFunarg', 'a']
);
assert.strictEqual(destruct4.names[1].TYPE, "Destructuring");
assert.deepEqual(
[
destruct4.names[1].names[0].TYPE,
destruct4.names[1].names[0].name
],
['SymbolFunarg', 'b']
);
assert.deepEqual(
get_args(destr_fun1.args_as_names()),
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun2.args_as_names()),
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun3.args_as_names()),
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun4.args_as_names()),
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
// Making sure we don't accidentally accept things which
// Aren't argument destructurings
assert.throws(function () {
uglify.parse('(function ( { a, [ b ] } ) { })')
});
assert.throws(function () {
uglify.parse('(function (1) { })');
}, /Invalid function parameter/);
assert.throws(function () {
uglify.parse('(function (this) { })');
});
assert.throws(function () {
uglify.parse('(function ([1]) { })');
}, /Invalid function parameter/);
assert.throws(function () {
uglify.parse('(function [a] { })');
});
// generators
var generators_def = uglify.parse('function* fn() {}').body[0];
assert.equal(generators_def.is_generator, true);
assert.throws(function () {
uglify.parse('function* (){ }');
});
var generators_yield_def = uglify.parse('function* fn() {\nyield remote();\}').body[0].body[0];
assert.strictEqual(generators_yield_def.body.is_star, false);
});
it("Should not accept spread on non-last parameters", function() {
var tests = [
"var a = function(...a, b) { return a.join(b) }",
"var b = function(a, b, ...c, d) { return c.join(a + b) + d }",
"function foo(...a, b) { return a.join(b) }",
"function bar(a, b, ...c, d) { return c.join(a + b) + d }",
"var a = function*(...a, b) { return a.join(b) }",
"var b = function*(a, b, ...c, d) { return c.join(a + b) + d }",
"function* foo(...a, b) { return a.join(b) }",
"function* bar(a, b, ...c, d) { return c.join(a + b) + d }"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
/^Unexpected token: /.test(e.message);
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept empty parameters after elision", function() {
var tests = [
"(function(,){})()",
"(function(a,){})()",
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Invalid function parameter";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept an initializer when parameter is a rest parameter", function() {
var tests = [
"(function(...a = b){})()",
"(function(a, ...b = [c, d]))"
];
var test = function(code) {
return function () {
uglify.parse(code);
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
it("Shoult not accept duplicated identifiers inside parameters in strict mode or when using default assigment or spread", function() {
// From: ES2016 9.2.12 FunctionDeclarationInstantiation (func, argumentsList)
// NOTE Early errors ensure that duplicate parameter names can only occur
// in non-strict functions that do not have parameter default values or
// rest parameters.
var tests = [
"(function(a = 1, a){})()",
"(function(a, [a = 3]){})()",
"(function(a, b, c, d, [{e: [...a]}]){})()",
"'use strict'; (function(a, a){})",
"(function({a, a = b}))",
"(function(a, [...a]){})",
"(function(a, ...a){})",
"(function(a, [a, ...b]){})",
"(function(a, {b: a, c: [...d]}){})",
"(function(a, a, {b: [...c]}){})"
];
var test = function(code) {
return function () {
uglify.parse(code);
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error &&
/^Parameter [a-zA-Z]+ was used already$/.test(e.message);
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
});

Some files were not shown because too many files have changed in this diff Show More