Compare commits

...

96 Commits

Author SHA1 Message Date
Alex Lam S.L
91f8b57b3e harmony-v3.0.25 2017-07-16 12:21:39 +08:00
alexlamsl
3a2b737c42 Merge branch 'master' into harmony-v3.0.25 2017-07-16 11:15:07 +08:00
Alex Lam S.L
4e12a6f740 v3.0.25 2017-07-16 11:05:53 +08:00
Alex Lam S.L
b35dfc2599 reject malformed CLI parameters (#2239)
fixes #2237
2017-07-15 23:50:27 +08:00
Alex Lam S.L
9e1da9235e ensure ie8 works with mangled properties (#2238)
fixes #2234
2017-07-15 22:50:59 +08:00
Alex Lam S.L
a5ffe2c23f drop unused builtin globals under unsafe (#2236)
fixes #2233
2017-07-15 15:16:11 +08:00
Alex Lam S.L
9282e7b0c6 fix unsafe evaluate of Object static methods (#2232)
fixes #2231
2017-07-14 19:52:01 +08:00
Alex Lam S.L
5229cb2b1b drop unused compound assignments (#2230)
fixes #2226
2017-07-14 00:39:34 +08:00
Alex Lam S.L
458e3e15f0 enhance passes (#2229)
- remove hardcoded upper limit
- continue based on node count reduction
- emit verbose statistics

fixes #2226
2017-07-13 02:18:59 +08:00
Alex Lam S.L
c615a1e80a fix gzip stream in test/benchmark.js (#2228) 2017-07-12 02:55:57 +08:00
Alex Lam S.L
10a938cb79 enhance source mapping on IIFEs (#2224)
fixes #2213
2017-07-11 02:34:28 +08:00
kzc
0f4278148d uglify-es: update repository and project tagline (#2221) 2017-07-09 23:55:38 +08:00
Alex Lam S.L
4956ad311b benchmark gzipped output (#2220) 2017-07-09 01:44:59 +08:00
kzc
145874e504 docs: update benchmarks using node 8, add babili (#2218) 2017-07-09 01:06:15 +08:00
kzc
f30375052b docs: update benchmarks using node 8, add babili (#2218) 2017-07-09 00:48:53 +08:00
Alex Lam S.L
3e1a8598bf harmony-v3.0.24 2017-07-08 14:51:47 +08:00
alexlamsl
ef63de6968 handle AST_Arrow IIFEs in collapse_vars 2017-07-08 14:27:06 +08:00
alexlamsl
2539fb8096 inline property access of AST_ConciseMethod 2017-07-08 14:25:58 +08:00
alexlamsl
a556dd2dcb Merge branch 'master' into harmony-v3.0.24 2017-07-08 13:12:54 +08:00
Alex Lam S.L
bd7be07c38 v3.0.24 2017-07-08 12:53:20 +08:00
Alex Lam S.L
71ee91e716 handle duplicate argument names in collapse_vars (#2215) 2017-07-08 04:42:35 +08:00
kzc
e7334b4048 uglify-es: have repository point to harmony branch (#2212) 2017-07-07 11:39:48 +08:00
Alex Lam S.L
4f70d2e28c inlining of static methods & constants (#2211)
- guard by `unsafe`
- support `Array`, `Math`, `Number`, `Object` & `String`

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

fixes #2208
2017-07-06 21:51:58 +08:00
Alex Lam S.L
f5c46db738 improve AST_ConciseMethod compression (#2202)
p(){return x;} ---> p:()=>x

Optimization subject to the `compress` option `arrows`.
2017-07-06 01:21:04 +08:00
Alex Lam S.L
9306da3c58 suppress collapse_vars of this as call argument (#2204)
fixes #2203
2017-07-06 01:03:52 +08:00
Alex Lam S.L
1ac25fc032 improve compress granularity through typeofs (#2201)
fixes #2198
2017-07-05 19:20:33 +08:00
kzc
fdbb1d09ef Convert p: function(){} to p(){} in object literals (#2199)
when `compress` option `ecma` is 6 or greater.
2017-07-04 14:35:58 +08:00
Alex Lam S.L
5f046c724b minor clean-ups to evaluate (#2197) 2017-07-03 18:52:39 +08:00
Alex Lam S.L
af0262b7e5 improve parenthesis emission (#2196)
- eliminate `throw` usages
- suppress extraneous parenthesis
  - `new function() {foo.bar()}.baz`
  - `for (function() { "foo" in bar; };;);`
2017-07-03 04:17:37 +08:00
Alex Lam S.L
6b3aeff1d8 clean up TreeWalker.pop() (#2195)
Remove superfluous parameter.
2017-07-03 03:23:38 +08:00
Alex Lam S.L
20e4f8277f refactor throw usage within compress (#2193)
Eliminate exceptional constructs from normal control flow.
2017-07-03 02:10:56 +08:00
kzc
f3a487a368 document fast mangle-only minify mode (#2194) 2017-07-03 01:37:04 +08:00
Alex Lam S.L
33ad0d258c harmony-v3.0.23 2017-07-02 19:04:15 +08:00
alexlamsl
5ea1da2d42 handle AST_Expansion in collapse_vars & inline 2017-07-02 18:15:16 +08:00
alexlamsl
e77b6d525c Merge branch 'master' into harmony-v3.0.23 2017-07-02 17:47:21 +08:00
Alex Lam S.L
2dde41615a v3.0.23 2017-07-02 17:24:22 +08:00
Alex Lam S.L
8b69a3d18e drop argument value after collapse_vars (#2190) 2017-07-02 04:28:11 +08:00
Alex Lam S.L
d40950b741 improve inline efficiency (#2188)
... by teaching `collapse_vars` some new tricks.

fixes #2187
2017-07-02 01:05:14 +08:00
Alex Lam S.L
a9eecd844f harmony-v3.0.22 2017-06-30 12:56:56 +08:00
alexlamsl
ed3032e52a Merge branch 'master' into harmony-v3.0.22 2017-06-30 11:24:07 +08:00
Alex Lam S.L
7659ea1d2e v3.0.22 2017-06-30 11:18:34 +08:00
Alex Lam S.L
52cc21d999 remove extraneous ! before AST_Arrow (#2185) 2017-06-30 11:17:58 +08:00
kzc
a938fe5e1f async arrow function IIFE fix (#2184)
fixes #2183
2017-06-30 10:12:42 +08:00
kzc
07a5a57336 fix await parens for property access and calls (#2181)
fixes #2179
2017-06-30 09:14:24 +08:00
Alex Lam S.L
bdeadffbf5 improve usability of name cache under minify() (#2176)
fixes #2174
2017-06-29 12:48:34 +08:00
Alex Lam S.L
945db924fc Merge pull request #2177 from alexlamsl/harmony-v3.0.21
Merging from master for 3.0.21
2017-06-29 02:37:28 +08:00
alexlamsl
087bce508a Merge branch 'master' into harmony-v3.0.21 2017-06-29 00:58:28 +08:00
Alex Lam S.L
5e6f26445f v3.0.21 2017-06-29 00:49:06 +08:00
kzc
fc7e33453f [ES6] document mangle option keep_classnames (#2175) 2017-06-28 23:51:58 +08:00
Alex Lam S.L
d052394621 fix line terminators in template literals (#2173)
fixes #2172
2017-06-28 22:52:29 +08:00
Alex Lam S.L
4d5aeeddfb compress AST_Arrow properly (#2170) 2017-06-28 01:06:30 +08:00
Alex Lam S.L
f0a99125ee improve unsafe_Func (#2171)
- minimise disturbance to `compute_char_frequency()`
- remove extraneous quotation marks
2017-06-27 23:53:42 +08:00
Alex Lam S.L
1e4de2e6d3 parse @global_defs as expressions (#2169)
- let parser rejects non-conformant input
- eliminate need for extraneous parenthesis
2017-06-27 10:31:19 +08:00
Alex Lam S.L
ad139aa34d fix side_effects on AST_Expansion (#2165)
fixes #2163
2017-06-27 01:13:00 +08:00
kzc
26be15f111 update uglify-es keywords in package.json (#2168) 2017-06-27 00:56:01 +08:00
kzc
179f33f08a update doc notes for uglify-es (#2164) 2017-06-26 11:04:22 +08:00
kzc
d260fe9018 more documentation for the ecma option (#2162) 2017-06-26 02:39:14 +08:00
Alex Lam S.L
96f9b8cba3 Merge pull request #2161 from alexlamsl/harmony-v3.0.20
Merging from master for 3.0.20
2017-06-25 17:18:06 +08:00
alexlamsl
11afa816e3 Merge branch 'master' into harmony-v3.0.20 2017-06-25 16:43:44 +08:00
Alex Lam S.L
8b4dcd8f3e v3.0.20 2017-06-25 15:05:05 +08:00
Alex Lam S.L
285401ced8 more tests for #2158 (#2160) 2017-06-25 14:21:48 +08:00
Alex Lam S.L
9db4c42380 fix cascade & collapse on property access of constants (#2158) 2017-06-24 21:30:06 +08:00
Alex Lam S.L
49f3de8397 toplevel shorthand for ecma (#2157) 2017-06-24 19:06:58 +08:00
Alex Lam S.L
94f93ad82d support trailing commas in function parameter lists and calls (#2156)
fixes #2155
2017-06-24 17:34:14 +08:00
Alex Lam S.L
d1f085bce7 add new arrows compress option (#2154)
Convert ES5 style anonymous function expressions
to arrow functions if permissible by language semantics.

Note: `arrows` requires that the `ecma` compress option
is set to `6` or greater.

fixes #2150
2017-06-24 14:45:24 +08:00
Alex Lam S.L
7b95b63ca1 [ES6] support async arrow functions (#2153)
fixes #2102
2017-06-24 05:26:35 +08:00
Alex Lam S.L
94e5e00c03 refactor compute_char_frequency() (#2152)
- minimise maintenance when updating AST
- maximise code sharing between `master` & `harmony`
2017-06-23 20:05:31 +08:00
Alex Lam S.L
dc6bcaa18e synchronise mangle.properties for minify() & test/compress (#2151) 2017-06-23 15:53:13 +08:00
Alex Lam S.L
d58b184835 refactor Compressor.toplevel (#2149) 2017-06-23 13:11:40 +08:00
Alex Lam S.L
137e4c4753 fix unused on AST_Destructuring (#2146) 2017-06-23 13:11:26 +08:00
Alex Lam S.L
b3a57ff019 minimise reduce_vars cloning overhead (#2148) 2017-06-23 06:59:53 +08:00
Alex Lam S.L
3d5bc08185 fix reduce_vars on this (#2145)
fixes #2140
2017-06-23 04:44:57 +08:00
Alex Lam S.L
0692435f01 fix for-in loop parsing (#2144) 2017-06-23 04:14:30 +08:00
Alex Lam S.L
b163b13a0b fix export of keyword and redirection (#2143)
fixes #2141
fixes #2142
2017-06-23 03:49:30 +08:00
Alex Lam S.L
402954bdf3 Merge pull request #2139 from alexlamsl/harmony-v3.0.19
Merging from master for 3.0.19
2017-06-22 05:36:14 +08:00
alexlamsl
f5931866e0 Merge branch 'master' into harmony-v3.0.19 2017-06-22 03:26:49 +08:00
Alex Lam S.L
f67a6b0e43 v3.0.19 2017-06-22 03:24:22 +08:00
Alex Lam S.L
471db8a717 fix inline & unused on AST_Expansion (#2138)
- handle rest parameters
- suppress cases with spread arguments

fixes #2136
2017-06-22 01:39:11 +08:00
Alex Lam S.L
8ba9e4e0da fix toplevel on export (#2137)
fixes #2134
2017-06-22 00:31:17 +08:00
Alex Lam S.L
71556d00b5 correctly parse export of function & class (#2135) 2017-06-21 23:15:39 +08:00
Alex Lam S.L
8709753bfb fix mangle on export (#2133)
- `export default ...`
- `export` with `AST_Destructuring`

fixes #2129
2017-06-21 14:22:09 +08:00
Alex Lam S.L
db877e8729 fix drop_unused() accounting of symbols within export function (#2132)
fixes #2131
2017-06-21 12:32:58 +08:00
Alex Lam S.L
11923e3ae8 reject non-toplevel import/export (#2128)
fixes #2124
2017-06-21 03:18:48 +08:00
Alex Lam S.L
62d1fbf645 support export statements properly (#2126)
- mangle & compress correctly with `toplevel`
- retain non-toplevel import/export
- parse & output `export { variable as name }`
- remove extraneous spaces from `beautify`


fixes #2038
fixes #2124
2017-06-21 00:51:36 +08:00
Alex Lam S.L
343ea326c2 ensure mangling works if catch reuses a scope variable (#2123)
fixes #2120
2017-06-20 02:14:05 +08:00
kzc
849ba79dee retain names in export default class and function (#2122)
fixes #2121
2017-06-19 14:30:59 +08:00
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
59 changed files with 4281 additions and 1166 deletions

120
README.md
View File

@@ -1,11 +1,11 @@
uglify-es uglify-es
========= =========
**uglify-es** is an ECMAScript 2015 parser, minifier, compressor and beautifier toolkit. A JavaScript parser, mangler/compressor and beautifier toolkit for ES6+.
#### Note: #### Note:
- **The `uglify-es` API and CLI is compatible with `uglify-js@3.x`.** - **`uglify-es` is API/CLI compatible with `uglify-js@3`.**
- **`uglify-es` is not backwards compatible with the `uglify-js@2.x` API and CLI.** - **`uglify-es` is not backwards compatible with `uglify-js@2`.**
Install Install
------- -------
@@ -102,13 +102,14 @@ a double dash to prevent input files being used as option arguments:
sequences. sequences.
--config-file <file> Read `minify()` options from JSON file. --config-file <file> Read `minify()` options from JSON file.
-d, --define <expr>[=value] Global definitions. -d, --define <expr>[=value] Global definitions.
--ecma <version> Specifiy ECMAScript release: 5, 6, 7 or 8.
--ie8 Support non-standard Internet Explorer 8. --ie8 Support non-standard Internet Explorer 8.
Equivalent to setting `ie8: true` in `minify()` Equivalent to setting `ie8: true` in `minify()`
for `compress`, `mangle` and `output` options. for `compress`, `mangle` and `output` options.
By default UglifyJS will not try to be IE-proof. By default UglifyJS will not try to be IE-proof.
--keep-fnames Do not mangle/drop function names. Useful for --keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name. code relying on Function.prototype.name.
--name-cache File to hold mangled name mappings. --name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS) --self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options: --source-map [options] Enable source map/specify source map options:
`base` Path to compute relative paths from input files. `base` Path to compute relative paths from input files.
@@ -380,7 +381,47 @@ var code = {
var options = { toplevel: true }; var options = { toplevel: true };
var result = UglifyJS.minify(code, options); var result = UglifyJS.minify(code, options);
console.log(result.code); console.log(result.code);
// console.log(function(n,o){return n+o}(3,7)); // console.log(3+7);
```
The `nameCache` option:
```javascript
var options = {
mangle: {
toplevel: true,
},
nameCache: {}
};
var result1 = UglifyJS.minify({
"file1.js": "function add(first, second) { return first + second; }"
}, options);
var result2 = UglifyJS.minify({
"file2.js": "console.log(add(1 + 2, 3 + 4));"
}, options);
console.log(result1.code);
// function n(n,r){return n+r}
console.log(result2.code);
// console.log(n(3,7));
```
You may persist the name cache to the file system in the following way:
```javascript
var cacheFileName = "/tmp/cache.json";
var options = {
mangle: {
properties: true,
},
nameCache: JSON.parse(fs.readFileSync(cacheFileName, "utf8"))
};
fs.writeFileSync("part1.js", UglifyJS.minify({
"file1.js": fs.readFileSync("file1.js", "utf8"),
"file2.js": fs.readFileSync("file2.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync("part2.js", UglifyJS.minify({
"file3.js": fs.readFileSync("file3.js", "utf8"),
"file4.js": fs.readFileSync("file4.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync(cacheFileName, JSON.stringify(options.nameCache), "utf8");
``` ```
An example of a combination of `minify()` options: An example of a combination of `minify()` options:
@@ -433,6 +474,9 @@ if (result.error) throw result.error;
## Minify options ## Minify options
- `ecma` (default `undefined`) - pass `5`, `6`, `7` or `8` to override `parse`,
`compress` and `output` options.
- `warnings` (default `false`) — pass `true` to return compressor warnings - `warnings` (default `false`) — pass `true` to return compressor warnings
in `result.warnings`. Use the value `"verbose"` for more detailed warnings. in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
@@ -458,13 +502,19 @@ if (result.error) throw result.error;
- `toplevel` (default `false`) - set to `true` if you wish to enable top level - `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. variable and function name mangling and to drop unused variables and functions.
- `nameCache` (default `null`) - pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
a read/write property. `minify()` will read the name cache state of this
object and update it during minification so that it may be
reused or externally persisted by the user.
- `ie8` (default `false`) - set to `true` to support IE8. - `ie8` (default `false`) - set to `true` to support IE8.
## Minify options structure ## Minify options structure
```javascript ```javascript
{ {
warnings: false,
parse: { parse: {
// parse options // parse options
}, },
@@ -484,8 +534,11 @@ if (result.error) throw result.error;
sourceMap: { sourceMap: {
// source map options // source map options
}, },
ecma: 5, // specify one of: 5, 6, 7 or 8
nameCache: null, // or specify a name cache object
toplevel: false, toplevel: false,
ie8: false, ie8: false,
warnings: false,
} }
``` ```
@@ -539,6 +592,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Parse options ## Parse options
- `bare_returns` (default `false`) -- support top level `return` statements - `bare_returns` (default `false`) -- support top level `return` statements
- `ecma` (default: `8`) -- specify one of `5`, `6`, `7` or `8`. Note: this setting
is not presently enforced except for ES8 optional trailing commas in function
parameter lists and calls with `ecma` `8`.
- `html5_comments` (default `true`) - `html5_comments` (default `true`)
- `shebang` (default `true`) -- support `#!command` as the first line - `shebang` (default `true`) -- support `#!command` as the first line
@@ -590,9 +646,17 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `evaluate` -- attempt to evaluate constant expressions - `evaluate` -- attempt to evaluate constant expressions
- `arrows` (default `true`) -- convert ES5 style anonymous function expressions
to arrow functions if permissible by language semantics.
Note: `arrows` requires that the `ecma` compress option is set to `6` or greater.
- `booleans` -- various optimizations for boolean context, for example `!!a - `booleans` -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c` ? b : c → a ? b : c`
- `typeofs` -- default `true`. Transforms `typeof foo == "undefined"` into
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues.
- `loops` -- optimizations for `do`, `while` and `for` loops when we can - `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition statically determine the condition
@@ -666,7 +730,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
compressor from discarding function names. Useful for code relying on compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle). `Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `passes` -- default `1`. Number of times to run compress with a maximum of 3. - `passes` -- default `1`. The maximum number of times to run compress.
In some cases more than one pass leads to further compressed code. Keep in In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time. mind more passes will take more time.
@@ -678,6 +742,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();` example: `/*@__PURE__*/foo();`
- `ecma` -- default `5`. Pass `6` or greater to enable `compress` options that
will transform ES5 code into smaller ES6+ equivalent forms.
## Mangle options ## Mangle options
- `reserved` (default `[]`). Pass an array of identifiers that should be - `reserved` (default `[]`). Pass an array of identifiers that should be
@@ -686,6 +753,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `toplevel` (default `false`). Pass `true` to mangle names declared in the - `toplevel` (default `false`). Pass `true` to mangle names declared in the
top level scope. top level scope.
- `keep_classnames` (default `false`). Pass `true` to not mangle class names.
- `keep_fnames` (default `false`). Pass `true` to not mangle function names. - `keep_fnames` (default `false`). Pass `true` to not mangle function names.
Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames` Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
[compress option](#compress-options). [compress option](#compress-options).
@@ -749,9 +818,12 @@ can pass additional arguments that control the code output:
- `comments` (default `false`) -- pass `true` or `"all"` to preserve all - `comments` (default `false`) -- pass `true` or `"all"` to preserve all
comments, `"some"` to preserve some comments, a regular expression string comments, `"some"` to preserve some comments, a regular expression string
(e.g. `/^!/`) or a function. (e.g. `/^!/`) or a function.
- `ecma` (default `5`) -- set output printing mode. This will only change the - `ecma` (default `5`) -- set output printing mode. Set `ecma` to `6` or
output in direct control of the beautifier. Non-compatible features in the greater to emit shorthand object properties - i.e.: `{a}` instead of `{a: a}`.
abstract syntax tree will still be outputted as is. The `ecma` option will only change the output in direct control of the
beautifier. Non-compatible features in the abstract syntax tree will still
be output as is. For example: an `ecma` setting of `5` will **not** convert
ES6+ code to ES5.
- `indent_level` (default 4) - `indent_level` (default 4)
- `indent_start` (default 0) -- prefix all lines by that many spaces - `indent_start` (default 0) -- prefix all lines by that many spaces
- `inline_script` (default `false`) -- escape the slash in occurrences of - `inline_script` (default `false`) -- escape the slash in occurrences of
@@ -829,7 +901,6 @@ when this flag is on:
- `new Object()` → `{}` - `new Object()` → `{}`
- `String(exp)` or `exp.toString()` → `"" + exp` - `String(exp)` or `exp.toString()` → `"" + exp`
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new` - `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
- `typeof foo == "undefined"` → `foo === void 0`
- `void 0` → `undefined` (if there is a variable named "undefined" in - `void 0` → `undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically scope; we do it because the variable name will be mangled, typically
reduced to a single character) reduced to a single character)
@@ -982,3 +1053,30 @@ in total it's a bit more than just using UglifyJS's own parser.
[acorn]: https://github.com/ternjs/acorn [acorn]: https://github.com/ternjs/acorn
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k [sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
### Uglify Fast Minify Mode
It's not well known, but whitespace removal and symbol mangling accounts
for 95% of the size reduction in minified code for most javascript - not
elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to
[`butternut`](https://www.npmjs.com/package/butternut):
| d3.js | minify size | gzip size | minify time (seconds) |
| --- | ---: | ---: | ---: |
| original | 451,131 | 108,733 | - |
| uglify-js@3.0.24 mangle=false, compress=false | 316,600 | 85,245 | 0.70 |
| uglify-js@3.0.24 mangle=true, compress=false | 220,216 | 72,730 | 1.13 |
| butternut@0.4.6 | 217,568 | 72,738 | 1.41 |
| uglify-js@3.0.24 mangle=true, compress=true | 212,511 | 71,560 | 3.36 |
| babili@0.1.4 | 210,713 | 72,140 | 12.64 |
To enable fast minify mode from the CLI use:
```
uglifyjs file.js -m
```
To enable fast minify mode with the API use:
```js
UglifyJS.minify(code, { compress: false, mangle: true });
```

View File

@@ -35,15 +35,16 @@ else if (process.argv.indexOf("options") >= 0) program.helpInformation = functio
} }
return text.join("\n"); return text.join("\n");
}; };
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true)); program.option("-p, --parse <options>", "Specify parser options.", parse_js());
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js());
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true)); program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js());
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true)); program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js());
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true)); program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js());
program.option("-o, --output <file>", "Output file (default STDOUT)."); program.option("-o, --output <file>", "Output file (default STDOUT).");
program.option("--comments [filter]", "Preserve copyright comments in the output."); program.option("--comments [filter]", "Preserve copyright comments in the output.");
program.option("--config-file <file>", "Read minify() options from JSON file."); program.option("--config-file <file>", "Read minify() options from JSON file.");
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define")); program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
program.option("--ecma <version>", "Specifiy ECMAScript release: 5, 6, 7 or 8.");
program.option("--ie8", "Support non-standard Internet Explorer 8."); program.option("--ie8", "Support non-standard Internet Explorer 8.");
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name."); program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
program.option("--name-cache <file>", "File to hold mangled name mappings."); program.option("--name-cache <file>", "File to hold mangled name mappings.");
@@ -73,6 +74,10 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
options[name] = program[name]; options[name] = program[name];
} }
}); });
if ("ecma" in program) {
if (program.ecma != (program.ecma | 0)) fatal("ERROR: ecma must be an integer");
options.ecma = program.ecma | 0;
}
if (program.beautify) { if (program.beautify) {
options.output = typeof program.beautify == "object" ? program.beautify : {}; options.output = typeof program.beautify == "object" ? program.beautify : {};
if (!("beautify" in options.output)) { if (!("beautify" in options.output)) {
@@ -106,17 +111,8 @@ if (program.mangleProps) {
if (typeof options.mangle != "object") options.mangle = {}; if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps; options.mangle.properties = program.mangleProps;
} }
var cache;
if (program.nameCache) { if (program.nameCache) {
cache = JSON.parse(read_file(program.nameCache, "{}")); options.nameCache = JSON.parse(read_file(program.nameCache, "{}"));
if (options.mangle) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.cache = to_cache("vars");
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
options.mangle.properties.cache = to_cache("props");
}
}
} }
if (program.output == "ast") { if (program.output == "ast") {
options.output = { options.output = {
@@ -266,9 +262,7 @@ function run() {
print(result.code); print(result.code);
} }
if (program.nameCache) { if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) { fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
} }
if (result.timings) for (var phase in result.timings) { if (result.timings) for (var phase in result.timings) {
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s"); print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
@@ -321,7 +315,7 @@ function read_file(path, default_value) {
} }
} }
function parse_js(flag, constants) { function parse_js(flag) {
return function(value, options) { return function(value, options) {
options = options || {}; options = options || {};
try { try {
@@ -339,7 +333,7 @@ function parse_js(flag, constants) {
if (node instanceof UglifyJS.AST_Assign) { if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string(); var name = node.left.print_to_string();
var value = node.right; var value = node.right;
if (!constants) { if (flag) {
options[name] = value; options[name] = value;
} else if (value instanceof UglifyJS.AST_Array) { } else if (value instanceof UglifyJS.AST_Array) {
options[name] = value.elements.map(to_string); options[name] = value.elements.map(to_string);
@@ -362,14 +356,18 @@ function parse_js(flag, constants) {
} }
})); }));
} catch(ex) { } catch(ex) {
if (flag) {
fatal("Error parsing arguments for '" + flag + "': " + value);
} else {
options[value] = null; options[value] = null;
} }
}
return options; return options;
} }
} }
function parse_source_map() { function parse_source_map() {
var parse = parse_js("sourceMap", true); var parse = parse_js();
return function(value, options) { return function(value, options) {
var hasContent = options && "content" in options; var hasContent = options && "content" in options;
var settings = parse(value, options); var settings = parse(value, options);
@@ -381,18 +379,6 @@ function parse_source_map() {
} }
} }
function to_cache(key) {
if (cache[key]) {
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
} else {
cache[key] = {
cname: -1,
props: new UglifyJS.Dictionary()
};
}
return cache[key];
}
function skip_key(key) { function skip_key(key) {
return skip_keys.indexOf(key) >= 0; return skip_keys.indexOf(key) >= 0;
} }

View File

@@ -345,7 +345,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
var AST_Expansion = DEFNODE("Expansion", "expression", { var AST_Expansion = DEFNODE("Expansion", "expression", {
$documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list", $documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list",
$propdoc: { $propdoc: {
expression: "AST_Symbol the thing to be expanded" expression: "[AST_Node] the thing to be expanded"
}, },
_walk: function(visitor) { _walk: function(visitor) {
var self = this; var self = this;
@@ -446,7 +446,7 @@ var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_str
var AST_TemplateString = DEFNODE("TemplateString", "segments", { var AST_TemplateString = DEFNODE("TemplateString", "segments", {
$documentation: "A template string literal", $documentation: "A template string literal",
$propdoc: { $propdoc: {
segments: "[AST_TemplateSegment|AST_Expression]* One or more segments, starting with AST_TemplateSegment. AST_Expression may follow AST_TemplateSegment, but each AST_Expression must be followed by AST_TemplateSegment." segments: "[AST_Node*] One or more segments, starting with AST_TemplateSegment. AST_Node may follow AST_TemplateSegment, but each AST_Node must be followed by AST_TemplateSegment."
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
@@ -629,11 +629,11 @@ var AST_Const = DEFNODE("Const", null, {
$documentation: "A `const` statement" $documentation: "A `const` statement"
}, AST_Definitions); }, AST_Definitions);
var AST_NameImport = DEFNODE("NameImport", "foreign_name name", { var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", {
$documentation: "The part of the import statement that imports names from a module.", $documentation: "The part of the export/import statement that declare names from a module.",
$propdoc: { $propdoc: {
foreign_name: "[AST_SymbolImportForeign] The name being imported (as specified in the module)", foreign_name: "[AST_SymbolExportForeign|AST_SymbolImportForeign] The name being exported/imported (as specified in the module)",
name: "[AST_SymbolImport] The name as it becomes available to this module." name: "[AST_SymbolExport|AST_SymbolImport] The name as it is visible to this module."
}, },
_walk: function (visitor) { _walk: function (visitor) {
return visitor._visit(this, function() { return visitor._visit(this, function() {
@@ -647,7 +647,7 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
$documentation: "An `import` statement", $documentation: "An `import` statement",
$propdoc: { $propdoc: {
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.", imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
imported_names: "[AST_NameImport*] The names of non-default imported variables", imported_names: "[AST_NameMapping*] The names of non-default imported variables",
module_name: "[AST_String] String literal describing where this module came from", module_name: "[AST_String] String literal describing where this module came from",
}, },
_walk: function(visitor) { _walk: function(visitor) {
@@ -656,7 +656,7 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
this.imported_name._walk(visitor); this.imported_name._walk(visitor);
} }
if (this.imported_names) { if (this.imported_names) {
this.imported_names.forEach(function (name_import) { this.imported_names.forEach(function(name_import) {
name_import._walk(visitor); name_import._walk(visitor);
}); });
} }
@@ -670,7 +670,7 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
$propdoc: { $propdoc: {
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition", exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
exported_value: "[AST_Node?] An exported value", exported_value: "[AST_Node?] An exported value",
exported_names: "[AST_NameImport*?] List of exported names", exported_names: "[AST_NameMapping*?] List of exported names",
module_name: "[AST_String?] Name of the file to load exports from", module_name: "[AST_String?] Name of the file to load exports from",
is_default: "[Boolean] Whether this is the default exported value of this module" is_default: "[Boolean] Whether this is the default exported value of this module"
}, },
@@ -682,6 +682,14 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
if (this.exported_value) { if (this.exported_value) {
this.exported_value._walk(visitor); this.exported_value._walk(visitor);
} }
if (this.exported_names) {
this.exported_names.forEach(function(name_export) {
name_export._walk(visitor);
});
}
if (this.module_name) {
this.module_name._walk(visitor);
}
}); });
} }
}, AST_Statement); }, AST_Statement);
@@ -996,7 +1004,7 @@ var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
}, AST_SymbolBlockDeclaration); }, AST_SymbolBlockDeclaration);
var AST_SymbolImport = DEFNODE("SymbolImport", null, { var AST_SymbolImport = DEFNODE("SymbolImport", null, {
$documentation: "Symbol refering to an imported name", $documentation: "Symbol referring to an imported name",
}, AST_SymbolBlockDeclaration); }, AST_SymbolBlockDeclaration);
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, { var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
@@ -1018,6 +1026,14 @@ var AST_SymbolRef = DEFNODE("SymbolRef", null, {
$documentation: "Reference to some symbol (not definition/declaration)", $documentation: "Reference to some symbol (not definition/declaration)",
}, AST_Symbol); }, AST_Symbol);
var AST_SymbolExport = DEFNODE("SymbolExport", null, {
$documentation: "Symbol referring to a name to export",
}, AST_SymbolRef);
var AST_SymbolExportForeign = DEFNODE("SymbolExportForeign", null, {
$documentation: "A symbol exported from this module, but it is used in the other module, and its real name is irrelevant for this module's purposes",
}, AST_Symbol);
var AST_LabelRef = DEFNODE("LabelRef", null, { var AST_LabelRef = DEFNODE("LabelRef", null, {
$documentation: "Reference to a label symbol", $documentation: "Reference to a label symbol",
}, AST_Symbol); }, AST_Symbol);
@@ -1144,7 +1160,7 @@ TreeWalker.prototype = {
if (!ret && descend) { if (!ret && descend) {
descend.call(node); descend.call(node);
} }
this.pop(node); this.pop();
return ret; return ret;
}, },
parent: function(n) { parent: function(n) {
@@ -1163,8 +1179,8 @@ TreeWalker.prototype = {
} }
this.stack.push(node); this.stack.push(node);
}, },
pop: function(node) { pop: function() {
this.stack.pop(); var node = this.stack.pop();
if (node instanceof AST_Lambda || node instanceof AST_Class) { if (node instanceof AST_Lambda || node instanceof AST_Class) {
this.directives = Object.getPrototypeOf(this.directives); this.directives = Object.getPrototypeOf(this.directives);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -27,14 +27,33 @@ function set_shorthand(name, options, keys) {
} }
} }
function init_cache(cache) {
if (!cache) return;
if (!("cname" in cache)) cache.cname = -1;
if (!("props" in cache)) {
cache.props = new Dictionary();
} else if (!(cache.props instanceof Dictionary)) {
cache.props = Dictionary.fromObject(cache.props);
}
}
function to_json(cache) {
return {
cname: cache.cname,
props: cache.props.toObject()
};
}
function minify(files, options) { function minify(files, options) {
var warn_function = AST_Node.warn_function; var warn_function = AST_Node.warn_function;
try { try {
options = defaults(options, { options = defaults(options, {
compress: {}, compress: {},
ecma: undefined,
ie8: false, ie8: false,
keep_fnames: false, keep_fnames: false,
mangle: {}, mangle: {},
nameCache: null,
output: {}, output: {},
parse: {}, parse: {},
sourceMap: false, sourceMap: false,
@@ -46,13 +65,14 @@ function minify(files, options) {
var timings = options.timings && { var timings = options.timings && {
start: Date.now() start: Date.now()
}; };
set_shorthand("ecma", options, [ "parse", "compress", "output" ]);
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]); set_shorthand("warnings", options, [ "compress" ]);
if (options.mangle) { if (options.mangle) {
options.mangle = defaults(options.mangle, { options.mangle = defaults(options.mangle, {
cache: null, cache: options.nameCache && (options.nameCache.vars || {}),
eval: false, eval: false,
ie8: false, ie8: false,
keep_classnames: false, keep_classnames: false,
@@ -62,6 +82,16 @@ function minify(files, options) {
safari10: false, safari10: false,
toplevel: false, toplevel: false,
}, true); }, true);
if (options.nameCache && options.mangle.properties) {
if (typeof options.mangle.properties != "object") {
options.mangle.properties = {};
}
if (!("cache" in options.mangle.properties)) {
options.mangle.properties.cache = options.nameCache.props || {};
}
}
init_cache(options.mangle.cache);
init_cache(options.mangle.properties.cache);
} }
if (options.sourceMap) { if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, { options.sourceMap = defaults(options.sourceMap, {
@@ -155,6 +185,12 @@ function minify(files, options) {
} }
} }
} }
if (options.nameCache && options.mangle) {
if (options.mangle.cache) options.nameCache.vars = to_json(options.mangle.cache);
if (options.mangle.properties && options.mangle.properties.cache) {
options.nameCache.props = to_json(options.mangle.properties.cache);
}
}
if (timings) { if (timings) {
timings.end = Date.now(); timings.end = Date.now();
result.timings = { result.timings = {

View File

@@ -533,6 +533,7 @@ function OutputStream(options) {
use_asm = prev_use_asm; use_asm = prev_use_asm;
} }
}); });
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
AST_Node.DEFMETHOD("print_to_string", function(options){ AST_Node.DEFMETHOD("print_to_string", function(options){
var s = OutputStream(options); var s = OutputStream(options);
@@ -672,6 +673,12 @@ function OutputStream(options) {
&& this.operator !== "--"; && this.operator !== "--";
}); });
PARENS(AST_Await, function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this
|| p instanceof AST_Call && p.expression === this;
});
PARENS(AST_Sequence, function(output){ PARENS(AST_Sequence, function(output){
var p = output.parent(); var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
@@ -738,14 +745,15 @@ function OutputStream(options) {
// parens around it too, otherwise the call will be // parens around it too, otherwise the call will be
// interpreted as passing the arguments to the upper New // interpreted as passing the arguments to the upper New
// expression. // expression.
try { var parens = false;
this.walk(new TreeWalker(function(node){ this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Call) throw p; if (parens || node instanceof AST_Scope) return true;
})); if (node instanceof AST_Call) {
} catch(ex) { parens = true;
if (ex !== p) throw ex;
return true; return true;
} }
}));
return parens;
} }
}); });
@@ -978,12 +986,12 @@ function OutputStream(options) {
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
var self = this; var self = this;
if (!nokeyword) { if (!nokeyword) {
if (this.async) { if (self.async) {
output.print("async"); output.print("async");
output.space(); output.space();
} }
output.print("function"); output.print("function");
if (this.is_generator) { if (self.is_generator) {
output.star(); output.star();
} }
if (self.name) { if (self.name) {
@@ -1039,6 +1047,10 @@ function OutputStream(options) {
parent instanceof AST_Unary || parent instanceof AST_Unary ||
(parent instanceof AST_Call && self === parent.expression); (parent instanceof AST_Call && self === parent.expression);
if (needs_parens) { output.print("(") } if (needs_parens) { output.print("(") }
if (self.async) {
output.print("async");
output.space();
}
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) { if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
self.argnames[0].print(output); self.argnames[0].print(output);
} else { } else {
@@ -1053,9 +1065,9 @@ function OutputStream(options) {
output.print('=>'); output.print('=>');
output.space(); output.space();
if (self.body instanceof AST_Node) { if (self.body instanceof AST_Node) {
this.body.print(output); self.body.print(output);
} else { } else {
print_bracketed(this.body, output); print_bracketed(self.body, output);
} }
if (needs_parens) { output.print(")") } if (needs_parens) { output.print(")") }
}); });
@@ -1280,7 +1292,6 @@ function OutputStream(options) {
name_import.print(output); name_import.print(output);
if (i < self.imported_names.length - 1) { if (i < self.imported_names.length - 1) {
output.print(","); output.print(",");
output.space();
} }
}); });
output.space(); output.space();
@@ -1296,17 +1307,26 @@ function OutputStream(options) {
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_NameImport, function(self, output) { DEFPRINT(AST_NameMapping, function(self, output) {
var is_import = output.parent() instanceof AST_Import;
var definition = self.name.definition(); var definition = self.name.definition();
var names_are_different = var names_are_different =
(definition && definition.mangled_name || self.name.name) !== (definition && definition.mangled_name || self.name.name) !==
self.foreign_name.name; self.foreign_name.name;
if (names_are_different) { if (names_are_different) {
if (is_import) {
output.print(self.foreign_name.name); output.print(self.foreign_name.name);
} else {
self.name.print(output);
}
output.space(); output.space();
output.print("as"); output.print("as");
output.space(); output.space();
if (is_import) {
self.name.print(output); self.name.print(output);
} else {
output.print(self.foreign_name.name);
}
} else { } else {
self.name.print(output); self.name.print(output);
} }
@@ -1320,24 +1340,20 @@ function OutputStream(options) {
output.space(); output.space();
} }
if (self.exported_names) { if (self.exported_names) {
output.space();
if (self.exported_names.length === 1 && self.exported_names[0].name.name === "*") { if (self.exported_names.length === 1 && self.exported_names[0].name.name === "*") {
self.exported_names[0].print(output); self.exported_names[0].print(output);
} else { } else {
output.print("{"); output.print("{");
self.exported_names.forEach(function (name_import, i) { self.exported_names.forEach(function(name_export, i) {
output.space(); output.space();
name_import.print(output); name_export.print(output);
if (i < self.exported_names.length - 1) { if (i < self.exported_names.length - 1) {
output.print(","); output.print(",");
output.space();
} }
}); });
output.space(); output.space();
output.print("}"); output.print("}");
} }
output.space();
} }
else if (self.exported_value) { else if (self.exported_value) {
self.exported_value.print(output); self.exported_value.print(output);
@@ -1354,19 +1370,17 @@ function OutputStream(options) {
}); });
function parenthesize_for_noin(node, output, noin) { function parenthesize_for_noin(node, output, noin) {
if (!noin) node.print(output); var parens = false;
else try {
// need to take some precautions here: // need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60 // https://github.com/mishoo/UglifyJS2/issues/60
node.walk(new TreeWalker(function(node){ if (noin) node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Binary && node.operator == "in") if (parens || node instanceof AST_Scope) return true;
throw output; if (node instanceof AST_Binary && node.operator == "in") {
})); parens = true;
node.print(output); return true;
} catch(ex) {
if (ex !== output) throw ex;
node.print(output, true);
} }
}));
node.print(output, parens);
}; };
DEFPRINT(AST_VarDef, function(self, output){ DEFPRINT(AST_VarDef, function(self, output){
@@ -1386,6 +1400,9 @@ function OutputStream(options) {
self.expression.print(output); self.expression.print(output);
if (self instanceof AST_New && !need_constructor_parens(self, output)) if (self instanceof AST_New && !need_constructor_parens(self, output))
return; return;
if (self.expression instanceof AST_Lambda) {
output.add_mapping(self.start);
}
output.with_parens(function(){ output.with_parens(function(){
self.args.forEach(function(expr, i){ self.args.forEach(function(expr, i){
if (i) output.comma(); if (i) output.comma();
@@ -1425,6 +1442,13 @@ function OutputStream(options) {
DEFPRINT(AST_Dot, function(self, output){ DEFPRINT(AST_Dot, function(self, output){
var expr = self.expression; var expr = self.expression;
expr.print(output); expr.print(output);
var prop = self.property;
if (output.option("ie8") && RESERVED_WORDS(prop)) {
output.print("[");
output.add_mapping(self.end);
output.print_string(prop);
output.print("]");
} else {
if (expr instanceof AST_Number && expr.getValue() >= 0) { if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.)]/i.test(output.last())) { if (!/[xa-f.)]/i.test(output.last())) {
output.print("."); output.print(".");
@@ -1433,7 +1457,8 @@ function OutputStream(options) {
output.print("."); output.print(".");
// the name after dot would be mapped about here. // the name after dot would be mapped about here.
output.add_mapping(self.end); output.add_mapping(self.end);
output.print_name(self.property); output.print_name(prop);
}
}); });
DEFPRINT(AST_Sub, function(self, output){ DEFPRINT(AST_Sub, function(self, output){
self.expression.print(output); self.expression.print(output);

View File

@@ -509,8 +509,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
} }
var content = "", raw = "", ch, tok; var content = "", raw = "", ch, tok;
next(true, true); next(true, true);
while ((ch = next(true, true)) !== "`") { while ((ch = next(true, true)) != "`") {
if (ch === "$" && peek() === "{") { if (ch == "\r") {
if (peek() == "\n") ++S.pos;
ch = "\n";
} else if (ch == "$" && peek() == "{") {
next(true, true); next(true, true);
S.brace_counter++; S.brace_counter++;
tok = token(begin ? "template_head" : "template_substitution", content); tok = token(begin ? "template_head" : "template_substitution", content);
@@ -521,7 +524,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
} }
raw += ch; raw += ch;
if (ch === "\\") { if (ch == "\\") {
var tmp = S.pos; var tmp = S.pos;
ch = read_escaped_char(); ch = read_escaped_char();
raw += S.text.substr(tmp, S.pos - tmp); raw += S.text.substr(tmp, S.pos - tmp);
@@ -856,6 +859,7 @@ function parse($TEXT, options) {
options = defaults(options, { options = defaults(options, {
bare_returns : false, bare_returns : false,
ecma : 8,
expression : false, expression : false,
filename : null, filename : null,
html5_comments : true, html5_comments : true,
@@ -1229,9 +1233,12 @@ function parse($TEXT, options) {
var is_in = is("operator", "in"); var is_in = is("operator", "in");
var is_of = is("name", "of"); var is_of = is("name", "of");
if (is_in || is_of) { if (is_in || is_of) {
if ((init instanceof AST_Definitions) && if (init instanceof AST_Definitions) {
init.definitions.length > 1) if (init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop"); croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
} else if (!(is_assignable(init) || (init = to_destructuring(init)) instanceof AST_Destructuring)) {
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
}
next(); next();
if (is_in) { if (is_in) {
return for_in(init); return for_in(init);
@@ -1281,18 +1288,19 @@ function parse($TEXT, options) {
}); });
}; };
var arrow_function = function(start, argnames) { var arrow_function = function(start, argnames, is_async) {
if (S.token.nlb) { if (S.token.nlb) {
croak("Unexpected newline before arrow (=>)"); croak("Unexpected newline before arrow (=>)");
} }
expect_token("arrow", "=>"); expect_token("arrow", "=>");
var body = _function_body(is("punc", "{")); var body = _function_body(is("punc", "{"), false, is_async);
return new AST_Arrow({ return new AST_Arrow({
start : start, start : start,
end : body.end, end : body.end,
async : is_async,
argnames : argnames, argnames : argnames,
body : body body : body
}); });
@@ -1385,22 +1393,20 @@ function parse($TEXT, options) {
function parameters() { function parameters() {
var start = S.token; var start = S.token;
var first = true;
var params = []; var params = [];
var used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict")); var used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
expect("("); expect("(");
while (!is("punc", ")")) { while (!is("punc", ")")) {
if (first) {
first = false;
} else {
expect(",");
}
var param = parameter(used_parameters); var param = parameter(used_parameters);
params.push(param); params.push(param);
if (!is("punc", ")")) {
expect(",");
if (is("punc", ")") && options.ecma < 8) unexpected();
}
if (param instanceof AST_Expansion) { if (param instanceof AST_Expansion) {
break; break;
} }
@@ -1616,25 +1622,40 @@ function parse($TEXT, options) {
} }
} }
function params_or_seq_() { function params_or_seq_(allow_arrows, maybe_sequence) {
var first = true; var spread_token;
var invalid_sequence;
var trailing_comma;
var a = []; var a = [];
expect("(");
while (!is("punc", ")")) { while (!is("punc", ")")) {
if (first) first = false; else expect(","); if (spread_token) unexpected(spread_token);
if (is("expand", "...")) { if (is("expand", "...")) {
var spread_token = S.token; spread_token = S.token;
if (maybe_sequence) invalid_sequence = S.token;
next(); next();
a.push(new AST_Expansion({ a.push(new AST_Expansion({
start: prev(), start: prev(),
expression: expression(), expression: expression(),
end: S.token, end: S.token,
})); }));
if (!is("punc", ")")) {
unexpected(spread_token);
}
} else { } else {
a.push(expression()); a.push(expression());
} }
if (!is("punc", ")")) {
expect(",");
if (is("punc", ")")) {
if (options.ecma < 8) unexpected();
trailing_comma = prev();
if (maybe_sequence) invalid_sequence = trailing_comma;
}
}
}
expect(")");
if (allow_arrows && is("arrow", "=>")) {
if (spread_token && trailing_comma) unexpected(trailing_comma);
} else if (invalid_sequence) {
unexpected(invalid_sequence);
} }
return a; return a;
} }
@@ -1882,7 +1903,7 @@ function parse($TEXT, options) {
var newexp = expr_atom(false), args; var newexp = expr_atom(false), args;
if (is("punc", "(")) { if (is("punc", "(")) {
next(); next();
args = expr_list(")"); args = expr_list(")", options.ecma >= 8);
} else { } else {
args = []; args = [];
} }
@@ -1989,21 +2010,24 @@ function parse($TEXT, options) {
} }
} }
var expr_atom = function(allow_calls) { var expr_atom = function(allow_calls, allow_arrows) {
if (is("operator", "new")) { if (is("operator", "new")) {
return new_(allow_calls); return new_(allow_calls);
} }
var start = S.token; var start = S.token;
var async = is("name", "async") && as_atom_node();
if (is("punc")) { if (is("punc")) {
switch (start.value) { switch (S.token.value) {
case "(": case "(":
next(); if (async && !allow_calls) break;
var exprs = params_or_seq_(); var exprs = params_or_seq_(allow_arrows, !async);
expect(")"); if (allow_arrows && is("arrow", "=>")) {
if (is("arrow", "=>")) { return arrow_function(start, exprs.map(to_fun_args), !!async);
return arrow_function(start, exprs.map(to_fun_args));
} }
var ex = exprs.length == 1 ? exprs[0] : new AST_Sequence({ var ex = async ? new AST_Call({
expression: async,
args: exprs
}) : exprs.length == 1 ? exprs[0] : new AST_Sequence({
expressions: exprs expressions: exprs
}); });
ex.start = start; ex.start = start;
@@ -2012,25 +2036,27 @@ function parse($TEXT, options) {
case "[": case "[":
return subscripts(array_(), allow_calls); return subscripts(array_(), allow_calls);
case "{": case "{":
return subscripts(object_or_object_destructuring_(), allow_calls); return subscripts(object_or_destructuring_(), allow_calls);
} }
unexpected(); if (!async) unexpected();
} }
if (is("name", "async") && is_token(peek(), "keyword", "function")) { if (allow_arrows && is("name") && is_token(peek(), "arrow")) {
var param = new AST_SymbolFunarg({
name: S.token.value,
start: start,
end: start,
});
next(); next();
next(); return arrow_function(start, [param], !!async);
var func = function_(AST_Function, false, true);
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
} }
if (is("keyword", "function")) { if (is("keyword", "function")) {
next(); next();
var func = function_(AST_Function); var func = function_(AST_Function, false, !!async);
func.start = start; func.start = start;
func.end = prev(); func.end = prev();
return subscripts(func, allow_calls); return subscripts(func, allow_calls);
} }
if (async) return subscripts(async, allow_calls);
if (is("keyword", "class")) { if (is("keyword", "class")) {
next(); next();
var cls = class_(AST_ClassExpression); var cls = class_(AST_ClassExpression);
@@ -2109,7 +2135,7 @@ function parse($TEXT, options) {
return function_(AST_Accessor, 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 = []; var start = S.token, first = true, a = [];
expect("{"); expect("{");
while (!is("punc", "}")) { while (!is("punc", "}")) {
@@ -2231,7 +2257,7 @@ function parse($TEXT, options) {
property_token = S.token; property_token = S.token;
name = as_property_name(); name = as_property_name();
} }
if (name === "async" && !is("punc", "(")) { if (name === "async" && !is("punc", "(") && !is("punc", ",") && !is("punc", "}")) {
is_async = true; is_async = true;
property_token = S.token; property_token = S.token;
name = as_property_name(); name = as_property_name();
@@ -2302,7 +2328,7 @@ function parse($TEXT, options) {
next(); next();
} }
imported_names = import_names(true); imported_names = map_names(true);
if (imported_names || imported_name) { if (imported_names || imported_name) {
expect_token("name", "from"); expect_token("name", "from");
@@ -2326,26 +2352,40 @@ function parse($TEXT, options) {
}); });
} }
function import_name() { function map_name(is_import) {
function make_symbol(type) {
return new type({
name: as_property_name(),
start: prev(),
end: prev()
});
}
var foreign_type = is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign;
var type = is_import ? AST_SymbolImport : AST_SymbolExport;
var start = S.token; var start = S.token;
var foreign_name; var foreign_name;
var name; var name;
if (peek().value === "as" && peek().type === "name") { if (is_import) {
foreign_name = as_symbol(AST_SymbolImportForeign); foreign_name = make_symbol(foreign_type);
} else {
name = make_symbol(type);
}
if (is("name", "as")) {
next(); // The "as" word next(); // The "as" word
if (is_import) {
name = make_symbol(type);
} else {
foreign_name = make_symbol(foreign_type);
} }
name = as_symbol(AST_SymbolImport); } else if (is_import) {
name = new type(foreign_name);
if (foreign_name === undefined) { } else {
foreign_name = new AST_SymbolImportForeign({ foreign_name = new foreign_type(name);
name: name.name,
start: name.start,
end: name.end,
});
} }
return new AST_NameImport({ return new AST_NameMapping({
start: start, start: start,
foreign_name: foreign_name, foreign_name: foreign_name,
name: name, name: name,
@@ -2353,26 +2393,26 @@ function parse($TEXT, options) {
}) })
} }
function import_nameAsterisk(name) { function map_nameAsterisk(is_import, name) {
var foreign_type = is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign;
var type = is_import ? AST_SymbolImport : AST_SymbolExport;
var start = S.token; var start = S.token;
var foreign_name; var foreign_name;
var end = prev(); var end = prev();
name = name || new AST_SymbolImport({ name = name || new type({
name: '*', name: '*',
start: start, start: start,
end: end, end: end,
}); });
foreign_name = new AST_SymbolImportForeign({ foreign_name = new foreign_type({
name: '*', name: '*',
start: start, start: start,
end: end, end: end,
}); });
return new AST_NameImport({ return new AST_NameMapping({
start: start, start: start,
foreign_name: foreign_name, foreign_name: foreign_name,
name: name, name: name,
@@ -2380,13 +2420,13 @@ function parse($TEXT, options) {
}) })
} }
function import_names(allow_as) { function map_names(is_import) {
var names; var names;
if (is("punc", "{")) { if (is("punc", "{")) {
next(); next();
names = []; names = [];
while (!is("punc", "}")) { while (!is("punc", "}")) {
names.push(import_name()); names.push(map_name(is_import));
if (is("punc", ",")) { if (is("punc", ",")) {
next(); next();
} }
@@ -2395,11 +2435,11 @@ function parse($TEXT, options) {
} else if (is("operator", "*")) { } else if (is("operator", "*")) {
var name; var name;
next(); next();
if (allow_as && is("name", "as")) { if (is_import && is("name", "as")) {
next(); // The "as" word next(); // The "as" word
name = as_symbol(AST_SymbolImportForeign); name = as_symbol(AST_SymbolImportForeign);
} }
names = [import_nameAsterisk(name)]; names = [map_nameAsterisk(is_import, name)];
} }
return names; return names;
} }
@@ -2407,17 +2447,12 @@ function parse($TEXT, options) {
function export_() { function export_() {
var start = S.token; var start = S.token;
var is_default; var is_default;
var exported_value;
var exported_definition;
var exported_names; var exported_names;
if (is("keyword", "default")) { if (is("keyword", "default")) {
is_default = true; is_default = true;
next(); next();
} else { } else if (exported_names = map_names(false)) {
exported_names = import_names(false);
if (exported_names) {
if (is("name", "from")) { if (is("name", "from")) {
next(); next();
@@ -2448,31 +2483,24 @@ function parse($TEXT, options) {
}); });
} }
} }
}
var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const"); var node;
if (is_definition) { var exported_value;
if (is_default) unexpected(); var exported_definition;
exported_definition = statement(); if (is("punc", "{")
} else if (is("keyword", "class")) { || is_default
var cls = expr_atom(false); && (is("keyword", "class") || is("keyword", "function"))
if (cls.name) { && is_token(peek(), "punc")) {
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(false); exported_value = expression(false);
semicolon(); semicolon();
} else if ((node = statement()) instanceof AST_Definitions && is_default) {
unexpected(node.start);
} else if (node instanceof AST_Definitions || node instanceof AST_Defun || node instanceof AST_DefClass) {
exported_definition = node;
} else if (node instanceof AST_SimpleStatement) {
exported_value = node.body;
} else {
unexpected(node.start);
} }
return new AST_Export({ return new AST_Export({
@@ -2598,11 +2626,9 @@ function parse($TEXT, options) {
return expr; return expr;
}; };
var call_args = embed_tokens(function call_args() { var call_args = embed_tokens(function _call_args() {
var first = true;
var args = []; var args = [];
while (!is("punc", ")")) { while (!is("punc", ")")) {
if (first) first = false; else expect(",");
if (is("expand", "...")) { if (is("expand", "...")) {
next(); next();
args.push(new AST_Expansion({ args.push(new AST_Expansion({
@@ -2612,12 +2638,16 @@ function parse($TEXT, options) {
} else { } else {
args.push(expression(false)); args.push(expression(false));
} }
if (!is("punc", ")")) {
expect(",");
if (is("punc", ")") && options.ecma < 8) unexpected();
}
} }
next(); next();
return args; return args;
}); });
var maybe_unary = function(allow_calls) { var maybe_unary = function(allow_calls, allow_arrows) {
var start = S.token; var start = S.token;
if (start.type == "name" && start.value == "await") { if (start.type == "name" && start.value == "await") {
if (is_in_async()) { if (is_in_async()) {
@@ -2635,8 +2665,9 @@ function parse($TEXT, options) {
ex.end = prev(); ex.end = prev();
return ex; return ex;
} }
var val = expr_atom(allow_calls); var val = expr_atom(allow_calls, allow_arrows);
while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
if (val instanceof AST_Arrow) unexpected();
val = make_unary(AST_UnaryPostfix, S.token, val); val = make_unary(AST_UnaryPostfix, S.token, val);
val.start = start; val.start = start;
val.end = S.token; val.end = S.token;
@@ -2684,7 +2715,7 @@ function parse($TEXT, options) {
}; };
function expr_ops(no_in) { function expr_ops(no_in) {
return expr_op(maybe_unary(true), 0, no_in); return expr_op(maybe_unary(true, true), 0, no_in);
}; };
var maybe_conditional = function(no_in) { var maybe_conditional = function(no_in) {
@@ -2765,22 +2796,6 @@ function parse($TEXT, options) {
} }
} }
if (start.type == "punc" && start.value == "(" && peek().value == ")") {
next();
next();
return arrow_function(start, []);
}
if (is("name") && is_token(peek(), "arrow")) {
var param = new AST_SymbolFunarg({
name: start.value,
start: start,
end: start,
});
next();
return arrow_function(start, [param]);
}
var left = maybe_conditional(no_in); var left = maybe_conditional(no_in);
var val = S.token.value; var val = S.token.value;

View File

@@ -94,7 +94,7 @@ function mangle_properties(ast, options) {
only_cache: false, only_cache: false,
regex: null, regex: null,
reserved: null, reserved: null,
}); }, true);
var reserved = options.reserved; var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = []; if (!Array.isArray(reserved)) reserved = [];

View File

@@ -85,7 +85,7 @@ SymbolDef.prototype = {
if (options.ie8 && sym instanceof AST_SymbolLambda) if (options.ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope; s = s.parent_scope;
var def; var def;
if (this.defun && (def = this.defun.variables.get(this.name))) { if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name; this.mangled_name = def.mangled_name || def.name;
} else } else
this.mangled_name = s.next_mangled(options, this); this.mangled_name = s.next_mangled(options, this);
@@ -93,6 +93,9 @@ SymbolDef.prototype = {
cache.set(this.name, this.mangled_name); cache.set(this.name, this.mangled_name);
} }
} }
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
} }
}; };
@@ -129,7 +132,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
scope = save_scope; scope = save_scope;
return true; return true;
} }
if (node instanceof AST_Destructuring && node.is_array === false) { if (node instanceof AST_Destructuring) {
in_destructuring = node; // These don't nest in_destructuring = node; // These don't nest
descend(); descend();
in_destructuring = null; in_destructuring = null;
@@ -223,10 +226,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
})); }));
node.thedef = sym; node.thedef = sym;
} }
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
js_error(
node.TYPE + " statement may only appear at top level",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
function mark_export(def, level) { function mark_export(def, level) {
if (in_destructuring) {
var i = 0;
do {
level++;
} while (tw.parent(i++) !== in_destructuring);
}
var node = tw.parent(level); var node = tw.parent(level);
def.export = node instanceof AST_Export && !node.is_default; def.export = node instanceof AST_Export;
} }
}); });
self.walk(tw); self.walk(tw);
@@ -245,8 +263,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
s.uses_eval = true; s.uses_eval = true;
} }
} }
var sym = node.scope.find_variable(name); var sym;
if (!sym) { if (tw.parent() instanceof AST_NameMapping && tw.parent(1).module_name
|| !(sym = node.scope.find_variable(name))) {
sym = self.def_global(node); sym = self.def_global(node);
} else if (sym.scope instanceof AST_Lambda && name == "arguments") { } else if (sym.scope instanceof AST_Lambda && name == "arguments") {
sym.scope.uses_arguments = true; sym.scope.uses_arguments = true;
@@ -255,6 +274,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.reference(options); node.reference(options);
return true; return true;
} }
// ensure mangling works if catch reuses a scope variable
var def;
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
var s = node.scope;
while (s) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
s = s.parent_scope;
}
}
}); });
self.walk(tw); self.walk(tw);
@@ -421,7 +450,7 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
AST_Symbol.DEFMETHOD("unmangleable", function(options){ AST_Symbol.DEFMETHOD("unmangleable", function(options){
var def = this.definition(); var def = this.definition();
return def && def.unmangleable(options); return !def || def.unmangleable(options);
}); });
// labels are always mangleable // labels are always mangleable
@@ -432,14 +461,6 @@ AST_Symbol.DEFMETHOD("unreferenced", function(){
&& !(this.scope.uses_eval || this.scope.uses_with); && !(this.scope.uses_eval || this.scope.uses_with);
}); });
AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared;
});
AST_LabelRef.DEFMETHOD("undeclared", return_false);
AST_Label.DEFMETHOD("undeclared", return_false);
AST_Symbol.DEFMETHOD("definition", function(){ AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef; return this.thedef;
}); });
@@ -526,113 +547,69 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options); options = this._default_mangler_options(options);
var tw = new TreeWalker(function(node){ try {
if (node instanceof AST_Constant) AST_Node.prototype.print = function(stream, force_parens) {
base54.consider(node.print_to_string()); this._print(stream, force_parens);
else if (node instanceof AST_Return) if (this instanceof AST_Symbol && !this.unmangleable(options)) {
base54.consider("return"); base54.consider(this.name, -1);
else if (node instanceof AST_Throw) } else if (options.properties) {
base54.consider("throw"); if (this instanceof AST_Dot) {
else if (node instanceof AST_Continue) base54.consider(this.property, -1);
base54.consider("continue"); } else if (this instanceof AST_Sub) {
else if (node instanceof AST_Break) skip_string(this.property);
base54.consider("break"); }
else if (node instanceof AST_Debugger) }
base54.consider("debugger"); };
else if (node instanceof AST_Directive) base54.consider(this.print_to_string(), 1);
base54.consider(node.value); } finally {
else if (node instanceof AST_While) AST_Node.prototype.print = AST_Node.prototype._print;
base54.consider("while");
else if (node instanceof AST_Do)
base54.consider("do while");
else if (node instanceof AST_If) {
base54.consider("if");
if (node.alternative) base54.consider("else");
} }
else if (node instanceof AST_Var)
base54.consider("var");
else if (node instanceof AST_Const)
base54.consider("const");
else if (node instanceof AST_Lambda)
base54.consider("function");
else if (node instanceof AST_For)
base54.consider("for");
else if (node instanceof AST_ForIn)
base54.consider("for in");
else if (node instanceof AST_Switch)
base54.consider("switch");
else if (node instanceof AST_Case)
base54.consider("case");
else if (node instanceof AST_Default)
base54.consider("default");
else if (node instanceof AST_With)
base54.consider("with");
else if (node instanceof AST_ObjectSetter)
base54.consider("set" + (typeof node.key === "string" ? node.key : ""));
else if (node instanceof AST_ObjectGetter)
base54.consider("get" + (typeof node.key === "string" ? node.key : ""));
else if (node instanceof AST_ObjectKeyVal && typeof node.key === "string")
base54.consider(node.key);
else if (node instanceof AST_ConciseMethod && typeof node.key === "string")
base54.consider(node.key);
else if (node instanceof AST_New)
base54.consider("new");
else if (node instanceof AST_This)
base54.consider("this");
else if (node instanceof AST_Super)
base54.consider("super");
else if (node instanceof AST_Try)
base54.consider("try");
else if (node instanceof AST_Catch)
base54.consider("catch");
else if (node instanceof AST_Finally)
base54.consider("finally");
else if (node instanceof AST_Yield)
base54.consider("yield");
else if (node instanceof AST_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)
base54.consider(node.operator);
else if (node instanceof AST_Dot)
base54.consider(node.property);
});
this.walk(tw);
base54.sort(); base54.sort();
function skip_string(node) {
if (node instanceof AST_String) {
base54.consider(node.value, -1);
} else if (node instanceof AST_Conditional) {
skip_string(node.consequent);
skip_string(node.alternative);
} else if (node instanceof AST_Sequence) {
skip_string(node.expressions[node.expressions.length - 1]);
}
}
}); });
var base54 = (function() { var base54 = (function() {
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"; var leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split("");
var digits = "0123456789".split("");
var chars, frequency; var chars, frequency;
function reset() { function reset() {
frequency = Object.create(null); frequency = Object.create(null);
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) }); leading.forEach(function(ch) {
chars.forEach(function(ch){ frequency[ch] = 0 }); frequency[ch] = 0;
});
digits.forEach(function(ch) {
frequency[ch] = 0;
});
} }
base54.consider = function(str){ base54.consider = function(str, delta) {
for (var i = str.length; --i >= 0;) { for (var i = str.length; --i >= 0;) {
var code = str.charCodeAt(i); frequency[str[i]] += delta;
if (code in frequency) ++frequency[code];
} }
}; };
base54.sort = function() { function compare(a, b) {
chars = mergeSort(chars, function(a, b){
if (is_digit(a) && !is_digit(b)) return 1;
if (is_digit(b) && !is_digit(a)) return -1;
return frequency[b] - frequency[a]; return frequency[b] - frequency[a];
}); }
base54.sort = function() {
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
}; };
base54.reset = reset; base54.reset = reset;
reset(); reset();
base54.get = function(){ return chars };
base54.freq = function(){ return frequency };
function base54(num) { function base54(num) {
var ret = "", base = 54; var ret = "", base = 54;
num++; num++;
do { do {
num--; num--;
ret += String.fromCharCode(chars[num % base]); ret += chars[num % base];
num = Math.floor(num / base); num = Math.floor(num / base);
base = 64; base = 64;
} while (num > 0); } while (num > 0);

View File

@@ -70,7 +70,7 @@ TreeTransformer.prototype = new TreeWalker;
if (y !== undefined) x = y; if (y !== undefined) x = y;
} }
} }
tw.pop(this); tw.pop();
return x; return x;
}); });
}; };
@@ -243,9 +243,22 @@ TreeTransformer.prototype = new TreeWalker;
self.expression = self.expression.transform(tw); self.expression = self.expression.transform(tw);
}); });
_(AST_Export, function(self, tw){ _(AST_NameMapping, function(self, tw) {
self.foreign_name = self.foreign_name.transform(tw);
self.name = self.name.transform(tw);
});
_(AST_Import, function(self, tw) {
if (self.imported_name) self.imported_name = self.imported_name.transform(tw);
if (self.imported_names) do_list(self.imported_names, tw);
self.module_name = self.module_name.transform(tw);
});
_(AST_Export, function(self, tw) {
if (self.exported_definition) self.exported_definition = self.exported_definition.transform(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); if (self.exported_value) self.exported_value = self.exported_value.transform(tw);
if (self.exported_names) do_list(self.exported_names, tw);
if (self.module_name) self.module_name = self.module_name.transform(tw);
}); });
_(AST_TemplateString, function(self, tw) { _(AST_TemplateString, function(self, tw) {

View File

@@ -1,20 +1,17 @@
{ {
"name": "uglify-es", "name": "uglify-es",
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit for ES6+",
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.0.17", "version": "3.0.25",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
"maintainers": [ "maintainers": [
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)" "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
], ],
"repository": { "repository": "git+https://github.com/mishoo/UglifyJS2.git#harmony",
"type": "git",
"url": "https://github.com/mishoo/UglifyJS2.git"
},
"bugs": { "bugs": {
"url": "https://github.com/mishoo/UglifyJS2/issues" "url": "https://github.com/mishoo/UglifyJS2/issues"
}, },
@@ -40,5 +37,22 @@
"scripts": { "scripts": {
"test": "node test/run-tests.js" "test": "node test/run-tests.js"
}, },
"keywords": ["uglify", "uglify-js", "uglify-es", "minify", "minifier", "es5", "es6", "es2015"] "keywords": [
"uglify",
"uglify-es",
"uglify-js",
"minify",
"minifier",
"javascript",
"ecmascript",
"es5",
"es6",
"es7",
"es8",
"es2015",
"es2016",
"es2017",
"async",
"await"
]
} }

View File

@@ -6,6 +6,7 @@
var createHash = require("crypto").createHash; var createHash = require("crypto").createHash;
var fetch = require("./fetch"); var fetch = require("./fetch");
var fork = require("child_process").fork; var fork = require("child_process").fork;
var zlib = require("zlib");
var args = process.argv.slice(2); var args = process.argv.slice(2);
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mc");
@@ -33,6 +34,7 @@ function done() {
console.log(info.log); console.log(info.log);
console.log("Original:", info.input, "bytes"); console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes"); console.log("Uglified:", info.output, "bytes");
console.log("GZipped: ", info.gzip, "bytes");
console.log("SHA1 sum:", info.sha1); console.log("SHA1 sum:", info.sha1);
if (info.code) { if (info.code) {
failures.push(url); failures.push(url);
@@ -51,6 +53,7 @@ urls.forEach(function(url) {
results[url] = { results[url] = {
input: 0, input: 0,
output: 0, output: 0,
gzip: 0,
log: "" log: ""
}; };
fetch(url, function(err, res) { fetch(url, function(err, res) {
@@ -61,6 +64,10 @@ urls.forEach(function(url) {
}).pipe(uglifyjs.stdin); }).pipe(uglifyjs.stdin);
uglifyjs.stdout.on("data", function(data) { uglifyjs.stdout.on("data", function(data) {
results[url].output += data.length; results[url].output += data.length;
}).pipe(zlib.createGzip({
level: zlib.Z_BEST_COMPRESSION
})).on("data", function(data) {
results[url].gzip += data.length;
}).pipe(createHash("sha1")).on("data", function(data) { }).pipe(createHash("sha1")).on("data", function(data) {
results[url].sha1 = data.toString("hex"); results[url].sha1 = data.toString("hex");
done(); done();

View File

@@ -202,3 +202,398 @@ arrow_unused_toplevel: {
expect_stdout: [ "0", "1", "2", "9" ] expect_stdout: [ "0", "1", "2", "9" ]
node_version: ">=6" node_version: ">=6"
} }
no_leading_parentheses: {
input: {
(x,y) => x(y);
async (x,y) => await x(y);
}
expect_exact: "(x,y)=>x(y);async(x,y)=>await x(y);"
}
async_identifiers: {
options = {
arrows: true,
ecma: 6,
}
input: {
var async = function(x){ console.log("async", x); };
var await = function(x){ console.log("await", x); };
async(1);
await(2);
}
expect: {
var async = x => { console.log("async", x); };
var await = x => { console.log("await", x); };
async(1);
await(2);
}
expect_stdout: [
"async 1",
"await 2",
]
node_version: ">=4"
}
async_function_expression: {
options = {
arrows: true,
ecma: 6,
evaluate: true,
side_effects: 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 foo() {
await bar(1);
};
var anon = async () => {
await 1, bar(2);
};
}
}
issue_27: {
options = {
arrows: true,
collapse_vars: true,
ecma: 6,
unused: true,
}
input: {
(function(jQuery) {
var $;
$ = jQuery;
$("body").addClass("foo");
})(jQuery);
}
expect: {
(jQuery => {
jQuery("body").addClass("foo");
})(jQuery);
}
}
issue_2105_1: {
options = {
arrows: true,
collapse_vars: true,
ecma: 6,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
return bar;
} );
});
}
expect: {
(() => {
var quux = () => {
console.log("PASS");
};
return {
prop() {
console.log;
quux();
}
};
})().prop();
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_2105_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
((factory) => {
factory();
})( () => {
return ((fn) => {
fn()().prop();
})( () => {
let bar = () => {
var quux = () => {
console.log("PASS");
}, foo = () => {
console.log;
quux();
};
return { prop: foo };
};
return bar;
} );
});
}
expect: {
(() => {
var quux = () => {
console.log("PASS");
};
return {
prop: () => {
console.log;
quux();
}
};
})().prop();
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_2136_2: {
options = {
arrows: true,
collapse_vars: true,
ecma: 6,
inline: true,
side_effects: true,
unused: true,
}
input: {
function f(x) {
console.log(x);
}
!function(a, ...b) {
f(b[0]);
}(1, 2, 3);
}
expect: {
function f(x) {
console.log(x);
}
f([2,3][0]);
}
expect_stdout: "2"
node_version: ">=6"
}
issue_2136_3: {
options = {
arrows: true,
collapse_vars: true,
ecma: 6,
evaluate: true,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function f(x) {
console.log(x);
}
!function(a, ...b) {
f(b[0]);
}(1, 2, 3);
}
expect: {
console.log(2);
}
expect_stdout: "2"
node_version: ">=6"
}
call_args: {
options = {
arrows: true,
ecma: 6,
evaluate: true,
inline: true,
reduce_vars: true,
}
input: {
const a = 1;
console.log(a);
+function(a) {
return a;
}(a);
}
expect: {
const a = 1;
console.log(1);
+(1, 1);
}
expect_stdout: true
}
call_args_drop_param: {
options = {
arrows: true,
ecma: 6,
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
unused: true,
}
input: {
const a = 1;
console.log(a);
+function(a) {
return a;
}(a, b);
}
expect: {
const a = 1;
console.log(1);
+(b, 1);
}
expect_stdout: true
}
issue_485_crashing_1530: {
options = {
arrows: true,
conditionals: true,
dead_code: true,
ecma: 6,
evaluate: true,
inline: true,
}
input: {
(function(a) {
if (true) return;
var b = 42;
})(this);
}
expect: {
this, void 0;
}
}
issue_2084: {
options = {
arrows: true,
collapse_vars: true,
conditionals: true,
ecma: 6,
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;
((c) => {
c = 1 + c,
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
})(-1),
console.log(c);
}
expect_stdout: "0"
node_version: ">=4"
}
export_default_object_expression: {
options = {
arrows: true,
evaluate: true,
}
input: {
export default {
foo: 1 + 2,
bar() { return 4; },
get baz() { return this.foo; },
};
}
expect_exact: "export default{foo:3,bar:()=>4,get baz(){return this.foo}};"
}
concise_methods_with_computed_property2: {
options = {
arrows: true,
evaluate: true,
}
input: {
var foo = {
[[1]](v) {
return v;
}
};
console.log(foo[[1]]("PASS"));
}
expect_exact: 'var foo={[[1]]:v=>v};console.log(foo[[1]]("PASS"));'
expect_stdout: "PASS"
node_version: ">=4"
}
async_object_literal: {
options = {
arrows: true,
ecma: 6,
evaluate: true,
}
input: {
var obj = {
async a() {
return await foo(1 + 0);
},
anon: async function() {
return await foo(2 + 0);
}
};
}
expect: {
var obj = {
a: async () => await foo(1),
anon: async () => await foo(2)
};
}
}

View File

@@ -6,6 +6,22 @@ await_precedence: {
expect_exact: "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)}"
} }
await_precedence_prop: {
input: {
async function f1(){ return (await foo()).bar; }
async function f2(){ return (await foo().bar); }
}
expect_exact: "async function f1(){return(await foo()).bar}async function f2(){return await foo().bar}"
}
await_precedence_call: {
input: {
async function f3(){ return (await foo())(); }
async function f4(){ return await (foo()()); }
}
expect_exact: "async function f3(){return(await foo())()}async function f4(){return await foo()()}"
}
async_function_declaration: { async_function_declaration: {
options = { options = {
side_effects: true, side_effects: true,
@@ -167,14 +183,14 @@ async_inline: {
async_identifiers: { async_identifiers: {
input: { input: {
let async = function(x){ console.log("async", x); }; var async = function(x){ console.log("async", x); };
let await = function(x){ console.log("await", x); }; var await = function(x){ console.log("await", x); };
async(1); async(1);
await(2); await(2);
} }
expect: { expect: {
let async = function(x){ console.log("async", x); }; var async = function(x){ console.log("async", x); };
let await = function(x){ console.log("await", x); }; var await = function(x){ console.log("await", x); };
async(1); async(1);
await(2); await(2);
} }
@@ -182,19 +198,97 @@ async_identifiers: {
"async 1", "async 1",
"await 2", "await 2",
] ]
node_version: ">=8"
} }
/* FIXME: add test when supported by parser 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"
}
async_arrow: { async_arrow: {
input: { input: {
let a1 = async x => await foo(x); let a1 = async x => await foo(x);
let a2 = async () => await bar(); let a2 = async () => await bar();
let a3 = async (x) => await baz(x); let a3 = async (x) => await baz(x);
let a4 = async (x, y) => { await far(x, y); } let a4 = async (x, y) => { await far(x, y); }
let a5 = async ({x = [1], y: z = 2}) => { await wow(x, y); } let a5 = async ({x = [1], y: z = 2}) => { await wow(x, z); }
} }
expect: { expect: {
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, z); }
} }
} }
*/
async_arrow_wait: {
input: {
var a = async (x, y) => await x(y);
}
expect_exact: "var a=async(x,y)=>await x(y);"
}
async_arrow_iife: {
input: {
(async () => {
await fetch({});
})();
}
expect_exact: "(async()=>{await fetch({})})();"
}
async_arrow_iife_negate_iife: {
options = {
negate_iife: true,
}
input: {
(async () => {
await fetch();
})();
(() => {
plain();
})();
}
expect_exact: "(async()=>{await fetch()})();(()=>{plain()})();"
}

View File

@@ -104,34 +104,36 @@ regression_block_scope_resolves: {
}; };
input: { input: {
(function () { (function () {
if(1) { if (1) {
let x; let x;
const y; const y = 1;
class Zee {}; class Zee {};
} }
if(1) { if (1) {
let ex; let ex;
const why; const why = 2;
class Zi {}; class Zi {};
} }
console.log(x, y, Zee, ex, why, Zi); console.log(typeof x, typeof y, typeof Zee, typeof ex, typeof why, typeof Zi);
}()); }());
} }
expect: { expect: {
(function () { (function () {
if (1) { if (1) {
let o; let e;
const n; const o = 1;
class c {}; class t {};
} }
if (1) { if (1) {
let o; let e;
const n; const o = 2;
class c {}; class t {};
} }
console.log(x, y, Zee, ex, why, Zi); console.log(typeof x, typeof y, typeof Zee, typeof ex, typeof why, typeof Zi);
}()); }());
} }
expect_stdout: "undefined undefined undefined undefined undefined undefined"
node_version: ">=6"
} }
switch_block_scope_mangler: { switch_block_scope_mangler: {
@@ -153,25 +155,37 @@ switch_block_scope_mangler: {
console.log(cat); console.log(cat);
} }
}; };
fn(1);
fn(2);
fn(3);
} }
expect: { expect: {
var fn = function(o) { var fn = function(e) {
switch (o) { switch (e) {
case 1: case 1:
let e = o + 1 let l = e + 1
let c = o + 4; let o = e + 4;
console.log(e, c); console.log(l, o);
break; break;
case 2: case 2:
let l = o + 2; let n = e + 2;
console.log(l); console.log(n);
break; break;
default: default:
let a = o + 3; let c = e + 3;
console.log(a); console.log(c);
} }
}; };
fn(1);
fn(2);
fn(3);
} }
expect_stdout: [
"2 5",
"4",
"6",
]
node_version: ">=6"
} }

View File

@@ -863,7 +863,7 @@ collapse_vars_unary: {
input: { input: {
function f0(o, p) { function f0(o, p) {
var x = o[p]; var x = o[p];
delete x; return delete x;
} }
function f1(n) { function f1(n) {
var k = !!n; var k = !!n;
@@ -893,7 +893,7 @@ collapse_vars_unary: {
expect: { expect: {
function f0(o, p) { function f0(o, p) {
var x = o[p]; var x = o[p];
delete x; return delete x;
} }
function f1(n) { function f1(n) {
return n > +!!n return n > +!!n
@@ -2077,10 +2077,10 @@ chained_3: {
} }
expect: { expect: {
console.log(function(a, b) { console.log(function(a, b) {
var c = a, c = b; var c = 1, c = b;
b++; b++;
return c; return c;
}(1, 2)); }(0, 2));
} }
expect_stdout: "2" expect_stdout: "2"
} }
@@ -2330,3 +2330,221 @@ reassign_const_2: {
} }
expect_stdout: true expect_stdout: true
} }
issue_2187_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 1;
!function(foo) {
foo();
var a = 2;
console.log(a);
}(function() {
console.log(a);
});
}
expect: {
var a = 1;
!function(foo) {
foo();
var a = 2;
console.log(a);
}(function() {
console.log(a);
});
}
expect_stdout: [
"1",
"2",
]
}
issue_2187_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var b = 1;
console.log(function(a) {
return a && ++b;
}(b--));
}
expect: {
var b = 1;
console.log(function(a) {
return b-- && ++b;
}());
}
expect_stdout: "1"
}
issue_2187_3: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
var b = 1;
console.log(function(a) {
return a && ++b;
}(b--));
}
expect: {
var b = 1;
console.log(b-- && ++b);
}
expect_stdout: "1"
}
issue_2203_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return function(c) {
return c.a;
}((String, (Object, this)));
}
}.b());
}
expect: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return function(c) {
return c.a;
}((String, (Object, this)));
}
}.b());
}
expect_stdout: "PASS"
}
issue_2203_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
a = "PASS";
console.log({
a: "FAIL",
b: function() {
return function(c) {
return c.a;
}((String, (Object, function() {
return this;
}())));
}
}.b());
}
expect: {
a = "PASS";
console.log({
a: "FAIL",
b: function() {
return function(c) {
return (String, (Object, function() {
return this;
}())).a;
}();
}
}.b());
}
expect_stdout: "PASS"
}
issue_2203_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return function(c) {
return c.a;
}((String, (Object, (() => this)())));
}
}.b());
}
expect: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return function(c) {
return c.a;
}((String, (Object, (() => this)())));
}
}.b());
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_2203_4: {
options = {
collapse_vars: true,
unused: true,
}
input: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return (c => {
return c.a;
})((String, (Object, (() => this)())));
}
}.b());
}
expect: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return (c => {
return (String, (Object, (() => this)())).a;
})();
}
}.b());
}
expect_stdout: "PASS"
node_version: ">=4"
}
duplicate_argname: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f() { return "PASS"; }
console.log(function(a, a) {
f++;
return a;
}("FAIL", f()));
}
expect: {
function f() { return "PASS"; }
console.log(function(a, a) {
f++;
return a;
}("FAIL", f()));
}
expect_stdout: "PASS"
}

View File

@@ -377,3 +377,82 @@ accessor: {
} }
expect: {} expect: {}
} }
issue_2233_1: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
Array.isArray;
Boolean;
console.log;
Error.name;
Function.length;
Math.random;
Number.isNaN;
RegExp;
Object.defineProperty;
String.fromCharCode;
}
expect: {}
expect_stdout: true
}
issue_2233_2: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
var RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Number.isNaN;
}
}
}
issue_2233_3: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
UndeclaredGlobal;
}
}

View File

@@ -425,8 +425,8 @@ mangle_destructuring_decl: {
expect: { expect: {
function test(t) { function test(t) {
let e = t.a || { e: 7, n: 8 }; let e = t.a || { e: 7, n: 8 };
let {t: n, e: o, n: s, s: a = 9, o: c, r: l} = e; let {t: n, e: o, n: s, s: l = 9, o: a, r: c} = e;
console.log(n, o, s, a, c, l); console.log(n, o, s, l, a, c);
} }
test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } }); test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
test({}); test({});
@@ -462,15 +462,15 @@ mangle_destructuring_assign_toplevel_true: {
test({}); test({});
} }
expect: { expect: {
function n(n) { function e(e) {
let t, a, c; let l, s, a;
let l = n.a || { e: 7, n: 8 }; let c = e.a || { e: 7, n: 8 };
({t: o, e, n: s, s: t = 9, o: a, r: c} = l); ({t: n, e: o, n: t, s: l = 9, o: s, r: a} = c);
console.log(o, e, s, t, a, c); console.log(n, o, t, l, s, a);
} }
let o, e, s; let n, o, t;
n({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } }); e({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
n({}); e({});
} }
expect_stdout: [ expect_stdout: [
"1 2 3 4 5 6", "1 2 3 4 5 6",
@@ -504,10 +504,10 @@ mangle_destructuring_assign_toplevel_false: {
} }
expect: { expect: {
function test(o) { function test(o) {
let s, a, c; let s, l, a;
let l = o.a || { e: 7, n: 8 }; let c = o.a || { e: 7, n: 8 };
({t, e, n, s = 9, o: a, r: c} = l); ({t, e, n, s = 9, o: l, r: a} = c);
console.log(t, e, n, s, a, c); console.log(t, e, n, s, l, a);
} }
let t, e, n; let t, e, n;
test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } }); test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
@@ -588,8 +588,8 @@ arrow_func_with_destructuring_args: {
})({bar: 5 - 0}, [, 6]); })({bar: 5 - 0}, [, 6]);
} }
expect: { expect: {
(({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) => { (({foo: o = 1, bar: a = 2}, [b = 3, l = 4]) => {
console.log(o, n, a, b); console.log(o, a, b, l);
})({bar: 5}, [, 6]); })({bar: 5}, [, 6]);
} }
expect_stdout: "1 5 3 6" expect_stdout: "1 5 3 6"
@@ -639,3 +639,23 @@ issue_2044_ecma_6_beautify: {
} }
expect_exact: "({x: a = 1, y = 2 + b, z = 3 - c} = obj);" expect_exact: "({x: a = 1, y = 2 + b, z = 3 - c} = obj);"
} }
issue_2140: {
options = {
unused: true,
}
input: {
!function() {
var t = {};
console.log(([t.a] = [42])[0]);
}();
}
expect: {
!function() {
var t = {};
console.log(([t.a] = [42])[0]);
}();
}
expect_stdout: "42"
node_version: ">=6"
}

View File

@@ -1210,6 +1210,7 @@ var_catch_toplevel: {
a--; a--;
try { try {
a++; a++;
x();
} catch(a) { } catch(a) {
if (a) var a; if (a) var a;
var a = 10; var a = 10;
@@ -1219,9 +1220,8 @@ var_catch_toplevel: {
} }
expect: { expect: {
!function() { !function() {
a--;
try { try {
a++; x();
} catch(a) { } catch(a) {
var a; var a;
} }
@@ -1294,3 +1294,222 @@ issue_2063: {
var a; var a;
} }
} }
issue_2105: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
return bar;
} );
});
}
expect: {
(function() {
var quux = function() {
console.log("PASS");
};
return {
prop: function() {
console.log;
quux();
}
};
})().prop();
}
expect_stdout: "PASS"
}
issue_2136_1: {
options = {
inline: true,
unused: true,
}
input: {
!function(a, ...b) {
console.log(b);
}();
}
expect: {
!function(a, ...b) {
console.log(b);
}();
}
expect_stdout: "[]"
node_version: ">=6"
}
issue_2136_2: {
options = {
collapse_vars: true,
inline: true,
side_effects: true,
unused: true,
}
input: {
function f(x) {
console.log(x);
}
!function(a, ...b) {
f(b[0]);
}(1, 2, 3);
}
expect: {
function f(x) {
console.log(x);
}
f([2,3][0]);
}
expect_stdout: "2"
node_version: ">=6"
}
issue_2136_3: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function f(x) {
console.log(x);
}
!function(a, ...b) {
f(b[0]);
}(1, 2, 3);
}
expect: {
console.log(2);
}
expect_stdout: "2"
node_version: ">=6"
}
issue_2163: {
options = {
pure_funcs: [ "pure" ],
side_effects: true,
}
input: {
var c;
/*@__PURE__*/f(...a);
pure(b, ...c);
}
expect: {
var c;
a;
b;
}
}
issue_2226_1: {
options = {
side_effects: true,
unused: true,
}
input: {
function f1() {
var a = b;
a += c;
}
function f2(a) {
a <<= b;
}
function f3(a) {
--a;
}
function f4() {
var a = b;
return a *= c;
}
function f5(a) {
x(a /= b);
}
}
expect: {
function f1() {
b;
c;
}
function f2(a) {
b;
}
function f3(a) {
0;
}
function f4() {
var a = b;
return a *= c;
}
function f5(a) {
x(a /= b);
}
}
}
issue_2226_2: {
options = {
cascade: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
console.log(function(a, b) {
a += b;
return a;
}(1, 2));
}
expect: {
console.log(function(a, b) {
return a += b;
}(1, 2));
}
expect_stdout: "3"
}
issue_2226_3: {
options = {
collapse_vars: true,
side_effects: true,
unused: true,
}
input: {
console.log(function(a, b) {
a += b;
return a;
}(1, 2));
}
expect: {
console.log(function(a, b) {
return a += 2;
}(1));
}
expect_stdout: "3"
}

View File

@@ -344,22 +344,26 @@ unsafe_constant: {
unsafe_object: { unsafe_object: {
options = { options = {
evaluate : true, evaluate: true,
unsafe : true reduce_vars: true,
toplevel: true,
unsafe: true,
} }
input: { input: {
var o = { a: 1 };
console.log( console.log(
({a:1}) + 1, o + 1,
({a:1}).a + 1, o.a + 1,
({a:1}).b + 1, o.b + 1,
({a:1}).a.b + 1 o.a.b + 1
); );
} }
expect: { expect: {
var o = { a: 1 };
console.log( console.log(
({a:1}) + 1, o + 1,
2, 2,
({a:1}).b + 1, o.b + 1,
1..b + 1 1..b + 1
); );
} }
@@ -368,22 +372,26 @@ unsafe_object: {
unsafe_object_nested: { unsafe_object_nested: {
options = { options = {
evaluate : true, evaluate: true,
unsafe : true reduce_vars: true,
toplevel: true,
unsafe: true,
} }
input: { input: {
var o = { a: { b: 1 } };
console.log( console.log(
({a:{b:1}}) + 1, o + 1,
({a:{b:1}}).a + 1, o.a + 1,
({a:{b:1}}).b + 1, o.b + 1,
({a:{b:1}}).a.b + 1 o.a.b + 1
); );
} }
expect: { expect: {
var o = { a: { b: 1 } };
console.log( console.log(
({a:{b:1}}) + 1, o + 1,
({a:{b:1}}).a + 1, o.a + 1,
({a:{b:1}}).b + 1, o.b + 1,
2 2
); );
} }
@@ -392,21 +400,25 @@ unsafe_object_nested: {
unsafe_object_complex: { unsafe_object_complex: {
options = { options = {
evaluate : true, evaluate: true,
unsafe : true reduce_vars: true,
toplevel: true,
unsafe: true,
} }
input: { input: {
var o = { a: { b: 1 }, b: 1 };
console.log( console.log(
({a:{b:1},b:1}) + 1, o + 1,
({a:{b:1},b:1}).a + 1, o.a + 1,
({a:{b:1},b:1}).b + 1, o.b + 1,
({a:{b:1},b:1}).a.b + 1 o.a.b + 1
); );
} }
expect: { expect: {
var o = { a: { b: 1 }, b: 1 };
console.log( console.log(
({a:{b:1},b:1}) + 1, o + 1,
({a:{b:1},b:1}).a + 1, o.a + 1,
2, 2,
2 2
); );
@@ -416,22 +428,26 @@ unsafe_object_complex: {
unsafe_object_repeated: { unsafe_object_repeated: {
options = { options = {
evaluate : true, evaluate: true,
unsafe : true reduce_vars: true,
toplevel: true,
unsafe: true,
} }
input: { input: {
var o = { a: { b: 1 }, a: 1 };
console.log( console.log(
({a:{b:1},a:1}) + 1, o + 1,
({a:{b:1},a:1}).a + 1, o.a + 1,
({a:{b:1},a:1}).b + 1, o.b + 1,
({a:{b:1},a:1}).a.b + 1 o.a.b + 1
); );
} }
expect: { expect: {
var o = { a: { b: 1 }, a: 1 };
console.log( console.log(
({a:{b:1},a:1}) + 1, o + 1,
2, 2,
({a:{b:1},a:1}).b + 1, o.b + 1,
1..b + 1 1..b + 1
); );
} }
@@ -480,9 +496,9 @@ unsafe_function: {
expect: { expect: {
console.log( console.log(
({a:{b:1},b:function(){}}) + 1, ({a:{b:1},b:function(){}}) + 1,
({a:{b:1},b:function(){}}).a + 1, ({b:function(){}}, {b:1}) + 1,
({a:{b:1},b:function(){}}).b + 1, ({a:{b:1}}, function(){}) + 1,
({a:{b:1},b:function(){}}).a.b + 1 ({b:function(){}}, {b:1}).b + 1
); );
} }
expect_stdout: true expect_stdout: true
@@ -730,8 +746,8 @@ unsafe_prototype_function: {
var d = ({toString: 0}) + ""; var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2]; var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2]; var f = (({toString: 0}) + "")[2];
var g = ({valueOf: 0}).valueOf(); var g = ({}, 0)();
var h = "" + ({toString: 0}); var h = ({}, 0)();
} }
} }
@@ -1162,3 +1178,103 @@ string_charCodeAt: {
} }
expect_stdout: "NaN" expect_stdout: "NaN"
} }
issue_2207_1: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(String.fromCharCode(65));
console.log(Math.max(3, 6, 2, 7, 3, 4));
console.log(Math.cos(1.2345));
console.log(Math.cos(1.2345) - Math.sin(4.321));
console.log(Math.pow(Math.PI, Math.E - Math.LN10));
}
expect: {
console.log("A");
console.log(7);
console.log(Math.cos(1.2345));
console.log(1.2543732512566947);
console.log(1.6093984514472044);
}
expect_stdout: true
}
issue_2207_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect_stdout: true
}
issue_2207_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);
console.log(Number.NaN);
console.log(Number.NEGATIVE_INFINITY);
console.log(Number.POSITIVE_INFINITY);
}
expect: {
console.log(Number.MAX_VALUE);
console.log(5e-324);
console.log(NaN);
console.log(-1/0);
console.log(1/0);
}
expect_stdout: true
}
issue_2231_1: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Object.keys(void 0));
}
expect: {
console.log(Object.keys(void 0));
}
expect_stdout: true
}
issue_2231_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Object.getOwnPropertyNames(null));
}
expect: {
console.log(Object.getOwnPropertyNames(null));
}
expect_stdout: true
}

228
test/compress/export.js Normal file
View File

@@ -0,0 +1,228 @@
issue_2038_1: {
options = {
toplevel: true,
unused: true,
}
mangle = {
toplevel: true,
}
input: {
export var V = 1;
export let L = 2;
export const C = 3;
}
expect: {
export var V = 1;
export let L = 2;
export const C = 3;
}
}
issue_2038_2: {
options = {
toplevel: true,
unused: true,
}
mangle = {
toplevel: true,
}
input: {
let LET = 1;
const CONST = 2;
var VAR = 3;
export { LET, CONST, VAR };
}
expect: {
let t = 1;
const e = 2;
var o = 3;
export { t as LET, e as CONST, o as VAR };
}
}
issue_2126: {
mangle = {
toplevel: true,
}
input: {
import { foo as bar, cat as dog } from "stuff";
console.log(bar, dog);
export { bar as qux };
export { dog };
}
expect: {
import { foo as o, cat as s } from "stuff";
console.log(o, s);
export { o as qux };
export { s as dog };
}
}
beautify: {
beautify = {
beautify: true,
}
input: {
export { A as B, C as D };
}
expect_exact: "export { A as B, C as D };"
}
issue_2131: {
options = {
toplevel: true,
unused: true,
}
input: {
function no() {
console.log(42);
}
function go() {
console.log(42);
}
var X = 1, Y = 2;
export function main() {
go(X);
};
}
expect: {
function go() {
console.log(42);
}
var X = 1;
export function main() {
go(X);
};
}
}
issue_2129: {
mangle = {
toplevel: true,
}
input: {
export const { keys } = Object;
}
expect: {
export const { keys } = Object;
}
}
async_func: {
options = {
keep_fargs: false,
unused: true,
}
input: {
export async function Foo(x){};
}
expect: {
export async function Foo(){};
}
}
issue_2134_1: {
options = {
keep_fargs: false,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export function Foo(x){};
Foo.prototype = {};
}
expect: {
export function Foo(){};
Foo.prototype = {};
}
}
issue_2134_2: {
options = {
keep_fargs: false,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
export async function Foo(x){};
Foo.prototype = {};
}
expect: {
export async function Foo(){};
Foo.prototype = {};
}
}
redirection: {
mangle = {
toplevel: true,
}
input: {
let foo = 1, bar = 2;
export { foo as delete };
export { bar as default };
export { foo as var } from "module.js";
}
expect: {
let e = 1, o = 2;
export { e as delete };
export { o as default };
export { foo as var } from "module.js";
}
}
keyword_invalid_1: {
input: {
export { default };
}
expect: {
export { default };
}
}
keyword_invalid_2: {
input: {
export { default as Alias };
}
expect: {
export { default as Alias };
}
}
keyword_invalid_3: {
input: {
export { default as default };
}
expect: {
export { default as default };
}
}
keyword_valid_1: {
input: {
export { default } from "module.js";
}
expect: {
export { default } from "module.js";
}
}
keyword_valid_2: {
input: {
export { default as Alias } from "module.js";
}
expect: {
export { default as Alias } from "module.js";
}
}
keyword_valid_3: {
input: {
export { default as default } from "module.js";
}
expect: {
export { default as default } from "module.js";
}
}

View File

@@ -265,7 +265,7 @@ issue_203: {
} }
expect: { expect: {
var m = {}; var m = {};
var fn = Function("a", "b", "b.exports=42"); var fn = Function("n,o", "o.exports=42");
fn(null, m, m.exports); fn(null, m, m.exports);
console.log(m.exports); console.log(m.exports);
} }
@@ -414,3 +414,97 @@ inner_ref: {
} }
expect_stdout: "1 undefined" expect_stdout: "1 undefined"
} }
issue_2107: {
options = {
cascade: true,
collapse_vars: true,
inline: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
c++;
}(c++ + new function() {
this.a = 0;
var a = (c = c + 1) + (c = 1 + c);
return c++ + a;
}());
console.log(c);
}
expect: {
var c = 0;
c++, new function() {
this.a = 0, c = 1 + (c += 1), c++;
}(), c++, console.log(c);
}
expect_stdout: "5"
}
issue_2114_1: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}();
console.log(c);
}
expect_stdout: "2"
}
issue_2114_2: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
passes: 2,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}();
console.log(c);
}
expect_stdout: "2"
}

View File

@@ -37,6 +37,7 @@ object: {
VALUE: 42, VALUE: 42,
}, },
}, },
side_effects: true,
unsafe: true, unsafe: true,
} }
input: { input: {
@@ -140,9 +141,9 @@ mixed: {
console.log(CONFIG); console.log(CONFIG);
} }
expect_warnings: [ expect_warnings: [
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:126,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]', 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]', 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:128,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:130,8]',
] ]
} }
@@ -174,3 +175,24 @@ issue_1986: {
console.log(42); console.log(42);
} }
} }
issue_2167: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
global_defs: {
"@isDevMode": "function(){}",
},
side_effects: true,
}
input: {
if (isDevMode()) {
greetOverlord();
}
doWork();
}
expect: {
doWork();
}
}

View File

@@ -16,9 +16,12 @@ typeof_arrow_functions: {
evaluate: true evaluate: true
} }
input: { input: {
var foo = typeof (x) => null; var foo = typeof (x => null);
console.log(foo);
} }
expect_exact: "var foo=\"function\";" expect_exact: "var foo=\"function\";console.log(foo);"
expect_stdout: "function"
node_version: ">=4"
} }
classes: { classes: {
@@ -60,15 +63,15 @@ class_name_can_be_mangled: {
function x() { function x() {
class Foo { class Foo {
} }
var class1 = Foo var class1 = Foo;
var class2 = class Bar {} var class2 = class Bar {};
} }
} }
expect: { expect: {
function x() { function x() {
class a { } class a { }
var n = a var s = a;
var r = class a {} var c = class a {};
} }
} }
} }
@@ -287,12 +290,12 @@ import_statement_mangling: {
Whatever(); Whatever();
} }
expect: { expect: {
import l from "foo"; import o from "foo";
import e, {Food as o} from "lel"; import m, {Food as r} from "lel";
import {What as f} from "lel"; import {What as f} from "lel";
l();
e();
o(); o();
m();
r();
f(); f();
} }
} }
@@ -466,10 +469,10 @@ issue_1898: {
expect: { expect: {
class Foo { class Foo {
bar() { bar() {
for (const n of [ 6, 5 ]) for (const f of [ 6, 5 ])
for (let r of [ 4, 3 ]) for (let r of [ 4, 3 ])
for (var o of [ 2, 1 ]) for (var o of [ 2, 1 ])
console.log(n, r, o); console.log(f, r, o);
} }
} }
new Foo().bar(); new Foo().bar();
@@ -494,9 +497,9 @@ issue_1753: {
expect: { expect: {
class SomeClass { class SomeClass {
constructor(r) { constructor(r) {
let a = []; let s = [];
for (let s = 0; s < 6; s++) for (let a = 0; a < 6; a++)
a.push({ s.push({
mainDrawNumbers: [], mainDrawNumbers: [],
extraDrawNumbers: [] extraDrawNumbers: []
}); });
@@ -523,9 +526,9 @@ issue_1753_disable: {
expect: { expect: {
class SomeClass { class SomeClass {
constructor(r) { constructor(r) {
let a = []; let s = [];
for (let r = 0; r < 6; r++) for (let r = 0; r < 6; r++)
a.push({ s.push({
mainDrawNumbers: [], mainDrawNumbers: [],
extraDrawNumbers: [] extraDrawNumbers: []
}); });
@@ -664,3 +667,31 @@ class_expression_statement_unused_toplevel: {
} }
expect_exact: "" expect_exact: ""
} }
export_default_function_decl: {
options = {
toplevel: true,
passes: 3,
unused: true,
}
input: {
// do not drop "unused" exports
export default function Foo() {};
export function Far() {};
}
expect_exact: "export default function Foo(){};export function Far(){};"
}
export_default_class_decl: {
options = {
toplevel: true,
passes: 3,
unused: true,
}
input: {
// do not drop "unused" exports
export default class Car {};
export class Cab {};
}
expect_exact: "export default class Car{};export class Cab{};"
}

View File

@@ -71,11 +71,13 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:51,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:51,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:48,20]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:48,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:55,21]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:55,21]",
"WARN: pass 0: last_count: Infinity, count: 37",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:53,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:53,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]", "WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]", "WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]",
"WARN: pass 1: last_count: 37, count: 18",
] ]
} }
@@ -109,11 +111,11 @@ non_hoisted_function_after_return_2b: {
} }
expect_warnings: [ expect_warnings: [
// duplicate warnings no longer emitted // duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:95,16]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:97,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:95,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:99,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:99,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:103,12]",
] ]
} }
@@ -151,10 +153,10 @@ non_hoisted_function_after_return_strict: {
} }
expect_stdout: "8 7" expect_stdout: "8 7"
expect_warnings: [ expect_warnings: [
'WARN: Dropping unreachable code [test/compress/issue-1034.js:131,16]', "WARN: Dropping unreachable code [test/compress/issue-1034.js:133,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:136,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:139,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,21]" "WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:140,21]",
] ]
} }
@@ -194,17 +196,19 @@ non_hoisted_function_after_return_2a_strict: {
} }
expect_stdout: "5 6" expect_stdout: "5 6"
expect_warnings: [ expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:173,16]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:175,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:175,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:178,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:175,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:182,21]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]", "WARN: pass 0: last_count: Infinity, count: 48",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:180,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:180,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:183,12]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]", "WARN: Dropping unused variable b [test/compress/issue-1034.js:178,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:180,16]",
"WARN: pass 1: last_count: 48, count: 29",
] ]
} }
@@ -243,10 +247,10 @@ non_hoisted_function_after_return_2b_strict: {
expect_stdout: "5 6" expect_stdout: "5 6"
expect_warnings: [ expect_warnings: [
// duplicate warnings no longer emitted // duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:225,16]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:229,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:229,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]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:231,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:235,12]",
] ]
} }

View File

@@ -10,9 +10,9 @@ issue_1321_no_debug: {
} }
expect: { expect: {
var x = {}; var x = {};
x.b = 1; x.o = 1;
x["a"] = 2 * x.b; x["a"] = 2 * x.o;
console.log(x.b, x["a"]); console.log(x.o, x["a"]);
} }
expect_stdout: true expect_stdout: true
} }
@@ -30,9 +30,9 @@ issue_1321_debug: {
} }
expect: { expect: {
var x = {}; var x = {};
x.a = 1; x.o = 1;
x["_$foo$_"] = 2 * x.a; x["_$foo$_"] = 2 * x.o;
console.log(x.a, x["_$foo$_"]); console.log(x.o, x["_$foo$_"]);
} }
expect_stdout: true expect_stdout: true
} }
@@ -49,9 +49,9 @@ issue_1321_with_quoted: {
} }
expect: { expect: {
var x = {}; var x = {};
x.a = 1; x.o = 1;
x["b"] = 2 * x.a; x["x"] = 2 * x.o;
console.log(x.a, x["b"]); console.log(x.o, x["x"]);
} }
expect_stdout: true expect_stdout: true
} }

View File

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

View File

@@ -33,8 +33,8 @@ same_variable_in_multiple_for_loop: {
console.log(o, l); console.log(o, l);
for (let o = 0; o < 2; o++) { for (let o = 0; o < 2; o++) {
console.log(o, l); console.log(o, l);
let c = 2; let e = 2;
console.log(c); console.log(e);
} }
} }
} }
@@ -114,12 +114,12 @@ same_variable_in_multiple_forIn: {
} }
expect: { expect: {
var test = [ "a", "b", "c" ]; var test = [ "a", "b", "c" ];
for (let o in test) { for (let e in test) {
console.log(o); console.log(e);
let e; let t;
e = [ "e", "f", "g" ]; t = [ "e", "f", "g" ];
for (let o in test) for (let e in test)
console.log(o); console.log(e);
} }
} }
expect_stdout: true expect_stdout: true
@@ -160,8 +160,8 @@ different_variable_in_multiple_for_loop: {
console.log(o, l); console.log(o, l);
for (let o = 0; o < 2; o++) { for (let o = 0; o < 2; o++) {
console.log(o, l); console.log(o, l);
let c = 2; let e = 2;
console.log(c); console.log(e);
} }
} }
} }
@@ -241,12 +241,12 @@ different_variable_in_multiple_forIn: {
} }
expect: { expect: {
var test = [ "a", "b", "c" ]; var test = [ "a", "b", "c" ];
for (let o in test) { for (let e in test) {
console.log(o); console.log(e);
let e; let t;
e = [ "e", "f", "g" ]; t = [ "e", "f", "g" ];
for (let o in test) for (let e in test)
console.log(o); console.log(e);
} }
} }
expect_stdout: true expect_stdout: true
@@ -281,10 +281,10 @@ more_variable_in_multiple_for: {
} }
expect: { expect: {
for (let o = 9, l = 0; l < 20; l += o) { for (let o = 9, l = 0; l < 20; l += o) {
let c = o++ + l; let e = o++ + l;
console.log(o, c, l); console.log(o, e, l);
for (let l = c, e = c * c, f = 0; f < 10; f++) for (let l = e, t = e * e, c = 0; c < 10; c++)
console.log(o, c, e, l, f); console.log(o, e, t, l, c);
} }
} }
expect_stdout: true expect_stdout: true

View File

@@ -82,7 +82,7 @@ numeric_literal: {
' 42: 2,', ' 42: 2,',
' "42": 3,', ' "42": 3,',
' 37: 4,', ' 37: 4,',
' a: 5,', ' o: 5,',
' 1e42: 6,', ' 1e42: 6,',
' b: 7,', ' b: 7,',
' "1e+42": 8', ' "1e+42": 8',
@@ -92,7 +92,7 @@ numeric_literal: {
'', '',
'console.log(obj[42], obj["42"]);', 'console.log(obj[42], obj["42"]);',
'', '',
'console.log(obj[37], obj["a"], obj[37], obj["37"]);', 'console.log(obj[37], obj["o"], obj[37], obj["37"]);',
'', '',
'console.log(obj[1e42], obj["b"], obj["1e+42"]);', 'console.log(obj[1e42], obj["b"], obj["1e+42"]);',
] ]

View File

@@ -45,7 +45,7 @@ export_default_func_1: {
input: { input: {
export default function f(){}; export default function f(){};
} }
expect_exact: "export default function(){};" expect_exact: "export default function f(){};"
} }
export_default_func_2: { export_default_func_2: {
@@ -58,7 +58,7 @@ export_default_func_2: {
input: { input: {
export default function f(){}(1); export default function f(){}(1);
} }
expect_exact: "export default function(){};1;" expect_exact: "export default function f(){};1;"
} }
export_default_func_3: { export_default_func_3: {
@@ -71,7 +71,7 @@ export_default_func_3: {
input: { input: {
export default function f(){}(1); export default function f(){}(1);
} }
expect_exact: "export default function(){};" expect_exact: "export default function f(){};"
} }
export_class_1: { export_class_1: {
@@ -121,7 +121,7 @@ export_default_class_1: {
input: { input: {
export default class C {}; export default class C {};
} }
expect_exact: "export default class{};" expect_exact: "export default class C{};"
} }
export_default_class_2: { export_default_class_2: {
@@ -134,7 +134,7 @@ export_default_class_2: {
input: { input: {
export default class C {}(1); export default class C {}(1);
} }
expect_exact: "export default class{};1;" expect_exact: "export default class C{};1;"
} }
export_default_class_3: { export_default_class_3: {
@@ -147,7 +147,7 @@ export_default_class_3: {
input: { input: {
export default class C {}(1); export default class C {}(1);
} }
expect_exact: "export default class{};" expect_exact: "export default class C{};"
} }
export_mangle_1: { export_mangle_1: {
@@ -159,7 +159,7 @@ export_mangle_1: {
return one - two; return one - two;
}; };
} }
expect_exact: "export function foo(n,o){return n-o};" expect_exact: "export function foo(o,n){return o-n};"
} }
export_mangle_2: { export_mangle_2: {
@@ -171,7 +171,7 @@ export_mangle_2: {
return one - two; return one - two;
}; };
} }
expect_exact: "export default function n(n,r){return n-r};" expect_exact: "export default function foo(o,t){return o-t};"
} }
export_mangle_3: { export_mangle_3: {
@@ -189,7 +189,7 @@ export_mangle_3: {
} }
}; };
} }
expect_exact: "export class C{go(n,r){return n-r+n}};" expect_exact: "export class C{go(r,e){return r-e+r}};"
} }
export_mangle_4: { export_mangle_4: {
@@ -207,7 +207,7 @@ export_mangle_4: {
} }
}; };
} }
expect_exact: "export default class n{go(n,r){return n-r+n}};" expect_exact: "export default class C{go(e,r){return e-r+e}};"
} }
export_mangle_5: { export_mangle_5: {
@@ -221,7 +221,7 @@ export_mangle_5: {
} }
}; };
} }
expect_exact: "export default{prop:function(n,r){return n-r}};" expect_exact: "export default{prop:function(r,t){return r-t}};"
} }
export_mangle_6: { export_mangle_6: {
@@ -232,7 +232,7 @@ export_mangle_6: {
var baz = 2; var baz = 2;
export let foo = 1, bar = baz; export let foo = 1, bar = baz;
} }
expect_exact: "var a=2;export let foo=1,bar=a;" expect_exact: "var o=2;export let foo=1,bar=o;"
} }
export_toplevel_1: { export_toplevel_1: {
@@ -247,7 +247,7 @@ export_toplevel_1: {
} }
expect: { expect: {
export function g(){}; export function g(){};
export default function(){}; export default function h(){};
} }
} }
@@ -263,7 +263,7 @@ export_toplevel_2: {
} }
expect: { expect: {
export class B {}; export class B {};
export default class {}; export default class C {};
} }
} }

View File

@@ -8,7 +8,7 @@ compress_new_function: {
new Function("aa, bb", 'return aa;'); new Function("aa, bb", 'return aa;');
} }
expect: { expect: {
Function("a", "b", "return a"); Function("n,r", "return n");
} }
} }
@@ -27,8 +27,30 @@ compress_new_function_with_destruct: {
new Function("[[aa]], [{bb}]", 'return aa;'); new Function("[[aa]], [{bb}]", 'return aa;');
} }
expect: { expect: {
Function("a", "[b]", "return a"); Function("n,[r]", "return n");
Function("a", "{bb:b}", "return a"); Function("n,{bb:b}", "return n");
Function("[[a]]", "[{bb:b}]", 'return a'); Function("[[n]],[{bb:b}]", "return n");
}
}
compress_new_function_with_destruct_arrows: {
options = {
arrows: true,
unsafe: true,
unsafe_Func: true,
ecma: 6
}
beautify = {
ecma: 6
}
input: {
new Function("aa, [bb]", 'return aa;');
new Function("aa, {bb}", 'return aa;');
new Function("[[aa]], [{bb}]", 'return aa;');
}
expect: {
Function("n,[a]", "return n");
Function("b,{bb:n}", "return b");
Function("[[b]],[{bb:n}]", "return b");
} }
} }

View File

@@ -419,7 +419,7 @@ wrap_iife_in_return_call: {
expect_exact: '(void console.log("test"))();' expect_exact: '(void console.log("test"))();'
} }
pure_annotation: { pure_annotation_1: {
options = { options = {
inline: true, inline: true,
side_effects: true, side_effects: true,
@@ -432,6 +432,20 @@ pure_annotation: {
expect_exact: "" expect_exact: ""
} }
pure_annotation_2: {
options = {
collapse_vars: true,
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function(n) {
console.log("hello", n);
}(42));
}
expect_exact: ""
}
drop_fargs: { drop_fargs: {
options = { options = {
cascade: true, cascade: true,
@@ -449,9 +463,7 @@ drop_fargs: {
} }
expect: { expect: {
var a = 1; var a = 1;
!function() { ++a && a.var, a++;
a++;
}(++a && a.var);
console.log(a); console.log(a);
} }
expect_stdout: "3" expect_stdout: "3"
@@ -474,9 +486,7 @@ keep_fargs: {
} }
expect: { expect: {
var a = 1; var a = 1;
!function(a_1) { ++a && a.var, a++;
a++;
}(++a && a.var);
console.log(a); console.log(a);
} }
expect_stdout: "3" expect_stdout: "3"

View File

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

View File

@@ -480,3 +480,17 @@ do_switch: {
} while (false); } while (false);
} }
} }
in_parenthesis_1: {
input: {
for (("foo" in {});0;);
}
expect_exact: 'for(("foo"in{});0;);'
}
in_parenthesis_2: {
input: {
for ((function(){ "foo" in {}; });0;);
}
expect_exact: 'for(function(){"foo"in{}};0;);'
}

View File

@@ -98,3 +98,19 @@ new_with_assignement_expression: {
new y([a, b] = [3, 4]); new y([a, b] = [3, 4]);
} }
} }
dot_parenthesis_1: {
input: {
console.log(new (Math.random().constructor) instanceof Number);
}
expect_exact: "console.log(new(Math.random().constructor)instanceof Number);"
expect_stdout: "true"
}
dot_parenthesis_2: {
input: {
console.log(typeof new function(){Math.random()}.constructor);
}
expect_exact: "console.log(typeof new function(){Math.random()}.constructor);"
expect_stdout: "function"
}

View File

@@ -105,7 +105,7 @@ getter_setter_mangler: {
}; };
} }
} }
expect_exact: "function f(n,t){return{get:n,set:t,get g(){},set s(n){},c,a:1,m(){}}}" expect_exact: "function f(t,e){return{get:t,set:e,get g(){},set s(t){},c,a:1,m(){}}}"
} }
use_shorthand_opportunity: { use_shorthand_opportunity: {
@@ -297,7 +297,7 @@ concise_methods_and_mangle_props: {
expect: { expect: {
function x() { function x() {
obj = { obj = {
a() { return 1; } o() { return 1; }
} }
} }
} }
@@ -510,3 +510,235 @@ variable_as_computed_property: {
} }
expect_exact: "function getLine(header){return{[header]:{}}}" expect_exact: "function getLine(header){return{[header]:{}}}"
} }
prop_func_to_concise_method: {
options = {
ecma: 6,
}
input: {
({
emit: function NamedFunctionExpression() {
console.log("PASS");
},
run: function() {
this.emit();
}
}).run();
}
expect: {
({
emit: function NamedFunctionExpression() {
console.log("PASS");
},
run() {
this.emit();
}
}).run();
}
expect_stdout: "PASS"
node_version: ">=6"
}
prop_arrow_to_concise_method: {
options = {
ecma: 6,
}
input: {
({
run: () => {
console.log("PASS");
}
}).run();
}
expect: {
({
run() {
console.log("PASS");
}
}).run();
}
expect_stdout: "PASS"
node_version: ">=6"
}
concise_method_to_prop_arrow: {
options = {
arrows: true,
ecma: 6,
}
input: {
console.log(({ a: () => 1 }).a());
console.log(({ a: () => { return 2; } }).a());
console.log(({ a() { return 3; } }).a());
console.log(({ a() { return this.b; }, b: 4 }).a());
}
expect: {
console.log({ a: () => 1 }.a());
console.log({ a: () => 2 }.a());
console.log({ a: () => 3 }.a());
console.log({ a() { return this.b; }, b: 4 }.a());
}
expect_stdout: [
"1",
"2",
"3",
"4",
]
node_version: ">=4"
}
prop_func_to_async_concise_method: {
options = {
ecma: 8,
}
input: {
({
run: async function() {
console.log("PASS");
}
}).run();
}
expect: {
({
async run() {
console.log("PASS");
}
}).run();
}
expect_stdout: "PASS"
node_version: ">=8"
}
prop_func_to_concise_method_various: {
options = {
ecma: 6,
}
input: {
({
null: function(x, y){ x(y); },
123: function(x, y){ x(y); },
"A B": function(x, y){ x(y); },
p1: function(x, y){ x(y); },
p2: function*(x, y){ yield x(y); },
p3: async function(x, y){ await x(y); },
[c1]: function(x, y){ x(y); },
[c2]: function*(x, y){ yield x(y); },
[c3]: async function(x, y){ await x(y); },
});
}
expect: {
({
null(x, y) { x(y); },
123(x, y) { x(y); },
"A B"(x, y) { x(y); },
p1(x, y) { x(y); },
*p2(x, y) { yield x(y); },
async p3(x, y) { await x(y); },
[c1](x, y) { x(y); },
*[c2](x, y) { yield x(y); },
async [c3](x, y) { await x(y); },
});
}
}
prop_arrows_to_concise_method_various: {
options = {
ecma: 6,
}
input: {
({
null: (x, y) => { x(y); },
123: (x, y) => { x(y); },
"A B": (x, y) => { x(y); },
p1: (x, y) => { x(y); },
p3: async (x, y) => { await x(y); },
[c1]: (x, y) => { x(y); },
[c3]: async (x, y) => { await x(y); },
});
}
expect: {
({
null(x, y) { x(y); },
123(x, y) { x(y); },
"A B"(x, y) { x(y); },
p1(x, y) { x(y); },
async p3(x, y) { await x(y); },
[c1](x, y) { x(y); },
async [c3](x, y) { await x(y); },
});
}
}
prop_arrow_with_this: {
options = {
ecma: 6,
}
input: {
function run(arg) {
console.log(arg === this ? "global" : arg === foo ? "foo" : arg);
}
var foo = {
func_no_this: function() { run(); },
func_with_this: function() { run(this); },
arrow_no_this: () => { run(); },
arrow_with_this: () => { run(this); },
};
for (var key in foo) foo[key]();
}
expect: {
function run(arg) {
console.log(arg === this ? "global" : arg === foo ? "foo" : arg);
}
var foo = {
func_no_this() { run(); },
func_with_this() { run(this); },
arrow_no_this() { run(); },
arrow_with_this: () => { run(this); },
};
for (var key in foo) foo[key]();
}
expect_stdout: [
"undefined",
"foo",
"undefined",
"global",
]
node_version: ">=4"
}
prop_arrow_with_nested_this: {
options = {
ecma: 6,
}
input: {
function run(arg) {
console.log(arg === this ? "global" : arg === foo ? "foo" : arg);
}
var foo = {
func_func_this: function() { (function() { run(this); })(); },
func_arrow_this: function() { (() => { run(this); })(); },
arrow_func_this: () => { (function() { run(this); })(); },
arrow_arrow_this: () => { (() => { run(this); })(); },
};
for (var key in foo) foo[key]();
}
expect: {
function run(arg) {
console.log(arg === this ? "global" : arg === foo ? "foo" : arg);
}
var foo = {
func_func_this() { (function() { run(this); })(); },
func_arrow_this() { (() => { run(this); })(); },
arrow_func_this() { (function() { run(this); })(); },
arrow_arrow_this: () => { (() => { run(this); })(); },
};
for (var key in foo) foo[key]();
}
expect_stdout: [
"global",
"foo",
"global",
"global",
]
node_version: ">=4"
}

View File

@@ -1,4 +1,7 @@
arrow_functions: { arrow_functions: {
options = {
arrows: true,
}
input: { input: {
(a) => b; // 1 args (a) => b; // 1 args
(a, b) => c; // n args (a, b) => c; // n args
@@ -13,6 +16,9 @@ arrow_functions: {
} }
arrow_return: { arrow_return: {
options = {
arrows: true,
}
input: { input: {
() => {}; () => {};
() => { return; }; () => { return; };

View File

@@ -13,8 +13,10 @@ keep_properties: {
dot_properties: { dot_properties: {
options = { options = {
properties: true, properties: true,
}
beautify = {
ie8: true, ie8: true,
}; }
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
a["if"] = "if"; a["if"] = "if";
@@ -36,8 +38,10 @@ dot_properties: {
dot_properties_es5: { dot_properties_es5: {
options = { options = {
properties: true, properties: true,
}
beautify = {
ie8: false, ie8: false,
}; }
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
a["if"] = "if"; a["if"] = "if";
@@ -135,11 +139,11 @@ mangle_properties: {
a['run']({color: "blue", foo: "baz"}); a['run']({color: "blue", foo: "baz"});
} }
expect: { expect: {
a["a"] = "bar"; a["o"] = "bar";
a.b = "red"; a.a = "red";
x = {c: 10}; x = {r: 10};
a.d(x.c, a.a); a.b(x.r, a.o);
a['d']({b: "blue", a: "baz"}); a['b']({a: "blue", o: "baz"});
} }
} }
@@ -178,16 +182,16 @@ mangle_unquoted_properties: {
function f1() { function f1() {
a["foo"] = "bar"; a["foo"] = "bar";
a.color = "red"; a.color = "red";
a.b = 2; a.o = 2;
x = {"bar": 10, c: 7}; x = {"bar": 10, f: 7};
a.c = 9; a.f = 9;
} }
function f2() { function f2() {
a.foo = "bar"; a.foo = "bar";
a['color'] = "red"; a['color'] = "red";
x = {bar: 10, c: 7}; x = {bar: 10, f: 7};
a.c = 9; a.f = 9;
a.b = 3; a.o = 3;
} }
} }
} }
@@ -659,3 +663,210 @@ accessor_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_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" expect_stdout: "1 2 2"
} }
issue_2208_1: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
p: function() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
issue_2208_2: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
a: 42,
p: function() {
return this.a;
}
}.p());
}
expect: {
console.log({
a: 42,
p: function() {
return this.a;
}
}.p());
}
expect_stdout: "42"
}
issue_2208_3: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
a = 42;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 42;
console.log(function() {
return this.a;
}());
}
expect_stdout: "42"
}
issue_2208_4: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
function foo() {}
console.log({
a: foo(),
p: function() {
return 42;
}
}.p());
}
expect: {
function foo() {}
console.log((foo(), function() {
return 42;
})());
}
expect_stdout: "42"
}
issue_2208_5: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
p: "FAIL",
p: function() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
issue_2208_6: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
p: () => 42
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
node_version: ">=4"
}
issue_2208_7: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
p() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
node_version: ">=4"
}
issue_2208_8: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
*p() {
return x();
}
}.p());
console.log({
async p() {
return await x();
}
}.p());
}
expect: {
console.log({
*p() {
return x();
}
}.p());
console.log(async function() {
return await x();
}());
}
}
issue_2208_9: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
a = 42;
console.log({
p: () => {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 42;
console.log(function() {
return this.a;
}());
}
expect_stdout: "42"
node_version: ">=4"
}

View File

@@ -178,3 +178,210 @@ impure_getter_2: {
} }
expect: {} expect: {}
} }
issue_2110_1: {
options = {
cascade: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
return f.g = function() {
return this;
}, f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
issue_2110_2: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
f.g = function() {
return this;
};
return f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
set_immutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_3: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: true
}
set_immutable_4: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_mutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
if (a.foo += "") console.log("PASS");
else console.log("FAIL");
}();
}
expect_stdout: "PASS"
}
set_mutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
(a.foo += "") ? console.log("PASS") : console.log("FAIL");
}();
}
expect_stdout: "PASS"
}

View File

@@ -1469,6 +1469,7 @@ issue_1670_1: {
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
switches: true, switches: true,
typeofs: true,
unused: true, unused: true,
} }
input: { input: {
@@ -1532,6 +1533,7 @@ issue_1670_3: {
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
switches: true, switches: true,
typeofs: true,
unused: true, unused: true,
} }
input: { input: {
@@ -2625,3 +2627,28 @@ issue_2090_2: {
expect_stdout: "1" expect_stdout: "1"
node_version: ">=4" node_version: ">=4"
} }
for_in_prop: {
options = {
reduce_vars: true,
}
input: {
var a = {
foo: function() {
for (this.b in [1, 2]);
}
};
a.foo();
console.log(a.b);
}
expect: {
var a = {
foo: function() {
for (this.b in [1, 2]);
}
};
a.foo();
console.log(a.b);
}
expect_stdout: "1"
}

View File

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

View File

@@ -176,6 +176,11 @@ for_sequences: {
// 4 // 4
x = (foo in bar); x = (foo in bar);
for (y = 5; false;); for (y = 5; false;);
// 5
x = function() {
foo in bar;
};
for (y = 5; false;);
} }
expect: { expect: {
// 1 // 1
@@ -188,6 +193,10 @@ for_sequences: {
// 4 // 4
x = (foo in bar); x = (foo in bar);
for (y = 5; false;); for (y = 5; false;);
// 5
for (x = function() {
foo in bar;
}, y = 5; false;);
} }
} }

View File

@@ -0,0 +1,3 @@
{
export var V = 1;
}

View File

@@ -0,0 +1 @@
export class{};

View File

@@ -0,0 +1 @@
export function(){};

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
{
import A from "B";
}

View File

@@ -0,0 +1 @@
(a, ...b);

View File

@@ -62,23 +62,20 @@ describe("Arrow functions", function() {
} }
}); });
it("Should not accept arrow functions in the middle or end of an expression", function() { it("Should not accept arrow functions in the middle or end of an expression", function() {
var tests = [ [
"0 + x => 0",
"0 + async x => 0",
"typeof x => 0", "typeof x => 0",
"0 + x => 0" "typeof async x => 0",
]; "typeof (x) => null",
var test = function(code) { "typeof async (x) => null",
return function() { ].forEach(function(code) {
assert.throws(function() {
uglify.parse(code); uglify.parse(code);
} }, function(e) {
} return e instanceof uglify.JS_Parse_Error && /^Unexpected /.test(e.message);
var error = function(e) { }, code);
return e instanceof uglify.JS_Parse_Error && });
e.message === "Unexpected token: arrow (=>)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
}); });
it("Should parse a function containing default assignment correctly", function() { it("Should parse a function containing default assignment correctly", function() {

View File

@@ -1,6 +1,7 @@
var assert = require("assert"); var assert = require("assert");
var exec = require("child_process").exec; var exec = require("child_process").exec;
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
var semver = require("semver");
function read(path) { function read(path) {
return readFileSync(path, "utf8"); return readFileSync(path, "utf8");
@@ -9,9 +10,11 @@ function read(path) {
describe("bin/uglifyjs", function () { describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) { it("should produce a functional build when using --self", function (done) {
this.timeout(30000); this.timeout(60000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS'; var command = uglifyjscmd + ' --self -mc ecma=';
command += semver.satisfies(process.version, ">=4") ? "6" : "5";
command += ',passes=3,keep_fargs=false,unsafe --wrap WrappedUglifyJS';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
@@ -63,7 +66,7 @@ describe("bin/uglifyjs", function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" + assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n"); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==\n");
done(); done();
}); });
}); });
@@ -192,7 +195,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [ assert.strictEqual(stdout, [
"var bar=function(){function foo(bar){return bar}return foo}();", "var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==",
"", "",
].join("\n")); ].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n"); assert.strictEqual(stderr, "WARN: inline source map not found\n");
@@ -533,6 +536,111 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should throw syntax error (block-level export)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/export_1.js -m';
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/export_1.js:2,4",
" export var V = 1;",
" ^",
"ERROR: Export statement may only appear at top level"
].join("\n"));
done();
});
});
it("Should throw syntax error (block-level import)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/import.js -m';
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/import.js:2,4",
' import A from "B";',
" ^",
"ERROR: Import statement may only appear at top level"
].join("\n"));
done();
});
});
it("Should throw syntax error (anonymous class)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/export_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/export_2.js:1,12",
"export class{};",
" ^",
"ERROR: Unexpected token: punc ({)"
].join("\n"));
done();
});
});
it("Should throw syntax error (anonymous function)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/export_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/export_3.js:1,15",
"export function(){};",
" ^",
"ERROR: Unexpected token: punc (()"
].join("\n"));
done();
});
});
it("Should throw syntax error (spread in sequence)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/sequence.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/sequence.js:1,4",
"(a, ...b);",
" ^",
"ERROR: Unexpected token: expand (...)"
].join("\n"));
done();
});
});
it("Should throw syntax error (for-in init)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-in_1.js:2,5",
"for (1, 2, a in b) {",
" ^",
"ERROR: Invalid left-hand side in for..in loop"
].join("\n"));
done();
});
});
it("Should throw syntax error (for-in var)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-in_2.js:2,5",
"for (var a, b in c) {",
" ^",
"ERROR: Only one variable declaration allowed in for..in loop"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) { it("Should handle literal string as source map input", function(done) {
var command = [ var command = [
uglifyjscmd, uglifyjscmd,
@@ -578,8 +686,8 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should work with --mangle reserved=[]", function (done) { it("Should work with --mangle reserved=[]", function(done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=[callback]'; var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=[callback]";
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
@@ -588,8 +696,8 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should work with --mangle reserved=false", function (done) { it("Should work with --mangle reserved=false", function(done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=false'; var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=false";
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
@@ -598,4 +706,22 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should fail with --mangle-props reserved=[in]", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js --mangle-props reserved=[in]";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `reserved=\[in]` is not a supported option/.test(stderr), stderr);
done();
});
});
it("Should fail with --define a-b", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js --define a-b";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr, "Error parsing arguments for 'define': a-b\n");
done();
});
});
}); });

View File

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

View File

@@ -191,15 +191,51 @@ describe("Function", function() {
]; ];
var test = function(code) { var test = function(code) {
return function() { return function() {
uglify.parse(code); uglify.parse(code, { ecma: 5 });
} }
} }
var error = function(e) { var error = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error;
e.message === "Invalid function parameter";
} }
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error); assert.throws(test(tests[i]), error, tests[i]);
}
});
it("Should accept trailing commas only for ES8", function() {
[
"new Foo(a, );",
"async(...[1, 2], );",
"console.log(...[1, 2], );",
"!function(a, b, ){ console.log(a + b); }(3, 4, );",
].forEach(function(code) {
uglify.parse(code, { ecma: 8 });
assert.throws(function() {
uglify.parse(code, { ecma: 6 });
}, function(e) {
return e instanceof uglify.JS_Parse_Error;
}, code);
});
});
it("Should not accept invalid trailing commas", function() {
var tests = [
"f(, );",
"(, ) => {};",
"(...p, ) => {};",
"function f(, ) {}",
"function f(...p, ) {}",
"function foo(a, b, , ) {}",
'console.log("hello", , );',
];
var test = function(code) {
return function() {
uglify.parse(code, { ecma: 8 });
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
} }
}); });
it("Should not accept an initializer when parameter is a rest parameter", function() { it("Should not accept an initializer when parameter is a rest parameter", function() {

View File

@@ -26,12 +26,12 @@ describe("bin/uglifyjs with input file globs", function() {
}); });
}); });
it("bin/uglifyjs with multiple input file globs.", function(done) { it("bin/uglifyjs with multiple input file globs.", function(done) {
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel'; var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel,passes=2';
exec(command, function(err, stdout) { exec(command, function(err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n'); assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",22);\n');
done(); done();
}); });
}); });

View File

@@ -2,29 +2,56 @@ var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("let", function() { describe("let", function() {
it("Should not produce `let` as a variable name in mangle", function(done) { this.timeout(30000);
this.timeout(10000); it("Should not produce reserved keywords as variable name in mangle", function() {
// Produce a lot of variables in a function and run it through mangle. // Produce a lot of variables in a function and run it through mangle.
var s = '"use strict"; function foo() {'; var s = '"dddddeeeeelllllooooottttt"; function foo() {';
for (var i = 0; i < 21000; ++i) { for (var i = 0; i < 18000; i++) {
s += "var v" + i + "=0;"; s += "var v" + i + "=0;";
} }
s += '}'; s += '}';
var result = Uglify.minify(s, {compress: false}); var result = Uglify.minify(s, {
compress: false
}).code;
// Verify that select keywords and reserved keywords not produced // Verify that select keywords and reserved keywords not produced
assert.strictEqual(result.code.indexOf("var let="), -1); [
assert.strictEqual(result.code.indexOf("var do="), -1); "do",
assert.strictEqual(result.code.indexOf("var var="), -1); "let",
"var",
].forEach(function(name) {
assert.strictEqual(result.indexOf("var " + name + "="), -1);
});
// Verify that the variable names that appeared immediately before // Verify that the variable names that appeared immediately before
// and after the erroneously generated `let` variable name still exist // and after the erroneously generated variable name still exist
// to show the test generated enough symbols. // to show the test generated enough symbols.
assert(result.code.indexOf("var ket=") >= 0); [
assert(result.code.indexOf("var met=") >= 0); "to", "eo",
"eet", "fet",
done(); "rar", "oar",
].forEach(function(name) {
assert.notStrictEqual(result.indexOf("var " + name + "="), -1);
});
});
it("Should quote mangled properties that are reserved keywords", function() {
var s = '"rrrrrnnnnniiiiiaaaaa";';
for (var i = 0; i < 18000; i++) {
s += "v.b" + i + ";";
}
var result = Uglify.minify(s, {
compress: false,
ie8: true,
mangle: {
properties: true,
}
}).code;
[
"in",
"var",
].forEach(function(name) {
assert.notStrictEqual(result.indexOf(name), -1);
assert.notStrictEqual(result.indexOf('v["' + name + '"]'), -1);
});
}); });
}); });

View File

@@ -1,6 +1,7 @@
var Uglify = require('../../'); var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code;
function read(path) { function read(path) {
return readFileSync(path, "utf8"); return readFileSync(path, "utf8");
@@ -20,6 +21,58 @@ describe("minify", function() {
assert.strictEqual(result.code, "alert(2);"); assert.strictEqual(result.code, "alert(2);");
}); });
it("Should work with mangle.cache", function() {
var cache = {};
var original = "";
var compressed = "";
[
"bar.es5",
"baz.es5",
"foo.es5",
"qux.js",
].forEach(function(file) {
var code = read("test/input/issue-1242/" + file);
var result = Uglify.minify(code, {
mangle: {
cache: cache,
toplevel: true
}
});
if (result.error) throw result.error;
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 20), '{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}function c(o){l("Foo:",2*o)}var l=console.log.bind(console);var f=n(3),i=r(12);l("qux",f,i),c(11);');
assert.strictEqual(run_code(compressed), run_code(original));
});
it("Should work with nameCache", function() {
var cache = {};
var original = "";
var compressed = "";
[
"bar.es5",
"baz.es5",
"foo.es5",
"qux.js",
].forEach(function(file) {
var code = read("test/input/issue-1242/" + file);
var result = Uglify.minify(code, {
mangle: {
toplevel: true
},
nameCache: cache
});
if (result.error) throw result.error;
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 28), '{"vars":{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}function c(o){l("Foo:",2*o)}var l=console.log.bind(console);var f=n(3),i=r(12);l("qux",f,i),c(11);');
assert.strictEqual(run_code(compressed), run_code(original));
});
describe("keep_quoted_props", function() { describe("keep_quoted_props", function() {
it("Should preserve quotes in object literals", function() { it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -212,7 +265,7 @@ describe("minify", function() {
}); });
var err = result.error; var err = result.error;
assert.ok(err instanceof Error); assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger"); assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token: keyword (debugger)");
}); });
it("should skip inherited properties", function() { it("should skip inherited properties", function() {
var foo = Object.create({ skip: this }); var foo = Object.create({ skip: this });

View File

@@ -4,7 +4,11 @@ var assert = require("assert");
describe("Object", function() { describe("Object", function() {
it("Should allow objects to have a methodDefinition as property", function() { it("Should allow objects to have a methodDefinition as property", function() {
var code = "var a = {test() {return true;}}"; var code = "var a = {test() {return true;}}";
assert.equal(Uglify.minify(code).code, "var a={test(){return!0}};"); assert.equal(Uglify.minify(code, {
compress: {
arrows: false
}
}).code, "var a={test(){return!0}};");
}); });
it("Should not allow objects to use static keywords like in classes", function() { it("Should not allow objects to use static keywords like in classes", function() {

View File

@@ -30,4 +30,13 @@ describe("Template string", function() {
assert.throws(exec(tests[i]), fail, tests[i]); assert.throws(exec(tests[i]), fail, tests[i]);
} }
}); });
it("Should process all line terminators as LF", function() {
[
"`a\rb`",
"`a\nb`",
"`a\r\nb`",
].forEach(function(code) {
assert.strictEqual(uglify.parse(code).print_to_string(), "`a\\nb`;");
});
});
}); });

View File

@@ -86,7 +86,6 @@ function run_compress_tests() {
log_start_file(file); log_start_file(file);
function test_case(test) { function test_case(test) {
log_test(test.name); log_test(test.name);
U.base54.reset();
var output_options = test.beautify || {}; var output_options = test.beautify || {};
var expect; var expect;
if (test.expect) { if (test.expect) {
@@ -101,9 +100,6 @@ function run_compress_tests() {
quote_style: 3, quote_style: 3,
keep_quoted_props: true keep_quoted_props: true
}); });
if (test.mangle_props) {
input = U.mangle_properties(input, test.mangle_props);
}
var options = U.defaults(test.options, { var options = U.defaults(test.options, {
warnings: false warnings: false
}); });
@@ -118,10 +114,16 @@ function run_compress_tests() {
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var output = cmp.compress(input); var output = cmp.compress(input);
output.figure_out_scope(test.mangle); output.figure_out_scope(test.mangle);
if (test.mangle) { if (test.mangle || test.mangle_props) {
U.base54.reset();
output.compute_char_frequency(test.mangle); output.compute_char_frequency(test.mangle);
}
if (test.mangle) {
output.mangle_names(test.mangle); output.mangle_names(test.mangle);
} }
if (test.mangle_props) {
output = U.mangle_properties(output, test.mangle_props);
}
output = make_code(output, output_options); output = make_code(output, output_options);
if (expect != output) { if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {