Compare commits

...

106 Commits

Author SHA1 Message Date
Alex Lam S.L
873755b35c harmony-v3.1.3 2017-10-01 14:41:52 +08:00
alexlamsl
744032755d add tests for #2336 & #2337 2017-10-01 13:20:17 +08:00
alexlamsl
4fac8076b8 Merge branch 'master' into harmony-v3.1.3 2017-10-01 12:42:40 +08:00
Alex Lam S.L
6920e898d1 v3.1.3 2017-10-01 12:36:07 +08:00
Alex Lam S.L
dd71639264 enhance reduce_vars for AST_Accessor (#2339)
fixes #2336
2017-10-01 03:01:50 +08:00
Alex Lam S.L
2dcc552ce0 trap invalid use of reserved words (#2338)
fixes #2337
2017-10-01 02:10:41 +08:00
kzc
a020d2ead3 support dynamic import(), trap invalid use of export (#2335) 2017-09-28 18:43:09 +08:00
Alex Lam S.L
68645b28d3 harmony-v3.1.2 2017-09-24 11:13:25 +08:00
alexlamsl
aaa8212837 improve test for #2316 2017-09-24 02:23:38 +08:00
alexlamsl
bd84007cf4 Merge branch 'master' into harmony-v3.1.2 2017-09-24 02:20:47 +08:00
Alex Lam S.L
55387e8fd0 v3.1.2 2017-09-24 02:02:04 +08:00
GUENEGO Jean-Louis
1241600013 mangle: do not mangle reserved class (#2317)
fixes #2316
2017-09-24 00:08:47 +08:00
kzc
7e3e9da860 fix "use asm" numeric output (#2328)
fixes #2324
2017-09-21 04:42:40 +08:00
kzc
a784717fe2 allow RegExp for unsafe_methods compress option (#2327) 2017-09-21 00:48:16 +08:00
Alex Lam S.L
00f509405b suppress collapse_vars of this into "use strict" (#2326)
fixes #2319
2017-09-20 05:23:20 +08:00
kzc
e8235657e4 add new compress option unsafe_methods for ecma >= 6 (#2325)
fixes #2321
2017-09-20 00:15:54 +08:00
Alex Lam S.L
c46b9f361a harmony-v3.1.1 2017-09-17 15:33:55 +08:00
alexlamsl
3b0b4d6abf handle AST_Super in collapse_vars & side_effects 2017-09-17 05:09:36 +08:00
alexlamsl
d73500e8d1 Merge branch 'master' into harmony-v3.1.1 2017-09-17 04:43:43 +08:00
Alex Lam S.L
aceb0af36b v3.1.1 2017-09-17 04:36:27 +08:00
Alex Lam S.L
4f0953f7e9 handle LHS side-effects on cascade & collapse_vars (#2314)
fixes #2313
2017-09-16 11:45:19 +08:00
Alex Lam S.L
182a47bfb1 improve source mapping (#2312)
fixes #2310
2017-09-15 12:46:48 +08:00
Alex Lam S.L
d8685f528d harmony-v3.1.0 2017-09-10 20:51:33 +08:00
alexlamsl
8891495789 Merge branch 'master' into harmony-v3.1.0 2017-09-10 15:39:33 +08:00
Alex Lam S.L
cd27f4ec38 v3.1.0 2017-09-10 15:17:24 +08:00
Mateusz Burzyński
8158b1bdcf Testing all leading comments against being PURE comments (#2305) 2017-09-10 02:08:15 +08:00
Alex Lam S.L
aacf3edc68 extend unsafe on pure global functions (#2303) 2017-09-07 22:08:34 +08:00
kzc
8b89072190 add Date and other known globals to unsafe compress option (#2302) 2017-09-07 02:44:26 +08:00
Alex Lam S.L
395a17ccda fix collapse_vars on default function argument (#2299)
Avoid collision with local variable `undefined` under certain corner cases.

fixes #2298
2017-09-04 02:32:33 +08:00
Alex Lam S.L
3f355866cf correctly count declarations after hoist_vars (#2297)
fixes #2295
2017-09-03 17:23:31 +08:00
kzc
2779a29a86 avoid generating ternary with spread (#2293)
fixes #2292
2017-08-31 00:57:07 +08:00
David Šanda
71d52f147d Fix CLI example for mangle reserved list of names (#2294) 2017-08-31 00:55:32 +08:00
David Šanda
eb7adaa6fc Fix CLI source-maps examples (#2291)
fixes #2284
2017-08-29 23:49:20 +08:00
Alex Lam S.L
e5cf7972ea fix unused patching of AST_For.init blocks (#2289)
fixes #2288
2017-08-29 01:10:04 +08:00
Alex Lam S.L
067d52b6ba harmony-v3.0.28 2017-08-20 01:19:06 +08:00
alexlamsl
e0e009ace2 Merge branch 'master' into harmony-v3.0.28 2017-08-20 00:35:46 +08:00
Alex Lam S.L
f81ff10a9b v3.0.28 2017-08-20 00:27:01 +08:00
kzc
ae0f117da6 Introduce new compress option unsafe_arrows (#2278)
* Not always safe to convert a function expression to an arrow
  function when code depends on the function prototype existing.

Fixes #2271
2017-08-16 22:51:26 +08:00
Alex Lam S.L
a5461e0adc prohibit let/const redeclaration (#2277)
fixes #2270
2017-08-14 12:31:12 +08:00
Erik Desjardins
16d40915b4 don't escape null characters as \0 when followed by any digit (#2273)
fixes #2272
2017-08-14 12:30:08 +08:00
Alex Lam S.L
2bf8216e50 fix pure_getters on spread of objects (#2275) 2017-08-13 22:08:33 +08:00
kzc
2ed3f8db44 fix output of spread of a sequence (#2268)
fixes #2267
2017-08-03 00:40:19 +08:00
kzc
4700c14855 implement object rest/spread (#2265)
- improve parse errors for destructuring spread elements
- `unsafe` for object literals with rest elements

Miscellaneous
- increase mocha unicode surrogate test timeout
2017-08-02 13:47:58 +08:00
Alex Lam S.L
e7c21e87e3 fix ie8 mangling of top-level AST_SymbolCatch (#2263)
fixes #2254
2017-08-01 02:38:32 +08:00
Alex Lam S.L
f54ab16843 harmony-v3.0.27 2017-07-30 15:15:29 +08:00
alexlamsl
69cb459c16 fix-ups for #2258 2017-07-30 04:26:21 +08:00
alexlamsl
1eae8f2dcc Merge branch 'master' into harmony 2017-07-30 01:57:34 +08:00
Alex Lam S.L
c4c2ef44d0 v3.0.27 2017-07-30 01:50:42 +08:00
Alex Lam S.L
a845897758 improve mangle.properties (#2261)
- include dead code when `keep_quoted`
- unify `keep_quoted` & `reserved`
- make `test/run-tests.js` consistent with `minify()`

fixes #2256
2017-07-29 23:02:04 +08:00
kzc
d600c78d7b have keep_quoted respect quoted method names (#2258)
fixes #2257
2017-07-28 19:42:12 +08:00
kzc
32ea2c5530 issue template: describe acceptable JS input (#2255) 2017-07-27 21:38:36 +08:00
Alex Lam S.L
d3df2f985d extend collapse_vars to let and const (#2252)
fixes #2250
2017-07-25 22:07:21 +08:00
Alex Lam S.L
69861824b5 enhance test for #2242 (#2248) 2017-07-24 00:32:33 +08:00
Alex Lam S.L
1e0c7d2bc5 harmony-v3.0.26 2017-07-23 16:54:46 +08:00
alexlamsl
98b850580b fix for #2242 on harmony 2017-07-23 16:20:53 +08:00
alexlamsl
29011ea60a remove ascii_identifiers 2017-07-23 12:54:50 +08:00
alexlamsl
77d18be073 Merge branch 'master' into harmony-v3.0.26 2017-07-23 12:53:13 +08:00
Alex Lam S.L
bc61deeca9 v3.0.26 2017-07-23 12:39:36 +08:00
Alex Lam S.L
6a5e74b44e unescape surrogate pairs only (#2246)
fixes #2242
2017-07-23 12:38:21 +08:00
Alex Lam S.L
54446341ee update dependencies (#2241)
- acorn@5.1.1
- commander@2.11.0
- mocha@3.4.2
2017-07-16 16:20:40 +08:00
Alex Lam S.L
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
59 changed files with 4035 additions and 798 deletions

View File

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

126
README.md
View File

@@ -1,7 +1,7 @@
uglify-es
=========
**uglify-es** is an ECMAScript parser, minifier, compressor and beautifier toolkit for ES6+.
A JavaScript parser, mangler/compressor and beautifier toolkit for ES6+.
#### Note:
- **`uglify-es` is API/CLI compatible with `uglify-js@3`.**
@@ -109,7 +109,7 @@ a double dash to prevent input files being used as option arguments:
By default UglifyJS will not try to be IE-proof.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--name-cache File to hold mangled name mappings.
--name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options:
`base` Path to compute relative paths from input files.
@@ -148,19 +148,19 @@ debugging your compressed JavaScript. To get a source map, pass
Additional options:
- `--source-map filename=<NAME>` to specify the name of the source map.
- `--source-map "filename='<NAME>'"` to specify the name of the source map.
- `--source-map root=<URL>` to pass the URL where the original files can be found.
- `--source-map "root='<URL>'"` to pass the URL where the original files can be found.
Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the
`//# sourceMappingURL=` directive.
- `--source-map url=<URL>` to specify the URL where the source map can be found.
- `--source-map "url='<URL>'"` to specify the URL where the source map can be found.
For example:
uglifyjs js/file1.js js/file2.js \
-o foo.min.js -c -m \
--source-map root="http://foo.com/src",url=foo.min.js.map
--source-map "root='http://foo.com/src',url='foo.min.js.map'"
The above will compress and mangle `file1.js` and `file2.js`, will drop the
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
@@ -179,8 +179,8 @@ CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript →
compressed JS by mapping every token in the compiled JS to its original
location.
To use this feature pass `--source-map content="/path/to/input/source.map"`
or `--source-map content=inline` if the source map is included inline with
To use this feature pass `--source-map "content='/path/to/input/source.map'"`
or `--source-map "content=inline"` if the source map is included inline with
the sources.
## CLI compress options
@@ -211,7 +211,7 @@ When mangling is enabled but you want to prevent certain names from being
mangled, you can declare those names with `--mangle reserved` — pass a
comma-separated list of names. For example:
uglifyjs ... -m reserved=[$,require,exports]
uglifyjs ... -m reserved=['$','require','exports']
to prevent the `require`, `exports` and `$` names from being changed.
@@ -381,7 +381,47 @@ var code = {
var options = { toplevel: true };
var result = UglifyJS.minify(code, options);
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:
@@ -462,6 +502,13 @@ if (result.error) throw result.error;
- `toplevel` (default `false`) - set to `true` if you wish to enable top level
variable and function name mangling and to drop unused variables and functions.
- `nameCache` (default `null`) - pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
a read/write property. `minify()` will read the name cache state of this
object and update it during minification so that it may be
reused or externally persisted by the user.
- `ie8` (default `false`) - set to `true` to support IE8.
## Minify options structure
@@ -488,6 +535,7 @@ if (result.error) throw result.error;
// source map options
},
ecma: 5, // specify one of: 5, 6, 7 or 8
nameCache: null, // or specify a name cache object
toplevel: false,
ie8: false,
warnings: false,
@@ -583,6 +631,13 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_methods` (default: false) -- Converts `{ m: function(){} }` to
`{ m(){} }`. `ecma` must be set to `6` or greater to enable this transform.
If `unsafe_methods` is a RegExp then key/value pairs with keys matching the
RegExp will be converted to concise methods.
Note: if enabled there is a risk of getting a "`<method name>` is not a
constructor" TypeError should any code try to `new` the former function.
- `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
@@ -598,13 +653,24 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `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.
- `arrows` (default `true`) -- Converts `()=>{return x}` to `()=>x`. Class
and object literal methods will also be converted to arrow expressions if
the resultant code is shorter: `m(){return x}` becomes `m:()=>x`.
This transform requires that the `ecma` compress option is set to `6` or greater.
- `unsafe_arrows` (default `false`) -- Convert ES5 style anonymous function
expressions to arrow functions if the function body does not reference `this`.
Note: it is not always safe to perform this conversion if code relies on the
the function having a `prototype`, which arrow functions lack.
This transform requires that the `ecma` compress option is set to `6` or greater.
- `booleans` -- various optimizations for boolean context, for example `!!a
? 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
statically determine the condition
@@ -678,7 +744,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
`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
mind more passes will take more time.
@@ -689,7 +755,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `ecma` -- default `5`. Pass `6` or greater to enable `compress` options that
will transform ES5 code into smaller ES6+ equivalent forms.
@@ -768,7 +834,7 @@ can pass additional arguments that control the code output:
(e.g. `/^!/`) or a function.
- `ecma` (default `5`) -- set output printing mode. Set `ecma` to `6` or
greater to emit shorthand object properties - i.e.: `{a}` instead of `{a: a}`.
The `ecma` option will only change the output in direct control of the
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.
@@ -849,7 +915,6 @@ when this flag is on:
- `new Object()` → `{}`
- `String(exp)` or `exp.toString()` → `"" + exp`
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
- `typeof foo == "undefined"` → `foo === void 0`
- `void 0` → `undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically
reduced to a single character)
@@ -1002,3 +1067,30 @@ in total it's a bit more than just using UglifyJS's own parser.
[acorn]: https://github.com/ternjs/acorn
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
### Uglify Fast Minify Mode
It's not well known, but whitespace removal and symbol mangling accounts
for 95% of the size reduction in minified code for most javascript - not
elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to
[`butternut`](https://www.npmjs.com/package/butternut):
| d3.js | minify size | gzip size | minify time (seconds) |
| --- | ---: | ---: | ---: |
| original | 451,131 | 108,733 | - |
| uglify-js@3.0.24 mangle=false, compress=false | 316,600 | 85,245 | 0.70 |
| uglify-js@3.0.24 mangle=true, compress=false | 220,216 | 72,730 | 1.13 |
| butternut@0.4.6 | 217,568 | 72,738 | 1.41 |
| uglify-js@3.0.24 mangle=true, compress=true | 212,511 | 71,560 | 3.36 |
| babili@0.1.4 | 210,713 | 72,140 | 12.64 |
To enable fast minify mode from the CLI use:
```
uglifyjs file.js -m
```
To enable fast minify mode with the API use:
```js
UglifyJS.minify(code, { compress: false, mangle: true });
```

View File

@@ -35,11 +35,11 @@ else if (process.argv.indexOf("options") >= 0) program.helpInformation = functio
}
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true));
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true));
program.option("-p, --parse <options>", "Specify parser options.", parse_js());
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js());
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js());
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js());
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js());
program.option("-o, --output <file>", "Output file (default STDOUT).");
program.option("--comments [filter]", "Preserve copyright comments in the output.");
program.option("--config-file <file>", "Read minify() options from JSON file.");
@@ -111,17 +111,8 @@ if (program.mangleProps) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps;
}
var cache;
if (program.nameCache) {
cache = JSON.parse(read_file(program.nameCache, "{}"));
if (options.mangle) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.cache = to_cache("vars");
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
options.mangle.properties.cache = to_cache("props");
}
}
options.nameCache = JSON.parse(read_file(program.nameCache, "{}"));
}
if (program.output == "ast") {
options.output = {
@@ -271,9 +262,7 @@ function run() {
print(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
}
if (result.timings) for (var phase in result.timings) {
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
@@ -326,7 +315,7 @@ function read_file(path, default_value) {
}
}
function parse_js(flag, constants) {
function parse_js(flag) {
return function(value, options) {
options = options || {};
try {
@@ -344,7 +333,7 @@ function parse_js(flag, constants) {
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string();
var value = node.right;
if (!constants) {
if (flag) {
options[name] = value;
} else if (value instanceof UglifyJS.AST_Array) {
options[name] = value.elements.map(to_string);
@@ -367,14 +356,18 @@ function parse_js(flag, constants) {
}
}));
} catch(ex) {
options[value] = null;
if (flag) {
fatal("Error parsing arguments for '" + flag + "': " + value);
} else {
options[value] = null;
}
}
return options;
}
}
function parse_source_map() {
var parse = parse_js("sourceMap", true);
var parse = parse_js();
return function(value, options) {
var hasContent = options && "content" in options;
var settings = parse(value, options);
@@ -386,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) {
return skip_keys.indexOf(key) >= 0;
}

View File

@@ -134,11 +134,10 @@ var AST_Debugger = DEFNODE("Debugger", null, {
$documentation: "Represents a debugger statement",
}, AST_Statement);
var AST_Directive = DEFNODE("Directive", "value scope quote", {
var AST_Directive = DEFNODE("Directive", "value quote", {
$documentation: "Represents a directive, like \"use strict\";",
$propdoc: {
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
scope: "[AST_Scope/S] The scope that this directive affects",
quote: "[string] the original quote character"
},
}, AST_Statement);
@@ -303,10 +302,9 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", {
$documentation: "Base class for all statements introducing a lexical scope",
$propdoc: {
directives: "[string*/S] an array of directives declared in this scope",
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
functions: "[Object/S] like `variables`, but only lists function declarations",
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
@@ -697,7 +695,7 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
var AST_VarDef = DEFNODE("VarDef", "name value", {
$documentation: "A variable declaration; only appears in a AST_Definitions node",
$propdoc: {
name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable",
name: "[AST_Destructuring|AST_SymbolConst|AST_SymbolLet|AST_SymbolVar] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer"
},
_walk: function(visitor) {
@@ -1160,7 +1158,7 @@ TreeWalker.prototype = {
if (!ret && descend) {
descend.call(node);
}
this.pop(node);
this.pop();
return ret;
},
parent: function(n) {
@@ -1179,8 +1177,8 @@ TreeWalker.prototype = {
}
this.stack.push(node);
},
pop: function(node) {
this.stack.pop();
pop: function() {
var node = this.stack.pop();
if (node instanceof AST_Lambda || node instanceof AST_Class) {
this.directives = Object.getPrototypeOf(this.directives);
}

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,23 @@ function set_shorthand(name, options, keys) {
}
}
function init_cache(cache) {
if (!cache) return;
if (!("cname" in cache)) cache.cname = -1;
if (!("props" in cache)) {
cache.props = new Dictionary();
} else if (!(cache.props instanceof Dictionary)) {
cache.props = Dictionary.fromObject(cache.props);
}
}
function to_json(cache) {
return {
cname: cache.cname,
props: cache.props.toObject()
};
}
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
@@ -36,6 +53,7 @@ function minify(files, options) {
ie8: false,
keep_fnames: false,
mangle: {},
nameCache: null,
output: {},
parse: {},
sourceMap: false,
@@ -52,9 +70,10 @@ function minify(files, options) {
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]);
var quoted_props;
if (options.mangle) {
options.mangle = defaults(options.mangle, {
cache: null,
cache: options.nameCache && (options.nameCache.vars || {}),
eval: false,
ie8: false,
keep_classnames: false,
@@ -64,6 +83,21 @@ function minify(files, options) {
safari10: false,
toplevel: false,
}, true);
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") {
options.mangle.properties = {};
}
if (options.mangle.properties.keep_quoted) {
quoted_props = options.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
options.mangle.properties.reserved = quoted_props;
}
if (options.nameCache && !("cache" in options.mangle.properties)) {
options.mangle.properties.cache = options.nameCache.props || {};
}
}
init_cache(options.mangle.cache);
init_cache(options.mangle.properties.cache);
}
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
@@ -101,6 +135,9 @@ function minify(files, options) {
}
toplevel = options.parse.toplevel;
}
if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props);
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
@@ -157,6 +194,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) {
timings.end = Date.now();
result.timings = {

View File

@@ -54,7 +54,6 @@ function OutputStream(options) {
options = defaults(options, {
ascii_only : false,
ascii_identifiers: undefined,
beautify : false,
bracketize : false,
comments : false,
@@ -78,9 +77,6 @@ function OutputStream(options) {
wrap_iife : false,
}, true);
if (typeof options.ascii_identifiers === 'undefined')
options.ascii_identifiers = options.ascii_only;
if (options.shorthand === undefined)
options.shorthand = options.ecma > 5;
@@ -118,20 +114,16 @@ function OutputStream(options) {
var current_pos = 0;
var OUTPUT = "";
function to_ascii(str, identifier) {
return str.replace(/[\ud800-\udbff][\udc00-\udfff]|[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
var code = get_full_char_code(ch, 0).toString(16);
if ((identifier && code.length === 1 && options.ecma >= 6) || code.length > 4) {
if (options.ecma < 6) {
if (identifier) {
return ch; // no \u{} support
}
return "\\u" + ch.charCodeAt(0).toString(16) + "\\u"
+ ch.charCodeAt(1).toString(16);
}
var to_utf8 = options.ascii_only ? function(str, identifier) {
if (options.ecma >= 6) {
str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
var code = get_full_char_code(ch, 0).toString(16);
return "\\u{" + code + "}";
} else if (code.length <= 2 && !identifier) {
});
}
return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
var code = ch.charCodeAt(0).toString(16);
if (code.length <= 2 && !identifier) {
while (code.length < 2) code = "0" + code;
return "\\x" + code;
} else {
@@ -139,6 +131,12 @@ function OutputStream(options) {
return "\\u" + code;
}
});
} : function(str) {
return str.replace(/[\ud800-\udbff](?![\udc00-\udfff])/g, function(ch) {
return "\\u" + ch.charCodeAt(0).toString(16);
}).replace(/(^|[^\ud800-\udbff])([\udc00-\udfff])/g, function(match, prefix, ch) {
return prefix + "\\u" + ch.charCodeAt(0).toString(16);
});
};
function make_string(str, quote) {
@@ -159,7 +157,7 @@ function OutputStream(options) {
case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff";
case "\0":
return /[0-7]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
return /[0-9]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
}
return s;
});
@@ -172,7 +170,7 @@ function OutputStream(options) {
function quote_template() {
return '`' + str.replace(/`/g, '\\`') + '`';
}
if (options.ascii_only) str = to_ascii(str);
str = to_utf8(str);
if (quote === "`") return quote_template();
switch (options.quote_style) {
case 1:
@@ -198,8 +196,7 @@ function OutputStream(options) {
function make_name(name) {
name = name.toString();
if (options.ascii_identifiers)
name = to_ascii(name, true);
name = to_utf8(name, true);
return name;
};
@@ -461,7 +458,7 @@ function OutputStream(options) {
last : function() { return last },
semicolon : semicolon,
force_semicolon : force_semicolon,
to_ascii : to_ascii,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
@@ -509,13 +506,17 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
};
var use_asm = false;
var in_directive = false;
var active_scope = null;
var use_asm = null;
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) {
use_asm = true;
var self = this, generator = self._codegen;
if (self instanceof AST_Scope) {
active_scope = self;
}
else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
use_asm = active_scope;
}
function doit() {
self.add_comments(stream);
@@ -529,8 +530,8 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
if (self === use_asm) {
use_asm = null;
}
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
@@ -673,6 +674,12 @@ function OutputStream(options) {
&& 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){
var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
@@ -686,6 +693,7 @@ function OutputStream(options) {
* ==> 20 (side effect, set a := 10 and b := 20) */
|| p instanceof AST_Arrow // x => (x, x)
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
|| p instanceof AST_Expansion // [...(a, b)]
;
});
@@ -739,14 +747,15 @@ function OutputStream(options) {
// parens around it too, otherwise the call will be
// interpreted as passing the arguments to the upper New
// expression.
try {
this.walk(new TreeWalker(function(node){
if (node instanceof AST_Call) throw p;
}));
} catch(ex) {
if (ex !== p) throw ex;
return true;
}
var parens = false;
this.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Call) {
parens = true;
return true;
}
}));
return parens;
}
});
@@ -1039,11 +1048,11 @@ function OutputStream(options) {
var needs_parens = parent instanceof AST_Binary ||
parent instanceof AST_Unary ||
(parent instanceof AST_Call && self === parent.expression);
if (needs_parens) { output.print("(") }
if (self.async) {
output.print("async");
output.space();
}
if (needs_parens) { output.print("(") }
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
self.argnames[0].print(output);
} else {
@@ -1363,19 +1372,17 @@ function OutputStream(options) {
});
function parenthesize_for_noin(node, output, noin) {
if (!noin) node.print(output);
else try {
// need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Binary && node.operator == "in")
throw output;
}));
node.print(output);
} catch(ex) {
if (ex !== output) throw ex;
node.print(output, true);
}
var parens = false;
// need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60
if (noin) node.walk(new TreeWalker(function(node) {
if (parens || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
parens = true;
return true;
}
}));
node.print(output, parens);
};
DEFPRINT(AST_VarDef, function(self, output){
@@ -1395,6 +1402,9 @@ function OutputStream(options) {
self.expression.print(output);
if (self instanceof AST_New && !need_constructor_parens(self, output))
return;
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start);
}
output.with_parens(function(){
self.args.forEach(function(expr, i){
if (i) output.comma();
@@ -1434,15 +1444,23 @@ function OutputStream(options) {
DEFPRINT(AST_Dot, function(self, output){
var expr = self.expression;
expr.print(output);
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
var prop = self.property;
if (output.option("ie8") && RESERVED_WORDS(prop)) {
output.print("[");
output.add_mapping(self.end);
output.print_string(prop);
output.print("]");
} else {
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
}
output.print(".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
output.print_name(prop);
}
output.print(".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
output.print_name(self.property);
});
DEFPRINT(AST_Sub, function(self, output){
self.expression.print(output);
@@ -1697,9 +1715,7 @@ function OutputStream(options) {
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
}
if (output.option("ascii_only")) {
str = output.to_ascii(str);
}
str = output.to_utf8(str);
output.print(str);
var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)

View File

@@ -44,9 +44,9 @@
"use strict";
var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import';
var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof let new return switch throw try typeof var void while with';
var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'enum implements interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS;
var RESERVED_WORDS = 'enum implements import interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield await';
KEYWORDS = makePredicate(KEYWORDS);
@@ -1013,6 +1013,12 @@ function parse($TEXT, options) {
next();
return function_(AST_Defun, false, true);
}
if (S.token.value == "import" && !is_token(peek(), "punc", "(")) {
next();
var node = import_();
semicolon();
return node;
}
return is_token(peek(), "punc", ":")
? labeled_statement()
: simple_statement();
@@ -1149,15 +1155,11 @@ function parse($TEXT, options) {
body : statement()
});
case "import":
next();
var node = import_();
semicolon();
return node;
case "export":
next();
return export_();
if (!is_token(peek(), "punc", "(")) {
next();
return export_();
}
}
}
unexpected();
@@ -1320,6 +1322,9 @@ function parse($TEXT, options) {
if (in_statement && !name)
unexpected();
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
unexpected(prev());
var args = parameters();
var body = _function_body(true, is_generator || is_generator_property, is_async, name, args);
return new ctor({
@@ -1522,7 +1527,7 @@ function parse($TEXT, options) {
}
if (is_expand) {
if (!is("punc", "]")) {
unexpected(); // Must be last element
croak("Rest element must be last element");
}
elements[elements.length - 1] = new AST_Expansion({
start: expand_token,
@@ -1547,18 +1552,33 @@ function parse($TEXT, options) {
} else {
expect(",");
}
if (is("expand", "...")) {
is_expand = true;
expand_token = S.token;
used_parameters.mark_spread(S.token);
next();
}
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) {
used_parameters.add_parameter(S.token);
elements.push(new AST_ObjectKeyVal({
start: prev(),
key: S.token.value,
value: new symbol_type({
start: S.token,
name: S.token.value,
end: S.token
}),
end: prev()
}));
var value = new symbol_type({
start: S.token,
name: S.token.value,
end: S.token,
});
if (is_expand) {
elements.push(new AST_Expansion({
start: expand_token,
expression: value,
end: value.end,
}));
} else {
elements.push(new AST_ObjectKeyVal({
start: prev(),
key: S.token.value,
value: value,
end: value.end,
}));
}
next();
} else if (is("punc", "}")) {
continue; // Allow trailing hole
@@ -1589,7 +1609,12 @@ function parse($TEXT, options) {
}));
}
}
if (is("operator", "=")) {
if (is_expand) {
if (!is("punc", "}")) {
croak("Rest element must be last element");
}
}
else if (is("operator", "=")) {
used_parameters.mark_default_assignment(S.token);
next();
elements[elements.length - 1].value = new AST_DefaultAssign({
@@ -1855,7 +1880,8 @@ function parse($TEXT, options) {
: !no_in && kind === "const" && S.input.has_directive("use strict")
? croak("Missing initializer in const declaration") : null,
end : prev()
})
});
if (def.name.name == "import") croak("Unexpected token: import");
}
a.push(def);
if (!is("punc", ","))
@@ -2143,7 +2169,18 @@ function parse($TEXT, options) {
if (!options.strict && is("punc", "}"))
// allow trailing comma
break;
start = S.token;
if (start.type == "expand") {
next();
a.push(new AST_Expansion({
start: start,
expression: expression(false),
end: prev(),
}));
continue;
}
var name = as_property_name();
var value;

View File

@@ -85,6 +85,36 @@ function find_builtins(reserved) {
}
}
function reserve_quoted_keys(ast, reserved) {
function add(name) {
push_uniq(reserved, name);
}
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) {
add(node.key);
} else if (node instanceof AST_ObjectProperty && node.quote) {
add(node.key.name);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
}
}));
}
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.expressions[node.expressions.length - 1], add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
}
return true;
}));
}
function mangle_properties(ast, options) {
options = defaults(options, {
builtins: false,
@@ -94,7 +124,7 @@ function mangle_properties(ast, options) {
only_cache: false,
regex: null,
reserved: null,
});
}, true);
var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
@@ -109,7 +139,6 @@ function mangle_properties(ast, options) {
}
var regex = options.regex;
var keep_quoted = options.keep_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
@@ -122,12 +151,11 @@ function mangle_properties(ast, options) {
var names_to_mangle = [];
var unmangleable = [];
var to_keep = {};
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
add(node.key, keep_quoted && node.quote);
add(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
@@ -137,18 +165,14 @@ function mangle_properties(ast, options) {
add(node.property);
}
else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted);
}
else if (node instanceof AST_ConciseMethod) {
add(node.name.name);
addStrings(node.property, add);
}
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) {
if (!(keep_quoted && node.quote))
node.key = mangle(node.key);
node.key = mangle(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter
@@ -157,27 +181,9 @@ function mangle_properties(ast, options) {
else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
if (!keep_quoted)
node.property = mangleStrings(node.property);
else if (!options.keep_quoted && node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
}
else if (node instanceof AST_ConciseMethod) {
if (should_mangle(node.name.name)) {
node.name.name = mangle(node.name.name);
}
}
// else if (node instanceof AST_String) {
// if (should_mangle(node.value)) {
// AST_Node.warn(
// "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
// file : node.start.file,
// line : node.start.line,
// col : node.start.col,
// prop : node.value
// }
// );
// }
// }
}));
// only function declarations after this line
@@ -193,19 +199,13 @@ function mangle_properties(ast, options) {
}
function should_mangle(name) {
if (keep_quoted && name in to_keep) return false;
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0;
}
function add(name, keep) {
if (keep) {
to_keep[name] = true;
return;
}
function add(name) {
if (can_mangle(name))
push_uniq(names_to_mangle, name);
@@ -225,19 +225,16 @@ function mangle_properties(ast, options) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) {
if (can_mangle(debug_mangled)) {
mangled = debug_mangled;
}
}
// either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) {
// Note: `can_mangle()` does not check if the name collides with the `to_keep` set
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this
// check so we don't collide with a quoted name.
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || keep_quoted && mangled in to_keep);
} while (!can_mangle(mangled));
}
cache.props.set(name, mangled);
@@ -245,32 +242,6 @@ function mangle_properties(ast, options) {
return mangled;
}
function addStrings(node, keep) {
var out = {};
try {
(function walk(node){
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Sequence) {
walk(node.expressions[node.expressions.length - 1]);
return true;
}
if (node instanceof AST_String) {
add(node.value, keep);
return true;
}
if (node instanceof AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Sequence) {

View File

@@ -116,7 +116,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var tw = new TreeWalker(function(node, descend){
if (node.is_block_scope()) {
var save_scope = scope;
scope = new AST_Scope(node);
node.block_scope = scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
if (!(node instanceof AST_Scope)) {
scope.uses_with = save_scope.uses_with;
@@ -203,6 +203,21 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node);
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring;
if (defun !== scope) {
@@ -300,6 +315,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
ref.reference(options);
});
node.thedef = def;
node.reference(options);
return true;
}
}));
@@ -357,7 +373,7 @@ AST_IterationStatement.DEFMETHOD("is_block_scope", return_true);
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false;
this.def_variable(new AST_SymbolConst({
this.def_variable(new AST_SymbolFunarg({
name: "arguments",
start: this.start,
end: this.end
@@ -461,14 +477,6 @@ AST_Symbol.DEFMETHOD("unreferenced", function(){
&& !(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(){
return this.thedef;
});
@@ -538,7 +546,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
var mangle_with_block_scope =
(!options.ie8 && node instanceof AST_SymbolCatch) ||
node instanceof AST_SymbolBlockDeclaration;
if (mangle_with_block_scope) {
if (mangle_with_block_scope && options.reserved.indexOf(node.name) < 0) {
to_mangle.push(node.definition());
return;
}

View File

@@ -70,7 +70,7 @@ TreeTransformer.prototype = new TreeWalker;
if (y !== undefined) x = y;
}
}
tw.pop(this);
tw.pop();
return x;
});
};

View File

@@ -1,20 +1,17 @@
{
"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",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.0.21",
"version": "3.1.3",
"engines": {
"node": ">=0.8.0"
},
"maintainers": [
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
],
"repository": {
"type": "git",
"url": "https://github.com/mishoo/UglifyJS2.git"
},
"repository": "git+https://github.com/mishoo/UglifyJS2.git#harmony",
"bugs": {
"url": "https://github.com/mishoo/UglifyJS2/issues"
},
@@ -29,25 +26,29 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.9.0",
"commander": "~2.11.0",
"source-map": "~0.5.1"
},
"devDependencies": {
"acorn": "~5.0.3",
"mocha": "~2.3.4",
"semver": "~5.3.0"
"acorn": "~5.1.1",
"mocha": "~3.5.1",
"semver": "~5.4.1"
},
"scripts": {
"test": "node test/run-tests.js"
},
"keywords": [
"uglify",
"uglify-js",
"uglify-es",
"uglify-js",
"minify",
"minifier",
"javascript",
"ecmascript",
"es5",
"es6",
"es7",
"es8",
"es2015",
"es2016",
"es2017",

View File

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

View File

@@ -213,7 +213,7 @@ no_leading_parentheses: {
async_identifiers: {
options = {
arrows: true,
unsafe_arrows: true,
ecma: 6,
}
input: {
@@ -237,7 +237,7 @@ async_identifiers: {
async_function_expression: {
options = {
arrows: true,
unsafe_arrows: true,
ecma: 6,
evaluate: true,
side_effects: true,
@@ -262,7 +262,7 @@ async_function_expression: {
issue_27: {
options = {
arrows: true,
unsafe_arrows: true,
collapse_vars: true,
ecma: 6,
unused: true,
@@ -283,12 +283,14 @@ issue_27: {
issue_2105_1: {
options = {
arrows: true,
unsafe_arrows: true,
collapse_vars: true,
ecma: 6,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
unsafe_methods: true,
unused: true,
}
input: {
@@ -312,12 +314,12 @@ issue_2105_1: {
});
}
expect: {
!void (() => {
(() => {
var quux = () => {
console.log("PASS");
};
return {
prop: () => {
prop() {
console.log;
quux();
}
@@ -332,6 +334,7 @@ issue_2105_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -357,7 +360,7 @@ issue_2105_2: {
});
}
expect: {
!void (() => {
(() => {
var quux = () => {
console.log("PASS");
};
@@ -499,7 +502,7 @@ issue_485_crashing_1530: {
issue_2084: {
options = {
arrows: true,
unsafe_arrows: true,
collapse_vars: true,
conditionals: true,
ecma: 6,
@@ -528,7 +531,7 @@ issue_2084: {
}
expect: {
var c = 0;
!((c) => {
((c) => {
c = 1 + c,
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
@@ -538,3 +541,93 @@ issue_2084: {
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,
unsafe_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)
};
}
}
issue_2271: {
options = {
arrows: true,
ecma: 6,
evaluate: true,
unsafe_arrows: false,
}
input: {
var Foo = function() {};
Foo.prototype.set = function(value) {
this.value = value;
return this;
}
Foo.prototype.print = function() {
console.log(this.value);
}
new Foo().set("PASS").print();
}
expect: {
var Foo = function() {};
Foo.prototype.set = function(value) {
this.value = value;
return this;
}
Foo.prototype.print = function() {
console.log(this.value);
}
new Foo().set("PASS").print();
}
expect_stdout: "PASS"
}

View File

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

View File

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

View File

@@ -6,6 +6,22 @@ await_precedence: {
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: {
options = {
side_effects: true,
@@ -252,3 +268,27 @@ async_arrow_wait: {
}
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

@@ -863,7 +863,7 @@ collapse_vars_unary: {
input: {
function f0(o, p) {
var x = o[p];
delete x;
return delete x;
}
function f1(n) {
var k = !!n;
@@ -893,7 +893,7 @@ collapse_vars_unary: {
expect: {
function f0(o, p) {
var x = o[p];
delete x;
return delete x;
}
function f1(n) {
return n > +!!n
@@ -2077,10 +2077,10 @@ chained_3: {
}
expect: {
console.log(function(a, b) {
var c = a, c = b;
var c = 1, c = b;
b++;
return c;
}(1, 2));
}(0, 2));
}
expect_stdout: "2"
}
@@ -2330,3 +2330,468 @@ reassign_const_2: {
}
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"
}
issue_2250_1: {
options = {
collapse_vars: true,
conditionals: true,
reduce_vars: true,
}
input: {
function f(x) {
if (x) {
const a = foo();
x(a);
}
}
function g(x) {
if (x) {
let a = foo();
x(a);
}
}
function h(x) {
if (x) {
var a = foo();
x(a);
}
}
}
expect: {
function f(x) {
x && x(foo());
}
function g(x) {
x && x(foo());
}
function h(x) {
x && x(foo());
}
}
}
issue_2250_2: {
options = {
collapse_vars: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
{
const foo = function(){};
foo(bar());
}
{
let foo = function(){};
foo(bar());
}
{
var foo = function(){};
foo(bar());
}
}
expect: {
bar();
bar();
bar();
}
}
issue_2298: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
function f() {
var a = undefined;
var undefined = a++;
try {
!function g(b) {
b[1] = "foo";
}();
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
f();
}();
}
expect: {
!function() {
(function() {
var a = undefined;
var undefined = a++;
try {
!function(b) {
(void 0)[1] = "foo";
}();
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
})();
}();
}
expect_stdout: "PASS"
}
issue_2313_1: {
options = {
collapse_vars: true,
conditionals: true,
}
input: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
if (this.c) console.log(a, b);
}
}
foo.d();
}
expect: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
this.c && console.log(a, b);
}
}
foo.d();
}
expect_stdout: "2 1"
}
issue_2313_2: {
options = {
collapse_vars: true,
}
input: {
var c = 0;
!function a() {
a && c++;
var a = 0;
a && c++;
}();
console.log(c);
}
expect: {
var c = 0;
!function a() {
a && c++;
var a = 0;
a && c++;
}();
console.log(c);
}
expect_stdout: "0"
}
issue_2319_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function(a) {
return a;
}(!function() {
return this;
}()));
}
expect: {
console.log(function(a) {
return !function() {
return this;
}();
}());
}
expect_stdout: "false"
}
issue_2319_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function(a) {
"use strict";
return a;
}(!function() {
return this;
}()));
}
expect: {
console.log(function(a) {
"use strict";
return a;
}(!function() {
return this;
}()));
}
expect_stdout: "false"
}
issue_2319_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
"use strict";
console.log(function(a) {
return a;
}(!function() {
return this;
}()));
}
expect: {
"use strict";
console.log(function(a) {
return !function() {
return this;
}();
}());
}
expect_stdout: "true"
}

View File

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

View File

@@ -377,3 +377,186 @@ accessor: {
}
expect: {}
}
issue_2233_1: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
Array.isArray;
Boolean;
console.log;
Date;
decodeURI;
decodeURIComponent;
encodeURI;
encodeURIComponent;
Error.name;
escape;
eval;
EvalError;
Function.length;
isFinite;
isNaN;
JSON;
Math.random;
Number.isNaN;
parseFloat;
parseInt;
RegExp;
Object.defineProperty;
String.fromCharCode;
RangeError;
ReferenceError;
SyntaxError;
TypeError;
unescape;
URIError;
}
expect: {}
expect_stdout: true
}
global_timeout_and_interval_symbols: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
// These global symbols do not exist in the test sandbox
// and must be tested separately.
clearInterval;
clearTimeout;
setInterval;
setTimeout;
}
expect: {}
}
issue_2233_2: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
var RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Number.isNaN;
}
}
}
issue_2233_3: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var RegExp;
Array.isArray;
RegExp;
UndeclaredGlobal;
function foo() {
var Number;
AnotherUndeclaredGlobal;
Math.sin;
Number.isNaN;
}
}
expect: {
UndeclaredGlobal;
}
}
global_fns: {
options = {
side_effects: true,
unsafe: true,
}
input: {
Boolean(1, 2);
decodeURI(1, 2);
decodeURIComponent(1, 2);
Date(1, 2);
encodeURI(1, 2);
encodeURIComponent(1, 2);
Error(1, 2);
escape(1, 2);
EvalError(1, 2);
isFinite(1, 2);
isNaN(1, 2);
Number(1, 2);
Object(1, 2);
parseFloat(1, 2);
parseInt(1, 2);
RangeError(1, 2);
ReferenceError(1, 2);
String(1, 2);
SyntaxError(1, 2);
TypeError(1, 2);
unescape(1, 2);
URIError(1, 2);
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect: {
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect_stdout: [
"SyntaxError",
"SyntaxError",
"RangeError",
]
}

View File

@@ -65,10 +65,10 @@ nested_destructuring_objects: {
}
input: {
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
let [{d},e] = f;
var [{g},h] = i;
}
expect_exact: 'const[{a},b]=c;let[{a},b]=c;var[{a},b]=c;';
expect_exact: 'const[{a},b]=c;let[{d},e]=f;var[{g},h]=i;';
}
destructuring_constdef_in_loops: {
@@ -274,8 +274,8 @@ reduce_vars: {
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}});
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
let [{d},e] = f;
var [{g},h] = i;
[{a},b] = c;
for (const [x,y] in pairs);
for (let [x,y] in pairs);
@@ -292,8 +292,8 @@ reduce_vars: {
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}});
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
let [{d},e] = f;
var [{g},h] = i;
[{a},b] = c;
for (const [x,y] in pairs);
for (let [x,y] in pairs);

View File

@@ -1210,6 +1210,7 @@ var_catch_toplevel: {
a--;
try {
a++;
x();
} catch(a) {
if (a) var a;
var a = 10;
@@ -1219,9 +1220,8 @@ var_catch_toplevel: {
}
expect: {
!function() {
a--;
try {
a++;
x();
} catch(a) {
var a;
}
@@ -1299,6 +1299,7 @@ issue_2105: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -1324,7 +1325,7 @@ issue_2105: {
});
}
expect: {
!void function() {
(function() {
var quux = function() {
console.log("PASS");
};
@@ -1334,7 +1335,7 @@ issue_2105: {
quux();
}
};
}().prop();
})().prop();
}
expect_stdout: "PASS"
}
@@ -1426,3 +1427,111 @@ issue_2163: {
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"
}
issue_2288: {
options = {
unused: true,
}
beautify = {
beautify: true,
}
input: {
function foo(o) {
for (var j = o.a, i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
}
}
expect: {
function foo(o) {
o.a;
for (i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
}
}
}

View File

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

View File

@@ -26,3 +26,46 @@ expand_parameters: {
expect_exact: "(function(a,...b){});(function(...args){});"
}
avoid_spread_in_ternary: {
options = {
comparisons: true,
conditionals: true,
evaluate: true,
}
input: {
function print(...x) {
console.log(...x);
}
var a = [1, 2], b = [3, 4];
if (Math)
print(a);
else
print(b);
if (Math)
print(...a);
else
print(b);
if (Math.no_such_property)
print(a);
else
print(...b);
}
expect: {
function print(...x) {
console.log(...x);
}
var a = [ 1, 2 ], b = [ 3, 4 ];
print(Math ? a : b);
Math ? print(...a) : print(b);
Math.no_such_property ? print(a) : print(...b);
}
expect_stdout: [
"[ 1, 2 ]",
"1 2",
"3 4",
]
node_version: ">=6"
}

View File

@@ -226,3 +226,27 @@ keyword_valid_3: {
export { default as default } from "module.js";
}
}
dynamic_import: {
mangle = {
toplevel: true,
}
input: {
import traditional from './traditional.js';
function imp(x) {
return import(x);
}
import("module_for_side_effects.js");
let dynamic = import("some/module.js");
dynamic.foo();
}
expect: {
import o from "./traditional.js";
function t(o) {
return import(o);
}
import("module_for_side_effects.js");
let r = import("some/module.js");
r.foo();
}
}

View File

@@ -468,11 +468,9 @@ issue_2114_1: {
}
expect: {
var c = 0;
!function() {
0;
}((c += 1, c = 1 + c, function() {
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}()));
}();
console.log(c);
}
expect_stdout: "2"

View File

@@ -37,6 +37,7 @@ object: {
VALUE: 42,
},
},
side_effects: true,
unsafe: true,
}
input: {
@@ -140,9 +141,9 @@ mixed: {
console.log(CONFIG);
}
expect_warnings: [
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:126,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:128,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:130,8]',
]
}

View File

@@ -211,13 +211,13 @@ export_statement: {
}
input: {
export default 1 + 2;
export var foo = 4;
export let foo = 6;
export const foo = 6;
export function foo() {};
export class foo { };
export var a = 4;
export let b = 6;
export const c = 6;
export function d() {};
export class e {};
}
expect_exact: "export default 3;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
expect_exact: "export default 3;export var a=4;export let b=6;export const c=6;export function d(){};export class e{};"
}
export_default_object_expression: {
@@ -695,3 +695,147 @@ export_default_class_decl: {
}
expect_exact: "export default class Car{};export class Cab{};"
}
object_rest_spread: {
mangle = {
toplevel: true,
}
input: {
var { w: w1, ...V } = { w: 7, x: 1, y: 2 }; console.log(w1, V);
let { w: w2, ...L } = { w: 8, x: 3, y: 4 }; console.log(w2, L);
const { w: w3, ...C } = { w: 9, x: 5, y: 6 }; console.log(w3, C);
let b;
({ b, ...V } = { a: 1, b: 2, c: 3 }); console.log(V);
({ b, ...L } = { a: 4, b: 5, c: 6 }); console.log(L);
(function({ y, ...p }){ console.log(p); })({ x: 1, y: 2, z: 3 });
(({ y, ...p }) => { console.log(p); })({ x: 4, y: 5, z: 6 });
const T = { a: 1, b: 2 }; console.log({ ...T, w: 0, ...{}, ...L, ...{K: 9} });
}
expect: {
var { w: o, ...l } = { w: 7, x: 1, y: 2 }; console.log(o, l);
let { w: c, ...n } = { w: 8, x: 3, y: 4 }; console.log(c, n);
const { w: e, ...s } = { w: 9, x: 5, y: 6 }; console.log(e, s);
let g;
({ b: g, ...l } = { a: 1, b: 2, c: 3 }); console.log(l);
({ b: g, ...n } = { a: 4, b: 5, c: 6 }); console.log(n);
(function({ y: o, ...l }) { console.log(l); })({ x: 1, y: 2, z: 3 });
(({ y: o, ...l }) => { console.log(l); })({ x: 4, y: 5, z: 6 });
const w = { a: 1, b: 2 }; console.log({ ...w, w: 0, ...{}, ...n, ...{ K: 9 } });
}
}
object_spread_unsafe: {
options = {
collapse_vars: true,
evaluate: true,
join_vars: true,
passes: 3,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
mangle = {
toplevel: true,
}
input: {
var o1 = { x: 1, y: 2 };
var o2 = { x: 3, z: 4 };
var cloned = { ...o1 };
var merged = { ...o1, ...o2 };
console.log(cloned, merged);
}
expect: {
var o = { x: 1, y: 2 }, l = { ...o }, x = { ...o, ...{ x: 3, z: 4 } };
console.log(l, x);
}
}
array_spread_of_sequence: {
mangle = {
toplevel: true,
}
input: {
var a = [1];
console.log([...(a, a)]);
console.log([...a, a]);
console.log([...(a || a)]);
console.log([...a || a]);
}
expect: {
var o = [1];
console.log([...(o, o)]);
console.log([...o, o]);
console.log([...o || o]);
console.log([...o || o]);
}
expect_stdout: [
"[ 1 ]",
"[ 1, [ 1 ] ]",
"[ 1 ]",
"[ 1 ]",
]
node_version: ">=6"
}
object_spread_of_sequence: {
mangle = {
toplevel: true,
}
input: {
var a = {x: 1};
console.log({ ...(a, a) });
console.log({ ...a, a });
console.log({ ...(a || a) });
console.log({ ...a || a });
}
expect: {
var o = { x: 1 };
console.log({ ...(o, o) });
console.log({ ...o, a: o });
console.log({ ...o || o });
console.log({ ...o || o });
}
}
// issue 2316
class_name_can_be_preserved_with_reserved: {
mangle = {
reserved: [ "Foo" ],
}
input: {
function x() {
class Foo {}
Foo.bar;
class Bar {}
Bar.foo;
}
function y() {
var Foo = class Foo {};
Foo.bar;
var Bar = class Bar {};
Bar.bar;
}
}
expect: {
function x() {
class Foo {}
Foo.bar;
class a {}
a.foo;
}
function y() {
var Foo = class Foo {};
Foo.bar;
var a = class a {};
a.bar;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,7 @@
mangle_props: {
mangle_props = {}
mangle = {
properties: true,
}
input: {
var obj = {
undefined: 1,
@@ -54,10 +56,12 @@ mangle_props: {
}
numeric_literal: {
mangle = {
properties: true,
}
beautify = {
beautify: true,
}
mangle_props = {}
input: {
var obj = {
0: 0,

View File

@@ -36,9 +36,10 @@ compress_new_function_with_destruct: {
compress_new_function_with_destruct_arrows: {
options = {
arrows: true,
unsafe_arrows: true,
unsafe: true,
unsafe_Func: true,
ecma: 6
ecma: 6,
}
beautify = {
ecma: 6

View File

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

View File

@@ -1,6 +1,8 @@
dont_reuse_prop: {
mangle_props = {
regex: /asd/
mangle = {
properties: {
regex: /asd/,
},
}
input: {
"aaaaaaaaaabbbbb";
@@ -20,8 +22,10 @@ dont_reuse_prop: {
}
unmangleable_props_should_always_be_reserved: {
mangle_props = {
regex: /asd/
mangle = {
properties: {
regex: /asd/,
},
}
input: {
"aaaaaaaaaabbbbb";

View File

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

View File

@@ -98,3 +98,19 @@ new_with_assignement_expression: {
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

@@ -284,9 +284,11 @@ concise_methods_with_various_property_names: {
}
concise_methods_and_mangle_props: {
mangle_props = {
regex: /_/
};
mangle = {
properties: {
regex: /_/,
},
}
input: {
function x() {
obj = {
@@ -510,3 +512,242 @@ variable_as_computed_property: {
}
expect_exact: "function getLine(header){return{[header]:{}}}"
}
prop_func_to_concise_method: {
options = {
ecma: 6,
unsafe_methods: true,
}
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,
unsafe_methods: true,
}
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,
unsafe_methods: true,
}
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,
unsafe_methods: true,
}
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,
unsafe_methods: true,
}
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,
unsafe_methods: true,
}
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,
unsafe_methods: true,
}
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

@@ -142,11 +142,11 @@ destructuring_arguments_3: {
}
input: {
function fn3({x: {y: {z: {} = 42}}}) {}
const { cover = (function () {}), xCover = (0, function() {}) } = {};
let { cover = (function () {}), xCover = (0, function() {}) } = {};
var { cover = (function () {}), xCover = (0, function() {}) } = {};
const { a = (function () {}), b = (0, function() {}) } = {};
let { c = (function () {}), d = (0, function() {}) } = {};
var { e = (function () {}), f = (0, function() {}) } = {};
}
expect_exact: "function fn3({x:{y:{z:{}=42}}}){}const{cover=function(){},xCover=(0,function(){})}={};let{cover=function(){},xCover=(0,function(){})}={};var{cover=function(){},xCover=(0,function(){})}={};"
expect_exact: "function fn3({x:{y:{z:{}=42}}}){}const{a=function(){},b=(0,function(){})}={};let{c=function(){},d=(0,function(){})}={};var{e=function(){},f=(0,function(){})}={};"
}
default_arguments: {

View File

@@ -13,8 +13,10 @@ keep_properties: {
dot_properties: {
options = {
properties: true,
}
beautify = {
ie8: true,
};
}
input: {
a["foo"] = "bar";
a["if"] = "if";
@@ -36,8 +38,10 @@ dot_properties: {
dot_properties_es5: {
options = {
properties: true,
}
beautify = {
ie8: false,
};
}
input: {
a["foo"] = "bar";
a["if"] = "if";
@@ -124,9 +128,11 @@ evaluate_string_length: {
}
mangle_properties: {
mangle_props = {
keep_quoted: false
};
mangle = {
properties: {
keep_quoted: false,
},
}
input: {
a["foo"] = "bar";
a.color = "red";
@@ -135,11 +141,11 @@ mangle_properties: {
a['run']({color: "blue", foo: "baz"});
}
expect: {
a["o"] = "bar";
a.a = "red";
x = {r: 10};
a.b(x.r, a.o);
a['b']({a: "blue", o: "baz"});
a["a"] = "bar";
a.b = "red";
x = {o: 10};
a.r(x.o, a.a);
a['r']({b: "blue", a: "baz"});
}
}
@@ -147,9 +153,11 @@ mangle_unquoted_properties: {
options = {
properties: false
}
mangle_props = {
builtins: true,
keep_quoted: true
mangle = {
properties: {
builtins: true,
keep_quoted: true,
},
}
beautify = {
beautify: false,
@@ -178,24 +186,26 @@ mangle_unquoted_properties: {
function f1() {
a["foo"] = "bar";
a.color = "red";
a.o = 2;
x = {"bar": 10, f: 7};
a.f = 9;
a.r = 2;
x = {"bar": 10, b: 7};
a.b = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, f: 7};
a.f = 9;
a.o = 3;
x = {bar: 10, b: 7};
a.b = 9;
a.r = 3;
}
}
}
mangle_debug: {
mangle_props = {
debug: ""
};
mangle = {
properties: {
debug: "",
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -207,9 +217,11 @@ mangle_debug: {
}
mangle_debug_true: {
mangle_props = {
debug: true
};
mangle = {
properties: {
debug: true,
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -221,9 +233,11 @@ mangle_debug_true: {
}
mangle_debug_suffix: {
mangle_props = {
debug: "XYZ"
};
mangle = {
properties: {
debug: "XYZ",
},
}
input: {
a.foo = "bar";
x = { baz: "ban" };
@@ -238,11 +252,13 @@ mangle_debug_suffix_keep_quoted: {
options = {
properties: false
}
mangle_props = {
builtins: true,
keep_quoted: true,
debug: "XYZ",
reserved: []
mangle = {
properties: {
builtins: true,
debug: "XYZ",
keep_quoted: true,
reserved: [],
},
}
beautify = {
beautify: false,
@@ -659,3 +675,380 @@ 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_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"
}
methods_keep_quoted_true: {
options = {
arrows: true,
ecma: 6,
unsafe_methods: true,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
class C { "Quoted"(){} Unquoted(){} }
f1({ "Quoted"(){}, Unquoted(){}, "Prop": 3 });
f2({ "Quoted": function(){} });
f3({ "Quoted": ()=>{} });
}
expect_exact: "class C{Quoted(){}o(){}}f1({Quoted(){},o(){},Prop:3});f2({Quoted(){}});f3({Quoted(){}});"
}
methods_keep_quoted_false: {
options = {
arrows: true,
ecma: 6,
unsafe_methods: true,
}
mangle = {
properties: {
keep_quoted: false,
},
}
input: {
class C { "Quoted"(){} Unquoted(){} }
f1({ "Quoted"(){}, Unquoted(){}, "Prop": 3 });
f2({ "Quoted": function(){} });
f3({ "Quoted": ()=>{} });
}
expect_exact: "class C{o(){}d(){}}f1({o(){},d(){},e:3});f2({o(){}});f3({o(){}});"
}
methods_keep_quoted_from_dead_code: {
options = {
arrows: true,
booleans: true,
conditionals: true,
dead_code: true,
ecma: 6,
evaluate: true,
reduce_vars: true,
side_effects: true,
unsafe_methods: true,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
class C { Quoted(){} Unquoted(){} }
f1({ Quoted(){}, Unquoted(){}, "Prop": 3 });
f2({ Quoted: function(){} });
f3({ Quoted: ()=>{} });
0 && obj["Quoted"];
}
expect_exact: "class C{Quoted(){}o(){}}f1({Quoted(){},o(){},Prop:3});f2({Quoted(){}});f3({Quoted(){}});"
}
issue_2256: {
options = {
side_effects: true,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
({ "keep": 1 });
g.keep = g.change;
}
expect: {
g.keep = g.g;
}
}
issue_2321: {
options = {
ecma: 6,
unsafe_methods: false,
}
input: {
var f = {
foo: function(){ console.log("foo") },
bar() { console.log("bar") }
};
var foo = new f.foo();
var bar = f.bar();
}
expect: {
var f = {
foo: function() {
console.log("foo");
},
bar() {
console.log("bar");
}
};
var foo = new f.foo();
var bar = f.bar();
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=6"
}
unsafe_methods_regex: {
options = {
ecma: 6,
unsafe_methods: /^[A-Z1]/,
}
input: {
var f = {
123: function(){ console.log("123") },
foo: function(){ console.log("foo") },
bar() { console.log("bar") },
Baz: function(){ console.log("baz") },
BOO: function(){ console.log("boo") },
null: function(){ console.log("null") },
undefined: function(){ console.log("undefined") },
};
f[123]();
new f.foo();
f.bar();
f.Baz();
f.BOO();
new f.null();
new f.undefined();
}
expect: {
var f = {
123() { console.log("123") },
foo: function(){ console.log("foo") },
bar() { console.log("bar"); },
Baz() { console.log("baz") },
BOO() { console.log("boo") },
null: function(){ console.log("null") },
undefined: function(){ console.log("undefined") },
};
f[123]();
new f.foo();
f.bar();
f.Baz();
f.BOO();
new f.null();
new f.undefined();
}
expect_stdout: [
"123",
"foo",
"bar",
"baz",
"boo",
"null",
"undefined",
]
node_version: ">=6"
}

View File

@@ -385,3 +385,338 @@ set_mutable_2: {
}
expect_stdout: "PASS"
}
issue_2265_1: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
({ ...{} }).p;
({ ...g }).p;
}
expect: {
({ ...g }).p;
}
}
issue_2265_2: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = {
get b() {
throw 0;
}
};
({...a}).b;
}
expect: {
var a = {
get b() {
throw 0;
}
};
({...a}).b;
}
}
issue_2265_3: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = {
set b() {
throw 0;
}
};
({...a}).b;
}
expect: {}
}
issue_2265_4: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = { b: 1 };
({...a}).b;
}
expect: {}
}
issue_2313_1: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
return console.log(1), {
y: function() {
return console.log(2), {
z: 0
};
}
};
}
x().y().z++,
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: true,
sequences: true,
side_effects: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
return console.log(1), {
y: function() {
return console.log(2), {
z: 0
};
}
};
}
x().y().z++,
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_3: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: "strict",
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_4: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_5: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
x().y++;
x().y;
}
expect: {
x().y++;
x().y;
}
}
issue_2313_6: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
x().y++;
x().y;
}
expect: {
x().y++;
x();
}
}
issue_2313_7: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: true,
}
input: {
var a = 0, b = 0;
class foo {
get c() {
a++;
return 42;
}
set c(c) {
b++;
}
}
class bar extends foo {
d() {
super.c++;
if (super.c) console.log(a, b);
}
}
new bar().d();
}
expect: {
var a = 0, b = 0;
class foo {
get c() {
a++;
return 42;
}
set c(c) {
b++;
}
}
class bar extends foo {
d() {
super.c++;
super.c && console.log(a, b);
}
}
new bar().d();
}
expect_stdout: "2 1"
node_version: ">=6"
}

View File

@@ -1469,6 +1469,7 @@ issue_1670_1: {
reduce_vars: true,
side_effects: true,
switches: true,
typeofs: true,
unused: true,
}
input: {
@@ -1532,6 +1533,7 @@ issue_1670_3: {
reduce_vars: true,
side_effects: true,
switches: true,
typeofs: true,
unused: true,
}
input: {
@@ -2547,7 +2549,7 @@ issue_1922_2: {
expect_stdout: "1"
}
accessor: {
accessor_1: {
options = {
evaluate: true,
reduce_vars: true,
@@ -2576,6 +2578,89 @@ accessor: {
expect_stdout: "1 1"
}
accessor_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var A = 1;
var B = {
get c() {
console.log(A);
}
};
B.c;
}
expect: {
({
get c() {
console.log(1);
}
}).c;
}
expect_stdout: "1"
}
method_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
console.log(new class {
a() {
a = 2;
return a;
}
}().a(), a);
}
expect: {
var a = 1;
console.log(new class {
a() {
a = 2;
return a;
}
}().a(), a);
}
expect_stdout: "2 2"
node_version: ">=6"
}
method_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var A = 1;
var B = class {
c() {
console.log(A);
}
};
new B().c();
}
expect: {
new class {
c() {
console.log(1);
}
}().c();
}
expect_stdout: "1"
node_version: ">=6"
}
issue_2090_1: {
options = {
evaluate: true,

View File

@@ -325,3 +325,69 @@ issue_2120_2: {
}
expect_stdout: "PASS"
}
issue_2254_1: {
mangle = {
ie8: false,
}
input: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(s) {
try {
throw "FAIL";
} catch (e) {
return s;
}
}
}
expect: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(e) {
try {
throw "FAIL";
} catch (t) {
return e;
}
}
}
expect_stdout: "PASS"
}
issue_2254_2: {
mangle = {
ie8: true,
}
input: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(s) {
try {
throw "FAIL";
} catch (e) {
return s;
}
}
}
expect: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(t) {
try {
throw "FAIL";
} catch (e) {
return t;
}
}
}
expect_stdout: "PASS"
}

View File

@@ -176,6 +176,11 @@ for_sequences: {
// 4
x = (foo in bar);
for (y = 5; false;);
// 5
x = function() {
foo in bar;
};
for (y = 5; false;);
}
expect: {
// 1
@@ -188,6 +193,10 @@ for_sequences: {
// 4
x = (foo in bar);
for (y = 5; false;);
// 5
for (x = function() {
foo in bar;
}, y = 5; false;);
}
}
@@ -754,3 +763,44 @@ issue_2062: {
}
expect_stdout: "1"
}
issue_2313: {
options = {
cascade: true,
sequences: true,
side_effects: true,
}
input: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
if (this.c) console.log(a, b);
}
}
foo.d();
}
expect: {
var a = 0, b = 0;
var foo = {
get c() {
return a++, 42;
},
set c(c) {
b++;
},
d: function() {
if (this.c++, this.c) console.log(a, b);
}
}
foo.d();
}
expect_stdout: "2 1"
}

View File

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

View File

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

View File

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

View File

@@ -66,7 +66,7 @@ describe("bin/uglifyjs", function () {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n");
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==\n");
done();
});
});
@@ -195,7 +195,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [
"var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==",
"",
].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n");
@@ -666,6 +666,25 @@ describe("bin/uglifyjs", function () {
return JSON.stringify(map).replace(/"/g, '\\"');
}
});
it("Should include function calls in source map", function(done) {
var command = [
uglifyjscmd,
"test/input/issue-2310/input.js",
"-c",
"--source-map", "url=inline",
].join(" ");
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, [
'function foo(){return function(){console.log("PASS")}}foo()();',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMjMxMC9pbnB1dC5qcyJdLCJuYW1lcyI6WyJmb28iLCJjb25zb2xlIiwibG9nIiwiZiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsTUFDTCxPQUFPLFdBQ0hDLFFBQVFDLElBQUksU0FLUkYsS0FDUkcifQ==",
""
].join("\n"));
done();
});
});
it("Should dump AST as JSON", function(done) {
var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast";
exec(command, function (err, stdout) {
@@ -686,8 +705,8 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should work with --mangle reserved=[]", function (done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=[callback]';
it("Should work with --mangle reserved=[]", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=[callback]";
exec(command, function (err, stdout) {
if (err) throw err;
@@ -696,8 +715,8 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should work with --mangle reserved=false", function (done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=false';
it("Should work with --mangle reserved=false", function(done) {
var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=false";
exec(command, function (err, stdout) {
if (err) throw err;
@@ -706,4 +725,22 @@ describe("bin/uglifyjs", function () {
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

@@ -1,7 +1,7 @@
var assert = require("assert");
var uglify = require("../node");
describe("Export", function() {
describe("Export/Import", function() {
it("Should parse export directives", function() {
var inputs = [
['export * from "a.js"', ['*'], "a.js"],
@@ -36,4 +36,22 @@ describe("Export", function() {
assert.equal(st.module_name.value, filename);
}
});
it("Should not parse invalid uses of export", function() {
assert.equal(uglify.minify("export").error.message, "Unexpected token: eof (undefined)");
assert.equal(uglify.minify("export;").error.message, "Unexpected token: punc (;)");
assert.equal(uglify.minify("export();").error.message, "Unexpected token: keyword (export)");
assert.equal(uglify.minify("export(1);").error.message, "Unexpected token: keyword (export)");
assert.equal(uglify.minify("var export;").error.message, "Name expected");
assert.equal(uglify.minify("var export = 1;").error.message, "Name expected");
assert.equal(uglify.minify("function f(export){}").error.message, "Invalid function parameter");
});
it("Should not parse invalid uses of import", function() {
assert.equal(uglify.minify("import").error.message, "Unexpected token: eof (undefined)");
assert.equal(uglify.minify("import;").error.message, "Unexpected token: punc (;)");
assert.equal(uglify.minify("var import;").error.message, "Unexpected token: import");
assert.equal(uglify.minify("var import = 1;").error.message, "Unexpected token: import");
assert.equal(uglify.minify("function f(import){}").error.message, "Unexpected token: name (import)");
});
});

View File

@@ -26,12 +26,12 @@ describe("bin/uglifyjs with input file globs", function() {
});
});
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) {
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();
});
});

View File

@@ -2,16 +2,17 @@ var Uglify = require('../../');
var assert = require("assert");
describe("let", function() {
it("Should not produce reserved keywords as variable name in mangle", function(done) {
this.timeout(10000);
this.timeout(30000);
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.
var s = '"dddddeeeeelllllooooottttt"; function foo() {';
for (var i = 0; i < 18000; i++) {
s += "var v" + i + "=0;";
}
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
[
@@ -19,7 +20,7 @@ describe("let", function() {
"let",
"var",
].forEach(function(name) {
assert.strictEqual(result.code.indexOf("var " + name + "="), -1);
assert.strictEqual(result.indexOf("var " + name + "="), -1);
});
// Verify that the variable names that appeared immediately before
@@ -30,9 +31,27 @@ describe("let", function() {
"eet", "fet",
"rar", "oar",
].forEach(function(name) {
assert.ok(result.code.indexOf("var " + name + "=") >= 0);
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);
});
done();
});
});

View File

@@ -253,13 +253,19 @@ describe("Left-hand side expressions", function () {
// Multiple spreads are not allowed in destructuring array
expect("[...a, ...b] = [1, 2, 3, 4]", "Spread must the be last element in destructuring array");
// Spread in obvious object pattern
expect("({...a} = foo)", "Unexpected token: expand (...)");
// Array spread must be last in destructuring declaration
expect("let [ ...x, a ] = o;", "Rest element must be last element");
// Only one spread per destructuring array declaration
expect("let [ a, ...x, ...y ] = o;", "Rest element must be last element");
// Spread in block should not be allowed
expect("{...a} = foo", "Unexpected token: expand (...)");
// Not in standard yet
expect("let foo = {bar: 42}, bar; bar = {...foo}", "Unexpected token: expand (...)");
// Object spread must be last in destructuring declaration
expect("let { ...x, a } = o;", "Rest element must be last element");
// Only one spread per destructuring declaration
expect("let { a, ...x, ...y } = o;", "Rest element must be last element");
});
});

View File

@@ -1,6 +1,7 @@
var Uglify = require('../../');
var assert = require("assert");
var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code;
function read(path) {
return readFileSync(path, "utf8");
@@ -20,6 +21,65 @@ describe("minify", function() {
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));
});
it("should not parse invalid use of reserved words", function() {
assert.strictEqual(Uglify.minify("function enum(){}").error, undefined);
assert.strictEqual(Uglify.minify("function static(){}").error, undefined);
assert.strictEqual(Uglify.minify("function super(){}").error.message, "Unexpected token: name (super)");
assert.strictEqual(Uglify.minify("function this(){}").error.message, "Unexpected token: name (this)");
});
describe("keep_quoted_props", function() {
it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -71,6 +131,17 @@ describe("minify", function() {
assert.strictEqual(result.code,
'a["foo"]="bar",a.a="red",x={"bar":10};');
});
it("Should not mangle quoted property within dead code", function() {
var result = Uglify.minify('({ "keep": 1 }); g.keep = g.change;', {
mangle: {
properties: {
keep_quoted: true
}
}
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "g.keep=g.g;");
});
});
describe("inSourceMap", function() {
@@ -227,4 +298,34 @@ describe("minify", function() {
assert.strictEqual(result.code, "alert({bar:42});");
});
});
describe("duplicated block-scoped declarations", function() {
[
"let a=1;let a=2;",
"let a=1;var a=2;",
"var a=1;let a=2;",
"let[a]=[1];var a=2;",
"let a=1;var[a]=[2];",
"let[a]=[1];var[a]=[2];",
"const a=1;const a=2;",
"const a=1;var a=2;",
"var a=1;const a=2;",
"const[a]=[1];var a=2;",
"const a=1;var[a]=[2];",
"const[a]=[1];var[a]=[2];",
].forEach(function(code) {
it(code, function() {
var result = Uglify.minify(code, {
compress: false,
mangle: false
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, code);
result = Uglify.minify(code);
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: a redeclared");
});
});
});
});

View File

@@ -4,7 +4,11 @@ var assert = require("assert");
describe("Object", function() {
it("Should allow objects to have a methodDefinition as property", function() {
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() {

View File

@@ -61,9 +61,9 @@ describe("String literals", function() {
var tests = [
['"\\76";', ';">";'],
['"\\0"', '"\\0";'],
['"\\08"', '"\\08";'],
['"\\008"', '"\\08";'],
['"\\0008"', '"\\08";'],
['"\\08"', '"\\x008";'],
['"\\008"', '"\\x008";'],
['"\\0008"', '"\\x008";'],
['"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";'],
['"use\\\n strict";\n"\\07";', ';"use strict";"\07";']
];
@@ -75,7 +75,44 @@ describe("String literals", function() {
});
it("Should not throw error when digit is 8 or 9", function() {
assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\08";');
assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\09";');
assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\x008";');
assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\x009";');
});
it("Should not unescape unpaired surrogates", function() {
var code = [];
for (var i = 0; i <= 0xF; i++) {
code.push("\\u000" + i.toString(16));
}
for (;i <= 0xFF; i++) {
code.push("\\u00" + i.toString(16));
}
for (;i <= 0xFFF; i++) {
code.push("\\u0" + i.toString(16));
}
for (; i <= 0xFFFF; i++) {
code.push("\\u" + i.toString(16));
}
code = '"' + code.join() + '"';
var normal = UglifyJS.minify(code, {
compress: false,
mangle: false,
output: {
ascii_only: false
}
});
if (normal.error) throw normal.error;
assert.ok(code.length > normal.code.length);
assert.strictEqual(eval(code), eval(normal.code));
var ascii = UglifyJS.minify(code, {
compress: false,
mangle: false,
output: {
ascii_only: false
}
});
if (ascii.error) throw ascii.error;
assert.ok(code.length > ascii.code.length);
assert.strictEqual(eval(code), eval(ascii.code));
});
});

View File

@@ -1,4 +1,5 @@
var assert = require("assert");
var semver = require("semver");
var uglify = require("../node");
describe("Unicode", function() {
@@ -138,8 +139,34 @@ describe("Unicode", function() {
it("Should parse raw characters correctly", function() {
var ast = uglify.parse('console.log("\\udbff");');
assert.strictEqual(ast.print_to_string(), 'console.log("\udbff");');
assert.strictEqual(ast.print_to_string(), 'console.log("\\udbff");');
ast = uglify.parse(ast.print_to_string());
assert.strictEqual(ast.print_to_string(), 'console.log("\udbff");');
assert.strictEqual(ast.print_to_string(), 'console.log("\\udbff");');
});
if (semver.satisfies(process.version, ">=4")) {
it("Should not unescape unpaired surrogates", function() {
this.timeout(5000);
var code = [];
for (var i = 0; i <= 0x20001; i++) {
code.push("\\u{" + i.toString(16) + "}");
}
code = '"' + code.join() + '"';
[true, false].forEach(function(ascii_only) {
[5, 6].forEach(function(ecma) {
var result = uglify.minify(code, {
compress: false,
mangle: false,
output: {
ascii_only: ascii_only
},
ecma: ecma
});
if (result.error) throw result.error;
if (ecma > 5) assert.ok(code.length > result.code.length);
assert.strictEqual(eval(code), eval(result.code));
});
});
});
}
});

View File

@@ -111,18 +111,22 @@ function run_compress_tests() {
};
if (!options.warnings) options.warnings = true;
}
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
var quoted_props = test.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
test.mangle.properties.reserved = quoted_props;
U.reserve_quoted_keys(input, quoted_props);
}
var cmp = new U.Compressor(options, true);
var output = cmp.compress(input);
output.figure_out_scope(test.mangle);
if (test.mangle || test.mangle_props) {
if (test.mangle) {
U.base54.reset();
output.compute_char_frequency(test.mangle);
}
if (test.mangle) {
output.mangle_names(test.mangle);
}
if (test.mangle_props) {
output = U.mangle_properties(output, test.mangle_props);
if (test.mangle.properties) {
output = U.mangle_properties(output, test.mangle.properties);
}
}
output = make_code(output, output_options);
if (expect != output) {