Compare commits

...

139 Commits

Author SHA1 Message Date
Alex Lam S.L
a298bcce02 Merge pull request #2119 from alexlamsl/harmony-v3.0.18
Merging from master for 3.0.18
2017-06-18 17:16:46 +08:00
alexlamsl
daaf1273fa Merge branch 'master' into harmony-v3.0.18 2017-06-18 15:49:49 +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
kzc
3fa862ce19 support shorthand property named "async" (#2108) 2017-06-16 12:18:18 +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
370f2cc906 Merge pull request #2104 from alexlamsl/harmony-v3.0.17
Merging from master for 3.0.17
2017-06-15 23:07:22 +08:00
alexlamsl
78cf35f89c Merge branch 'master' into harmony-v3.0.17 2017-06-15 19:01:36 +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
kzc
100e18305d first cut of async/await (#2098)
- async arrow functions not yet supported


fixes #1789
2017-06-15 06:15:48 +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
7cc03d4d40 fix parsing of expect_stdout (#2096)
fixes #2095
2017-06-15 01:26:49 +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
c28056d7ed Merge pull request #2094 from alexlamsl/harmony-v3.0.16
Merging from master for 3.0.16
2017-06-14 19:25:21 +08:00
alexlamsl
8af362ed57 Merge branch 'master' into harmony-v3.0.16 2017-06-14 17:09:30 +08:00
Alex Lam S.L
4231f7323e v3.0.16 2017-06-14 16:45:09 +08:00
Alex Lam S.L
68138f2281 fix reduce_vars on AST_Arrow (#2091)
fixes #2090
2017-06-14 16:40:37 +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
a7971f4e34 fix unused crash with top-level AST_Var (#2066)
fixes #2063
2017-06-07 19:12:35 +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
kzc
82fefc5d29 fix class expression statements (#2051)
- class expression statements require parentheses
- allow unused class expression statements to be dropped

fixes #1782
closes #1784
2017-06-04 02:45:26 +08:00
kzc
753932b302 drop unused arrow functions (#2050) 2017-06-04 00:20:46 +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
Alex Lam S.L
1edbd6556f fix beautify whitespace output within AST_Destructuring (#2046)
fixes #2044
2017-06-02 18:50:39 +08:00
kzc
f330ab743a better document behavior of unsafe_Func (#2043) 2017-06-02 12:07:17 +08:00
Alex Lam S.L
888a321417 Merge pull request #2042 from alexlamsl/harmony-v3.0.15
Merging from master for 3.0.15
2017-06-01 19:28:30 +08:00
alexlamsl
ee5c03f7f1 Merge branch 'master' into harmony-v3.0.15 2017-06-01 18:26:09 +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
0cb75089f0 document safari10 mangle option (#2035) 2017-05-31 23:16:20 +08:00
kzc
f71e8fd948 reformat mangle options section of README (#2036) 2017-05-31 21:52:43 +08:00
Alex Lam S.L
a1647ee0c5 Merge pull request #2034 from alexlamsl/harmony-v3.0.14
Merging from master for 3.0.14
2017-05-31 12:44:58 +08:00
alexlamsl
c814060b4a Merge branch 'master' into harmony-v3.0.14 2017-05-31 11:42:54 +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
303293e4aa fix side_effects on AST_Class (#2031)
fixes #2028
2017-05-31 01:44:29 +08:00
Alex Lam S.L
23265ac253 mangle destructuring function parameters (#2029)
fixes #2025
2017-05-30 23:41:55 +08:00
Alex Lam S.L
0cc6dedccc fix block elimination (#2023)
fixes #1664
fixes #1672
2017-05-30 14:59:54 +08:00
kzc
ec63588496 fix compress of IIFE with destructuring args (#2022) 2017-05-30 13:17:06 +08:00
Alex Lam S.L
c2e471e3ad fix if_return on block-scoped variables (#2021)
fixes #1317
2017-05-29 18:08:08 +08:00
Alex Lam S.L
ee23a84e14 Merge pull request #2020 from alexlamsl/harmony-v3.0.13
Merging from master for 3.0.13
2017-05-29 12:24:38 +08:00
alexlamsl
520da57fdc Merge branch 'master' into harmony-v3.0.13 2017-05-29 10:58:05 +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
e5e0ce0b42 Merge pull request #2014 from alexlamsl/harmony-v3.0.12
Merging from master for 3.0.12
2017-05-28 00:08:08 +08:00
Alex Lam S.L
79131cd647 extend node_version range on applicable tests (#2015) 2017-05-27 22:18:28 +08:00
alexlamsl
94d2aeee89 fix block-scoped function for ES6
fixes #1903
2017-05-27 19:28:07 +08:00
alexlamsl
aa835eb0f6 Merge branch 'master' into harmony-v3.0.12 2017-05-27 18:12:10 +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
39d4d7e20a fix export related issues (#2005)
- `mangle` non-exported names
- `unused` on `export` of `function`
- `hoist_funs` on `export`
- `export default`
  - prohibit definition statements
  - parse `AST_Defun` properly
  - drop only unused class and function names


fixes #2001
fixes #2004
2017-05-26 13:35:40 +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
02811ce35e fix issues related to export & function (#2002)
- `unused` function names
- confusion with function call syntax

fixes #2001
2017-05-26 03:12:52 +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
c988e5f4d6 remove AST_ArrowParametersOrSeq (#1997) 2017-05-24 17:45:18 +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
075b648bb1 Merge pull request #1994 from alexlamsl/harmony-v3.0.11
Merging from master for 3.0.11
2017-05-24 00:04:47 +08:00
alexlamsl
37e549ff4f Merge branch 'master' into harmony-v3.0.11 2017-05-23 22:29:04 +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
7bcb442e4c fix destructuring bugs in mangle and compress (#1992)
- destructuring mangle
- destructuring array default values

fixes #1335
2017-05-23 02:53:01 +08:00
kzc
a658cd84a5 fix destructuring of non string keys (#1989) 2017-05-22 16:38:03 +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
5b22334f3b Merge pull request #1982 from alexlamsl/harmony-v3.0.10
Merging from master for 3.0.10
2017-05-21 03:23:59 +08:00
alexlamsl
a3053c537a Merge branch 'master' into harmony-v3.0.10 2017-05-21 01:36:38 +08:00
Alex Lam S.L
d3c4a8e9e7 v3.0.10 2017-05-21 01:30:17 +08:00
kzc
d6f77a6352 update keywords in package.json (#1981) 2017-05-20 22:10:51 +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
Alex Lam S.L
a2172e1a99 fix parsing of yield as object key (#1976)
fixes #1974
2017-05-20 13:11:37 +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
9a074c2637 Merge pull request #1972 from alexlamsl/harmony-v3.0.9
Merging from master for 3.0.9
2017-05-19 10:38:45 +08:00
alexlamsl
02b14528fa Merge branch 'master' into harmony-v3.0.9 2017-05-19 09:51:00 +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
Alex Lam S.L
3db2001633 suppress unused on block variables (#1969)
fixes #1968
2017-05-19 00:28:19 +08:00
Alex Lam S.L
aaba482e48 Merge pull request #1967 from alexlamsl/harmony-v3.0.8
Merging from master for 3.0.8
2017-05-18 16:02:29 +08:00
alexlamsl
5f29fced0a Merge branch 'master' into harmony-v3.0.8 2017-05-18 14:54:18 +08:00
Alex Lam S.L
43add9416b v3.0.8 2017-05-18 14:49:40 +08:00
Alex Lam S.L
efcf167e5e make expect_stdout node version specific (#1963)
... via semver string on `node_version` label.
2017-05-18 11:28:35 +08:00
Kara
6ed90913ca fix docs for side_effects flag to reflect current behavior (#1966) 2017-05-18 10:51:49 +08:00
kzc
b1b918e6d6 better extends paren fix (#1962) 2017-05-18 02:36:29 +08:00
Alex Lam S.L
569c21e952 improve RegExp handling (#1959)
- remove `options.output.unescape_regexps`
- preserve original pattern whenever possible

fixes #54
fixes #1929
2017-05-17 20:10:50 +08:00
Alex Lam S.L
87c3a2c0ce remove space_colon (#1960)
Always emit space after colon when `options.output.beautify` is enabled.
2017-05-17 14:07:34 +08:00
Rob Garrison
baef8bf050 update output options in readme (#1958) 2017-05-17 11:54:46 +08:00
alexlamsl
0813c5316f remove Travis CI badge 2017-05-17 10:32:59 +08:00
kzc
ebb469e4cd fix class extends expression (#1956) 2017-05-17 03:29:25 +08:00
kzc
c22d26b483 support export default of anonymous functions and classes (#1954) 2017-05-17 03:28:24 +08:00
Alex Lam S.L
f751e64d49 Merge pull request #1951 from alexlamsl/harmony-v3.0.7
Merging from master for 3.0.7
2017-05-17 01:03:55 +08:00
alexlamsl
60c56a24b9 Merge branch 'master' into harmony-v3.0.7 2017-05-16 20:02:30 +08:00
Alex Lam S.L
c88139492d v3.0.7 2017-05-16 19:59:40 +08:00
Alex Lam S.L
cb45886512 export TreeTransformer (#1950)
- link to existing documentation on `TreeWalker` & `TreeTransformer`
- fix Travis build failures

fixes #1949
2017-05-16 19:59:05 +08:00
Alex Lam S.L
01f23cf5a1 Merge pull request #1948 from alexlamsl/harmony-v3.0.6
Merging from master for 3.0.6
2017-05-16 13:26:45 +08:00
alexlamsl
99fb3e8f0d Merge branch 'master' into harmony-v3.0.6 2017-05-16 06:48:23 +08:00
Alex Lam S.L
050474ab44 v3.0.6 2017-05-16 06:38:58 +08:00
Alex Lam S.L
f6c805ae1d print package name alongside version in CLI (#1946)
fixes #1945
2017-05-16 06:34:32 +08:00
Alex Lam S.L
9464d3c20f fix parsing of property access after new line (#1944)
Account for comments when detecting property access in `tokenizer`.

fixes #1943
2017-05-16 05:40:49 +08:00
alexlamsl
f18abd1b9c minor fixes to README.md 2017-05-16 01:33:01 +08:00
kzc
3be06ad085 reorg README for 3.x (#1942) 2017-05-16 01:12:00 +08:00
Alex Lam S.L
265008c948 improve keyword-related parser errors (#1941)
fixes #1937
2017-05-15 23:02:55 +08:00
63 changed files with 5144 additions and 1443 deletions

View File

@@ -1,12 +1,14 @@
language: node_js
before_install: "npm install -g npm"
node_js:
- "0.10"
- "0.12"
- "4"
- "6"
- "8"
env:
- UGLIFYJS_TEST_ALL=1
matrix:
fast_finish: true
sudo: false
cache:
directories: tmp

779
README.md
View File

@@ -1,6 +1,5 @@
uglify-es
=========
[![Build Status](https://travis-ci.org/mishoo/UglifyJS2.svg)](https://travis-ci.org/mishoo/UglifyJS2)
**uglify-es** is an ECMAScript 2015 parser, minifier, compressor and beautifier toolkit.
@@ -22,8 +21,7 @@ From NPM for programmatic use:
npm install uglify-es
Usage
-----
# Command line usage
uglifyjs [input files] [options]
@@ -40,10 +38,11 @@ a double dash to prevent input files being used as option arguments:
uglifyjs --compress --mangle -- input.js
The available options are:
### Command line options
```
-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.
@@ -125,8 +124,8 @@ The available options are:
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
@@ -139,7 +138,7 @@ The available options are:
Specify `--output` (`-o`) to declare the output file. Otherwise the output
goes to STDOUT.
## Source map options
## CLI source map options
UglifyJS can generate a source map file, which is highly useful for
debugging your compressed JavaScript. To get a source map, pass
@@ -183,12 +182,25 @@ 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.
## Mangler options
## CLI compress options
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
you can pass a comma-separated list of [compress options](#compress-options).
Options are in the form `foo=bar`, or just `foo` (the latter implies
a boolean option that you want to set `true`; it's effectively a
shortcut for `foo=true`).
Example:
uglifyjs file.js -c toplevel,sequences=false
## CLI mangle options
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
- `toplevel` — mangle names declared in the top level scope (disabled by
default).
- `eval` — mangle names visible in scopes where `eval` or `with` are used
@@ -202,26 +214,56 @@ comma-separated list of names. For example:
to prevent the `require`, `exports` and `$` names from being changed.
### Mangling property names (`--mangle-props`)
### 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:
```js
```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).
@@ -230,7 +272,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.
@@ -240,10 +282,10 @@ mangled to the same name in all of them. For this, pass `--name-cache filename.
and UglifyJS will maintain these mappings in a file which can then be reused.
It should be initially empty. Example:
```
rm -f /tmp/cache.json # start fresh
uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
```bash
$ rm -f /tmp/cache.json # start fresh
$ uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
$ uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
```
Now, `part1.js` and `part2.js` will be consistent with each other in terms
@@ -252,18 +294,29 @@ of mangled property names.
Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS.
#### Mangling unquoted names (`--mangle-props keep_quoted`)
### Mangling unquoted names (`--mangle-props keep_quoted`)
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);
```
$ 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);
```bash
$ 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
### Debugging property name mangling
You can also pass `--mangle-props debug` in order to mangle property names
without completely obscuring them. For example the property `o.foo`
@@ -271,6 +324,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
@@ -278,12 +338,211 @@ random number on every compile to simulate mangling changing with different
inputs (e.g. as you update the input script with new properties), and to help
identify mistakes like writing mangled keys to storage.
## Compressor options
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
you can pass a comma-separated list of options. Options are in the form
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
to set `true`; it's effectively a shortcut for `foo=true`).
# API Reference
Assuming installation via NPM, you can load UglifyJS in your application
like this:
```javascript
var UglifyJS = require("uglify-es");
```
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 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 `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 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(function(n,o){return n+o}(3,7));
```
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 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).
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compress options](#compress-options).
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
an object to specify [mangle options](#mangle-options) (see below).
- `mangle.properties` (default `false`) — a subcategory of the mangle option.
Pass an object to specify custom [mangle property options](#mangle-properties-options).
- `output` (default `null`) — pass an object if you wish to specify
additional [output options](#output-options). The defaults are optimized
for best compression.
- `sourceMap` (default `false`) - pass an object if you wish to specify
[source map options](#source-map-options).
- `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.
- `ie8` (default `false`) - set to `true` to support IE8.
## Minify options structure
```javascript
{
warnings: false,
parse: {
// parse options
},
compress: {
// compress options
},
mangle: {
// mangle options
properties: {
// mangle property options
}
},
output: {
// output options
},
sourceMap: {
// source map options
},
toplevel: false,
ie8: false,
}
```
### Source map options
To generate a source map:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
sourceMap: {
filename: "out.js",
url: "out.js.map"
}
});
console.log(result.code); // minified output
console.log(result.map); // source map
```
Note that the source map is not saved in a file, it's just returned in
`result.map`. The value passed for `sourceMap.url` is only used to set
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
in source map file.
You can set option `sourceMap.url` to be `"inline"` and source map will
be appended to code.
You can also specify sourceRoot property to be included in source map:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
sourceMap: {
root: "http://example.com/src",
url: "out.js.map"
}
});
```
If you're compressing compiled JavaScript and have a source map for it, you
can use `sourceMap.content`:
```javascript
var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
sourceMap: {
content: "content from compiled.js.map",
url: "minified.js.map"
}
});
// same as before, it returns `code` and `map`
```
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`.
## 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
@@ -310,12 +569,18 @@ to set `true`; it's effectively a shortcut for `foo=true`).
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `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.
- `conditionals` -- apply optimizations for `if`-s and conditional
expressions
@@ -335,7 +600,7 @@ to set `true`; it's effectively a shortcut for `foo=true`).
assignments do not count as references unless set to `"keep_assign"`)
- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the toplevel scope (`false` by default, `true` to drop both unreferenced
in the top level scope (`false` by default, `true` to drop both unreferenced
functions and variables)
- `top_retain` -- prevent specific toplevel functions and variables from `unused`
@@ -348,13 +613,15 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `if_return` -- optimizations for if/return and if/continue
- `inline` -- embed simple functions
- `join_vars` -- join consecutive `var` statements
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `collapse_vars` -- Collapse single-use `var` and `const` definitions
when possible.
- `collapse_vars` -- Collapse single-use non-constant variables - side
effects permitting.
- `reduce_vars` -- Improve optimization on variables assigned with and
used as constant values.
@@ -406,13 +673,152 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `side_effects` -- default `false`. Pass `true` to potentially drop 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()`;
- `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();`
## Mangle options
### The `unsafe` option
- `reserved` (default `[]`). Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` (default `false`). Pass `true` to mangle names declared in the
top level scope.
- `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.
- `safari10` (default `false`). Pass `true` to work around the Safari 10 loop
iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041)
"Cannot declare a let variable twice".
Examples:
```javascript
// test.js
var globalVar;
function funcName(firstLongName, anotherLongName) {
var myVariable = firstLongName + anotherLongName;
}
```
```javascript
var code = fs.readFileSync("test.js", "utf8");
UglifyJS.minify(code).code;
// 'function funcName(a,n){}var globalVar;'
UglifyJS.minify(code, { mangle: { reserved: ['firstLongName'] } }).code;
// 'function funcName(firstLongName,a){}var globalVar;'
UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
// 'function n(n,a){}var a;'
```
### Mangle properties options
- `reserved` (default: `[]`) -- Do not mangle property names listed in the
`reserved` array.
- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property
names matching the regular expression.
- `keep_quoted` (default: `false`) -— Only mangle unquoted property names.
- `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.
- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
## Output options
The code generator tries to output shortest code possible by default. In
case you want beautified output, pass `--beautify` (`-b`). Optionally you
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
- `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. `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
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.
# Miscellaneous
### Keeping copyright notices or other comments
You can pass `--comments` to retain certain comments in the output. By
default it will keep JSDoc-style comments that contain "@preserve",
"@license" or "@cc_on" (conditional compilation for IE). You can pass
`--comments all` to keep all the comments, or a valid JavaScript regexp to
keep only comments that match this regexp. For example `--comments /^!/`
will keep comments like `/*! Copyright Notice */`.
Note, however, that there might be situations where comments are lost. For
example:
```javascript
function f() {
/** @preserve Foo Bar */
function g() {
// this function is never called
}
return something();
}
```
Even though it has "@preserve", the comment will be lost because the inner
function `g` (which is the AST node to which the comment is attached to) is
discarded by the compressor as not referenced.
The safest comments where to place copyright information (or other info that
needs to be kept in the output) are comments attached to toplevel nodes.
### The `unsafe` `compress` option
It enables some transformations that *might* break code logic in certain
contrived cases, but should be fine for most code. You might want to try it
@@ -436,7 +842,7 @@ scope). For example if you pass `--define DEBUG=false` then, coupled with
dead code removal UglifyJS will discard the following from the output:
```javascript
if (DEBUG) {
console.log("debug stuff");
console.log("debug stuff");
}
```
@@ -450,8 +856,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.
```
@@ -465,12 +871,13 @@ code as usual. The build will contain the `const` declarations if you use
them. If you are targeting < ES6 environments which does not support `const`,
using `var` with `reduce_vars` (enabled by default) should suffice.
#### Conditional compilation, API
### Conditional compilation API
You can also use conditional compilation via the programmatic API. With the difference that the
property name is `global_defs` and is a compressor property:
```js
uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
```javascript
var result = UglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
compress: {
dead_code: true,
global_defs: {
@@ -480,84 +887,72 @@ uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
});
```
## Beautifier options
The code generator tries to output shortest code possible by default. In
case you want beautified output, pass `--beautify` (`-b`). Optionally you
can pass additional arguments that control the code output:
- `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.
- `indent_level` (default 4)
- `indent_start` (default 0) -- prefix all lines by that many spaces
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
objects
- `space_colon` (default `true`) -- insert a space after the colon signs
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `inline_script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `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.
- `max_line_len` (default 32000) -- maximum line length (for uglified code)
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
- `semicolons` (default `true`) -- separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `preamble` (default `null`) -- when passed it must be a string and
it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
licensing information, for example.
- `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
more double quotes in the string itself.
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
- `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.
### Keeping copyright notices or other comments
You can pass `--comments` to retain certain comments in the output. By
default it will keep JSDoc-style comments that contain "@preserve",
"@license" or "@cc_on" (conditional compilation for IE). You can pass
`--comments all` to keep all the comments, or a valid JavaScript regexp to
keep only comments that match this regexp. For example `--comments /^!/`
will keep comments like `/*! Copyright Notice */`.
Note, however, that there might be situations where comments are lost. For
example:
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
function f() {
/** @preserve Foo Bar */
function g() {
// this function is never called
}
return something();
}
UglifyJS.minify("alert('hello');", {
compress: {
global_defs: {
"@alert": "console.log"
}
}
}).code;
// returns: 'console.log("hello");'
```
Even though it has "@preserve", the comment will be lost because the inner
function `g` (which is the AST node to which the comment is attached to) is
discarded by the compressor as not referenced.
Otherwise it would be replaced as string literal:
```javascript
UglifyJS.minify("alert('hello');", {
compress: {
global_defs: {
"alert": "console.log"
}
}
}).code;
// returns: '"console.log"("hello");'
```
The safest comments where to place copyright information (or other info that
needs to be kept in the output) are comments attached to toplevel nodes.
### Using native Uglify AST with `minify()`
```javascript
// example: parse only, produce native Uglify AST
## Support for the SpiderMonkey AST
var result = UglifyJS.minify(code, {
parse: {},
compress: false,
mangle: false,
output: {
ast: true,
code: false // optional - faster if false
}
});
// result.ast contains native Uglify AST
```
```javascript
// example: accept native Uglify AST input and then compress and mangle
// to produce both code and native AST.
var result = UglifyJS.minify(ast, {
compress: {},
mangle: {},
output: {
ast: true,
code: true // optional - faster if false
}
});
// result.ast contains native Uglify AST
// result.code contains the minified code in string form.
```
### Working with Uglify AST
Transversal and transformation of the native AST can be performed through
[`TreeWalker`](http://lisperator.net/uglifyjs/walk) and
[`TreeTransformer`](http://lisperator.net/uglifyjs/transform) respectively.
### ESTree / SpiderMonkey AST
UglifyJS has its own abstract syntax tree format; for
[practical reasons](http://lisperator.net/blog/uglifyjs-why-not-switching-to-spidermonkey-ast/)
@@ -585,141 +980,5 @@ Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
in total it's a bit more than just using UglifyJS's own parser.
API Reference
-------------
Assuming installation via NPM, you can load UglifyJS in your application
like this:
```javascript
var UglifyJS = require("uglify-es");
```
There is a single toplevel function, `minify(files, options)`, which will
performs all the steps in a configurable manner.
Example:
```javascript
var result = UglifyJS.minify("var b = function() {};");
console.log(result.code); // minified output
console.log(result.error); // runtime error
```
You can also compress multiple files:
```javascript
var result = UglifyJS.minify({
"file1.js": "var a = function() {};",
"file2.js": "var b = function() {};"
});
console.log(result.code);
```
To generate a source map:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
sourceMap: {
filename: "out.js",
url: "out.js.map"
}
});
console.log(result.code); // minified output
console.log(result.map); // source map
```
Note that the source map is not saved in a file, it's just returned in
`result.map`. The value passed for `sourceMap.url` is only used to set
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
in source map file.
You can set option `sourceMap.url` to be `"inline"` and source map will
be appended to code.
You can also specify sourceRoot property to be included in source map:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
sourceMap: {
root: "http://example.com/src",
url: "out.js.map"
}
});
```
If you're compressing compiled JavaScript and have a source map for it, you
can use `sourceMap.content`:
```javascript
var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
sourceMap: {
content: "content from compiled.js.map",
url: "minified.js.map"
}
});
// same as before, it returns `code` and `map`
```
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`.
Other options:
- `warnings` (default `false`) — pass `true` to display compressor warnings.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
an object to specify mangling options (see below).
- `mangleProperties` (default `false`) — pass an object to specify custom
mangle property options.
- `output` (default `null`) — pass an object if you wish to specify
additional [output options](#beautifier-options). The defaults are optimized
for best compression.
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compressor options](#compressor-options).
- `parse` (default {}) — pass an object if you wish to specify some
additional [parser options](#the-parser).
##### mangle
- `reserved` - pass an array of identifiers that should be excluded from mangling
- `toplevel` — mangle names declared in the toplevel scope (disabled by
default).
- `eval` — mangle names visible in scopes where eval or with are used
(disabled by default).
- `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](#compressor-options).
Examples:
```javascript
// test.js
var globalVar;
function funcName(firstLongName, anotherLongName)
{
var myVariable = firstLongName + anotherLongName;
}
```
```javascript
var code = fs.readFileSync("test.js", "utf8");
UglifyJS.minify(code).code;
// 'function funcName(a,n){}var globalVar;'
UglifyJS.minify(code, { mangle: { reserved: ['firstLongName'] } }).code;
// 'function funcName(firstLongName,a){}var globalVar;'
UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
// 'function n(n,a){}var a;'
```
##### 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.
[acorn]: https://github.com/ternjs/acorn
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k

View File

@@ -21,10 +21,20 @@ var options = {
compress: false,
mangle: false
};
program._name = info.name;
program.version(info.version);
program.version(info.name + " " + info.version);
program.parseArgv = program.parse;
program.parse = undefined;
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("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));
@@ -39,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.");
@@ -115,10 +125,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) {
@@ -140,7 +150,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) {
@@ -170,9 +180,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) {
@@ -199,7 +209,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];
@@ -208,21 +218,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;
@@ -238,7 +249,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: {
@@ -252,19 +263,21 @@ 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;
}));
}
if (program.stats) console.error("Elapsed:", Date.now() - program.stats);
if (result.timings) for (var phase in result.timings) {
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
}
}
function fatal(message) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
console.error(message);
print_error(message);
process.exit(1);
}
@@ -349,7 +362,7 @@ function parse_js(flag, constants) {
}
}));
} catch(ex) {
fatal("Error parsing arguments for '" + flag + "': " + value);
options[value] = null;
}
return options;
}
@@ -358,10 +371,10 @@ function parse_js(flag, constants) {
function parse_source_map() {
var parse = parse_js("sourceMap", true);
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;
@@ -383,3 +396,25 @@ function to_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

@@ -355,86 +355,14 @@ var AST_Expansion = DEFNODE("Expansion", "expression", {
}
});
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 is_generator async", {
$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",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
is_generator: "[boolean] is this a generator method",
async: "[boolean] is this method async",
},
args_as_names: function () {
var out = [];
@@ -966,11 +894,12 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", {
$documentation: "An object getter property",
}, AST_ObjectProperty);
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator", {
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator async", {
$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",
static: "[boolean] is this method static (classes only)",
is_generator: "[boolean] is this a generator method",
async: "[boolean] is this method async",
},
$documentation: "An ES6 concise method inside an object or class"
}, AST_ObjectProperty);
@@ -1174,7 +1103,17 @@ var AST_True = DEFNODE("True", null, {
value: true
}, AST_Boolean);
/* -----[ Yield ]----- */
var AST_Await = DEFNODE("Await", "expression", {
$documentation: "An `await` statement",
$propdoc: {
expression: "[AST_Node] the mandatory expression being awaited",
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.expression._walk(visitor);
});
}
});
var AST_Yield = DEFNODE("Yield", "expression is_star", {
$documentation: "A `yield` statement",

File diff suppressed because it is too large Load Diff

View File

@@ -30,9 +30,6 @@ function set_shorthand(name, options, keys) {
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
if (typeof files == "string") {
files = [ files ];
}
options = defaults(options, {
compress: {},
ie8: false,
@@ -41,10 +38,14 @@ function minify(files, options) {
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" ]);
@@ -77,13 +78,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") {
@@ -97,19 +102,23 @@ function minify(files, options) {
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 +134,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 +155,18 @@ function minify(files, options) {
}
}
}
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

@@ -73,8 +73,7 @@ function OutputStream(options) {
shebang : true,
shorthand : undefined,
source_map : null,
space_colon : true,
unescape_regexps : false,
webkit : false,
width : 80,
wrap_iife : false,
}, true);
@@ -214,12 +213,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++;
@@ -229,7 +259,10 @@ 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("( [ + * / - , .");
@@ -289,6 +322,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;
@@ -384,27 +429,12 @@ function OutputStream(options) {
function colon() {
print(":");
if (options.space_colon) space();
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() {
@@ -600,6 +630,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;
@@ -613,6 +650,10 @@ function OutputStream(options) {
return p instanceof AST_PropAccess && p.expression === this;
});
PARENS(AST_ClassExpression, function(output){
return output.parent() instanceof AST_SimpleStatement;
});
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
PARENS(AST_Object, function(output){
@@ -644,7 +685,6 @@ function OutputStream(options) {
* ==> 20 (side effect, set a := 10 and b := 20) */
|| p instanceof AST_Arrow // x => (x, x)
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
|| (p instanceof AST_Class && p.extends === this) // class D extends (calls++, C) {}
;
});
@@ -777,17 +817,15 @@ function OutputStream(options) {
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(); }
if (i > 0) output.comma();
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();
})
if (i == len - 1 && name instanceof AST_Hole) output.comma();
});
output.print(self.is_array ? "]" : "}");
});
@@ -940,6 +978,10 @@ function OutputStream(options) {
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
var self = this;
if (!nokeyword) {
if (this.async) {
output.print("async");
output.space();
}
output.print("function");
if (this.is_generator) {
output.star();
@@ -1045,6 +1087,22 @@ function OutputStream(options) {
}
});
DEFPRINT(AST_Await, function(self, output){
output.print("await");
output.space();
var e = self.expression;
var parens = !(
e instanceof AST_Call
|| e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess
|| e instanceof AST_Unary
|| e instanceof AST_Constant
);
if (parens) output.print("(");
self.expression.print(output);
if (parens) output.print(")");
});
/* -----[ loop control ]----- */
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
@@ -1474,10 +1532,24 @@ function OutputStream(options) {
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");
output.space();
if (parens) {
output.print("(");
} else {
output.space();
}
self.extends.print(output);
output.space();
if (parens) {
output.print(")");
} else {
output.space();
}
}
if (self.properties.length > 0) output.with_block(function(){
self.properties.forEach(function(prop, i){
@@ -1494,7 +1566,8 @@ function OutputStream(options) {
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"
@@ -1511,7 +1584,8 @@ function OutputStream(options) {
} else {
output.print_string(key, quote);
}
});
}
DEFPRINT(AST_ObjectKeyVal, function(self, output){
function get_name(self) {
var def = self.definition();
@@ -1524,7 +1598,7 @@ function OutputStream(options) {
is_identifier_string(self.key) &&
get_name(self.value) === self.key
) {
self.print_property_name(self.key, self.quote, output);
print_property_name(self.key, self.quote, output);
} else if (allowShortHand &&
self.value instanceof AST_DefaultAssign &&
@@ -1532,12 +1606,14 @@ function OutputStream(options) {
is_identifier_string(self.key) &&
get_name(self.value.left) === self.key
) {
self.print_property_name(self.key, self.quote, output);
print_property_name(self.key, self.quote, output);
output.space();
output.print("=");
output.space();
self.value.right.print(output);
} else {
if (!(self.key instanceof AST_Node)) {
self.print_property_name(self.key, self.quote, output);
print_property_name(self.key, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
@@ -1547,15 +1623,18 @@ function OutputStream(options) {
self.value.print(output);
}
});
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) {
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
var self = this;
if (self.static) {
output.print("static");
output.space();
}
output.print(type);
output.space();
if (type) {
output.print(type);
output.space();
}
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
@@ -1564,27 +1643,13 @@ function OutputStream(options) {
self.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);
self._print_getter_setter(self.is_generator && "*" || self.async && "async", output);
});
AST_Symbol.DEFMETHOD("_do_print", function(output){
var def = this.definition();
@@ -1617,45 +1682,14 @@ function OutputStream(options) {
}
});
function regexp_safe_literal(code) {
return [
0x5c , // \
0x2f , // /
0x2e , // .
0x2b , // +
0x2a , // *
0x3f , // ?
0x28 , // (
0x29 , // )
0x5b , // [
0x5d , // ]
0x7b , // {
0x7d , // }
0x24 , // $
0x5e , // ^
0x3a , // :
0x7c , // |
0x21 , // !
0x0a , // \n
0x0d , // \r
0x00 , // \0
0xfeff , // Unicode BOM
0x2028 , // unicode "line separator"
0x2029 , // unicode "paragraph separator"
].indexOf(code) < 0;
};
DEFPRINT(AST_RegExp, function(self, output){
var str = self.getValue().toString();
var regexp = self.getValue();
var str = regexp.toString();
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
}
if (output.option("ascii_only")) {
str = output.to_ascii(str);
} else if (output.option("unescape_regexps")) {
str = str.split("\\\\").map(function(str){
return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
var code = parseInt(s.substr(2), 16);
return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
});
}).join("\\\\");
}
output.print(str);
var p = output.parent();

View File

@@ -47,7 +47,7 @@
var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import';
var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'enum implements interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield';
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield await';
KEYWORDS = makePredicate(KEYWORDS);
RESERVED_WORDS = makePredicate(RESERVED_WORDS);
@@ -122,8 +122,6 @@ var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
/* -----[ Tokenizer ]----- */
// surrogate safe regexps adapted from https://github.com/mathiasbynens/unicode-8.0.0/tree/89b412d8a71ecca9ed593d9e9fa073ab64acfebe/Binary_Property
@@ -345,7 +343,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value))) ||
(type == "arrow");
prev_was_dot = (type == "punc" && value == ".");
if (type == "punc" && value == ".") {
prev_was_dot = true;
} else if (!is_comment) {
prev_was_dot = false;
}
var ret = {
type : type,
value : value,
@@ -607,31 +609,33 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return name;
});
var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
var read_regexp = with_eof_error("Unterminated regular expression", function(source) {
var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
parse_error("Unexpected line terminator");
} else if (prev_backslash) {
regexp += "\\" + ch;
source += "\\" + ch;
prev_backslash = false;
} else if (ch == "[") {
in_class = true;
regexp += ch;
source += ch;
} else if (ch == "]" && in_class) {
in_class = false;
regexp += ch;
source += ch;
} else if (ch == "/" && !in_class) {
break;
} else if (ch == "\\") {
prev_backslash = true;
} else {
regexp += ch;
source += ch;
}
var mods = read_name();
try {
return token("regexp", new RegExp(regexp, mods));
var regexp = new RegExp(source, mods);
regexp.raw_source = source;
return token("regexp", regexp);
} catch(e) {
parse_error(e.message);
parse_error(e.message);
}
});
@@ -844,9 +848,7 @@ var PRECEDENCE = (function(a, ret){
{}
);
var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]);
/* -----[ Parser ]----- */
@@ -860,7 +862,7 @@ function parse($TEXT, options) {
shebang : true,
strict : false,
toplevel : null,
});
}, true);
var S = {
input : (typeof $TEXT == "string"
@@ -871,6 +873,7 @@ function parse($TEXT, options) {
prev : null,
peeked : null,
in_function : 0,
in_async : -1,
in_generator : -1,
in_directives : true,
in_loop : 0,
@@ -941,6 +944,10 @@ function parse($TEXT, options) {
return S.in_generator === S.in_function;
}
function is_in_async() {
return S.in_async === S.in_function;
}
function semicolon(optional) {
if (is("punc", ";")) next();
else if (!optional && !can_insert_semicolon()) unexpected();
@@ -972,17 +979,16 @@ function parse($TEXT, options) {
};
var statement = embed_tokens(function() {
var tmp;
handle_regexp();
switch (S.token.type) {
case "string":
if (S.in_directives) {
tmp = peek();
var token = peek();
if (S.token.raw.indexOf("\\") == -1
&& (tmp.nlb
|| is_token(tmp, "eof")
|| is_token(tmp, "punc", ";")
|| is_token(tmp, "punc", "}"))) {
&& (token.nlb
|| is_token(token, "eof")
|| is_token(token, "punc", ";")
|| is_token(token, "punc", "}"))) {
S.input.add_directive(S.token.value);
} else {
S.in_directives = false;
@@ -998,6 +1004,11 @@ function parse($TEXT, options) {
return simple_statement();
case "name":
if (S.token.value == "async" && is_token(peek(), "keyword", "function")) {
next();
next();
return function_(AST_Defun, false, true);
}
return is_token(peek(), "punc", ":")
? labeled_statement()
: simple_statement();
@@ -1022,90 +1033,126 @@ function parse($TEXT, options) {
}
case "keyword":
switch (tmp = S.token.value, next(), tmp) {
switch (S.token.value) {
case "break":
next();
return break_cont(AST_Break);
case "continue":
next();
return break_cont(AST_Continue);
case "debugger":
next();
semicolon();
return new AST_Debugger();
case "do":
next();
var body = in_loop(statement);
expect_token("keyword", "while");
var condition = parenthesised();
semicolon(true);
return new AST_Do({
body : in_loop(statement),
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
body : body,
condition : condition
});
case "while":
next();
return new AST_While({
condition : parenthesised(),
body : in_loop(statement)
});
case "for":
next();
return for_();
case "class":
next();
return class_(AST_DefClass);
case "function":
next();
return function_(AST_Defun);
case "if":
next();
return if_();
case "return":
if (S.in_function == 0 && !options.bare_returns)
croak("'return' outside of function");
next();
var value = null;
if (is("punc", ";")) {
next();
} else if (!can_insert_semicolon()) {
value = expression(true);
semicolon();
}
return new AST_Return({
value: ( is("punc", ";")
? (next(), null)
: can_insert_semicolon()
? null
: (tmp = expression(true), semicolon(), tmp) )
value: value
});
case "switch":
next();
return new AST_Switch({
expression : parenthesised(),
body : in_loop(switch_body_)
});
case "throw":
next();
if (S.token.nlb)
croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
return new AST_Throw({
value: (tmp = expression(true), semicolon(), tmp)
value: value
});
case "try":
next();
return try_();
case "var":
return tmp = var_(), semicolon(), tmp;
next();
var node = var_();
semicolon();
return node;
case "let":
return tmp = let_(), semicolon(), tmp;
next();
var node = let_();
semicolon();
return node;
case "const":
return tmp = const_(), semicolon(), tmp;
next();
var node = const_();
semicolon();
return node;
case "with":
if (S.input.has_directive("use strict")) {
croak("Strict mode may not include a with statement");
}
next();
return new AST_With({
expression : parenthesised(),
body : statement()
});
case "import":
return tmp = import_(), semicolon(), tmp;
next();
var node = import_();
semicolon();
return node;
case "export":
next();
return export_();
}
}
@@ -1118,6 +1165,9 @@ function parse($TEXT, options) {
// Ecma-262, 12.1.1 Static Semantics: Early Errors
token_error(S.prev, "Yield cannot be used as label inside generators");
}
if (label.name === "await" && is_in_async()) {
token_error(S.prev, "await cannot be used as label inside async function");
}
if (find_if(function(l){ return l.name == label.name }, S.labels)) {
// ECMA-262, 12.12: An ECMAScript program is considered
// syntactically incorrect if it contains a
@@ -1231,39 +1281,31 @@ function parse($TEXT, options) {
});
};
var arrow_function = function(args) {
var arrow_function = function(start, argnames) {
if (S.token.nlb) {
croak("Unexpected newline before arrow (=>)");
}
expect_token("arrow", "=>");
var argnames;
if (typeof args.length === 'number') {
argnames = args;
} else {
argnames = args.as_params(croak);
}
var body = is("punc", "{") ?
_function_body(true) :
_function_body(false);
var body = _function_body(is("punc", "{"));
return new AST_Arrow({
start : args.start,
start : start,
end : body.end,
argnames : argnames,
body : body
});
};
var function_ = function(ctor, is_generator_property) {
var function_ = function(ctor, is_generator_property, is_async) {
if (is_generator_property && is_async) croak("generators cannot be async");
var start = S.token
var in_statement = ctor === AST_Defun;
var is_generator = is("operator", "*");
if (is_generator) {
next();
next();
}
var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
@@ -1271,11 +1313,12 @@ function parse($TEXT, options) {
unexpected();
var args = parameters();
var body = _function_body(true, is_generator || is_generator_property, name, args);
var body = _function_body(true, is_generator || is_generator_property, is_async, name, args);
return new ctor({
start : args.start,
end : body.end,
is_generator: is_generator,
async : is_async,
name : name,
argnames: args,
body : body
@@ -1574,8 +1617,6 @@ function parse($TEXT, options) {
}
function params_or_seq_() {
var start = S.token
expect("(");
var first = true;
var a = [];
while (!is("punc", ")")) {
@@ -1585,32 +1626,29 @@ function parse($TEXT, options) {
next();
a.push(new AST_Expansion({
start: prev(),
expression: expression(false),
expression: expression(),
end: S.token,
}));
if (!is("punc", ")")) {
unexpected(spread_token);
}
} else {
a.push(expression(false));
a.push(expression());
}
}
var end = S.token
next();
return new AST_ArrowParametersOrSeq({
start: start,
end: end,
expressions: a
});
return a;
}
function _function_body(block, generator, name, args) {
function _function_body(block, generator, is_async, name, args) {
var loop = S.in_loop;
var labels = S.labels;
var current_generator = S.in_generator;
var current_async = S.in_async;
++S.in_function;
if (generator)
S.in_generator = S.in_function;
if (is_async)
S.in_async = S.in_function;
if (block)
S.in_directives = true;
S.in_loop = 0;
@@ -1630,9 +1668,22 @@ function parse($TEXT, options) {
S.in_loop = loop;
S.labels = labels;
S.in_generator = current_generator;
S.in_async = current_async;
return a;
}
function _await_expression() {
// Previous token must be "await" and not be interpreted as an identifier
if (!is_in_async()) {
croak("Unexpected await expression outside async function",
S.prev.line, S.prev.col, S.prev.pos);
}
// the await expression is parsed as a unary expression in Babel
return new AST_Await({
expression : maybe_unary(true),
});
}
function _yield_expression() {
// Previous token must be keyword yield and not be interpret as an identifier
if (!is_in_generator()) {
@@ -1641,7 +1692,6 @@ function parse($TEXT, options) {
}
var star = false;
var has_expression = true;
var tmp;
// Attempt to get expression or star (and then the mandatory expression)
// behind yield on the same line.
@@ -1848,7 +1898,6 @@ function parse($TEXT, options) {
var tok = S.token, ret;
switch (tok.type) {
case "name":
case "keyword":
ret = _make_symbol(AST_SymbolRef);
break;
case "num":
@@ -1878,18 +1927,68 @@ function parse($TEXT, options) {
break;
}
break;
case "operator":
if (!is_identifier_string(tok.value)) {
croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos);
}
ret = _make_symbol(AST_SymbolRef);
break;
}
next();
return ret;
};
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);
}
}
var expr_atom = function(allow_calls) {
if (is("operator", "new")) {
return new_(allow_calls);
@@ -1898,23 +1997,33 @@ function parse($TEXT, options) {
if (is("punc")) {
switch (start.value) {
case "(":
var ex = params_or_seq_();
next();
var exprs = params_or_seq_();
expect(")");
if (is("arrow", "=>")) {
ex.start = start;
ex.end = S.token;
return arrow_function(ex);
return arrow_function(start, exprs.map(to_fun_args));
}
ex = ex.as_expr(croak);
var ex = exprs.length == 1 ? exprs[0] : new AST_Sequence({
expressions: exprs
});
ex.start = start;
ex.end = S.token;
return subscripts(ex, allow_calls);
case "[":
return subscripts(array_(), allow_calls);
case "{":
return subscripts(object_or_object_destructuring_(), allow_calls);
return subscripts(object_or_destructuring_(), allow_calls);
}
unexpected();
}
if (is("name", "async") && is_token(peek(), "keyword", "function")) {
next();
next();
var func = function_(AST_Function, false, true);
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
}
if (is("keyword", "function")) {
next();
var func = function_(AST_Function);
@@ -1932,7 +2041,7 @@ function parse($TEXT, options) {
if (is("template_head")) {
return subscripts(template_string(), allow_calls);
}
if (ATOMIC_START_TOKEN[S.token.type]) {
if (ATOMIC_START_TOKEN(S.token.type)) {
return subscripts(as_atom_node(), allow_calls);
}
unexpected();
@@ -1996,11 +2105,11 @@ function parse($TEXT, options) {
});
});
var create_accessor = embed_tokens(function(is_generator) {
return function_(AST_Accessor, is_generator);
var create_accessor = embed_tokens(function(is_generator, is_async) {
return function_(AST_Accessor, is_generator, is_async);
});
var object_or_object_destructuring_ = embed_tokens(function() {
var object_or_destructuring_ = embed_tokens(function object_or_destructuring_() {
var start = S.token, first = true, a = [];
expect("{");
while (!is("punc", "}")) {
@@ -2113,6 +2222,7 @@ function parse($TEXT, options) {
}
return name;
}
var is_async = false;
var is_static = false;
var is_generator = false;
var property_token = start;
@@ -2121,6 +2231,11 @@ function parse($TEXT, options) {
property_token = S.token;
name = as_property_name();
}
if (name === "async" && !is("punc", "(") && !is("punc", ",") && !is("punc", "}")) {
is_async = true;
property_token = S.token;
name = as_property_name();
}
if (name === null) {
is_generator = true;
property_token = S.token;
@@ -2135,10 +2250,11 @@ function parse($TEXT, options) {
start : start,
static : is_static,
is_generator: is_generator,
async : is_async,
key : name,
quote : name instanceof AST_SymbolMethod ?
property_token.quote : undefined,
value : create_accessor(is_generator),
value : create_accessor(is_generator, is_async),
end : prev()
});
return node;
@@ -2334,14 +2450,28 @@ function parse($TEXT, options) {
}
}
var is_definition =
is("keyword", "var") || is("keyword", "let") || is("keyword", "const") ||
is("keyword", "class") || is("keyword", "function");
var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const");
if (is_definition) {
if (is_default) unexpected();
exported_definition = statement();
} else if (is("keyword", "class")) {
var cls = expr_atom(false);
if (cls.name) {
cls.name = new AST_SymbolDefClass(cls.name);
exported_definition = new AST_DefClass(cls);
} else {
exported_value = cls;
}
} else if (is("keyword", "function")) {
var func = expr_atom(false);
if (func.name) {
func.name = new AST_SymbolDefun(func.name);
exported_definition = new AST_Defun(func);
} else {
exported_value = func;
}
} else {
exported_value = expression();
exported_value = expression(false);
semicolon();
}
@@ -2373,7 +2503,8 @@ function parse($TEXT, options) {
unexpected(tmp);
}
case "name":
if (tmp.value === "yield" && S.input.has_directive("use strict") && !is_in_generator()) {
if (tmp.value == "yield" && !is_token(peek(), "punc", ":")
&& S.input.has_directive("use strict") && !is_in_generator()) {
token_error(tmp, "Unexpected yield identifier inside strict mode");
}
case "string":
@@ -2488,6 +2619,14 @@ function parse($TEXT, options) {
var maybe_unary = function(allow_calls) {
var start = S.token;
if (start.type == "name" && start.value == "await") {
if (is_in_async()) {
next();
return _await_expression();
} else if (S.input.has_directive("use strict")) {
token_error(S.token, "Unexpected await identifier inside strict mode")
}
}
if (is("operator") && UNARY_PREFIX(start.value)) {
next();
handle_regexp();
@@ -2629,7 +2768,7 @@ function parse($TEXT, options) {
if (start.type == "punc" && start.value == "(" && peek().value == ")") {
next();
next();
return arrow_function([]);
return arrow_function(start, []);
}
if (is("name") && is_token(peek(), "arrow")) {
@@ -2639,7 +2778,7 @@ function parse($TEXT, options) {
end: start,
});
next();
return arrow_function([param])
return arrow_function(start, [param]);
}
var left = maybe_conditional(no_in);
@@ -2670,16 +2809,7 @@ function parse($TEXT, options) {
next();
commas = true;
}
if (exprs.length == 1) {
var expr = exprs[0];
if (!(expr instanceof AST_SymbolRef) || !is("arrow", "=>")) return expr;
return arrow_function(new AST_ArrowParametersOrSeq({
start: expr.start,
end: expr.end,
expressions: [expr]
}));
}
return new AST_Sequence({
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
start : start,
expressions : exprs,
end : peek()

View File

@@ -96,7 +96,8 @@ function mangle_properties(ast, options) {
reserved: null,
});
var reserved = options.reserved || [];
var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved);
var cache = options.cache;

View File

@@ -51,7 +51,6 @@ function SymbolDef(scope, index, orig) {
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++;
@@ -65,7 +64,6 @@ SymbolDef.prototype = {
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
@@ -111,8 +109,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
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()) {
@@ -152,22 +148,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 +166,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
@@ -206,23 +183,24 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
while (parent_lambda.is_block_scope()) {
parent_lambda = parent_lambda.parent_scope;
}
(node.scope = parent_lambda).def_function(node, in_export, in_block);
mark_export((node.scope = parent_lambda).def_function(node), 1);
}
else if (node instanceof AST_SymbolClass) {
defun.def_variable(node, in_export, in_block);
mark_export(defun.def_variable(node), 1);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node, in_export, in_block);
scope.def_variable(node);
}
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);
mark_export((node.scope = defun.parent_scope).def_function(node), 1);
}
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);
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node);
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
node.mark_enclosed(options);
@@ -234,7 +212,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);
@@ -245,28 +223,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}));
node.thedef = sym;
}
function mark_export(def, level) {
var node = tw.parent(level);
def.export = node instanceof AST_Export && !node.is_default;
}
});
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,11 +246,10 @@ 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);
@@ -351,22 +317,13 @@ 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_Node.DEFMETHOD("is_block_scope", return_false);
AST_Class.DEFMETHOD("is_block_scope", return_false);
AST_Lambda.DEFMETHOD("is_block_scope", return_false);
AST_Toplevel.DEFMETHOD("is_block_scope", return_false);
AST_SwitchBranch.DEFMETHOD("is_block_scope", return_false);
AST_Block.DEFMETHOD("is_block_scope", return_true);
AST_IterationStatement.DEFMETHOD("is_block_scope", return_true);
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
@@ -404,24 +361,18 @@ 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){
var def = this.def_variable(symbol);
this.functions.set(symbol.name, def);
return def;
});
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);
@@ -474,9 +425,7 @@ AST_Symbol.DEFMETHOD("unmangleable", function(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
@@ -487,13 +436,9 @@ AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared;
});
AST_LabelRef.DEFMETHOD("undeclared", function(){
return false;
});
AST_LabelRef.DEFMETHOD("undeclared", return_false);
AST_Label.DEFMETHOD("undeclared", function(){
return false;
});
AST_Label.DEFMETHOD("undeclared", return_false);
AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef;
@@ -504,7 +449,7 @@ 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,
@@ -512,6 +457,8 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
reserved : [],
toplevel : false,
});
if (!Array.isArray(options.reserved)) options.reserved = [];
return options;
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
@@ -569,7 +516,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
});
this.walk(tw);
to_mangle.forEach(function(def){
if (def.destructuring && !def.destructuring.is_array) return;
def.mangle(options);
});
@@ -643,6 +589,8 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
base54.consider("finally");
else if (node instanceof AST_Yield)
base54.consider("yield");
else if (node instanceof AST_Await)
base54.consider("await");
else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)

View File

@@ -199,6 +199,10 @@ TreeTransformer.prototype = new TreeWalker;
if (self.expression) self.expression = self.expression.transform(tw);
});
_(AST_Await, function(self, tw){
self.expression = self.expression.transform(tw);
});
_(AST_Unary, function(self, tw){
self.expression = self.expression.transform(tw);
});
@@ -240,6 +244,7 @@ TreeTransformer.prototype = new TreeWalker;
});
_(AST_Export, function(self, tw){
if (self.exported_definition) self.exported_definition = self.exported_definition.transform(tw);
if (self.exported_value) self.exported_value = self.exported_value.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

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.0.5",
"version": "3.0.18",
"engines": {
"node": ">=0.8.0"
},
@@ -34,10 +34,11 @@
},
"devDependencies": {
"acorn": "~5.0.3",
"mocha": "~2.3.4"
"mocha": "~2.3.4",
"semver": "~5.3.0"
},
"scripts": {
"test": "node test/run-tests.js"
},
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
"keywords": ["uglify", "uglify-js", "uglify-es", "minify", "minifier", "es5", "es6", "es2015"]
}

View File

@@ -4,12 +4,13 @@
"use strict";
var createHash = require("crypto").createHash;
var fetch = require("./fetch");
var fork = require("child_process").fork;
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,12 +30,7 @@ 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("SHA1 sum:", info.sha1);
@@ -57,7 +53,8 @@ urls.forEach(function(url) {
output: 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;

View File

@@ -135,3 +135,70 @@ arrow_with_regexp: {
num => /\d{11,14}/.test( num )
}
}
arrow_unused: {
options = {
toplevel: false,
side_effects: true,
unused: true,
}
input: {
top => dog;
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let unused = x => { console.log(x); };
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect: {
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect_stdout: [ "0", "1", "2", "9" ]
node_version: ">=6"
}
arrow_unused_toplevel: {
options = {
toplevel: true,
side_effects: true,
unused: true,
}
input: {
top => dog;
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let unused = x => { console.log(x); };
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect: {
let fn = a => { console.log(a * a); };
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect_stdout: [ "0", "1", "2", "9" ]
node_version: ">=6"
}

245
test/compress/async.js Normal file
View File

@@ -0,0 +1,245 @@
await_precedence: {
input: {
async function f1() { await x + y; }
async function f2() { await (x + y); }
}
expect_exact: "async function f1(){await x+y}async function f2(){await(x+y)}"
}
async_function_declaration: {
options = {
side_effects: true,
unused: true,
}
input: {
async function f0() {}
async function f1() { await x + y; }
async function f2() { await (x + y); }
async function f3() { await x + await y; }
async function f4() { await (x + await y); }
async function f5() { await x; await y; }
async function f6() { await x, await y; }
}
expect: {
async function f0() {}
async function f1() { await x, y; }
async function f2() { await (x + y); }
async function f3() { await x, await y; }
async function f4() { await (x + await y); }
async function f5() { await x; await y; }
async function f6() { await x, await y; }
}
}
async_function_expression: {
options = {
evaluate: true,
side_effects: true,
unused: true,
}
input: {
var named = async function foo() {
await bar(1 + 0) + (2 + 0);
}
var anon = async function() {
await (1 + 0) + bar(2 + 0);
}
}
expect: {
var named = async function() {
await bar(1);
};
var anon = async function() {
await 1, bar(2);
};
}
}
async_class: {
options = {
evaluate: true,
}
input: {
class Foo {
async m1() {
return await foo(1 + 2);
}
static async m2() {
return await foo(3 + 4);
}
}
}
expect: {
class Foo {
async m1() {
return await foo(3);
}
static async m2() {
return await foo(7);
}
}
}
}
async_object_literal: {
options = {
evaluate: true,
}
input: {
var obj = {
async a() {
await foo(1 + 0);
},
anon: async function(){
await foo(2 + 0);
}
};
}
expect: {
var obj = {
async a() {
await foo(1);
},
anon: async function() {
await foo(2);
}
};
}
}
async_export: {
input: {
export async function run() {};
export default async function def() {};
}
expect: {
export async function run() {};
export default async function def() {};
}
}
async_inline: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
(async function(){ return await 3; })();
(async function(x){ await console.log(x); })(4);
function echo(x) { return x; }
echo( async function(){ return await 1; }() );
echo( async function(x){ await console.log(x); }(2) );
function top() { console.log("top"); }
top();
async function async_top() { console.log("async_top"); }
async_top();
}
expect: {
!async function(){await 3}();
!async function(x){await console.log(4)}();
function echo(x){return x}
echo(async function(){return await 1}());
echo(async function(x){await console.log(2)}());
console.log("top");
!async function(){console.log("async_top")}();
}
expect_stdout: [
"4",
"2",
"top",
"async_top",
]
node_version: ">=8"
}
async_identifiers: {
input: {
let async = function(x){ console.log("async", x); };
let await = function(x){ console.log("await", x); };
async(1);
await(2);
}
expect: {
let async = function(x){ console.log("async", x); };
let await = function(x){ console.log("await", x); };
async(1);
await(2);
}
expect_stdout: [
"async 1",
"await 2",
]
node_version: ">=8"
}
async_shorthand_property: {
mangle = {
toplevel: true,
}
input: {
function print(o) { console.log(o.async + " " + o.await); }
var async = "Async", await = "Await";
print({ async });
print({ await });
print({ async, await });
print({ await, async });
print({ async: async });
print({ await: await });
print({ async: async, await: await });
print({ await: await, async: async });
}
expect: {
function a(a) { console.log(a.async + " " + a.await); }
var n = "Async", c = "Await";
a({ async: n });
a({ await: c });
a({ async: n, await: c });
a({ await: c, async: n });
a({ async: n });
a({ await: c });
a({ async: n, await: c });
a({ await: c, async: n });
}
expect_stdout: [
"Async undefined",
"undefined Await",
"Async Await",
"Async Await",
"Async undefined",
"undefined Await",
"Async Await",
"Async Await",
]
node_version: ">=4"
}
/* FIXME: add test when supported by parser
async_arrow: {
input: {
let a1 = async x => await foo(x);
let a2 = async () => await bar();
let a3 = async (x) => await baz(x);
let a4 = async (x, y) => { await far(x, y); }
let a5 = async ({x = [1], y: z = 2}) => { await wow(x, y); }
}
expect: {
}
}
*/

View File

@@ -47,3 +47,142 @@ keep_some_blocks: {
} else stuff();
}
}
issue_1664: {
input: {
var a = 1;
function f() {
if (undefined) a = 2;
{
function undefined() {}
undefined();
}
}
f();
console.log(a);
}
expect: {
var a = 1;
function f() {
if (undefined) a = 2;
{
function undefined() {}
undefined();
}
}
f();
console.log(a);
}
expect_stdout: "1"
node_version: ">=6"
}
issue_1672_for: {
input: {
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect: {
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_for_strict: {
input: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_if: {
input: {
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect: {
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) function xxx() {}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_if_strict: {
input: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}

View File

@@ -1146,7 +1146,7 @@ collapse_vars_constants: {
function f3(x) {
var b = x.prop;
sideeffect1();
return b + -9;
return b + (function() { return -9; })();
}
}
}

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(){};
var g;
}
f();
}
expect_stdout: true
node_version: ">=6"
}
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,16 +122,55 @@ dead_code_constant_boolean_should_warn_more: {
foo();
var moo;
}
bar();
}
expect: {
var foo;
function bar() {}
var bar;
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo;
bar();
}
expect_stdout: true
node_version: ">=6"
}
dead_code_constant_boolean_should_warn_more_strict: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
side_effects : true,
};
input: {
"use strict";
while (!((foo && bar) || (x + "0"))) {
console.log("unreachable");
var foo;
function bar() {}
}
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
}
bar();
}
expect: {
"use strict";
var foo;
// nothing for the while
// 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: {
@@ -134,7 +217,7 @@ dead_code_const_declaration: {
var unused;
const CONST_FOO = !1;
var moo;
function bar() {}
var bar;
}
expect_stdout: true
}
@@ -162,7 +245,7 @@ dead_code_const_annotation: {
var unused;
var CONST_FOO_ANN = !1;
var moo;
function bar() {}
var bar;
}
expect_stdout: true
}
@@ -229,7 +312,7 @@ dead_code_const_annotation_complex_scope: {
var CONST_FOO_ANN = !1;
var unused_var_2;
var moo;
function bar() {}
var bar;
var beef = 'good';
var meat = 'beef';
var pork = 'bad';

View File

@@ -328,5 +328,314 @@ issue_1886: {
let [a] = [1];
console.log(a);
}
expect_exact: "1"
}
destructuring_decl_of_numeric_key: {
options = {
evaluate: true,
unused: true,
}
input: {
let { 3: x } = { [1 + 2]: 42 };
console.log(x);
}
expect: {
let { 3: x } = { [3]: 42 };
console.log(x);
}
expect_stdout: "42"
node_version: ">=6"
}
destructuring_decl_of_computed_key: {
options = {
evaluate: true,
unused: true,
}
input: {
let four = 4;
let { [7 - four]: x } = { [1 + 2]: 42 };
console.log(x);
}
expect: {
let four = 4;
let { [7 - four]: x } = { [3]: 42 };
console.log(x);
}
expect_stdout: "42"
node_version: ">=6"
}
destructuring_assign_of_numeric_key: {
options = {
evaluate: true,
unused: true,
}
input: {
let x;
({ 3: x } = { [1 + 2]: 42 });
console.log(x);
}
expect: {
let x;
({ 3: x } = { [3]: 42 });
console.log(x);
}
expect_stdout: "42"
node_version: ">=6"
}
destructuring_assign_of_computed_key: {
options = {
evaluate: true,
unused: true,
}
input: {
let x;
let four = 4;
({ [(5 + 2) - four]: x } = { [1 + 2]: 42 });
console.log(x);
}
expect: {
let x;
let four = 4;
({ [7 - four]: x } = { [3]: 42 });
console.log(x);
}
expect_stdout: "42"
node_version: ">=6"
}
mangle_destructuring_decl: {
options = {
evaluate: true,
unused: true,
}
mangle = {
}
input: {
function test(opts) {
let a = opts.a || { e: 7, n: 8 };
let { t, e, n, s = 5 + 4, o, r } = a;
console.log(t, e, n, s, o, r);
}
test({a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 }});
test({});
}
expect: {
function test(t) {
let e = t.a || { e: 7, n: 8 };
let {t: n, e: o, n: s, s: a = 9, o: c, r: l} = e;
console.log(n, o, s, a, c, l);
}
test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
test({});
}
expect_stdout: [
"1 2 3 4 5 6",
"undefined 7 8 9 undefined undefined",
]
node_version: ">=6"
}
mangle_destructuring_assign_toplevel_true: {
options = {
toplevel: true,
evaluate: true,
unused: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 6
}
input: {
function test(opts) {
let s, o, r;
let a = opts.a || { e: 7, n: 8 };
({ t, e, n, s = 5 + 4, o, r } = a);
console.log(t, e, n, s, o, r);
}
let t, e, n;
test({a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 }});
test({});
}
expect: {
function n(n) {
let t, a, c;
let l = n.a || { e: 7, n: 8 };
({t: o, e, n: s, s: t = 9, o: a, r: c} = l);
console.log(o, e, s, t, a, c);
}
let o, e, s;
n({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
n({});
}
expect_stdout: [
"1 2 3 4 5 6",
"undefined 7 8 9 undefined undefined",
]
node_version: ">=6"
}
mangle_destructuring_assign_toplevel_false: {
options = {
toplevel: false,
evaluate: true,
unused: true,
}
mangle = {
toplevel: false,
}
beautify = {
ecma: 6
}
input: {
function test(opts) {
let s, o, r;
let a = opts.a || { e: 7, n: 8 };
({ t, e, n, s = 9, o, r } = a);
console.log(t, e, n, s, o, r);
}
let t, e, n;
test({a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 }});
test({});
}
expect: {
function test(o) {
let s, a, c;
let l = o.a || { e: 7, n: 8 };
({t, e, n, s = 9, o: a, r: c} = l);
console.log(t, e, n, s, a, c);
}
let t, e, n;
test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
test({});
}
expect_stdout: [
"1 2 3 4 5 6",
"undefined 7 8 9 undefined undefined",
]
node_version: ">=6"
}
mangle_destructuring_decl_array: {
options = {
evaluate: true,
unused: true,
toplevel: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 6
}
input: {
var [, t, e, n, s, o = 2, r = [ 1 + 2 ]] = [ 9, 8, 7, 6 ];
console.log(t, e, n, s, o, r);
}
expect: {
var [, o, l, a, c, e = 2, g = [ 3 ]] = [ 9, 8, 7, 6 ];
console.log(o, l, a, c, e, g);
}
expect_stdout: "8 7 6 undefined 2 [ 3 ]"
node_version: ">=6"
}
anon_func_with_destructuring_args: {
options = {
evaluate: true,
unused: true,
toplevel: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 5,
}
input: {
(function({foo = 1 + 0, bar = 2}, [car = 3, far = 4]) {
console.log(foo, bar, car, far);
})({bar: 5 - 0}, [, 6]);
}
expect: {
(function({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) {
console.log(o, n, a, b);
})({bar: 5}, [, 6]);
}
expect_stdout: "1 5 3 6"
node_version: ">=6"
}
arrow_func_with_destructuring_args: {
options = {
evaluate: true,
unused: true,
toplevel: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 5,
}
input: {
(({foo = 1 + 0, bar = 2}, [car = 3, far = 4]) => {
console.log(foo, bar, car, far);
})({bar: 5 - 0}, [, 6]);
}
expect: {
(({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) => {
console.log(o, n, a, b);
})({bar: 5}, [, 6]);
}
expect_stdout: "1 5 3 6"
node_version: ">=6"
}
issue_2044_ecma_5: {
beautify = {
beautify: false,
ecma: 5,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x:a=1,y:y=2+b,z:z=3-c}=obj);"
}
issue_2044_ecma_6: {
beautify = {
beautify: false,
ecma: 6,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x:a=1,y=2+b,z=3-c}=obj);"
}
issue_2044_ecma_5_beautify: {
beautify = {
beautify: true,
ecma: 5,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x: a = 1, y: y = 2 + b, z: z = 3 - c} = obj);"
}
issue_2044_ecma_6_beautify: {
beautify = {
beautify: true,
ecma: 6,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x: a = 1, y = 2 + b, z = 3 - c} = obj);"
}

View File

@@ -863,12 +863,12 @@ issue_1583: {
expect: {
function m(t) {
(function(e) {
t = (function() {
return (function(a) {
return a;
})(function(a) {});
})();
})();
t = e();
})(function() {
return (function(a) {
return a;
})(function(a) {});
});
}
}
}
@@ -1253,3 +1253,88 @@ reassign_const: {
}
expect_stdout: true
}
issue_1968: {
options = {
unused: true,
}
input: {
function f(c) {
var a;
if (c) {
let b;
return (a = 2) + (b = 3);
}
}
console.log(f(1));
}
expect: {
function f(c) {
if (c) {
let b;
return 2 + (b = 3);
}
}
console.log(f(1));
}
expect_stdout: "5"
node_version: ">=6"
}
issue_2063: {
options = {
unused: true,
}
input: {
var a;
var a;
}
expect: {
var a;
var a;
}
}
issue_2105: {
options = {
collapse_vars: true,
inline: true,
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: {
!void function() {
var quux = function() {
console.log("PASS");
};
return {
prop: function() {
console.log;
quux();
}
};
}().prop();
}
expect_stdout: "PASS"
}

View File

@@ -738,6 +738,7 @@ unsafe_prototype_function: {
call_args: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
}
input: {
@@ -758,6 +759,7 @@ call_args: {
call_args_drop_param: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
unused: true,
@@ -873,13 +875,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 +1087,78 @@ 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/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect_stdout: "b"
}
issue_1964_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
return "a b c".split(/\s/)[1];
}
console.log(f());
}
expect_stdout: "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"
}

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,342 @@ 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 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 'function' 'function'",
]
node_version: ">=6"
}
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("a", "b", "b.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;
!function() {
0;
}((c += 1, c = 1 + c, 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

@@ -160,3 +160,17 @@ issue_1801: {
console.log(!0);
}
}
issue_1986: {
options = {
global_defs: {
"@alert": "console.log",
},
}
input: {
alert(42);
}
expect: {
console.log(42);
}
}

View File

@@ -241,6 +241,30 @@ export_default_array: {
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";
@@ -509,3 +533,134 @@ issue_1753_disable: {
}
}
}
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/){}}"
}
issue_2028: {
options = {
side_effects: true,
}
input: {
var a = {};
(function(x) {
x.X = function() {
return X;
};
class X {
static hello() {
console.log("hello");
}
}
}(a));
a.X().hello();
}
expect: {
var a = {};
(function(x) {
x.X = function() {
return X;
};
class X {
static hello() {
console.log("hello");
}
}
}(a));
a.X().hello();
}
expect_stdout: "hello"
node_version: ">=6"
}
class_expression_statement: {
options = {
toplevel: false,
side_effects: false,
unused: false,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: "(class{});(class NamedClassExpr{});let expr=class AnotherClassExpr{};class C{}"
}
class_expression_statement_unused: {
options = {
toplevel: false,
side_effects: true,
unused: true,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: "let expr=class{};class C{}"
}
class_expression_statement_unused_toplevel: {
options = {
toplevel: true,
side_effects: true,
unused: true,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: ""
}

View File

@@ -302,3 +302,85 @@ 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();
}
}
}
issue_1317: {
options = {
if_return: true,
}
input: {
!function(a) {
if (a) return;
let b = 1;
function g() {
return b;
}
console.log(g());
}();
}
expect: {
!function(a) {
if (a) return;
let b = 1;
function g() {
return b;
}
console.log(g());
}();
}
expect_stdout: "1"
node_version: ">=6"
}
issue_1317_strict: {
options = {
if_return: true,
}
input: {
"use strict";
!function(a) {
if (a) return;
let b = 1;
function g() {
return b;
}
console.log(g());
}();
}
expect: {
"use strict";
!function(a) {
if (a) return;
let b = 1;
function g() {
return b;
}
console.log(g());
}();
}
expect_stdout: "1"
node_version: ">=4"
}

View File

@@ -116,3 +116,137 @@ non_hoisted_function_after_return_2b: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,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:131,16]',
"WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,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:173,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]",
]
}
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:225,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:227,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:227,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]",
]
}

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,69 @@ 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 h() {}
if (window) function g() {}
}
}
}
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,6 +1,7 @@
unary_prefix: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}

View File

@@ -0,0 +1,31 @@
operator: {
input: {
a. //comment
typeof
}
expect_exact: "a.typeof;"
}
name: {
input: {
a. //comment
b
}
expect_exact: "a.b;"
}
keyword: {
input: {
a. //comment
default
}
expect_exact: "a.default;"
}
atom: {
input: {
a. //comment
true
}
expect_exact: "a.true;"
}

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

@@ -0,0 +1,281 @@
export_func_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export function f(){};
}
expect_exact: "export function f(){};"
}
export_func_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export function f(){}(1);
}
expect_exact: "export function f(){};1;"
}
export_func_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export function f(){}(1);
}
expect_exact: "export function f(){};"
}
export_default_func_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export default function f(){};
}
expect_exact: "export default function(){};"
}
export_default_func_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export default function f(){}(1);
}
expect_exact: "export default function(){};1;"
}
export_default_func_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export default function f(){}(1);
}
expect_exact: "export default function(){};"
}
export_class_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export class C {};
}
expect_exact: "export class C{};"
}
export_class_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export class C {}(1);
}
expect_exact: "export class C{};1;"
}
export_class_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export class C {}(1);
}
expect_exact: "export class C{};"
}
export_default_class_1: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export default class C {};
}
expect_exact: "export default class{};"
}
export_default_class_2: {
options = {
hoist_funs: true,
side_effects: false,
toplevel: true,
unused: true,
}
input: {
export default class C {}(1);
}
expect_exact: "export default class{};1;"
}
export_default_class_3: {
options = {
hoist_funs: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
export default class C {}(1);
}
expect_exact: "export default class{};"
}
export_mangle_1: {
mangle = {
toplevel: true,
}
input: {
export function foo(one, two) {
return one - two;
};
}
expect_exact: "export function foo(n,o){return n-o};"
}
export_mangle_2: {
mangle = {
toplevel: true,
}
input: {
export default function foo(one, two) {
return one - two;
};
}
expect_exact: "export default function n(n,r){return n-r};"
}
export_mangle_3: {
options = {
collapse_vars: true,
}
mangle = {
toplevel: true,
}
input: {
export class C {
go(one, two) {
var z = one;
return one - two + z;
}
};
}
expect_exact: "export class C{go(n,r){return n-r+n}};"
}
export_mangle_4: {
options = {
collapse_vars: true,
}
mangle = {
toplevel: true,
}
input: {
export default class C {
go(one, two) {
var z = one;
return one - two + z;
}
};
}
expect_exact: "export default class n{go(n,r){return n-r+n}};"
}
export_mangle_5: {
mangle = {
toplevel: true,
}
input: {
export default {
prop: function(one, two) {
return one - two;
}
};
}
expect_exact: "export default{prop:function(n,r){return n-r}};"
}
export_mangle_6: {
mangle = {
toplevel: true,
}
input: {
var baz = 2;
export let foo = 1, bar = baz;
}
expect_exact: "var a=2;export let foo=1,bar=a;"
}
export_toplevel_1: {
options = {
toplevel: true,
unused: true,
}
input: {
function f(){}
export function g(){};
export default function h(){};
}
expect: {
export function g(){};
export default function(){};
}
}
export_toplevel_2: {
options = {
toplevel: true,
unused: true,
}
input: {
class A {}
export class B {};
export default class C {};
}
expect: {
export class B {};
export default class {};
}
}
export_default_func_ref: {
options = {
hoist_funs: true,
toplevel: true,
unused: true,
}
input: {
export default function f(){};
f();
}
expect_exact: "export default function f(){};f();"
}

View File

@@ -1,7 +1,8 @@
compress_new_function: {
options = {
unsafe: true
unsafe: true,
unsafe_Func: true,
}
input: {
new Function("aa, bb", 'return aa;');
@@ -14,6 +15,7 @@ compress_new_function: {
compress_new_function_with_destruct: {
options = {
unsafe: true,
unsafe_Func: true,
ecma: 6
}
beautify = {
@@ -26,9 +28,7 @@ compress_new_function_with_destruct: {
}
expect: {
Function("a", "[b]", "return a");
Function("a", "{bb}", "return a");
Function("[[a]]", "[{bb}]", 'return a');
Function("a", "{bb:b}", "return a");
Function("[[a]]", "[{bb:b}]", 'return a');
}
}

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

@@ -0,0 +1,483 @@
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: {
options = {
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function() {
console.log("hello");
}());
}
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;
!function() {
a++;
}(++a && a.var);
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;
!function(a_1) {
a++;
}(++a && a.var);
console.log(a);
}
expect_stdout: "3"
}

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

@@ -0,0 +1,38 @@
eval_let_6: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
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

@@ -557,3 +557,105 @@ 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"
}

View File

@@ -178,3 +178,66 @@ 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"
}

View File

@@ -2,6 +2,7 @@ reduce_vars: {
options = {
conditionals : true,
evaluate : true,
inline : true,
global_defs : {
C : 0
},
@@ -1032,6 +1033,7 @@ defun_inline_2: {
defun_inline_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
@@ -1054,6 +1056,7 @@ defun_inline_3: {
defun_call: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1080,6 +1083,7 @@ defun_call: {
defun_redefine: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1112,6 +1116,7 @@ defun_redefine: {
func_inline: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1138,6 +1143,7 @@ func_inline: {
func_modified: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1311,19 +1317,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: {
@@ -1687,6 +1721,7 @@ redefine_arguments_1: {
redefine_arguments_2: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
side_effects: true,
@@ -1723,6 +1758,7 @@ redefine_arguments_2: {
redefine_arguments_3: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
reduce_vars: true,
@@ -1799,6 +1835,7 @@ redefine_farg_1: {
redefine_farg_2: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
side_effects: true,
@@ -1835,6 +1872,7 @@ redefine_farg_2: {
redefine_farg_3: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
reduce_vars: true,
@@ -2537,3 +2575,53 @@ accessor: {
}
expect_stdout: "1 1"
}
issue_2090_1: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
var x = 1;
[].forEach(() => x = 2);
return x;
}());
}
expect: {
console.log(function() {
var x = 1;
[].forEach(() => x = 2);
return x;
}());
}
expect_stdout: "1"
node_version: ">=4"
}
issue_2090_2: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
var x = 1;
[].forEach(() => {
x = 2;
});
return x;
}());
}
expect: {
console.log(function() {
var x = 1;
[].forEach(() => {
x = 2;
});
return x;
}());
}
expect_stdout: "1"
node_version: ">=4"
}

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

@@ -734,3 +734,23 @@ reassign_const: {
}
expect_stdout: true
}
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"
}

View File

@@ -8,3 +8,12 @@ octal_escape_sequence: {
var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30";
}
}
issue_1929: {
input: {
function f(s) {
return s.split(/[\\/]/);
}
}
expect_exact: "function f(s){return s.split(/[\\\\/]/)}"
}

View File

@@ -190,3 +190,12 @@ yield_sub: {
}
expect_exact: 'function*foo(){yield x["foo"];(yield x)["foo"];yield(yield obj.foo())["bar"]()}'
}
yield_as_ES5_property: {
input: {
"use strict";
console.log({yield: 42}.yield);
}
expect_exact: '"use strict";console.log({yield:42}.yield);'
expect_stdout: "42"
}

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

@@ -0,0 +1 @@
if (0) else 1;

View File

@@ -0,0 +1 @@
return 42;

View File

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

View File

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

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

@@ -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();
});
@@ -55,15 +57,15 @@ describe("bin/uglifyjs", function () {
});
});
it("Should append source map to output when using --source-map url=inline", function (done) {
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map url=inline";
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map url=inline";
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
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");
done();
});
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n");
done();
});
});
it("should not append source map to output when not using --source-map url=inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js';
@@ -75,85 +77,102 @@ 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';
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --keep-fnames (mangle & compress)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m -c unused=false';
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m -c unused=false';
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(5==f(g)());\n");
done();
});
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(5==f(g)());\n");
done();
});
});
it("Should work with keep_fnames under mangler options", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep_fnames=true';
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep_fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --define (simple)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define D=5 -c';
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define D=5 -c';
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(5);\n");
done();
});
assert.strictEqual(stdout, "console.log(5);\n");
done();
});
});
it("Should work with --define (nested)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/nested.js --define C.D=5,C.V=3 -c';
var command = uglifyjscmd + ' test/input/global_defs/nested.js --define C.D=5,C.V=3 -c';
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(3,5);\n");
done();
});
assert.strictEqual(stdout, "console.log(3,5);\n");
done();
});
});
it("Should work with --define (AST_Node)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define console.log=stdout.println -c';
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define console.log=stdout.println -c';
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "stdout.println(D);\n");
done();
});
assert.strictEqual(stdout, "stdout.println(D);\n");
done();
});
});
it("Should work with `--beautify`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b';
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b';
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
done();
});
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
done();
});
});
it("Should work with `--beautify bracketize`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize';
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize';
exec(command, function (err, stdout) {
if (err) throw err;
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/bracketize.js"));
done();
});
assert.strictEqual(stdout, read("test/input/issue-1482/bracketize.js"));
done();
});
});
it("Should process inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js -mc toplevel --source-map content=inline,url=inline";
@@ -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,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=",
"",
].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n");
@@ -260,229 +279,259 @@ describe("bin/uglifyjs", function () {
});
});
it("Should throw syntax error (5--)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_1.js';
var command = uglifyjscmd + ' test/input/invalid/assign_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/assign_1.js:1,18",
"console.log(1 || 5--);",
" ^",
"ERROR: Invalid use of -- operator"
].join("\n"));
done();
});
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/assign_1.js:1,18",
"console.log(1 || 5--);",
" ^",
"ERROR: Invalid use of -- operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (Math.random() /= 2)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_2.js';
var command = uglifyjscmd + ' test/input/invalid/assign_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/assign_2.js:1,32",
"console.log(2 || (Math.random() /= 2));",
" ^",
"ERROR: Invalid assignment"
].join("\n"));
done();
});
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/assign_2.js:1,32",
"console.log(2 || (Math.random() /= 2));",
" ^",
"ERROR: Invalid assignment"
].join("\n"));
done();
});
});
it("Should throw syntax error (++this)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_3.js';
var command = uglifyjscmd + ' test/input/invalid/assign_3.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/assign_3.js:1,17",
"console.log(3 || ++this);",
" ^",
"ERROR: Invalid use of ++ operator"
].join("\n"));
done();
});
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/assign_3.js:1,17",
"console.log(3 || ++this);",
" ^",
"ERROR: Invalid use of ++ operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (++null)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_4.js';
var command = uglifyjscmd + ' test/input/invalid/assign_4.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/assign_4.js:1,0",
"++null",
"^",
"ERROR: Invalid use of ++ operator"
].join("\n"));
done();
});
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/assign_4.js:1,0",
"++null",
"^",
"ERROR: Invalid use of ++ operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (a.=)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_1.js';
var command = uglifyjscmd + ' test/input/invalid/dot_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/dot_1.js:1,2",
"a.=",
" ^",
"ERROR: Unexpected token: operator (=)"
].join("\n"));
done();
});
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/dot_1.js:1,2",
"a.=",
" ^",
"ERROR: Unexpected token: operator (=)"
].join("\n"));
done();
});
});
it("Should throw syntax error (%.a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_2.js';
var command = uglifyjscmd + ' test/input/invalid/dot_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/dot_2.js:1,0",
"%.a;",
"^",
"ERROR: Unexpected token: operator (%)"
].join("\n"));
done();
});
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/dot_2.js:1,0",
"%.a;",
"^",
"ERROR: Unexpected token: operator (%)"
].join("\n"));
done();
});
});
it("Should throw syntax error (a./();)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_3.js';
var command = uglifyjscmd + ' test/input/invalid/dot_3.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/dot_3.js:1,2",
"a./();",
" ^",
"ERROR: Unexpected token: operator (/)"
].join("\n"));
done();
});
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/dot_3.js:1,2",
"a./();",
" ^",
"ERROR: Unexpected token: operator (/)"
].join("\n"));
done();
});
});
it("Should throw syntax error ({%: 1})", function(done) {
var command = uglifyjscmd + ' test/input/invalid/object.js';
var command = uglifyjscmd + ' test/input/invalid/object.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/object.js:1,13",
"console.log({%: 1});",
" ^",
"ERROR: Unexpected token: operator (%)"
].join("\n"));
done();
});
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/object.js:1,13",
"console.log({%: 1});",
" ^",
"ERROR: Unexpected token: operator (%)"
].join("\n"));
done();
});
});
it("Should throw syntax error (const a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/const.js';
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();
});
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';
var command = uglifyjscmd + ' test/input/invalid/delete.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/delete.js:13,11",
" delete x;",
" ^",
"ERROR: Calling delete on expression not allowed in strict mode"
].join("\n"));
done();
});
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/delete.js:13,11",
" delete x;",
" ^",
"ERROR: Calling delete on expression not allowed in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (function g(arguments))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_1.js';
var command = uglifyjscmd + ' test/input/invalid/function_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/function_1.js:4,11",
"function g(arguments) {",
" ^",
"ERROR: Unexpected arguments in strict mode"
].join("\n"));
done();
});
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/function_1.js:4,11",
"function g(arguments) {",
" ^",
"ERROR: Unexpected arguments in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (function eval())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_2.js';
var command = uglifyjscmd + ' test/input/invalid/function_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/function_2.js:4,9",
"function eval() {",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
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/function_2.js:4,9",
"function eval() {",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (iife arguments())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_3.js';
var command = uglifyjscmd + ' test/input/invalid/function_3.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/function_3.js:4,10",
"!function arguments() {",
" ^",
"ERROR: Unexpected arguments in strict mode"
].join("\n"));
done();
});
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/function_3.js:4,10",
"!function arguments() {",
" ^",
"ERROR: Unexpected arguments in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (catch(eval))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/try.js';
var command = uglifyjscmd + ' test/input/invalid/try.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/try.js:7,18",
" try {} catch (eval) {}",
" ^",
"ERROR: Unexpected eval identifier as parameter inside strict mode"
].join("\n"));
done();
});
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/try.js:7,18",
" try {} catch (eval) {}",
" ^",
"ERROR: Unexpected eval identifier as parameter inside strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (var eval)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/var.js';
var command = uglifyjscmd + ' test/input/invalid/var.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/var.js:7,8",
" var eval;",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
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/var.js:7,8",
" var eval;",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.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/else.js:1,7",
"if (0) else 1;",
" ^",
"ERROR: Unexpected token: keyword (else)"
].join("\n"));
done();
});
});
it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.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/return.js:1,0",
"return 42;",
"^",
"ERROR: 'return' outside of function"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) {
var command = [
@@ -497,7 +546,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();
@@ -520,4 +569,33 @@ 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();
});
});
});

View File

@@ -31,7 +31,7 @@ describe("bin/uglifyjs with input file globs", function() {
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n');
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
done();
});
});

View File

@@ -13,6 +13,13 @@ describe("minify", function() {
assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
});
it("Should skip inherited keys from `files`", function() {
var files = Object.create({ skip: this });
files[0] = "alert(1 + 1)";
var result = Uglify.minify(files);
assert.strictEqual(result.code, "alert(2);");
});
describe("keep_quoted_props", function() {
it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -106,7 +113,7 @@ describe("minify", function() {
content: "inline"
}
});
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found");
} finally {
@@ -138,13 +145,25 @@ describe("minify", function() {
});
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0=");
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
});
it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };');
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};");
});
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
});
describe("#__PURE__", function() {
@@ -181,4 +200,31 @@ describe("minify", function() {
assert.strictEqual(err.col, 12);
});
});
describe("global_defs", function() {
it("should throw for non-trivial expressions", function() {
var result = Uglify.minify("alert(42);", {
compress: {
global_defs: {
"@alert": "debugger"
}
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
});
it("should skip inherited properties", function() {
var foo = Object.create({ skip: this });
foo.bar = 42;
var result = Uglify.minify("alert(FOO);", {
compress: {
global_defs: {
FOO: foo
}
}
});
assert.strictEqual(result.code, "alert({bar:42});");
});
});
});

View File

@@ -1,23 +1,20 @@
var assert = require("assert");
var semver = require("semver");
var spawn = require("child_process").spawn;
if (!process.env.UGLIFYJS_TEST_ALL) return;
function run(command, args, done) {
var id = setInterval(function() {
process.stdout.write("\0");
}, 5 * 60 * 1000);
spawn(command, args, {
stdio: "ignore"
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
clearInterval(id);
assert.strictEqual(code, 0);
done();
});
}
describe("test/benchmark.js", function() {
this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
[
"-b",
"-b bracketize",
@@ -36,11 +33,9 @@ describe("test/benchmark.js", function() {
});
});
if (semver.satisfies(process.version, "0.12")) return;
describe("test/jetstream.js", function() {
this.timeout(20 * 60 * 1000);
it("Should install phantomjs-prebuilt", function(done) {
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
});
[
"-mc",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
@@ -48,6 +43,7 @@ describe("test/jetstream.js", function() {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
run(process.argv[0], args, done);
});
});

View File

@@ -4,7 +4,7 @@ var uglify = require("../node");
describe("spidermonkey export/import sanity test", function() {
it("should produce a functional build when using --self with spidermonkey", function(done) {
this.timeout(30000);
this.timeout(60000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +
@@ -15,7 +15,9 @@ describe("spidermonkey export/import sanity test", function() {
eval(stdout);
assert.strictEqual(typeof SpiderUglify, "object");
assert.strictEqual(SpiderUglify.minify("foo([true,,2+3]);").code, "foo([!0,,5]);");
var result = SpiderUglify.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done();
});

View File

@@ -65,9 +65,9 @@ describe("Yield", function() {
);
});
it("Should not allow yield to be used as symbol, identifier or property outside generators in strict mode", function() {
it("Should not allow yield to be used as symbol, identifier or shorthand property outside generators in strict mode", function() {
var tests = [
// Fail as as_symbol
// Fail in as_symbol
'"use strict"; import yield from "bar";',
'"use strict"; yield = 123;',
'"use strict"; yield: "123";',
@@ -79,13 +79,12 @@ describe("Yield", function() {
'"use strict"; var yield = "foo";',
'"use strict"; class yield {}',
// Fail as maybe_assign
// Fail in maybe_assign
'"use strict"; var foo = yield;',
'"use strict"; var foo = bar = yield',
// Fail as as_property_name
// Fail in as_property_name
'"use strict"; var foo = {yield};',
'"use strict"; var bar = {yield: "foo"};'
];
var fail = function(e) {
@@ -103,4 +102,4 @@ describe("Yield", function() {
assert.throws(test(tests[i]), fail, tests[i]);
}
});
});
});

View File

@@ -5,6 +5,7 @@ var path = require("path");
var fs = require("fs");
var assert = require("assert");
var sandbox = require("./sandbox");
var semver = require("semver");
var tests_dir = path.dirname(module.filename);
var failures = 0;
@@ -164,7 +165,8 @@ function run_compress_tests() {
failed_files[file] = 1;
}
}
if (test.expect_stdout) {
if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = sandbox.run_code(input_code);
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
@@ -274,7 +276,14 @@ function parse_test(file) {
if (node instanceof U.AST_LabeledStatement) {
var label = node.label;
assert.ok(
["input", "expect", "expect_exact", "expect_warnings", "expect_stdout"].indexOf(label.name) >= 0,
[
"input",
"expect",
"expect_exact",
"expect_warnings",
"expect_stdout",
"node_version",
].indexOf(label.name) >= 0,
tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name,
line: label.start.line,
@@ -282,11 +291,25 @@ function parse_test(file) {
})
);
var stat = node.body;
if (label.name == "expect_exact") {
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
test[label.name] = stat.body.value;
var body = stat.body;
if (body instanceof U.AST_Boolean) {
test[label.name] = body.value;
} else if (body instanceof U.AST_Call) {
var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
return node.value;
}));
} else {
test[label.name] = read_string(stat) + "\n";
}

View File

@@ -1,3 +1,4 @@
var semver = require("semver");
var vm = require("vm");
function safe_log(arg, level) {
@@ -18,23 +19,25 @@ function safe_log(arg, level) {
var FUNC_TOSTRING = [
"Function.prototype.toString = Function.prototype.valueOf = function() {",
" var id = 0;",
" var id = 100000;",
" return function() {",
' if (this === Array) return "[Function: Array]";',
' if (this === Object) return "[Function: Object]";',
" var i = this.name;",
' if (typeof i != "number") {',
" i = ++id;",
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {',
" get: function() {",
" return i;",
" }",
" });",
] : [], [
" }",
' return "[Function: " + i + "]";',
" }",
"}();",
].join("\n");
]).join("\n");
exports.run_code = function(code) {
var stdout = "";
var original_write = process.stdout.write;
@@ -49,7 +52,10 @@ exports.run_code = function(code) {
"}();",
].join("\n"), {
console: {
log: function() {
log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") {
return console.log("%s", msg);
}
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
@@ -63,7 +69,7 @@ exports.run_code = function(code) {
process.stdout.write = original_write;
}
};
exports.same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? function(expected, actual) {
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false;
if (typeof expected != "string") {
if (expected.name != actual.name) return false;

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("./node");
var UglifyJS = require("..");
var ok = require("assert");
module.exports = function () {
@@ -26,11 +26,11 @@ module.exports = function () {
}
function source_map(js) {
var source_map = UglifyJS.SourceMap();
var stream = UglifyJS.OutputStream({ source_map: source_map });
var parsed = UglifyJS.parse(js);
parsed.print(stream);
return JSON.parse(source_map.toString());
return JSON.parse(UglifyJS.minify(js, {
compress: false,
mangle: false,
sourceMap: true
}).map);
}
// Run standalone

View File

@@ -12,7 +12,7 @@
stream._handle.setBlocking(true);
});
var UglifyJS = require("./node");
var UglifyJS = require("..");
var randomBytes = require("crypto").randomBytes;
var sandbox = require("./sandbox");
@@ -102,23 +102,23 @@ for (var i = 2; i < process.argv.length; ++i) {
case '--help':
case '-h':
case '-?':
console.log('** UglifyJS fuzzer help **');
console.log('Valid options (optional):');
console.log('<number>: generate this many cases (if used must be first arg)');
console.log('-v: print every generated test case');
console.log('-V: print every 100th generated test case');
console.log('-t <int>: generate this many toplevels per run (more take longer)');
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
console.log('--no-catch-redef: do not redefine catch variables');
console.log('--no-directive: do not generate directives');
console.log('--use-strict: generate "use strict"');
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
console.log('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
console.log('** UglifyJS fuzzer exiting **');
println('** UglifyJS fuzzer help **');
println('Valid options (optional):');
println('<number>: generate this many cases (if used must be first arg)');
println('-v: print every generated test case');
println('-V: print every 100th generated test case');
println('-t <int>: generate this many toplevels per run (more take longer)');
println('-r <int>: maximum recursion depth for generator (higher takes longer)');
println('-s1 <statement name>: force the first level statement to be this one (see list below)');
println('-s2 <statement name>: force the second level statement to be this one (see list below)');
println('--no-catch-redef: do not redefine catch variables');
println('--no-directive: do not generate directives');
println('--use-strict: generate "use strict"');
println('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
println('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
println('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
println('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
println('** UglifyJS fuzzer exiting **');
return 0;
default:
// first arg may be a number.
@@ -941,7 +941,17 @@ if (require.main !== module) {
return;
}
function try_beautify(code, result) {
function println(msg) {
if (typeof msg != "undefined") process.stdout.write(msg);
process.stdout.write("\n");
}
function errorln(msg) {
if (typeof msg != "undefined") process.stderr.write(msg);
process.stderr.write("\n");
}
function try_beautify(code, result, printfn) {
var beautified = UglifyJS.minify(code, {
compress: false,
mangle: false,
@@ -951,51 +961,34 @@ function try_beautify(code, result) {
},
});
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
printfn("// !!! beautify failed !!!");
printfn(beautified.error.stack);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
console.log("// (beautified)");
console.log(beautified.code);
printfn("// (beautified)");
printfn(beautified.code);
return;
}
console.log("//");
console.log(code);
printfn("//");
printfn(code);
}
function infer_options(ctor) {
try {
ctor({ 0: 0 });
} catch (e) {
return e.defs;
}
}
var default_options = {
compress: infer_options(UglifyJS.Compressor),
mangle: {
"cache": null,
"eval": false,
"ie8": false,
"keep_fnames": false,
"toplevel": false,
},
output: infer_options(UglifyJS.OutputStream),
};
var default_options = UglifyJS.default_options();
function log_suspects(minify_options, component) {
var options = component in minify_options ? minify_options[component] : true;
if (!options) return;
options = UglifyJS.defaults(options, default_options[component]);
var suspects = Object.keys(default_options[component]).filter(function(name) {
if (options[name]) {
if (typeof options != "object") options = {};
var defs = default_options[component];
var suspects = Object.keys(defs).filter(function(name) {
if ((name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options));
o[name] = false;
m[component] = o;
var result = UglifyJS.minify(original_code, m);
if (result.error) {
console.log("Error testing options." + component + "." + name);
console.log(result.error);
errorln("Error testing options." + component + "." + name);
errorln(result.error.stack);
} else {
var r = sandbox.run_code(result.code);
return sandbox.same_stdout(original_result, r);
@@ -1003,49 +996,49 @@ function log_suspects(minify_options, component) {
}
});
if (suspects.length > 0) {
console.log("Suspicious", component, "options:");
errorln("Suspicious " + component + " options:");
suspects.forEach(function(name) {
console.log(" " + name);
errorln(" " + name);
});
console.log();
errorln();
}
}
function log(options) {
if (!ok) console.log('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
console.log("//=============================================================");
if (!ok) console.log("// !!!!!! Failed... round", round);
console.log("// original code");
try_beautify(original_code, original_result);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code");
try_beautify(original_code, original_result, errorln);
errorln();
errorln();
errorln("//-------------------------------------------------------------");
if (typeof uglify_code == "string") {
console.log("// uglified code");
try_beautify(uglify_code, uglify_result);
console.log();
console.log();
console.log("original result:");
console.log(original_result);
console.log("uglified result:");
console.log(uglify_result);
errorln("// uglified code");
try_beautify(uglify_code, uglify_result, errorln);
errorln();
errorln();
errorln("original result:");
errorln(typeof original_result == "string" ? original_result : original_result.stack);
errorln("uglified result:");
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
} else {
console.log("// !!! uglify failed !!!");
console.log(uglify_code.stack);
errorln("// !!! uglify failed !!!");
errorln(uglify_code.stack);
if (typeof original_result != "string") {
console.log();
console.log();
console.log("original stacktrace:");
console.log(original_result.stack);
errorln();
errorln();
errorln("original stacktrace:");
errorln(original_result.stack);
}
}
console.log("minify(options):");
errorln("minify(options):");
options = JSON.parse(options);
console.log(options);
console.log();
errorln(JSON.stringify(options, null, 2));
errorln();
if (!ok && typeof uglify_code == "string") {
Object.keys(default_options).forEach(log_suspects.bind(null, options));
console.log("!!!!!! Failed... round", round);
errorln("!!!!!! Failed... round " + round);
}
}
@@ -1075,19 +1068,19 @@ for (var round = 1; round <= num_iterations; round++) {
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (typeof original_result != "string") {
console.log("//=============================================================");
console.log("// original code");
try_beautify(original_code, original_result);
console.log();
console.log();
console.log("original result:");
console.log(original_result);
console.log();
println("//=============================================================");
println("// original code");
try_beautify(original_code, original_result, println);
println();
println();
println("original result:");
println(original_result.stack);
println();
}
if (!ok && isFinite(num_iterations)) {
console.log();
println();
process.exit(1);
}
});
}
console.log();
println();

View File

@@ -1,4 +1,5 @@
exports["Dictionary"] = Dictionary;
exports["TreeWalker"] = TreeWalker;
exports["TreeTransformer"] = TreeTransformer;
exports["minify"] = minify;
exports["_push_uniq"] = push_uniq;

View File

@@ -61,5 +61,22 @@ function describe_ast() {
}
};
doitem(AST_Node);
return out + "";
return out + "\n";
}
function infer_options(options) {
var result = UglifyJS.minify("", options);
return result.error && result.error.defs;
}
UglifyJS.default_options = function() {
var defs = {};
Object.keys(infer_options({ 0: 0 })).forEach(function(component) {
var options = {};
options[component] = { 0: 0 };
if (options = infer_options(options)) {
defs[component] = options;
}
});
return defs;
};