Compare commits

...

252 Commits

Author SHA1 Message Date
Alex Lam S.L
40ceddb48a v2.8.4 2017-03-02 00:24:49 +08:00
Alex Lam S.L
7aa69117e1 fix corner cases in reduce_vars (#1524)
Avoid variable substitution in the following cases:
- use of variable before declaration
- declaration within conditional code blocks
- declaration within loop body

fixes #1518
fixes #1525
2017-03-02 00:20:53 +08:00
Alex Lam S.L
bff7ad67bb v2.8.3 2017-03-01 15:28:46 +08:00
Alex Lam S.L
c2334baa48 fix crash on missing props to string_template() (#1523)
Patched up `make_node()` without `orig`.

There may be other cases where `start` could be missing, so make it print "undefined" instead of crashing.

fixes #1518
2017-03-01 15:25:26 +08:00
Alex Lam S.L
fb2b6c7c6f v2.8.2 2017-03-01 04:46:12 +08:00
Alex Lam S.L
f5cbe19b75 invert reduce_vars tracking flag (#1519)
Modules like webpack and grunt-contrib-uglify still uses `ast.transform(compressor)` before `Compressor.compress(ast)` was introduced.

Workaround this compatibility issue by deactivating `reduce_vars` in such case.

Also fix use case with omitted `options` when calling `Compressor()`.

fixes #1516
2017-03-01 04:12:10 +08:00
Alex Lam S.L
b34fa11a13 fix evaluate on object getter & setter (#1515) 2017-03-01 02:03:47 +08:00
Alex Lam S.L
320984c5f5 v2.8.1 2017-03-01 00:27:08 +08:00
Alex Lam S.L
4365a51237 temporarily disables reduce_vars (#1517)
... as we investigate #1516
2017-03-01 00:25:43 +08:00
Alex Lam S.L
858e6c78a4 warn & drop #__PURE__ iff IIFE is dropped (#1511)
- consolidate `side-effects` optimisations
- improve string `+` optimisation
- enhance literal & `conditionals` optimisations
2017-02-28 02:25:44 +08:00
Alex Lam S.L
0b0296eb2a v2.8.0 2017-02-27 03:47:54 +08:00
Alex Lam S.L
872270b149 improve error messages (#1506)
- better inheritance of `Error` sub-classes
- mark parse error against source in CLI

closes #235
closes #348
closes #524
closes #1356
closes #1405
2017-02-27 03:40:54 +08:00
kzc
b1c593a041 add harmony branch details in README (#1507) 2017-02-27 01:55:24 +08:00
Alex Lam S.L
13be50a4a9 faster tree transversal (#1462)
- convert `[].forEach()` to for-loops
2017-02-26 05:58:26 +08:00
Alex Lam S.L
16cd5d57a5 consolidate evaluate & reduce_vars (#1505)
- improve marking efficiency
- apply smarter `const` replacement to `var`

fixes #1501
2017-02-26 00:40:33 +08:00
Alex Lam S.L
834f9f3924 update docs for pure_funcs & drop_console (#1503)
closes #1362
closes #1399
2017-02-25 04:13:10 +08:00
Alex Lam S.L
cf0951f726 allow --in-source-map inline (#1490)
- limited to one input file (or `stdin`)
- only works with built-in parser

fixes #520
2017-02-25 04:11:21 +08:00
Ondřej Španěl
852f78491a Avoid using exports when undefined (#1471)
Makes direct usage within web browser easier, even if officially unsupported.
2017-02-24 08:51:24 +08:00
Alex Lam S.L
229e42cdee Merge pull request #1485 from alexlamsl/merge-2.8.0
2.8.0 staging
2017-02-24 07:33:57 +08:00
alexlamsl
4e49302916 enable collapse_vars & reduce_vars by default
- fix corner cases in `const` optimisation
- deprecate `/*@const*/`

fixes #1497
closes #1498
2017-02-24 01:46:57 +08:00
kzc
1e51586996 Support marking a call as pure
A function call or IIFE with an immediately preceding comment
containing `@__PURE__` or `#__PURE__` is deemed to be a
side-effect-free pure function call and can potentially be
dropped.

Depends on `side_effects` option.

`[#@]__PURE__` hint will be removed from comment when pure
call is dropped.

fixes #1261
closes #1448
2017-02-21 14:24:18 +08:00
Ondřej Španěl
d48a3080ac Fix: AST_Accessor missing start / end tokens
fixes #1492
closes #1493
2017-02-21 13:32:16 +08:00
alexlamsl
26fbeece1c fix pure_funcs & improve side_effects
- only drops side-effect-free arguments
- drop side-effect-free parts with discarded value from `AST_Seq` & `AST_SimpleStatement`

closes #1494
2017-02-21 13:31:59 +08:00
alexlamsl
8898b8a0fe clean up max_line_len
- never exceed specified limit
- otherwise warning is shown
- enabled only for final output

closes #1496
2017-02-21 13:29:58 +08:00
alexlamsl
ec64acd2c8 introduce unsafe_proto
- `Array.prototype.slice` => `[].slice`

closes #1491
2017-02-21 13:29:58 +08:00
alexlamsl
ac0b61ed6e remove extraneous spaces between ++/+/--/-
fixes #1377
closes #1488
2017-02-21 13:29:58 +08:00
Anthony Van de Gejuchte
c06a50f338 Add .gitattributes to checkout lf eol style
closes #1487
2017-02-21 13:29:58 +08:00
alexlamsl
09f9ae2de9 improve --beautify bracketize
reduce whitespaces from if-else statements

fixes #1482
closes #1483
2017-02-21 13:29:58 +08:00
alexlamsl
7e6331bb39 add benchmark & JetStream tests
- `test/benchmark.js` measures performance
- `test/jetstream.js` verifies correctness
- configurable mangle/compress/output options

closes #1479
2017-02-21 13:29:58 +08:00
alexlamsl
e275148998 enhance global_defs
- support arrays, objects & AST_Node
- support `"a.b":1` on both cli & API
- emit warning if variable is modified
- override top-level variables

fixes #1416
closes #1198
closes #1469
2017-02-21 13:29:58 +08:00
alexlamsl
974247c8c0 evaluate AST_SymbolRef as parameter
fix invalid boolean conversion now exposed in `make_node_from_constant()`

closes #1477
2017-02-21 13:29:58 +08:00
alexlamsl
a0f4fd390a improve reduce_vars and fix a bug
- update modified flag between compress() passes
- support IIFE arguments
- fix corner case with multiple definitions

closes #1473
2017-02-21 13:29:58 +08:00
alexlamsl
b8b133d91a improve keep_fargs & keep_fnames
- utilise in_use_ids instead of unreferenced()
- drop_unused now up-to-date for subsequent passes

closes #1476
2017-02-21 13:29:58 +08:00
alexlamsl
c525a2b190 fix duplicated test names
previously test cases with the same name would be skipped except for the last one

`test/run-test.js` will now report duplicated names as errors

closes #1461
2017-02-21 13:29:58 +08:00
kzc
6ffbecb72b smarter const replacement taking name length into account
closes #1459
2017-02-21 13:29:58 +08:00
alexlamsl
f0ff6189be clean up negate_iife
- remove extra tree scanning phase for `negate_iife`
- `negate_iife` now only deals with the narrowest form, i.e. IIFE sitting directly under `AST_SimpleStatement`
- `booleans`, `conditionals` etc. will now take care the rest via more accurate accounting
- `a(); void b();` => `a(); b();`

fixes #1288
closes #1451
2017-02-21 13:29:58 +08:00
alexlamsl
6b3c49e458 improve string concatenation
shuffle associative operations to minimise parentheses and aid other uglification efforts

closes #1454
2017-02-21 13:29:57 +08:00
alexlamsl
f584ca8d07 -c sequences=N suboptimal at N expression cutoff
N = 2:
  a;
  b;
  c;
  d;
was:
  a, b;
  c;
  d;
now:
  a, b;
  c, d;

fixes #1455
closes #1457
2017-02-21 13:29:57 +08:00
alexlamsl
ae4db00991 tweak do-while loops
- `do{...}while(false)` => `{...}`
- clean up `AST_While` logic

closes #1452
2017-02-21 13:29:57 +08:00
alexlamsl
100307ab31 fixes & improvements to [].join()
fixes
- [a].join() => "" + a
- ["a", , "b"].join() => "a,,b"
- ["a", null, "b"].join() => "a,,b"
- ["a", undefined, "b"].join() => "a,,b"

improvements
- ["a", "b"].join(null) => "anullb"
- ["a", "b"].join(undefined) => "a,b"
- [a + "b", c].join("") => a + "b" + c

closes #1453
2017-02-21 13:29:57 +08:00
alexlamsl
148047fbbf drop unused: toplevel, assign-only
- assign statement does not count towards variable usage by default
- only works with assignments on the same scope level as declaration
- can be disabled with `unused` set to "keep_assign"
- `toplevel` to drop unused top-level variables and/or functions
- `top_retain` to whitelist top-level exceptions

closes #1450
2017-02-21 13:29:57 +08:00
kzc
d11dca3cf9 fix stray else in compress with conditionals=false
closes #1449
2017-02-21 13:29:57 +08:00
alexlamsl
e5badb9541 enable typeof "undefined" for general use
move out of unsafe, guard corner case with screw_id8 instead

closes #1446
2017-02-18 19:01:42 +08:00
alexlamsl
fa668a28b4 fix corner case in keep_fnames
happens when inner function:
- just below top level
- not referenced
- `unused` is disabled

closes #1445
2017-02-18 19:00:54 +08:00
alexlamsl
686a496b1c remove unused AST_Scope.nesting & AST_SymbolRef.frame
they are computed but never used

closes #1444
2017-02-18 18:59:40 +08:00
alexlamsl
11676f9d72 fix crash in unsafe replacement of undefined
remove extraneous call to AST_SymbolRef.reference()

closes #1443
2017-02-18 18:58:23 +08:00
Anthony Van de Gejuchte
dd31d12a91 Improve optimizing function() { if(c){return foo} bar();}
closes #1437
2017-02-18 18:56:18 +08:00
Mihai Bazon
eb55d8a9bb Merge pull request #1481 from anatdagan/propsmangle_only_identifiers
verify that property names after mangle are legal
2017-02-12 10:59:43 +02:00
Anat Dagan
81f1df14d7 in mangle_names there is a check that the variable name is legal and that it is not a reserved word. This should apply to propsmangle as well. 2017-02-10 14:13:47 +02:00
Alex Lam S.L
7f8d72d9d3 update test (#1441)
improved reduce_vars & binary operands produce more optimal results
2017-01-26 12:59:32 +01:00
Alex Lam S.L
1eaa211e09 fix mangling collision with keep_fnames (#1431)
* fix mangling collision with keep_fnames
fixes #1423

* pass mangle options to figure_out_scope()
bring command-line in line with minify()
2017-01-26 12:18:28 +01:00
Alex Lam S.L
0610c020b1 optimise binary operands with evaluate() (#1427)
- remove call to evaluate() in is_constant() and let nested optimize() does its job instead
- reject RegExp in is_constant() and remove special case logic under collapse_vars
- operands to conditionals optimisation are now always evaluate()-ed
- throw error in constant_value() instead of returning undefined to catch possible bugs, similar to make_node_from_constant()
- optimise binary boolean operators under `evaluate` instead of `conditionals`
2017-01-26 12:16:50 +01:00
Alex Lam S.L
0d7d4918eb augment evaluate to extract within objects (#1425)
- gated by `unsafe`
- replaces previous optimisation specific to String.length
- "123"[0] => 1
- [1, 2, 3][0] => 1
- [1, 2, 3].length => 3
- does not apply to objects with overridden prototype functions
2017-01-26 12:14:18 +01:00
alexlamsl
48284844a4 add missing LHS cases which global_defs should avoid 2017-01-19 21:06:28 +01:00
kzc
ec2e5fa3a2 Have minify() and tests use figure_out_scope() as uglifyjs CLI does
Clarify docs, help and tests for --support-ie8 and screw_ie8=false
2017-01-19 17:14:33 +01:00
Anthony Van de Gejuchte
da17766ddd Add preventive test involving non-ascii function identifiers 2017-01-19 17:13:33 +01:00
Wiktor Kwapisiewicz
0913db8c84 Add note about name mangling when using --mangle-props=unquoted (#1314) 2017-01-19 16:47:10 +01:00
kzc
5c7705fcad remove npm-shrinkwrap.json to work around npm@4.0.2 bug (#1384) 2016-11-30 18:09:52 +01:00
Richard van Velzen
f6372483a0 v2.7.5 2016-11-29 22:29:59 +01:00
Martijn Swaagman
98f330658f Generate source map data from normalized files
If using `inSourceMap` this fix will ensure the copying of `sourcesContent` is based on potentially normalized `sources` values (https://github.com/mozilla/source-map/blob/master/lib/source-map-consumer.js#L304-L309).

For example `normalize` (https://github.com/mozilla/source-map/blob/master/lib/util.js#L80-L123) will rewrite `./dist/mySource.js` to `dist/mySource.js` in the target `_sources` of the `SourceMapConsumer`. As a result `orig_map.sourceContentFor(source, true);` would return `null` since the orginal `source` was no longer available in the consumer. By using the keys generating from the `SourceMapConsumer.constructor` consistency is ensured.
2016-11-29 20:42:56 +01:00
1111hui
a7b3b0d3a5 docs: add doc for option.outFileName 2016-11-29 20:29:12 +01:00
1111hui
0a35acbbe7 feat: add option.outFileName for JS API, if absense, sourceMap.file field will deduced 2016-11-29 20:29:12 +01:00
Ashley (Scirra)
2a9989dd18 Add --mangle-props-debug and fix --mangle-props=unquoted collision
Patch by @AshleyScirra

Based on: PR #1316

Renamed the CLI debug option to --mangle-props-debug

Fixes: #1321 name collision in --mangle-props=unquoted
2016-11-29 20:25:39 +01:00
Anthony Van de Gejuchte
79b98a9fe8 Do not overwrite options.comments + cleanup 2016-11-29 20:24:08 +01:00
Anthony Van de Gejuchte
057de570e6 Pass mangle options to figure_out_scope before mangling in tests 2016-10-27 22:55:49 +02:00
Richard van Velzen
557b3e412f v2.7.4 2016-10-23 21:46:22 +02:00
Anthony Van de Gejuchte
8d74f34373 Don't filter shebangs when using the 'some' comment filter
Also clarify documentation a bit more about using regexp as filter
2016-10-23 21:31:03 +02:00
Jann Horn
266ddd9639 fix uses_arguments handling (broken since 6605d15783)
Using the symbol declaration tracking of UglifyJS doesn't make sense here
anyway, `arguments` always comes from something in the current scope.

fixes #1299
2016-10-23 21:29:18 +02:00
pengzhenqing
e51c6ba380 Add an option for writing inline source map 2016-10-23 21:21:39 +02:00
Richard van Velzen
6389e52305 Remove console.log and add extra test case 2016-10-06 14:11:32 +02:00
Richard van Velzen
e05510f3bc Add an option to wrap IIFEs in parenthesis
For #1307.
2016-10-06 14:11:32 +02:00
kzc
fc9804b909 Fix (typeof side_effect()) in boolean context
Fixes #1289 with suggestion by @rvanvelzen
2016-10-06 13:50:11 +02:00
alexlamsl
4761d07e0b Optimize unmodified variables 2016-10-01 11:36:11 +02:00
Anthony Van de Gejuchte
0111497fc9 Make all comment options in cli available in js api
Also removing more code within "loop" while at it.
2016-09-06 17:54:45 +02:00
Mihai Bazon
7d8dea3b26 Merge pull request #1277 from kzc/fix-string-plus-opt
Account for side effects in `string + expr` optimization
2016-09-01 16:32:22 +03:00
kzc
25fc02743a Account for side effects in string + expr optimization 2016-09-01 09:24:56 -04:00
kzc
0bd8053524 implement optimization: (x = 2 * x) ---> (x *= 2) 2016-08-30 08:43:02 -04:00
Richard van Velzen
1a78bbcd23 v2.7.3 2016-08-17 20:34:27 +02:00
Richard van Velzen
8430123e9d Fix negate_iife transform to return a correct tree for nested IIFEs
Fix for #1256, partially reverts d854523783
2016-08-17 11:55:59 +02:00
Richard van Velzen
614db97cca v2.7.2 2016-08-17 08:51:23 +02:00
kzc
d854523783 Fix negate_iife regression #1254 2016-08-17 01:29:34 -04:00
Richard van Velzen
781f26eda1 v2.7.1 2016-08-14 22:02:01 +02:00
Timothy Gu
37f4395cc0 Add missing { in README
Also fix a trivial style mistake.
2016-08-14 21:52:39 +02:00
kzc
de619ae5a6 Fix --mangle-props and --mangle-props=unquoted
Fixes: #1247

Fix --mangle-props and --name-cache inconsistency.
AST_Dot and AST_Sub properties are now mangled by --mangle-props
without regard to being used in an assignment statement.

Note: if --mangle-props is used then *all* javascript files used must
be uglified with the same mangle options.

Fix the ignore_quoted=true mangle option, also known as
`--mangle-props=unquoted`.  If a given property is quoted anywhere
it will not be mangled in any quoted or non-quoted context.
2016-08-14 21:51:25 +02:00
kzc
86859f6d7e Additional object literal property tests 2016-08-14 21:49:43 +02:00
kzc
dcdcfe4d39 Add input file glob support to minify() 2016-08-14 21:46:38 +02:00
kzc
72306b9885 Add simple file globbing to bin/uglifyjs for Windows 2016-08-14 21:46:38 +02:00
Lucas Wiener
38756b1f26 Moved test input files to test/input. 2016-08-14 21:40:14 +02:00
Lucas Wiener
85a09fc3b6 Added test for #1236 2016-08-14 21:40:14 +02:00
Lucas Wiener
307b88d6cc Fixed sourceMapIncludeSources and inSourceMap = string combination of the UglifyJS.minify function. 2016-08-14 21:40:14 +02:00
kzc
fb049d3a81 Fix unneeded parens around unary args in new expression. 2016-08-14 21:38:38 +02:00
kzc
67cca43358 Test reparsing test/compress/*.js output 2016-08-14 21:27:23 +02:00
Anthony Van de Gejuchte
642273c290 Legacy octal integer strict mode fixes 2016-07-21 14:42:16 +02:00
Richard van Velzen
e8b23c7798 Build with AppVeyor on windows 2016-07-17 20:00:41 +02:00
homuler
9edbe93df5 Fix the document of keep_fnames option 2016-07-17 19:50:48 +02:00
Yotam Spenser
af37ecafe1 Source map URL override from programmatic API 2016-07-17 19:39:08 +02:00
Lauri Pokka
41a9329409 lib/sourcemap.js: Copy sourceContent from old souce-map to the new source-map. Should fix #882 2016-07-17 19:36:15 +02:00
Anthony Van de Gejuchte
7eb52d2837 Keep const in own scope while compressing
- Fixes #1205
- Fix provided by @kzc
2016-07-15 13:20:52 +02:00
kzc
eb63fece2f Fix mangle with option keep_fnames=true for Safari.
Fixes: #1202
2016-07-15 13:18:14 +02:00
Anthony Van de Gejuchte
2d8af8947e Fix error style for regex errors 2016-07-15 13:14:30 +02:00
Anthony Van de Gejuchte
2650182f47 Backport mocha with test from harmony 2016-07-04 00:51:09 +02:00
Richard van Velzen
572b97b0bb v2.7.0 2016-07-03 21:46:14 +02:00
Anthony Van de Gejuchte
698705a820 Don't convert all strings to directives from moz-ast 2016-07-03 12:36:57 +02:00
Richard van Velzen
debc525fa1 Introduce a test that tests the --self build 2016-07-01 09:46:05 +02:00
kzc
5576e2737a Document that the smallest sequences optimization length is 2
and a sequences value of 1 is considered to be `true` - which
will be set to the default value of 200.
2016-07-01 09:41:31 +02:00
kzc
b40d5de69c Change the default sequences limit to 200 to speed up compress.
Has little or no impact on minification size in the majority of
cases but can speed up rollup builds significantly.

This sequences change also has the beneficial side effect of avoiding
"stack size exceeded" errors on very large input files.

The user is free to alter the sequences limit if they are so inclined.
The previous sequences limit was 2000. 20 is often sufficient.
2016-07-01 09:41:31 +02:00
kzc
b7ef7840f3 Allow sequences maximum length to be user configurable. 2016-07-01 09:41:31 +02:00
Geraint
85924bb32e Allow input files to be map (url->filename) 2016-06-30 22:23:59 +02:00
Anthony Van de Gejuchte
a97690fc72 Various LineTerminator changes
* Escaped newlines should also produce SyntaxError
* Fix multiline comment parsing and add tests
* Adapt makePredicate to handle \u2028 and \u2029
* Move up nlb check in regex so it's checked before any escape handling
* Change error messages to conform ecma standard
* Find_eol not recornizing \u2028 and \u2029 as line terminator
* Remove \u180e as it is removed in unicode 6.3.0 from the category zs
2016-06-30 22:12:50 +02:00
kzc
02c638209e Enable --screw-ie8 by default.
catch identifier is mangled correctly for ES5 standards-compliant JS engines by default.

Unconditionally use the ie8 if/do-while workaround whether or not --screw-ie8 is enabled.

To support non-standard ie8 javascript use: uglifyjs --support-ie8
2016-06-30 21:49:48 +02:00
iliashk
030611b729 Add Node API documentation for mangling options 2016-06-30 21:45:25 +02:00
kzc
335b72df03 Fix spidermonkey AST (ESTree) export and import, Array holes
Fixes: #1156 #1161

Also add test to exercise Uglify after spidermonkey export/import of itself.
2016-06-30 21:44:12 +02:00
Anthony Van de Gejuchte
3a7d53f3cf Move OctalEscapeSequence to read_escape_char
This should simplify and improve implementation, make it easier to
implement template strings, and keep master a bit more in sync with
harmony.

Previous implementation wasn't broken, though the loop gave me the
impression it could read infinite numbers and annoyed me a bit. It was
also slightly unnecessary because the lookup involved only 3 characters.
2016-06-30 21:42:15 +02:00
Richard van Velzen
9676167aac v2.6.4 2016-06-22 12:24:31 +02:00
Mihai Bazon
1840a0b282 Merge pull request #1155 from kzc/issue_1154
Fix conditional expressions of form (x ? -1 : -1)
2016-06-21 23:14:05 +03:00
kzc
ace8aaa0f4 Fix conditional expressions of form (x ? -1 : -1)
Fixes #1154, #1153
2016-06-21 14:52:13 -04:00
kzc
0c003c92a8 Don't replace undefined, NaN and Infinity within with scope 2016-06-21 10:53:29 +02:00
Anthony Van de Gejuchte
85fbf86d7b Keep master in sync with harmony
* Do not mangle when no mangle is required
 * Improve use_asm reset while printing code
2016-06-20 18:42:17 +02:00
Richard van Velzen
aa82027a17 Don't assume DEBUG is defined when exporting --self
Potential fix for #1148
2016-06-20 08:40:45 +02:00
Richard van Velzen
55c592dd43 v2.6.3 2016-06-19 21:56:06 +02:00
Asia
fc1abd1c11 Document the except option to mangle
Added documentation for the `except` option to the `mangle` option in the API reference.
2016-06-19 21:17:31 +02:00
Shrey Banga
e645ba84cf Respect quote style in object literals
The option added in fbbaa42ee5 wasn't
being respected inside object literals, so quoted property names would
still be stripped out with this option.

This is mostly a corner-case, but useful when the output is passed to
something like the Closure compiler, where quoted property names can be
used to prevent mangling.
2016-06-19 21:13:31 +02:00
Anthony Van de Gejuchte
6c99816855 Normalize error messages 2016-06-19 21:08:34 +02:00
Anthony Van de Gejuchte
2149bfb707 Don't mix strings with directives in output
* Don't interpret strings with escaped content as directive
 * Don't interpret strings after empty statement as directive
 * Adapt output to prevent strings being represent as directive
 * Introduce UGLIFY_DEBUG to allow internal testing like EXPECT_DIRECTIVE
2016-06-19 20:59:17 +02:00
Anthony Van de Gejuchte
d7971ba0e4 Fix test262 failures related to <, <=, in and instanceof
Fixed-by: @kzc
2016-06-15 23:11:08 +02:00
Anthony Van de Gejuchte
5c4cfaa0a7 Re-add parens after new expression in beautify mode 2016-06-12 20:03:48 +02:00
Anthony Van de Gejuchte
bb9c9707aa Don't allow with statements in strict mode 2016-06-12 19:08:16 +02:00
Anthony Van de Gejuchte
6c8e001fee Stop dropping args in new expressions 2016-06-12 17:17:17 +02:00
Richard van Velzen
9c53c7ada7 Fix octal string strict mode tests 2016-06-12 14:35:43 +02:00
David Bau
f99b7b630d Escape null characters as \0 unless followed by 0-7. 2016-06-12 14:32:32 +02:00
Anthony Van de Gejuchte
ea31da2455 Don't drop unused if scope uses with statement
Fix provided by @kzc
2016-06-12 14:30:28 +02:00
Anthony Van de Gejuchte
4d7746baf3 Throw errors in strict mode for octal strings
Adds a directive tracker for the parser/tokenizer to
allow parsing depending on directive context.
2016-06-12 14:27:08 +02:00
Anthony Van de Gejuchte
31d5825a86 Catch errors when compression test fails to parse 2016-06-09 21:12:15 +02:00
Anthony Van de Gejuchte
8287ef6781 Fix uglify attempting to rewrite invalid new expressions 2016-06-08 19:45:21 +02:00
ChALkeR
5cb5305cf3 Export tokenizer function
In uglify-js@1, both parser and tokenizer methods were exported

This allows to use tokenizer() separately, e.g. to wrap or override it, as
parse() method accepts not only text, but also tokenized functions.
2016-06-07 12:25:16 +03:00
Anthony Van de Gejuchte
00ad57e393 Do not allow newlines in regex 2016-06-05 17:02:19 +02:00
kzc
09d5707a8a collapse_vars: Do not consider RegExp literals to be constants
Fixes #1100
2016-05-27 00:03:51 -04:00
kzc
1e390269d4 Optimize if_return for single if/return cases.
Fixes #1089
2016-05-24 17:54:08 +02:00
Richard van Velzen
bc49dfd27a Completely allow evaluating -0 2016-05-24 17:50:29 +02:00
Richard van Velzen
27eedbc302 Never produce -0 when evaluating expressions (like -"")
Fix for #1085. The major case was already there, but more expressions can result in -0.
2016-05-17 22:34:38 +02:00
kzc
5f464b41e2 Simplify iife new fix
as suggested by @rvanvelzen.

Added a test for IIFEs in nested contexts.
2016-05-15 19:12:17 -04:00
kzc
bcc1318d4b Do not apply negate_iife optimization to new expression 2016-05-09 03:19:28 -04:00
kzc
a0e03c9df4 Retain comments before AST_Constants during mangle. 2016-05-04 20:11:45 +02:00
Anthony Van de Gejuchte
6641dcafb6 Fix regression causing tests to fail on windows 2016-05-04 20:05:51 +02:00
kzc
d2945744f2 Workaround for process.exit() tty output truncation.
Fixes #1055
2016-05-04 20:04:48 +02:00
Anthony Van de Gejuchte
35bc716625 Add node 6 to travis 2016-05-04 20:03:39 +02:00
kzc
f39fd3d583 Handle CR line endings in comments.
Fixes #1050
2016-05-04 20:02:29 +02:00
Mihai Bazon
65887d9a56 Merge pull request #1053 from rvanvelzen/hoist_if_return_funs
Hoist functions when reversing if (x) return; ... vs. if (!x) ...
2016-04-26 22:09:52 +03:00
Richard van Velzen
e9224ab444 Add test cases for slightly more esoteric cases 2016-04-26 11:49:55 +02:00
Richard van Velzen
4d9a085687 Add test case for hoisting a single function 2016-04-26 11:43:03 +02:00
Richard van Velzen
4fe630431c Hoist functions when reversing if (x) return; ... vs. if (!x) ...
Fixes #1052
2016-04-23 23:48:33 +02:00
kzc
c55dd5ed74 Add passes compress option. Fix duplicate compress warnings. 2016-04-19 20:05:33 +02:00
kzc
e4fa4b109a Parse comments without recursion to avoid RangeError.
Fixes #993
2016-04-16 02:02:47 -04:00
Richard van Velzen
4b4528ee05 Prevent endless recursion when evaluating self-referencing consts
Fix #1041
2016-04-13 15:03:31 +02:00
Richard van Velzen
187a0caf9d Add base54.reset() to compress tests
Without this reset, char counts bleed to next tests. One test had a bad expect clause.
2016-04-12 20:08:09 +02:00
Mihai Bazon
b5a7a231f7 Actually limit sequence length.
Fix #1038
2016-04-12 14:17:24 +03:00
kzc
3907a5e3b2 Fix warnings for referenced non-hoisted functions.
Fixes #1034

Also added `expect_warnings` functionality to test framework.
2016-04-11 18:15:20 +02:00
Mihai Bazon
b434b75b36 Merge pull request #1032 from kzc/member
Simplify member(name, array) implementation.
2016-04-08 00:32:14 +03:00
kzc
c70d176f35 Simplify member(name, array) implementation. 2016-04-07 09:57:30 -04:00
Mihai Bazon
9317237372 Avoid using inherited hasOwnProperty
Fix #1031
2016-04-07 13:16:22 +03:00
kzc
98434258d0 Optimize ternaries with boolean consequent or alternative.
Fixes #511
2016-04-02 17:22:12 +02:00
kzc
45ddb9caeb Speedup unused compress option for already minified code
Fixes: #321 #917 #1022
2016-03-28 17:58:50 -04:00
Sebastien Daniel
9bcf702a6e added documentation on conditional compilation using API 2016-03-27 19:42:52 +02:00
Mihai Bazon
f68de86a17 Merge pull request #1011 from kzc/dont-produce-let-in-mangle
Do not produce `let` as a variable name in mangle.
2016-03-24 18:16:26 +02:00
Mihai Bazon
c3c7587796 Merge pull request #1019 from kzc/escape-ascii-only
Escape all ASCII control characters within strings for ascii_only
2016-03-24 18:08:57 +02:00
kzc
07bb7262d0 Escape all ASCII control characters within strings when using ascii_only.
Fixes #1017.

Tab characters within strings are now output as `\t` in all output modes.
2016-03-24 11:51:54 -04:00
kzc
21befe583f Attempt to increase timeout for mocha let test. 2016-03-15 11:44:09 -04:00
kzc
a9d4a6291b Do not produce let as a variable name in mangle.
Would previously occur in large generated functions with 21,000+ variables.
Fixes #986.
2016-03-15 11:20:32 -04:00
philippsimon
ee6c9fabb7 Fix: Uglified Number.prototype functions on big numbers 2016-03-14 12:41:06 +01:00
kzc
102d1b9137 #877 Ignore mangle sort option 2016-02-27 15:33:10 -05:00
Mihai Bazon
294861ba96 v2.6.2 2016-02-22 21:39:14 +02:00
kzc
11b0efdf84 boolean_expression ? true : false --> boolean_expression 2016-02-22 17:59:36 +01:00
kzc
5486b68850 Take operator || precendence into account for AST_If optimization.
Fixes #979.
2016-02-21 12:05:02 -05:00
Richard van Velzen
bdd8e34f63 Allow --no-* options to disable their respective parameter
Fixes #974 and #972
2016-02-17 20:04:45 +01:00
alexlamsl
6547437725 preserve ThisBinding for side_effects 2016-02-17 19:34:01 +01:00
Richard van Velzen
9662228f6a Don't compress (0, eval)() to eval() 2016-02-16 19:00:48 +01:00
alexlamsl
31a9b05c96 Preserve ThisBinding in conditionals & collapse_vars
Fixes #973
2016-02-16 18:47:49 +01:00
Richard van Velzen
63b01fe8f9 Merge pull request #948 from kzc/collapse_vars_doc
collapse_vars: document the compress option in README
2016-02-11 22:13:30 +01:00
sergeyv
7a4ed9d200 Revert "using the original sourcemap as the base"
This reverts commit ad18689d92.

Reason for revert: introduce issue #882

Currently, generated sourcemap contains copy of all existing mappings and adds new mappings from uglified code to original one.
However, previous mapping are no longer valid and shouldn't be added.
2016-02-10 10:19:39 +01:00
Richard van Velzen
d5c651a5e5 Allow cli options to be specified in separate definitions
Fix for #963. This allows stuff like `--define a=1 --define b=1` besides only `--define a=1,b=1`
2016-02-10 10:14:46 +01:00
Martii
cdba43cfa4 Create and map bare-returns into new parse property name 2016-02-08 10:45:42 +01:00
Boris Letocha
a123e232b9 Fixes #951 missing export for SymbolDef 2016-01-31 21:41:38 +01:00
Mihai Bazon
601780acc1 Merge pull request #949 from kzc/collapse_vars_conditions
collapse_vars: fix if/else and ternary operator side effects
2016-01-29 18:05:39 +02:00
kzc
7c3fee9e31 collapse_vars: avoid replacement across AST_Case nodes to be on safe side even though no issues seen. 2016-01-29 10:35:07 -05:00
kzc
929de2b0de collapse_vars: fix if/else and ternary operator side effects 2016-01-28 12:17:06 -05:00
kzc
12e6ad326c collapse_vars: small change to README 2016-01-28 11:04:30 -05:00
kzc
00c8d1d241 collapse_vars: document option in README 2016-01-28 11:01:17 -05:00
kzc
af2472d85e collapse_vars: fix bug in repeated var defs of same name 2016-01-28 16:48:50 +01:00
Bryan Rayner
3eb9101918 Add mangleProperties documentation to README
Add additional documentation to mangleProperties.
2016-01-27 14:24:32 -06:00
kzc
0a38a688f9 fix bug in collapse_vars for right side of "||" and "&&" 2016-01-27 14:18:46 -05:00
kzc
f4c2ea37bf Collapse single use var definitions
Fix #721
2016-01-27 11:48:15 +02:00
Mihai Bazon
915f907186 Add start/end in the arguments definition
(keeps my https://github.com/mishoo/jsinfo.el working)
2016-01-27 11:36:03 +02:00
Jeremy Marzka
799509e145 Added a mangle properties option 2016-01-26 22:10:08 +01:00
Richard van Velzen
b5a7197ae5 Merge pull request #928 from STRML/constPragma
Mark vars with /** @const */ pragma as consts so they can be eliminated.
2016-01-20 19:04:36 +01:00
Samuel Reed
1b703349cf Tighten up @const regex. 2016-01-20 11:35:45 -06:00
Samuel Reed
4a7179ff91 Simplify by skipping extra tree walk. 2016-01-20 11:03:41 -06:00
Samuel Reed
f97da4294a Use TreeWalker for more accurate @const results and update tests 2016-01-20 10:54:00 -06:00
Samuel Reed
918c17bd88 Update README for /** @const */ 2016-01-19 13:24:36 -06:00
Samuel Reed
8b71c6559b Mark vars with /** @const */ pragma as consts so they can be eliminated.
Fixes older browser support for consts and allows more flexibility
in dead code removal.
2016-01-19 13:23:02 -06:00
Anthony Van de Gejuchte
26641f3fb2 Allow operator names as getters/setters
Fixes #919

Fix provided by @kzc
2016-01-19 19:28:51 +01:00
Anthony Van de Gejuchte
ebe118dc79 Add keywords to package.json
Should hopefully bump up on the results of the npm site when searching `uglify`
2016-01-19 19:26:55 +01:00
Anthony Van de Gejuchte
70e5b6f15b Add some tests for comment-filters through api
Also never bother comment options to filter comment5/shebang comments
as they have their custom filter.
2016-01-19 19:14:19 +01:00
Richard van Velzen
57e0fafd5c Merge pull request #918 from avdg/fix-arguments-handling
Never mangle arguments and keep them in their scope
2016-01-18 18:35:48 +01:00
Anthony Van de Gejuchte
8439c8ba98 Make arguments test slightly more strict 2016-01-15 00:04:05 +01:00
Anthony Van de Gejuchte
5c4e470d43 Add scope test for arguments 2016-01-14 22:32:46 +01:00
Anthony Van de Gejuchte
6605d15783 Never mangle arguments and keep them in their scope
Fixes #892

Helped-by: kzc
2016-01-14 19:45:52 +01:00
Richard van Velzen
ac8db977b9 Merge pull request #905 from avdg/unit-tests
Add unit tests
2016-01-14 08:54:40 +01:00
Anthony Van de Gejuchte
88b77ddaa9 Add test case for line continuation 2016-01-13 00:34:56 +01:00
Mihai Bazon
fe4e9f9d97 Fix hoisting the var in ForIn
Close #913
2016-01-05 13:56:52 +02:00
Anthony Van de Gejuchte
8c6af09ae0 Add mocha tests 2015-12-27 22:38:20 +01:00
Anthony Van de Gejuchte
6f3e35bb3f Fix ch that could contain other newline characters 2015-12-27 22:24:37 +01:00
Anthony Van de Gejuchte
174404c0f3 Do not allow newlines in string literals 2015-12-26 15:08:37 +01:00
Richard van Velzen
60c4030a4d Merge pull request #874 from kzc/fix-conditionals
#873 Fix `conditionals` optimizations with default compress options
2015-12-26 14:28:33 +01:00
Richard van Velzen
ac810dc07a Merge pull request #896 from avdg/do-while-semicolon
Semicolon after do...while statement is optional
2015-12-26 14:26:22 +01:00
Anthony Van de Gejuchte
0cabedc526 Disable loop optimization for parse-only tests 2015-12-18 19:20:56 +01:00
Anthony Van de Gejuchte
5cd26c005b Add tests 2015-12-18 14:39:48 +01:00
Anthony Van de Gejuchte
bd99b00413 Semicolon after do...while statement is optional 2015-12-17 23:02:35 +01:00
Richard van Velzen
9e2f9f7910 Merge pull request #879 from ReadmeCritic/master
Update README URLs based on HTTP redirects
2015-12-07 19:04:56 +01:00
ReadmeCritic
e87c77ed41 Update README URLs based on HTTP redirects 2015-11-27 08:46:55 -08:00
kzc
774bda13cd #873 Fix conditionals optimizations with default compress options 2015-11-24 13:27:50 -05:00
Mihai Bazon
15b5f70338 v2.6.1 2015-11-16 12:10:47 +02:00
Mihai Bazon
7f48d5b33c Fix endless loop
Close #866
2015-11-16 12:08:24 +02:00
Mihai Bazon
b6968b6bd2 Limit max iterations for tighten_body
Ref #866
2015-11-16 12:08:24 +02:00
Richard van Velzen
08b80302eb Merge pull request #864 from plievone/patch-1
Fix docs for keep_fargs
2015-11-14 12:04:49 +01:00
plievone
645626ebe8 Fix docs for keep_fargs
Compression options `keep_fargs` and `unsafe` were decoupled in v.2.5.0 (commit 5fd1245), so document actual keep_fargs default.
2015-11-14 11:38:00 +02:00
Mihai Bazon
d895c09c70 v2.6.0 2015-11-12 12:46:28 +02:00
Mihai Bazon
08623aa6a7 Fix output for "use asm" code from SpiderMonkey AST
(will only work properly if the SM tree contains "raw" properties for
Literal number nodes)
2015-11-12 12:18:25 +02:00
Mihai Bazon
c898a26117 Build label def/refs info when figuring out scope
Fix #862
2015-11-12 11:48:06 +02:00
Mihai Bazon
619adb0308 Replace util.error with console.log 2015-11-12 11:47:37 +02:00
Mihai Bazon
7691bebea5 Rework has_directive
It's now available during tree walking, i.e. walker.has_directive("use
asm"), rather than as part of the scope.  It's thus no longer necessary
to call `figure_out_scope` before codegen.  Added special bits in the
code generator to overcome the fact that it doesn't inherit from
TreeWalker.

Fix #861
2015-11-11 22:15:25 +02:00
Mihai Bazon
3c4346728e Merge pull request #854 from kzc/moz-regexp-2
Have mozilla AST RegExpLiteral parser use regex.pattern and regex.flags
2015-11-10 10:12:30 +02:00
Mihai Bazon
18d37ac761 Fix parsing invalid input
i.e. `x = 1.xe` — because parseFloat("1.xe") returns 1, this parsed as
`x = 1`.

Ref #857
2015-11-09 13:15:20 +02:00
Richard van Velzen
63d35f8f6d Prevent ReDoS by not using a regexp to verify floating point numbers
`parseFloat` will return `NaN` for invalid numbers anyway, which is the check used to throw the parse error.

Fixes #857
2015-11-09 11:28:27 +01:00
kzc
7dbe961b2d simplify mozilla AST RegExpLiteral token parse and handle corner cases of regex.pattern better 2015-11-02 13:10:37 -05:00
kzc
94c4daaf9e Have mozilla AST RegExpLiteral parser use regex.pattern and regex.flags rather than non-standard raw property. 2015-11-02 12:24:09 -05:00
kzc
37ee9de902 rename To_Moz_Literal to To_Moz_RegExp 2015-11-01 10:20:42 -05:00
kzc
83db98ad3b Fixed RegExp literal in mozilla AST generation/output and added a --dump-spidermonkey-ast flag 2015-11-01 01:02:52 -04:00
kzc
bd0ae6569f return undefined optimization no longer uses return_void_0 option 2015-10-29 08:19:12 +01:00
kzc
841a661071 more tests for return undefined optimization 2015-10-29 08:19:12 +01:00
kzc
7491d07666 optimize return undefined and return void 0 2015-10-29 08:19:12 +01:00
Richard van Velzen
335e349314 Allow specification beautify options in tests
Caught an error in #847 as well - `output` wasn't passed anywhere which led to an exception. `options` was available though.
2015-10-28 20:50:01 +01:00
Richard van Velzen
2a88d07b3a Stop building for io.js 2015-10-28 20:36:03 +01:00
Michael Ficarra
a887cde9f2 fixes #845: \v escaping should be restricted to "screw_ie8" mode 2015-10-27 09:05:21 -07:00
Fábio Santos
b5623b19d4 Fix #836 2015-10-20 19:48:56 +01:00
startswithaj
6b2861e086 Make_string was missing \v and wasnt reversing vertical tabs even though read_escaped_char coverts them 2015-10-15 17:42:16 +10:00
Damian Krzeminski
d5138f7467 add --pure-funcs option
it has the same effect as specifying `pure_funcs` in `--compressor`
option, however it's much easier to use

instead of:

    --compressor 'pure_func=["Math.floor","debug","console.logTime"]'

it's now possible:

    --compressor --pure-funcs Math.floor debug console.logTime

fixes #684
2015-10-13 21:24:14 -04:00
Damian Krzeminski
eac67b2816 upgrade yargs 3.5.4 -> 3.10.0
we need a version with better support for 'array' params
see: https://github.com/bcoe/yargs/pull/164
2015-10-13 21:01:36 -04:00
Mihai Bazon
ce10072824 Merge pull request #829 from kzc/html_comment_ops
Fix other operator output producing <!-- or -->
2015-10-13 09:59:40 +03:00
kzc
dff54a6552 Fix other operator output related to <!-- or --> 2015-10-13 01:17:10 -04:00
Mihai Bazon
1940fb682c Fix tests 2015-10-12 10:27:00 +03:00
Mihai Bazon
17eef5a3c2 Only encode <!-- and --> in strings when inline_script 2015-10-12 10:21:22 +03:00
kzc
9f1f21b810 Output -- > instead of --> in expressions. Escape <!-- and --> within string literals. 2015-10-12 10:19:17 +03:00
118 changed files with 13676 additions and 1300 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.js text eol=lf

View File

@@ -1,10 +1,10 @@
language: node_js
before_install: "npm install -g npm"
node_js:
- "iojs"
- "0.12"
- "0.10"
- "4"
- "6"
matrix:
fast_finish: true
sudo: false

271
README.md
View File

@@ -10,6 +10,9 @@ There's also an
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
Chrome and probably Safari).
Note: release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
Install
-------
@@ -62,12 +65,18 @@ The available options are:
--source-map-include-sources Pass this flag if you want to include the
content of source files in the source map as
sourcesContent property.
--source-map-inline Write base64-encoded source map to the end of js output.
--in-source-map Input source map, useful if you're compressing
JS that was generated from some other original
code.
--screw-ie8 Pass this flag if you don't care about full
compliance with Internet Explorer 6-8 quirks
(by default UglifyJS will try to be IE-proof).
--screw-ie8 Use this flag if you don't wish to support
Internet Explorer 6/7/8.
By default UglifyJS will not try to be IE-proof.
--support-ie8 Use this flag to support Internet Explorer 6/7/8.
Equivalent to setting `screw_ie8: false` in `minify()`
for `compress`, `mangle` and `output` options.
Note: `--support-ie8` may generate incorrect code
for `try`/`catch` in ES5 compliant browsers.
--expr Parse a single expression, rather than a
program (for parsing JSON)
-p, --prefix Skip prefix for original filenames that appear
@@ -81,10 +90,9 @@ The available options are:
-b, --beautify Beautify output/specify output options.
-m, --mangle Mangle names/pass mangler options.
-r, --reserved Reserved names to exclude from mangling.
-c, --compress Enable compressor/pass compressor options. Pass
options like -c
hoist_vars=false,if_return=false. Use -c with
no argument to use the default compression
-c, --compress Enable compressor/pass compressor options, e.g.
`-c 'if_return=false,pure_funcs=["Math.pow","console.log"]'`
Use `-c` with no argument to enable default compression
options.
-d, --define Global definitions
-e, --enclose Embed everything in a big function, with a
@@ -95,8 +103,8 @@ The available options are:
"@preserve". You can optionally pass one of the
following arguments to this flag:
- "all" to keep all comments
- a valid JS regexp (needs to start with a
slash) to keep only comments that match.
- a valid JS RegExp like `/foo/` or `/^!/` to
keep only matching comments.
Note that currently not *all* comments can be
kept when compression is on, because of dead
code removal or cascading statements into
@@ -125,15 +133,30 @@ The available options are:
--noerr Don't throw an error for unknown options in -c,
-b or -m.
--bare-returns Allow return outside of functions. Useful when
minifying CommonJS modules.
minifying CommonJS modules and Userscripts that
may be anonymous function wrapped (IIFE) by the
.user.js engine `caller`.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--reserved-file File containing reserved names
--reserve-domprops Make (most?) DOM properties reserved for
--mangle-props
--mangle-props Mangle property names
--mangle-props Mangle property names (default `0`). Set to
`true` or `1` to mangle all property names. Set
to `unquoted` or `2` to only mangle unquoted
property names. Mode `2` also enables the
`keep_quoted_props` beautifier option to
preserve the quotes around property names and
disables the `properties` compressor option to
prevent rewriting quoted properties with dot
notation. You can override these by setting
them explicitly on the command line.
--mangle-regex Only mangle property names matching the regex
--name-cache File to hold mangled names mappings
--pure-funcs Functions that can be safely removed if their
return value is not used, e.g.
`--pure-funcs Math.floor console.info`
(requires `--compress`)
```
Specify `--output` (`-o`) to declare the output file. Otherwise the output
@@ -188,11 +211,6 @@ input files from the command line.
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `sort` — to assign shorter names to most frequently used variables. This
saves a few hundred bytes on jQuery before gzip, but the output is
_bigger_ after gzip (and seems to happen for other libraries I tried it
on) therefore it's not enabled by default.
- `toplevel` — mangle names declared in the toplevel scope (disabled by
default).
@@ -274,6 +292,32 @@ of mangled property names.
Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS.
#### Mangling unquoted names (`--mangle-props=unquoted` or `--mangle-props=2`)
Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
so that it is not mangled throughout the entire script even when used in an
unquoted style (`o.foo`). Example:
```
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props=2 -mc
var o={"foo":1,a:3};o.foo+=o.a,console.log(o.foo);
```
#### Debugging property name mangling
You can also pass `--mangle-props-debug` in order to mangle property names
without completely obscuring them. For example the property `o.foo`
would mangle to `o._$foo$_` with this option. This allows property mangling
of a large codebase while still being able to debug the code and identify
where mangling is breaking things.
You can also pass a custom suffix using `--mangle-props-debug=XYZ`. This would then
mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a
script to identify how a property got mangled. One technique is to pass a
random number on every compile to simulate mangling changing with different
inputs (e.g. as you update the input script with new properties), and to help
identify mistakes like writing mangled keys to storage.
## Compressor options
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
@@ -281,7 +325,14 @@ you can pass a comma-separated list of options. Options are in the form
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
to set `true`; it's effectively a shortcut for `foo=true`).
- `sequences` -- join consecutive simple statements using the comma operator
- `sequences` (default: true) -- join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
to disable. The smallest `sequences` length is `2`. A `sequences` value of `1`
is grandfathered to be equivalent to `true` and as such means `200`. On rare
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `properties` -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
@@ -292,12 +343,22 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: false) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`,
or `valueOf`. This could cause change in execution order after operands in the
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `conditionals` -- apply optimizations for `if`-s and conditional
expressions
- `comparisons` -- apply certain optimizations to binary nodes, for example:
`!(a <= b) → a > b` (only when `unsafe`), attempts to negate binary nodes,
e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
`!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `evaluate` -- attempt to evaluate constant expressions
@@ -307,7 +368,15 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition
- `unused` -- drop unreferenced functions and variables
- `unused` -- drop unreferenced functions and variables (simple direct variable
assignments do not count as references unless set to `"keep_assign"`)
- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the toplevel scope (`false` by default, `true` to drop both unreferenced
functions and variables)
- `top_retain` -- prevent specific toplevel functions and variables from `unused`
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`)
- `hoist_funs` -- hoist function declarations
@@ -321,6 +390,12 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `collapse_vars` -- default `false`. Collapse single-use `var` and `const`
definitions when possible.
- `reduce_vars` -- default `false`. Improve optimization on variables assigned
with and used as constant values.
- `warnings` -- display warnings when dropping unreachable code or unused
declarations etc.
@@ -344,16 +419,21 @@ to set `true`; it's effectively a shortcut for `foo=true`).
overhead (compression will be slower).
- `drop_console` -- default `false`. Pass `true` to discard calls to
`console.*` functions.
`console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `keep_fargs` -- default `false`. Pass `true` to prevent the
- `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
- `keep_fnames` -- default `false`. Pass `true` to prevent the
compressor from mangling/discarding function names. Useful for code relying on
`Function.prototype.name`.
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. Use an
integer argument larger than 1 to further reduce code size in some cases.
Note: raising the number of passes will increase uglify compress time.
### The `unsafe` option
@@ -370,7 +450,6 @@ when this flag is on:
- `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)
- discards unused function arguments (affects `function.length`)
### Conditional compilation
@@ -384,6 +463,8 @@ if (DEBUG) {
}
```
You can specify nested constants in the form of `--define env.DEBUG=false`.
UglifyJS will warn about the condition being always false and about dropping
unreachable code; for now there is no option to turn off only this specific
warning, you can pass `warnings=false` to turn off *all* warnings.
@@ -403,10 +484,27 @@ and build your code like this:
UglifyJS will notice the constants and, since they cannot be altered, it
will evaluate references to them to the value itself and drop unreachable
code as usual. The possible downside of this approach is that the build
will contain the `const` declarations.
code as usual. The build will contain the `const` declarations if you use
them. If you are targeting < ES6 environments which does not support `const`,
using `var` with `reduce_vars` (enabled by default) should suffice.
<a name="codegen-options"></a>
#### Conditional compilation, API
You can also use conditional compilation via the programmatic API. With the difference that the
property name is `global_defs` and is a compressor property:
```js
uglifyJS.minify([ "input.js"], {
compress: {
dead_code: true,
global_defs: {
DEBUG: false
}
}
});
```
## Beautifier options
The code generator tries to output shortest code possible by default. In
@@ -423,7 +521,7 @@ can pass additional arguments that control the code output:
objects
- `space-colon` (default `true`) -- insert a space after the colon signs
- `ascii-only` (default `false`) -- escape Unicode characters in strings and
regexps
regexps (affects directives with non-ascii characters becoming invalid)
- `inline-script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `width` (default 80) -- only takes effect when beautification is on, this
@@ -450,6 +548,8 @@ can pass additional arguments that control the code output:
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
### Keeping copyright notices or other comments
@@ -521,7 +621,7 @@ function uglify(ast, options, mangle) {
// Compression
uAST.figure_out_scope();
uAST = uAST.transform(UglifyJS.Compressor(options));
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
@@ -579,9 +679,26 @@ console.log(result.code); // minified output
console.log(result.map);
```
To generate a source map with the fromString option, you can also use an object:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function () {};"}, {
outSourceMap: "out.js.map",
outFileName: "out.js",
fromString: true
});
```
Note that the source map is not saved in a file, it's just returned in
`result.map`. The value passed for `outSourceMap` is only used to set the
`file` attribute in the source map (see [the spec][sm-spec]).
`result.map`. The value passed for `outSourceMap` is only used to set
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
`outFileName` is only used to set `file` attribute in source map file.
The `file` attribute in the source map (see [the spec][sm-spec]) will
use `outFileName` firstly, if it's falsy, then will be deduced from
`outSourceMap` (by removing `'.map'`).
You can set option `sourceMapInline` to be `true` and source map will
be appended to code.
You can also specify sourceRoot property to be included in source map:
```javascript
@@ -614,6 +731,17 @@ var result = UglifyJS.minify("compiled.js", {
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
no sense otherwise).
To set the source map url, use the `sourceMapUrl` option.
If you're using the X-SourceMap header instead, you can just set the `sourceMapUrl` option to false.
Defaults to outSourceMap:
```javascript
var result = UglifyJS.minify([ "file1.js" ], {
outSourceMap: "out.js.map",
sourceMapUrl: "localhost/out.js.map"
});
```
Other options:
- `warnings` (default `false`) — pass `true` to display compressor warnings.
@@ -621,7 +749,11 @@ Other options:
- `fromString` (default `false`) — if you pass `true` then you can pass
JavaScript source code, rather than file names.
- `mangle` — pass `false` to skip mangling names.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
an object to specify mangling options (see below).
- `mangleProperties` (default `false`) — pass an object to specify custom
mangle property options.
- `output` (default `null`) — pass an object if you wish to specify
additional [output options][codegen]. The defaults are optimized
@@ -630,6 +762,49 @@ Other options:
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compressor options][compressor].
- `parse` (default {}) — pass an object if you wish to specify some
additional [parser options][parser]. (not all options available... see below)
##### mangle
- `except` - pass an array of identifiers that should be excluded from mangling
- `toplevel` — mangle names declared in the toplevel scope (disabled by
default).
- `eval` — mangle names visible in scopes where eval or with are used
(disabled by default).
- `keep_fnames` -- default `false`. Pass `true` to not mangle
function names. Useful for code relying on `Function.prototype.name`.
See also: the `keep_fnames` [compress option](#compressor-options).
Examples:
```javascript
//tst.js
var globalVar;
function funcName(firstLongName, anotherLongName)
{
var myVariable = firstLongName + anotherLongName;
}
UglifyJS.minify("tst.js").code;
// 'function funcName(a,n){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { except: ['firstLongName'] } }).code;
// 'function funcName(firstLongName,a){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { toplevel: true } }).code;
// 'function n(n,a){}var a;'
```
##### mangleProperties options
- `regex` — Pass a RegExp to only mangle certain names (maps to the `--mangle-regex` CLI arguments option)
- `ignore_quoted` Only mangle unquoted property names (maps to the `--mangle-props 2` CLI arguments option)
- `debug` Mangle names with the original name still present (maps to the `--mangle-props-debug` CLI arguments option). Defaults to `false`. Pass an empty string to enable, or a non-empty string to set the suffix.
We could add more options to `UglifyJS.minify` — if you need additional
functionality please suggest!
@@ -648,6 +823,9 @@ properties are available:
- `strict` — disable automatic semicolon insertion and support for trailing
comma in arrays and objects
- `bare_returns` — Allow return outside of functions. (maps to the
`--bare-returns` CLI arguments option and available to `minify` `parse`
other options object)
- `filename` — the name of the file where this code is coming from
- `toplevel` — a `toplevel` node (as returned by a previous invocation of
`parse`)
@@ -687,7 +865,7 @@ toplevel.figure_out_scope()
Like this:
```javascript
var compressor = UglifyJS.Compressor(options);
var compressed_ast = toplevel.transform(compressor);
var compressed_ast = compressor.compress(toplevel);
```
The `options` can be missing. Available options are discussed above in
@@ -732,8 +910,11 @@ which we care about here are `source_map` and `comments`.
#### Keeping comments in the output
In order to keep certain comments in the output you need to pass the
`comments` option. Pass a RegExp or a function. If you pass a RegExp, only
those comments whose body matches the regexp will be kept. Note that body
`comments` option. Pass a RegExp (as string starting and closing with `/`
or pass a RegExp object), a boolean or a function. Stringified options
`all` and `some` can be passed too, where `some` behaves like it's cli
equivalent `--comments` without passing a value. If you pass a RegExp,
only those comments whose body matches the RegExp will be kept. Note that body
means without the initial `//` or `/*`. If you pass a function, it will be
called for every comment in the tree and will receive two arguments: the
node that the comment is attached to, and the comment token itself.
@@ -782,8 +963,26 @@ The `source_map_options` (optional) can contain the following properties:
came from. It can be simply a string in JSON, or a JSON object containing
the original source map.
[acorn]: https://github.com/marijnh/acorn
[acorn]: https://github.com/ternjs/acorn
[source-map]: https://github.com/mozilla/source-map
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
[codegen]: http://lisperator.net/uglifyjs/codegen
[compressor]: http://lisperator.net/uglifyjs/compress
[parser]: http://lisperator.net/uglifyjs/parser
#### Harmony
If you wish to use the experimental [harmony](https://github.com/mishoo/UglifyJS2/commits/harmony)
branch to minify ES2015+ (ES6+) code please use the following in your `package.json` file:
```
"uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony"
```
or to directly install the experimental harmony version of uglify:
```
npm install --save-dev uglify-js@github:mishoo/UglifyJS2#harmony
```
See [#448](https://github.com/mishoo/UglifyJS2/issues/448) for additional details.

24
appveyor.yml Normal file
View File

@@ -0,0 +1,24 @@
environment:
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "4.0"
- nodejs_version: "6.0"
matrix:
fast_finish: true
platform:
- x86
- x64
install:
- ps: Install-Product node $env:nodejs_version $env:platform
- npm install
test_script:
- node --version
- npm --version
- npm test
build: off

View File

@@ -10,6 +10,7 @@ var fs = require("fs");
var path = require("path");
var async = require("async");
var acorn;
var screw_ie8 = true;
var ARGS = yargs
.usage("$0 input1.js [input2.js ...] [options]\n\
Use a single dash to read input from the standard input.\
@@ -22,9 +23,11 @@ mangling you need to use `-c` and `-m`.\
.describe("source-map", "Specify an output file where to generate source map.")
.describe("source-map-root", "The path to the original source to be included in the source map.")
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
.describe("source-map-inline", "Write base64-encoded source map to the end of js output. Disabled by default")
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
.describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).")
.describe("screw-ie8", "Do not support Internet Explorer 6/7/8. This flag is enabled by default.")
.describe("support-ie8", "Support non-standard Internet Explorer 6/7/8 javascript. Note: may generate incorrect code for try/catch in ES5 compliant browsers.")
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
.describe("p", "Skip prefix for original filenames that appear in source maps. \
For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
@@ -44,7 +47,7 @@ Use -c with no argument to use the default compression options.")
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
You can optionally pass one of the following arguments to this flag:\n\
- \"all\" to keep all comments\n\
- a valid JS regexp (needs to start with a slash) to keep only comments that match.\n\
- a valid JS RegExp like `/foo/`or `/^!/` to keep only matching comments.\n\
\
Note that currently not *all* comments can be kept when compression is on, \
because of dead code removal or cascading statements into sequences.")
@@ -69,9 +72,12 @@ You need to pass an argument to this option to specify the name that your module
.describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
.describe("reserved-file", "File containing reserved names")
.describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
.describe("mangle-props", "Mangle property names")
.describe("mangle-props", "Mangle property names (0 - disabled, 1 - mangle all properties, 2 - mangle unquoted properies)")
.describe("mangle-regex", "Only mangle property names matching the regex")
.describe("name-cache", "File to hold mangled names mappings")
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
.describe("wrap-iife", "Wrap IIFEs in parenthesis. Note: this disables the negate_iife compression option")
.alias("p", "prefix")
.alias("o", "output")
@@ -92,6 +98,7 @@ You need to pass an argument to this option to specify the name that your module
.string("beautify")
.string("m")
.string("mangle")
.string("mangle-props-debug")
.string("c")
.string("compress")
.string("d")
@@ -103,11 +110,15 @@ You need to pass an argument to this option to specify the name that your module
.string("p")
.string("prefix")
.string("name-cache")
.array("reserved-file")
.array("pure-funcs")
.boolean("expr")
.boolean("source-map-inline")
.boolean("source-map-include-sources")
.boolean("screw-ie8")
.boolean("support-ie8")
.boolean("export-all")
.boolean("self")
.boolean("v")
@@ -115,14 +126,15 @@ You need to pass an argument to this option to specify the name that your module
.boolean("stats")
.boolean("acorn")
.boolean("spidermonkey")
.boolean("dump-spidermonkey-ast")
.boolean("lint")
.boolean("V")
.boolean("version")
.boolean("noerr")
.boolean("bare-returns")
.boolean("keep-fnames")
.boolean("mangle-props")
.boolean("reserve-domprops")
.boolean("wrap-iife")
.wrap(80)
@@ -175,6 +187,10 @@ if (ARGS.d) {
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
}
if (ARGS.pure_funcs) {
if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
}
if (ARGS.r) {
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
}
@@ -205,49 +221,57 @@ if (ARGS.quotes === true) {
ARGS.quotes = 3;
}
if (ARGS.mangle_props === true) {
ARGS.mangle_props = 1;
} else if (ARGS.mangle_props === "unquoted") {
ARGS.mangle_props = 2;
}
var OUTPUT_OPTIONS = {
beautify : BEAUTIFY ? true : false,
preamble : ARGS.preamble || null,
quote_style : ARGS.quotes != null ? ARGS.quotes : 0
beautify : BEAUTIFY ? true : false,
max_line_len : 32000,
preamble : ARGS.preamble || null,
quote_style : ARGS.quotes != null ? ARGS.quotes : 0,
};
if (ARGS.screw_ie8) {
if (COMPRESS) COMPRESS.screw_ie8 = true;
if (MANGLE) MANGLE.screw_ie8 = true;
OUTPUT_OPTIONS.screw_ie8 = true;
if (ARGS.mangle_props == 2) {
OUTPUT_OPTIONS.keep_quoted_props = true;
if (COMPRESS && !("properties" in COMPRESS))
COMPRESS.properties = false;
}
if (ARGS.support_ie8 === true && ARGS.screw_ie8 !== true) {
screw_ie8 = false;
}
if (COMPRESS) COMPRESS.screw_ie8 = screw_ie8;
if (MANGLE) MANGLE.screw_ie8 = screw_ie8;
OUTPUT_OPTIONS.screw_ie8 = screw_ie8;
if (ARGS.keep_fnames) {
if (COMPRESS) COMPRESS.keep_fnames = true;
if (MANGLE) MANGLE.keep_fnames = true;
}
if (ARGS.wrap_iife) {
if (COMPRESS) COMPRESS.negate_iife = false;
OUTPUT_OPTIONS.wrap_iife = true;
}
if (BEAUTIFY)
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
if (ARGS.comments != null) {
if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) {
try {
OUTPUT_OPTIONS.comments = extractRegex(ARGS.comments);
} catch (e) {
print_error("ERROR: Invalid --comments: " + e.message);
}
} else if (ARGS.comments == "all") {
OUTPUT_OPTIONS.comments = true;
} else {
OUTPUT_OPTIONS.comments = function(node, comment) {
var text = comment.value;
var type = comment.type;
if (type == "comment2") {
// multiline comment
return /@preserve|@license|@cc_on/i.test(text);
}
}
}
if (ARGS.comments === "") {
OUTPUT_OPTIONS.comments = "some";
} else {
OUTPUT_OPTIONS.comments = ARGS.comments;
}
var files = ARGS._.slice();
if (process.platform === "win32")
files = UglifyJS.simple_glob(files);
if (ARGS.self) {
if (files.length > 0) {
print_error("WARN: Ignoring input files since --self was passed");
@@ -258,21 +282,29 @@ if (ARGS.self) {
var ORIG_MAP = ARGS.in_source_map;
if (ORIG_MAP) {
if (ORIG_MAP && ORIG_MAP != "inline") {
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
if (files.length == 0) {
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
files = [ ORIG_MAP.file ];
}
if (ARGS.source_map_root == null) {
ARGS.source_map_root = ORIG_MAP.sourceRoot;
}
}
if (files.length == 0) {
files = [ "-" ];
}
if (ORIG_MAP == "inline") {
if (files.length > 1) {
print_error("ERROR: Inline source map only works with singular input");
process.exit(1);
}
if (ARGS.acorn || ARGS.spidermonkey) {
print_error("ERROR: Inline source map only works with built-in parser");
process.exit(1);
}
}
if (files.indexOf("-") >= 0 && ARGS.source_map) {
print_error("ERROR: Source map doesn't work with input from STDIN");
process.exit(1);
@@ -284,37 +316,19 @@ if (files.filter(function(el){ return el == "-" }).length > 1) {
}
var STATS = {};
var OUTPUT_FILE = ARGS.o;
var TOPLEVEL = null;
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
var SOURCES_CONTENT = {};
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
root: ARGS.source_map_root,
orig: ORIG_MAP,
}) : null;
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
try {
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.msg);
print_error("Supported options:");
print_error(sys.inspect(ex.defs));
process.exit(1);
}
}
async.eachLimit(files, 1, function (file, cb) {
read_whole_file(file, function (err, code) {
if (err) {
print_error("ERROR: can't read file: " + file);
process.exit(1);
}
if (ORIG_MAP == "inline") {
ORIG_MAP = read_source_map(code);
}
if (ARGS.p != null) {
if (P_RELATIVE) {
file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
@@ -350,7 +364,21 @@ async.eachLimit(files, 1, function (file, cb) {
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
print_error(ex.message);
var col = ex.col;
var line = code.split(/\r?\n/)[ex.line - (col ? 1 : 2)];
if (line) {
if (col > 40) {
line = line.slice(col - 40);
col = 40;
}
if (col) {
print_error(line.slice(0, 80));
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
} else {
print_error(line.slice(-40));
print_error(line.slice(-40).replace(/\S/g, " ") + "^");
}
}
print_error(ex.stack);
process.exit(1);
}
@@ -361,6 +389,28 @@ async.eachLimit(files, 1, function (file, cb) {
cb();
});
}, function () {
var OUTPUT_FILE = ARGS.o;
var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
root: ARGS.source_map_root || ORIG_MAP && ORIG_MAP.sourceRoot,
orig: ORIG_MAP,
}) : null;
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
try {
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.message);
print_error("Supported options:");
print_error(sys.inspect(ex.defs));
process.exit(1);
}
}
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
});
@@ -393,38 +443,45 @@ async.eachLimit(files, 1, function (file, cb) {
}
TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, {
reserved : reserved,
cache : cache,
only_cache : !ARGS.mangle_props,
regex : regex
reserved : reserved,
cache : cache,
only_cache : !ARGS.mangle_props,
regex : regex,
ignore_quoted : ARGS.mangle_props == 2,
debug : typeof ARGS.mangle_props_debug === "undefined" ? false : ARGS.mangle_props_debug
});
writeNameCache("props", cache);
})();
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
var TL_CACHE = readNameCache("vars");
if (MANGLE) MANGLE.cache = TL_CACHE;
time_it("scope", function(){
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
if (ARGS.lint) {
TOPLEVEL.scope_warnings();
}
});
if (COMPRESS) {
time_it("squeeze", function(){
TOPLEVEL = TOPLEVEL.transform(compressor);
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (ARGS.lint) {
TOPLEVEL.scope_warnings();
}
});
}
time_it("scope", function(){
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
if (MANGLE && !TL_CACHE) {
TOPLEVEL.compute_char_frequency(MANGLE);
}
});
if (COMPRESS) {
time_it("squeeze", function(){
TOPLEVEL = compressor.compress(TOPLEVEL);
});
}
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (MANGLE && !TL_CACHE) {
TOPLEVEL.compute_char_frequency(MANGLE);
}
});
}
if (MANGLE) time_it("mangle", function(){
MANGLE.cache = TL_CACHE;
TOPLEVEL.mangle_names(MANGLE);
});
@@ -438,26 +495,35 @@ async.eachLimit(files, 1, function (file, cb) {
}
}
time_it("generate", function(){
TOPLEVEL.print(output);
});
output = output.get();
if (SOURCE_MAP) {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
var source_map_url = ARGS.source_map_url || (
P_RELATIVE
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
: ARGS.source_map
);
output += "\n//# sourceMappingURL=" + source_map_url;
}
if (OUTPUT_FILE) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
if (ARGS.dump_spidermonkey_ast) {
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
} else {
print(output);
time_it("generate", function(){
TOPLEVEL.print(output);
});
output = output.get();
if (SOURCE_MAP) {
if (ARGS.source_map_inline) {
var base64_string = new Buffer(SOURCE_MAP.toString()).toString('base64');
output += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + base64_string;
} else {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
var source_map_url = ARGS.source_map_url || (
P_RELATIVE
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
: ARGS.source_map
);
output += "\n//# sourceMappingURL=" + source_map_url;
}
}
if (OUTPUT_FILE) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
} else {
print(output);
}
}
if (ARGS.stats) {
@@ -482,24 +548,26 @@ function normalize(o) {
}
}
function getOptions(x, constants) {
x = ARGS[x];
if (x == null) return null;
function getOptions(flag, constants) {
var x = ARGS[flag];
if (x == null || x === false) return null;
var ret = {};
if (x !== "") {
if (Array.isArray(x)) x = x.map(function (v) { return "(" + v + ")"; }).join(", ");
var ast;
try {
ast = UglifyJS.parse(x, { expression: true });
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Error parsing arguments in: " + x);
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}
}
ast.walk(new UglifyJS.TreeWalker(function(node){
if (node instanceof UglifyJS.AST_Seq) return; // descend
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string({ beautify: false }).replace(/-/g, "_");
var name = node.left.print_to_string().replace(/-/g, "_");
var value = node.right;
if (constants)
value = new Function("return (" + value.print_to_string() + ")")();
@@ -507,12 +575,12 @@ function getOptions(x, constants) {
return true; // no descend
}
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
var name = node.print_to_string({ beautify: false }).replace(/-/g, "_");
var name = node.print_to_string().replace(/-/g, "_");
ret[name] = true;
return true; // no descend
}
print_error(node.TYPE)
print_error("Error parsing arguments in: " + x);
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}));
}
@@ -534,6 +602,15 @@ function read_whole_file(filename, cb) {
}
}
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
print_error("WARN: inline source map not found");
return null;
}
return JSON.parse(new Buffer(match[2], "base64"));
}
function time_it(name, cont) {
var t1 = new Date().getTime();
var ret = cont();

View File

@@ -71,7 +71,7 @@ function DEFNODE(type, props, methods, base) {
if (type) {
ctor.prototype.TYPE = ctor.TYPE = type;
}
if (methods) for (i in methods) if (methods.hasOwnProperty(i)) {
if (methods) for (i in methods) if (HOP(methods, i)) {
if (/^\$/.test(i)) {
ctor[i.substr(1)] = methods[i];
} else {
@@ -81,11 +81,13 @@ function DEFNODE(type, props, methods, base) {
ctor.DEFMETHOD = function(name, method) {
this.prototype[name] = method;
};
exports["AST_" + type] = ctor;
if (typeof exports !== "undefined") {
exports["AST_" + type] = ctor;
}
return ctor;
};
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file", {
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
@@ -143,12 +145,13 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
}, AST_Statement);
function walk_body(node, visitor) {
if (node.body instanceof AST_Statement) {
node.body._walk(visitor);
var body = node.body;
if (body instanceof AST_Statement) {
body._walk(visitor);
}
else for (var i = 0, len = body.length; i < len; i++) {
body[i]._walk(visitor);
}
else node.body.forEach(function(stat){
stat._walk(visitor);
});
};
var AST_Block = DEFNODE("Block", "body", {
@@ -369,9 +372,10 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.name) this.name._walk(visitor);
this.argnames.forEach(function(arg){
arg._walk(visitor);
});
var argnames = this.argnames;
for (var i = 0, len = argnames.length; i < len; i++) {
argnames[i]._walk(visitor);
}
walk_body(this, visitor);
});
}
@@ -531,9 +535,10 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.definitions.forEach(function(def){
def._walk(visitor);
});
var definitions = this.definitions;
for (var i = 0, len = definitions.length; i < len; i++) {
definitions[i]._walk(visitor);
}
});
}
}, AST_Statement);
@@ -571,9 +576,10 @@ var AST_Call = DEFNODE("Call", "expression args", {
_walk: function(visitor) {
return visitor._visit(this, function(){
this.expression._walk(visitor);
this.args.forEach(function(arg){
arg._walk(visitor);
});
var args = this.args;
for (var i = 0, len = args.length; i < len; i++) {
args[i]._walk(visitor);
}
});
}
});
@@ -633,6 +639,13 @@ var AST_Seq = DEFNODE("Seq", "car cdr", {
p = p.cdr;
}
},
len: function() {
if (this.cdr instanceof AST_Seq) {
return this.cdr.len() + 1;
} else {
return 2;
}
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.car._walk(visitor);
@@ -733,9 +746,10 @@ var AST_Array = DEFNODE("Array", "elements", {
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.elements.forEach(function(el){
el._walk(visitor);
});
var elements = this.elements;
for (var i = 0, len = elements.length; i < len; i++) {
elements[i]._walk(visitor);
}
});
}
});
@@ -747,9 +761,10 @@ var AST_Object = DEFNODE("Object", "properties", {
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.properties.forEach(function(prop){
prop._walk(visitor);
});
var properties = this.properties;
for (var i = 0, len = properties.length; i < len; i++) {
properties[i]._walk(visitor);
}
});
}
});
@@ -927,27 +942,36 @@ var AST_True = DEFNODE("True", null, {
function TreeWalker(callback) {
this.visit = callback;
this.stack = [];
this.directives = Object.create(null);
};
TreeWalker.prototype = {
_visit: function(node, descend) {
this.stack.push(node);
this.push(node);
var ret = this.visit(node, descend ? function(){
descend.call(node);
} : noop);
if (!ret && descend) {
descend.call(node);
}
this.stack.pop();
this.pop(node);
return ret;
},
parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)];
},
push: function (node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive) {
this.directives[node.value] = this.directives[node.value] ? "up" : true;
}
this.stack.push(node);
},
pop: function() {
return this.stack.pop();
pop: function(node) {
this.stack.pop();
if (node instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives);
}
},
self: function() {
return this.stack[this.stack.length - 1];
@@ -960,7 +984,16 @@ TreeWalker.prototype = {
}
},
has_directive: function(type) {
return this.find_parent(AST_Scope).has_directive(type);
var dir = this.directives[type];
if (dir) return dir;
var node = this.stack[this.stack.length - 1];
if (node instanceof AST_Scope) {
for (var i = 0; i < node.body.length; ++i) {
var st = node.body[i];
if (!(st instanceof AST_Directive)) break;
if (st.value == type) return true;
}
}
},
in_boolean_context: function() {
var stack = this.stack;

File diff suppressed because it is too large Load Diff

View File

@@ -45,20 +45,55 @@
(function(){
var MOZ_TO_ME = {
ExpressionStatement: function(M) {
var expr = M.expression;
if (expr.type === "Literal" && typeof expr.value === "string") {
return new AST_Directive({
start: my_start_token(M),
end: my_end_token(M),
value: expr.value
var normalize_directives = function(body) {
var in_directive = true;
for (var i = 0; i < body.length; i++) {
if (in_directive && body[i] instanceof AST_Statement && body[i].body instanceof AST_String) {
body[i] = new AST_Directive({
start: body[i].start,
end: body[i].end,
value: body[i].body.value
});
} else if (in_directive && !(body[i] instanceof AST_Statement && body[i].body instanceof AST_String)) {
in_directive = false;
}
}
return body;
};
var MOZ_TO_ME = {
Program: function(M) {
return new AST_Toplevel({
start: my_start_token(M),
end: my_end_token(M),
body: normalize_directives(M.body.map(from_moz))
});
},
FunctionDeclaration: function(M) {
return new AST_Defun({
start: my_start_token(M),
end: my_end_token(M),
name: from_moz(M.id),
argnames: M.params.map(from_moz),
body: normalize_directives(from_moz(M.body).body)
});
},
FunctionExpression: function(M) {
return new AST_Function({
start: my_start_token(M),
end: my_end_token(M),
name: from_moz(M.id),
argnames: M.params.map(from_moz),
body: normalize_directives(from_moz(M.body).body)
});
},
ExpressionStatement: function(M) {
return new AST_SimpleStatement({
start: my_start_token(M),
end: my_end_token(M),
body: from_moz(expr)
body: from_moz(M.expression)
});
},
TryStatement: function(M) {
@@ -94,6 +129,15 @@
return new AST_ObjectGetter(args);
}
},
ArrayExpression: function(M) {
return new AST_Array({
start : my_start_token(M),
end : my_end_token(M),
elements : M.elements.map(function(elem){
return elem === null ? new AST_Hole() : from_moz(elem);
})
});
},
ObjectExpression: function(M) {
return new AST_Object({
start : my_start_token(M),
@@ -146,7 +190,14 @@
case "boolean":
return new (val ? AST_True : AST_False)(args);
default:
args.value = val;
var rx = M.regex;
if (rx && rx.pattern) {
// RegExpLiteral as per ESTree AST spec
args.value = new RegExp(rx.pattern, rx.flags).toString();
} else {
// support legacy RegExp
args.value = M.regex && M.raw ? M.raw : val;
}
return new AST_RegExp(args);
}
},
@@ -178,7 +229,6 @@
});
};
map("Program", AST_Toplevel, "body@body");
map("EmptyStatement", AST_EmptyStatement);
map("BlockStatement", AST_BlockStatement, "body@body");
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
@@ -194,13 +244,10 @@
map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body");
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
map("DebuggerStatement", AST_Debugger);
map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
map("CatchClause", AST_Catch, "param>argname, body%body");
map("ThisExpression", AST_This);
map("ArrayExpression", AST_Array, "elements@elements");
map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
@@ -208,6 +255,31 @@
map("NewExpression", AST_New, "callee>expression, arguments@args");
map("CallExpression", AST_Call, "callee>expression, arguments@args");
def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
return {
type: "Program",
body: M.body.map(to_moz)
};
});
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
return {
type: "FunctionDeclaration",
id: to_moz(M.name),
params: M.argnames.map(to_moz),
body: to_moz_block(M)
}
});
def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) {
return {
type: "FunctionExpression",
id: to_moz(M.name),
params: M.argnames.map(to_moz),
body: to_moz_block(M)
}
});
def_to_moz(AST_Directive, function To_Moz_Directive(M) {
return {
type: "ExpressionStatement",
@@ -295,6 +367,13 @@
};
});
def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) {
return {
type: "ArrayExpression",
elements: M.elements.map(to_moz)
};
});
def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) {
return {
type: "ObjectExpression",
@@ -334,6 +413,19 @@
};
});
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
var value = M.value;
return {
type: "Literal",
value: value,
raw: value.toString(),
regex: {
pattern: value.source,
flags: value.toString().match(/[gimuy]*$/)[0]
}
};
});
def_to_moz(AST_Constant, function To_Moz_Literal(M) {
var value = M.value;
if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
@@ -343,13 +435,15 @@
prefix: true,
argument: {
type: "Literal",
value: -value
value: -value,
raw: M.start.raw
}
};
}
return {
type: "Literal",
value: value
value: value,
raw: M.start.raw
};
});
@@ -369,6 +463,12 @@
/* -----[ tools ]----- */
function raw_token(moznode) {
if (moznode.type == "Literal") {
return moznode.raw != null ? moznode.raw : moznode.value + "";
}
}
function my_start_token(moznode) {
var loc = moznode.loc, start = loc && loc.start;
var range = moznode.range;
@@ -379,7 +479,8 @@
pos : range ? range[0] : moznode.start,
endline : start && start.line,
endcol : start && start.column,
endpos : range ? range[0] : moznode.start
endpos : range ? range[0] : moznode.start,
raw : raw_token(moznode),
});
};
@@ -393,7 +494,8 @@
pos : range ? range[1] : moznode.end,
endline : end && end.line,
endcol : end && end.column,
endpos : range ? range[1] : moznode.end
endpos : range ? range[1] : moznode.end,
raw : raw_token(moznode),
});
};

View File

@@ -43,6 +43,22 @@
"use strict";
var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
function is_some_comments(comment) {
var text = comment.value;
var type = comment.type;
if (type == "comment2") {
// multiline comment
return /@preserve|@license|@cc_on/i.test(text);
}
return type == "comment5";
}
function is_comment5(comment) {
return comment.type == "comment5";
}
function OutputStream(options) {
options = defaults(options, {
@@ -54,7 +70,7 @@ function OutputStream(options) {
unescape_regexps : false,
inline_script : false,
width : 80,
max_line_len : 32000,
max_line_len : false,
beautify : false,
source_map : null,
bracketize : false,
@@ -62,11 +78,41 @@ function OutputStream(options) {
comments : false,
shebang : true,
preserve_line : false,
screw_ie8 : false,
screw_ie8 : true,
preamble : null,
quote_style : 0
quote_style : 0,
keep_quoted_props: false,
wrap_iife : false,
}, true);
// Convert comment option to RegExp if neccessary and set up comments filter
var comment_filter = options.shebang ? is_comment5 : return_false; // Default case, throw all comments away except shebangs
if (options.comments) {
var comments = options.comments;
if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) {
var regex_pos = options.comments.lastIndexOf("/");
comments = new RegExp(
options.comments.substr(1, regex_pos - 1),
options.comments.substr(regex_pos + 1)
);
}
if (comments instanceof RegExp) {
comment_filter = function(comment) {
return comment.type == "comment5" || comments.test(comment.value);
};
}
else if (typeof comments === "function") {
comment_filter = function(comment) {
return comment.type == "comment5" || comments(this, comment);
};
}
else if (comments === "some") {
comment_filter = is_some_comments;
} else { // NOTE includes "all" option
comment_filter = return_true;
}
}
var indentation = 0;
var current_col = 0;
var current_line = 1;
@@ -74,7 +120,7 @@ function OutputStream(options) {
var OUTPUT = "";
function to_ascii(str, identifier) {
return str.replace(/[\u0080-\uffff]/g, function(ch) {
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;
@@ -88,19 +134,23 @@ function OutputStream(options) {
function make_string(str, quote) {
var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g,
function(s, i){
switch (s) {
case "\\": return "\\\\";
case "\b": return "\\b";
case "\f": return "\\f";
case "\n": return "\\n";
case "\r": return "\\r";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
case '"': ++dq; return '"';
case "'": ++sq; return "'";
case "\0": return "\\x00";
case "\\": return "\\\\";
case "\n": return "\\n";
case "\r": return "\\r";
case "\t": return "\\t";
case "\b": return "\\b";
case "\f": return "\\f";
case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff";
case "\0":
return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
}
return s;
});
@@ -125,8 +175,11 @@ function OutputStream(options) {
function encode_string(str, quote) {
var ret = make_string(str, quote);
if (options.inline_script)
if (options.inline_script) {
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
ret = ret.replace(/--\x3e/g, "--\\x3e");
}
return ret;
};
@@ -145,16 +198,29 @@ function OutputStream(options) {
var might_need_space = false;
var might_need_semicolon = false;
var might_add_newline = 0;
var last = null;
function last_char() {
return last.charAt(last.length - 1);
};
function maybe_newline() {
if (options.max_line_len && current_col > options.max_line_len)
print("\n");
};
var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) {
if (might_add_newline) {
var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline);
OUTPUT = left + "\n" + right;
current_line++;
current_pos++;
current_col = right.length;
}
if (current_col > options.max_line_len) {
AST_Node.warn("Output exceeds {max_line_len} characters", options);
}
}
might_add_newline = 0;
} : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , .");
@@ -170,6 +236,7 @@ function OutputStream(options) {
current_col++;
current_pos++;
} else {
ensure_line_len();
OUTPUT += "\n";
current_pos++;
current_line++;
@@ -190,6 +257,7 @@ function OutputStream(options) {
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
var target_line = stack[stack.length - 1].start.line;
while (current_line < target_line) {
ensure_line_len();
OUTPUT += "\n";
current_pos++;
current_line++;
@@ -201,8 +269,9 @@ function OutputStream(options) {
if (might_need_space) {
var prev = last_char();
if ((is_identifier_char(prev)
&& (is_identifier_char(ch) || ch == "\\"))
|| (/^[\+\-\/]$/.test(ch) && ch == prev))
&& (is_identifier_char(ch) || ch == "\\"))
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last))
{
OUTPUT += " ";
current_col++;
@@ -210,16 +279,16 @@ function OutputStream(options) {
}
might_need_space = false;
}
OUTPUT += str;
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1;
current_line += n;
if (n == 0) {
current_col += a[n].length;
} else {
current_col += a[0].length;
if (n > 0) {
ensure_line_len();
current_col = a[n].length;
}
current_pos += str.length;
last = str;
OUTPUT += str;
};
var space = options.beautify ? function() {
@@ -245,7 +314,10 @@ function OutputStream(options) {
var newline = options.beautify ? function() {
print("\n");
} : maybe_newline;
} : options.max_line_len ? function() {
ensure_line_len();
might_add_newline = OUTPUT.length;
} : noop;
var semicolon = options.beautify ? function() {
print(";");
@@ -322,6 +394,9 @@ function OutputStream(options) {
} : noop;
function get() {
if (might_add_newline) {
ensure_line_len();
}
return OUTPUT;
};
@@ -347,7 +422,18 @@ function OutputStream(options) {
force_semicolon : force_semicolon,
to_ascii : to_ascii,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote) { print(encode_string(str, quote)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
if (escape_directive === true && encoded.indexOf("\\") === -1) {
// Insert semicolons to break directive prologue
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
force_semicolon();
}
force_semicolon();
}
print(encoded);
},
encode_string : encode_string,
next_indent : next_indent,
with_indent : with_indent,
with_block : with_block,
@@ -355,12 +441,12 @@ function OutputStream(options) {
with_square : with_square,
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
comment_filter : comment_filter,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
push_node : function(node) { stack.push(node) },
pop_node : function() { return stack.pop() },
stack : function() { return stack },
parent : function(n) {
return stack[stack.length - 2 - (n || 0)];
}
@@ -378,8 +464,14 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
};
var use_asm = false;
var in_directive = false;
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen;
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;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
@@ -392,10 +484,14 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
}
});
AST_Node.DEFMETHOD("print_to_string", function(options){
var s = OutputStream(options);
if (!options) s._readonly = true;
this.print(s);
return s.get();
});
@@ -403,7 +499,8 @@ function OutputStream(options) {
/* -----[ comments ]----- */
AST_Node.DEFMETHOD("add_comments", function(output){
var c = output.option("comments"), self = this;
if (output._readonly) return;
var self = this;
var start = self.start;
if (start && !start._comments_dumped) {
start._comments_dumped = true;
@@ -426,19 +523,7 @@ function OutputStream(options) {
}));
}
if (!c) {
comments = comments.filter(function(comment) {
return comment.type == "comment5";
});
} else if (c.test) {
comments = comments.filter(function(comment){
return c.test(comment.value) || comment.type == "comment5";
});
} else if (typeof c == "function") {
comments = comments.filter(function(comment){
return c(self, comment) || comment.type == "comment5";
});
}
comments = comments.filter(output.comment_filter, self);
// Keep single line comments after nlb, after nlb
if (!output.option("beautify") && comments.length > 0 &&
@@ -489,7 +574,16 @@ function OutputStream(options) {
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
PARENS(AST_Function, function(output){
return first_in_statement(output);
if (first_in_statement(output)) {
return true;
}
if (output.option('wrap_iife')) {
var p = output.parent();
return p instanceof AST_Call && p.expression === this;
}
return false;
});
// same goes for an object literal, because otherwise it would be
@@ -500,7 +594,8 @@ function OutputStream(options) {
PARENS([ AST_Unary, AST_Undefined ], function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this;
return p instanceof AST_PropAccess && p.expression === this
|| p instanceof AST_Call && p.expression === this;
});
PARENS(AST_Seq, function(output){
@@ -576,7 +671,7 @@ function OutputStream(options) {
PARENS(AST_New, function(output){
var p = output.parent();
if (no_constructor_parens(this, output)
if (!need_constructor_parens(this, output)
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
return true;
@@ -584,8 +679,12 @@ function OutputStream(options) {
PARENS(AST_Number, function(output){
var p = output.parent();
if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
return true;
if (p instanceof AST_PropAccess && p.expression === this) {
var value = this.getValue();
if (value < 0 || /^0/.test(make_num(value))) {
return true;
}
}
});
PARENS([ AST_Assign, AST_Conditional ], function (output){
@@ -620,9 +719,16 @@ function OutputStream(options) {
/* -----[ statements ]----- */
function display_body(body, is_toplevel, output) {
function display_body(body, is_toplevel, output, allow_directives) {
var last = body.length - 1;
in_directive = allow_directives;
body.forEach(function(stmt, i){
if (in_directive === true && !(stmt instanceof AST_Directive ||
stmt instanceof AST_EmptyStatement ||
(stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String)
)) {
in_directive = false;
}
if (!(stmt instanceof AST_EmptyStatement)) {
output.indent();
stmt.print(output);
@@ -631,7 +737,14 @@ function OutputStream(options) {
if (is_toplevel) output.newline();
}
}
if (in_directive === true &&
stmt instanceof AST_SimpleStatement &&
stmt.body instanceof AST_String
) {
in_directive = false;
}
});
in_directive = false;
};
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){
@@ -643,7 +756,7 @@ function OutputStream(options) {
output.semicolon();
});
DEFPRINT(AST_Toplevel, function(self, output){
display_body(self.body, true, output);
display_body(self.body, true, output, true);
output.print("");
});
DEFPRINT(AST_LabeledStatement, function(self, output){
@@ -655,9 +768,9 @@ function OutputStream(options) {
self.body.print(output);
output.semicolon();
});
function print_bracketed(body, output) {
function print_bracketed(body, output, allow_directives) {
if (body.length > 0) output.with_block(function(){
display_body(body, false, output);
display_body(body, false, output, allow_directives);
});
else output.print("{}");
};
@@ -757,7 +870,7 @@ function OutputStream(options) {
});
});
output.space();
print_bracketed(self.body, output);
print_bracketed(self.body, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
@@ -810,8 +923,8 @@ function OutputStream(options) {
// adds the block brackets if needed.
if (!self.body)
return output.force_semicolon();
if (self.body instanceof AST_Do
&& !output.option("screw_ie8")) {
if (self.body instanceof AST_Do) {
// Unconditionally use the if/do-while workaround for all browsers.
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
// croaks with "syntax error" on code like this: if (foo)
// do ... while(cond); else ... we need block brackets
@@ -847,7 +960,10 @@ function OutputStream(options) {
output.space();
output.print("else");
output.space();
force_statement(self.alternative, output);
if (self.alternative instanceof AST_If)
self.alternative.print(output);
else
force_statement(self.alternative, output);
} else {
self._do_print_body(output);
}
@@ -973,7 +1089,7 @@ function OutputStream(options) {
/* -----[ other expressions ]----- */
DEFPRINT(AST_Call, function(self, output){
self.expression.print(output);
if (self instanceof AST_New && no_constructor_parens(self, output))
if (self instanceof AST_New && !need_constructor_parens(self, output))
return;
output.with_parens(function(){
self.args.forEach(function(expr, i){
@@ -1014,7 +1130,7 @@ function OutputStream(options) {
var expr = self.expression;
expr.print(output);
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.]/i.test(output.last())) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
}
@@ -1045,16 +1161,24 @@ function OutputStream(options) {
output.print(self.operator);
});
DEFPRINT(AST_Binary, function(self, output){
var op = self.operator;
self.left.print(output);
output.space();
output.print(self.operator);
if (self.operator == "<"
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
&& self.left instanceof AST_UnaryPostfix
&& self.left.operator == "--") {
// space is mandatory to avoid outputting -->
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space();
}
output.print(op);
if ((op == "<" || op == "<<")
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "!"
&& self.right.expression instanceof AST_UnaryPrefix
&& self.right.expression.operator == "--") {
// space is mandatory to avoid outputting <!--
// http://javascript.spec.whatwg.org/#comment-syntax
output.print(" ");
} else {
// the space is optional depending on "beautify"
@@ -1115,7 +1239,11 @@ function OutputStream(options) {
&& parseFloat(key) >= 0) {
output.print(make_num(key));
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
output.print_name(key);
if (quote && output.option("keep_quoted_props")) {
output.print_string(key, quote);
} else {
output.print_name(key);
}
} else {
output.print_string(key, quote);
}
@@ -1155,13 +1283,11 @@ function OutputStream(options) {
output.print(self.getValue());
});
DEFPRINT(AST_String, function(self, output){
output.print_string(self.getValue(), self.quote);
output.print_string(self.getValue(), self.quote, in_directive);
});
DEFPRINT(AST_Number, function(self, output){
if (self.literal !== undefined
&& +self.literal === self.value /* paranoid check */
&& self.scope && self.scope.has_directive('use asm')) {
output.print(self.literal);
if (use_asm && self.start && self.start.raw != null) {
output.print(self.start.raw);
} else {
output.print(make_num(self.getValue()));
}
@@ -1232,33 +1358,12 @@ function OutputStream(options) {
}
};
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(output) {
var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
while (i > 0) {
if (p instanceof AST_Statement && p.body === node)
return true;
if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) ||
(p instanceof AST_Conditional && p.condition === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_UnaryPostfix && p.expression === node ))
{
node = p;
p = a[--i];
} else {
return false;
}
}
};
// self should be AST_New. decide if we want to show parens or not.
function no_constructor_parens(self, output) {
return self.args.length == 0 && !output.option("beautify");
function need_constructor_parens(self, output) {
// Always print parentheses with arguments
if (self.args.length > 0) return true;
return output.option("beautify");
};
function best_of(a) {
@@ -1340,6 +1445,12 @@ function OutputStream(options) {
DEFMAP(AST_Finally, basic_sourcemap_gen);
DEFMAP(AST_Definitions, basic_sourcemap_gen);
DEFMAP(AST_Constant, basic_sourcemap_gen);
DEFMAP(AST_ObjectSetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectGetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectProperty, function(self, output){
output.add_mapping(self.start, self.key);
});

View File

@@ -46,7 +46,7 @@
var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield'
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield'
+ " " + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
@@ -59,7 +59,6 @@ var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
var RE_OCT_NUMBER = /^0[0-7]+$/;
var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
var OPERATORS = makePredicate([
"in",
@@ -108,7 +107,9 @@ var OPERATORS = makePredicate([
"||"
]);
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF"));
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\uFEFF"));
var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029"));
var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
@@ -182,8 +183,9 @@ function parse_js_number(num) {
return parseInt(num.substr(2), 16);
} else if (RE_OCT_NUMBER.test(num)) {
return parseInt(num.substr(1), 8);
} else if (RE_DEC_NUMBER.test(num)) {
return parseFloat(num);
} else {
var val = parseFloat(num);
if (val == num) return val;
}
};
@@ -193,12 +195,11 @@ function JS_Parse_Error(message, filename, line, col, pos) {
this.line = line;
this.col = col;
this.pos = pos;
this.stack = new Error().stack;
};
JS_Parse_Error.prototype.toString = function() {
return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
};
JS_Parse_Error.prototype = Object.create(Error.prototype);
JS_Parse_Error.prototype.constructor = JS_Parse_Error;
JS_Parse_Error.prototype.name = "SyntaxError";
configure_error_stack(JS_Parse_Error);
function js_error(message, filename, line, col, pos) {
throw new JS_Parse_Error(message, filename, line, col, pos);
@@ -223,7 +224,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
tokcol : 0,
newline_before : false,
regex_allowed : false,
comments_before : []
comments_before : [],
directives : {},
directive_stack : []
};
function peek() { return S.text.charAt(S.pos); };
@@ -232,7 +235,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
var ch = S.text.charAt(S.pos++);
if (signal_eof && !ch)
throw EX_EOF;
if ("\r\n\u2028\u2029".indexOf(ch) >= 0) {
if (NEWLINE_CHARS(ch)) {
S.newline_before = S.newline_before || !in_string;
++S.line;
S.col = 0;
@@ -255,6 +258,16 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return S.text.substr(S.pos, str.length) == str;
};
function find_eol() {
var text = S.text;
for (var i = S.pos, n = S.text.length; i < n; ++i) {
var ch = text[i];
if (NEWLINE_CHARS(ch))
return i;
}
return -1;
};
function find(what, signal_eof) {
var pos = S.text.indexOf(what, S.pos);
if (signal_eof && pos == -1) throw EX_EOF;
@@ -285,6 +298,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
nlb : S.newline_before,
file : filename
};
if (/^(?:num|string|regexp)$/i.test(type)) {
ret.raw = $TEXT.substring(ret.pos, ret.endpos);
}
if (!is_comment) {
ret.comments_before = S.comments_before;
S.comments_before = [];
@@ -298,8 +314,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
};
function skip_whitespace() {
var ch;
while (WHITESPACE_CHARS(ch = peek()) || ch == "\u2028" || ch == "\u2029")
while (WHITESPACE_CHARS(peek()))
next();
};
@@ -333,13 +348,12 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return is_alphanumeric_char(code);
});
if (prefix) num = prefix + num;
if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
parse_error("Legacy octal literals are not allowed in strict mode");
}
var valid = parse_js_number(num);
if (!isNaN(valid)) {
var tok = token("num", valid);
if (num.indexOf('.') >= 0) {
tok.literal = num;
}
return tok;
return token("num", valid);
} else {
parse_error("Invalid syntax: " + num);
}
@@ -354,7 +368,6 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
case 98 : return "\b";
case 118 : return "\u000b"; // \v
case 102 : return "\f";
case 48 : return "\0";
case 120 : return String.fromCharCode(hex_bytes(2)); // \x
case 117 : return String.fromCharCode(hex_bytes(4)); // \u
case 10 : return ""; // newline
@@ -364,9 +377,27 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return "";
}
}
if (ch >= "0" && ch <= "7")
return read_octal_escape_sequence(ch);
return ch;
};
function read_octal_escape_sequence(ch) {
// Read
var p = peek();
if (p >= "0" && p <= "7") {
ch += next(true);
if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7")
ch += next(true);
}
// Parse
if (ch === "0") return "\0";
if (ch.length > 0 && next_token.has_directive("use strict"))
parse_error("Legacy octal escape sequences are not allowed in strict mode");
return String.fromCharCode(parseInt(ch, 8));
}
function hex_bytes(n) {
var num = 0;
for (; n > 0; --n) {
@@ -382,24 +413,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
var quote = next(), ret = "";
for (;;) {
var ch = next(true, true);
if (ch == "\\") {
// read OctalEscapeSequence (XXX: deprecated if "strict mode")
// https://github.com/mishoo/UglifyJS/issues/178
var octal_len = 0, first = null;
ch = read_while(function(ch){
if (ch >= "0" && ch <= "7") {
if (!first) {
first = ch;
return ++octal_len;
}
else if (first <= "3" && octal_len <= 2) return ++octal_len;
else if (first >= "4" && octal_len <= 1) return ++octal_len;
}
return false;
});
if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
else ch = read_escaped_char(true);
}
if (ch == "\\") ch = read_escaped_char(true);
else if (NEWLINE_CHARS(ch)) parse_error("Unterminated string constant");
else if (ch == quote) break;
ret += ch;
}
@@ -410,7 +425,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function skip_line_comment(type) {
var regex_allowed = S.regex_allowed;
var i = find("\n"), ret;
var i = find_eol(), ret;
if (i == -1) {
ret = S.text.substr(S.pos);
S.pos = S.text.length;
@@ -421,25 +436,18 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
S.col = S.tokcol + (S.pos - S.tokpos);
S.comments_before.push(token(type, ret, true));
S.regex_allowed = regex_allowed;
return next_token();
return next_token;
};
var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
var regex_allowed = S.regex_allowed;
var i = find("*/", true);
var text = S.text.substring(S.pos, i);
var a = text.split("\n"), n = a.length;
var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n');
// update stream position
S.pos = i + 2;
S.line += n - 1;
if (n > 1) S.col = a[n - 1].length;
else S.col += a[n - 1].length;
S.col += 2;
var nlb = S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2);
S.comments_before.push(token("comment2", text, true));
S.regex_allowed = regex_allowed;
S.newline_before = nlb;
return next_token();
return next_token;
});
function read_name() {
@@ -467,7 +475,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (prev_backslash) {
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
parse_error("Unexpected line terminator");
} else if (prev_backslash) {
regexp += "\\" + ch;
prev_backslash = false;
} else if (ch == "[") {
@@ -548,36 +558,45 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function next_token(force_regexp) {
if (force_regexp != null)
return read_regexp(force_regexp);
skip_whitespace();
start_token();
if (html5_comments) {
if (looking_at("<!--")) {
forward(4);
return skip_line_comment("comment3");
for (;;) {
skip_whitespace();
start_token();
if (html5_comments) {
if (looking_at("<!--")) {
forward(4);
skip_line_comment("comment3");
continue;
}
if (looking_at("-->") && S.newline_before) {
forward(3);
skip_line_comment("comment4");
continue;
}
}
if (looking_at("-->") && S.newline_before) {
forward(3);
return skip_line_comment("comment4");
var ch = peek();
if (!ch) return token("eof");
var code = ch.charCodeAt(0);
switch (code) {
case 34: case 39: return read_string(ch);
case 46: return handle_dot();
case 47: {
var tok = handle_slash();
if (tok === next_token) continue;
return tok;
}
}
}
var ch = peek();
if (!ch) return token("eof");
var code = ch.charCodeAt(0);
switch (code) {
case 34: case 39: return read_string(ch);
case 46: return handle_dot();
case 47: return handle_slash();
}
if (is_digit(code)) return read_num();
if (PUNC_CHARS(ch)) return token("punc", next());
if (OPERATOR_CHARS(ch)) return read_operator();
if (code == 92 || is_identifier_start(code)) return read_word();
if (shebang) {
if (S.pos == 0 && looking_at("#!")) {
forward(2);
return skip_line_comment("comment5");
if (is_digit(code)) return read_num();
if (PUNC_CHARS(ch)) return token("punc", next());
if (OPERATOR_CHARS(ch)) return read_operator();
if (code == 92 || is_identifier_start(code)) return read_word();
if (shebang) {
if (S.pos == 0 && looking_at("#!")) {
forward(2);
skip_line_comment("comment5");
continue;
}
}
break;
}
parse_error("Unexpected character '" + ch + "'");
};
@@ -587,6 +606,35 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return S;
};
next_token.add_directive = function(directive) {
S.directive_stack[S.directive_stack.length - 1].push(directive);
if (S.directives[directive] === undefined) {
S.directives[directive] = 1;
} else {
S.directives[directive]++;
}
}
next_token.push_directives_stack = function() {
S.directive_stack.push([]);
}
next_token.pop_directives_stack = function() {
var directives = S.directive_stack[S.directive_stack.length - 1];
for (var i = 0; i < directives.length; i++) {
S.directives[directives[i]]--;
}
S.directive_stack.pop();
}
next_token.has_directive = function(directive) {
return S.directives[directive] !== undefined &&
S.directives[directive] > 0;
}
return next_token;
};
@@ -725,9 +773,9 @@ function parse($TEXT, options) {
);
};
function semicolon() {
function semicolon(optional) {
if (is("punc", ";")) next();
else if (!can_insert_semicolon()) unexpected();
else if (!optional && !can_insert_semicolon()) unexpected();
};
function parenthesised() {
@@ -760,9 +808,16 @@ function parse($TEXT, options) {
handle_regexp();
switch (S.token.type) {
case "string":
var dir = false;
if (S.in_directives === true) {
if ((is_token(peek(), "punc", ";") || peek().nlb) && S.token.raw.indexOf("\\") === -1) {
S.input.add_directive(S.token.value);
} else {
S.in_directives = false;
}
}
var dir = S.in_directives, stat = simple_statement();
// XXXv2: decide how to fix directives
if (dir && stat.body instanceof AST_String && !is("punc", ",")) {
if (dir) {
return new AST_Directive({
start : stat.body.start,
end : stat.body.end,
@@ -794,6 +849,7 @@ function parse($TEXT, options) {
case "(":
return simple_statement();
case ";":
S.in_directives = false;
next();
return new AST_EmptyStatement();
default:
@@ -815,7 +871,7 @@ function parse($TEXT, options) {
case "do":
return new AST_Do({
body : in_loop(statement),
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
});
case "while":
@@ -867,6 +923,9 @@ function parse($TEXT, options) {
return tmp = const_(), semicolon(), tmp;
case "with":
if (S.input.has_directive("use strict")) {
croak("Strict mode may not include a with statement");
}
return new AST_With({
expression : parenthesised(),
body : statement()
@@ -991,9 +1050,11 @@ function parse($TEXT, options) {
body: (function(loop, labels){
++S.in_function;
S.in_directives = true;
S.input.push_directives_stack();
S.in_loop = 0;
S.labels = [];
var a = block_();
S.input.pop_directives_stack();
--S.in_function;
S.in_loop = loop;
S.labels = labels;
@@ -1152,7 +1213,7 @@ function parse($TEXT, options) {
ret = _make_symbol(AST_SymbolRef);
break;
case "num":
ret = new AST_Number({ start: tok, end: tok, value: tok.value, literal: tok.literal });
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
break;
case "string":
ret = new AST_String({
@@ -1178,6 +1239,13 @@ function parse($TEXT, options) {
break;
}
break;
case "operator":
if (!is_identifier_string(tok.value)) {
croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos);
}
ret = _make_symbol(AST_SymbolRef);
break;
}
next();
return ret;
@@ -1239,6 +1307,10 @@ function parse($TEXT, options) {
});
});
var create_accessor = embed_tokens(function() {
return function_(AST_Accessor);
});
var object_ = embed_tokens(function() {
expect("{");
var first = true, a = [];
@@ -1255,7 +1327,7 @@ function parse($TEXT, options) {
a.push(new AST_ObjectGetter({
start : start,
key : as_atom_node(),
value : function_(AST_Accessor),
value : create_accessor(),
end : prev()
}));
continue;
@@ -1264,7 +1336,7 @@ function parse($TEXT, options) {
a.push(new AST_ObjectSetter({
start : start,
key : as_atom_node(),
value : function_(AST_Accessor),
value : create_accessor(),
end : prev()
}));
continue;
@@ -1486,8 +1558,10 @@ function parse($TEXT, options) {
return (function(){
var start = S.token;
var body = [];
S.input.push_directives_stack();
while (!is("eof"))
body.push(statement());
S.input.pop_directives_stack();
var end = prev();
var toplevel = options.toplevel;
if (toplevel) {

View File

@@ -65,7 +65,9 @@ function mangle_properties(ast, options) {
reserved : null,
cache : null,
only_cache : false,
regex : null
regex : null,
ignore_quoted : false,
debug : false
});
var reserved = options.reserved;
@@ -81,35 +83,43 @@ function mangle_properties(ast, options) {
}
var regex = options.regex;
var ignore_quoted = options.ignore_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'
// the same as passing an empty string.
var debug = (options.debug !== false);
var debug_name_suffix;
if (debug) {
debug_name_suffix = (options.debug === true ? "" : options.debug);
}
var names_to_mangle = [];
var unmangleable = [];
var ignored = {};
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
add(node.key);
add(node.key, ignore_quoted && node.quote);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
add(node.key.name);
}
else if (node instanceof AST_Dot) {
if (this.parent() instanceof AST_Assign) {
add(node.property);
}
add(node.property);
}
else if (node instanceof AST_Sub) {
if (this.parent() instanceof AST_Assign) {
addStrings(node.property);
}
addStrings(node.property, ignore_quoted);
}
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) {
node.key = mangle(node.key);
if (!(ignore_quoted && node.quote))
node.key = mangle(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter
@@ -119,7 +129,8 @@ function mangle_properties(ast, options) {
node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
if (!ignore_quoted)
node.property = mangleStrings(node.property);
}
// else if (node instanceof AST_String) {
// if (should_mangle(node.value)) {
@@ -138,6 +149,7 @@ function mangle_properties(ast, options) {
// only function declarations after this line
function can_mangle(name) {
if (!is_identifier(name)) return false;
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {
@@ -148,13 +160,19 @@ function mangle_properties(ast, options) {
}
function should_mangle(name) {
if (ignore_quoted && name in ignored) 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) {
function add(name, ignore) {
if (ignore) {
ignored[name] = true;
return;
}
if (can_mangle(name))
push_uniq(names_to_mangle, name);
@@ -170,15 +188,31 @@ function mangle_properties(ast, options) {
var mangled = cache.props.get(name);
if (!mangled) {
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled));
if (debug) {
// 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) && !(ignore_quoted && debug_mangled in ignored)) {
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 'ignored' set
// (filled with quoted properties when ignore_quoted 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) || (ignore_quoted && mangled in ignored));
}
cache.props.set(name, mangled);
}
return mangled;
}
function addStrings(node) {
function addStrings(node, ignore) {
var out = {};
try {
(function walk(node){
@@ -188,7 +222,7 @@ function mangle_properties(ast, options) {
return true;
}
if (node instanceof AST_String) {
add(node.value);
add(node.value, ignore);
return true;
}
if (node instanceof AST_Conditional) {

View File

@@ -51,10 +51,12 @@ function SymbolDef(scope, index, orig) {
this.global = false;
this.mangled_name = null;
this.undeclared = false;
this.constant = false;
this.index = index;
this.id = SymbolDef.next_id++;
};
SymbolDef.next_id = 1;
SymbolDef.prototype = {
unmangleable: function(options) {
if (!options) options = {};
@@ -85,43 +87,47 @@ SymbolDef.prototype = {
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, {
screw_ie8: false,
screw_ie8: true,
cache: null
});
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var nesting = 0;
var tw = new TreeWalker(function(node, descend){
if (options.screw_ie8 && node instanceof AST_Catch) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(nesting);
scope.init_scope_vars();
scope.parent_scope = save_scope;
descend();
scope = save_scope;
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(nesting);
node.init_scope_vars();
var save_scope = node.parent_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
++nesting; descend(); --nesting;
labels = new Dictionary();
descend();
scope = save_scope;
defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Directive) {
node.scope = scope;
push_uniq(scope.directives, node.value);
return true;
}
if (node instanceof AST_Number) {
node.scope = scope;
return true;
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
throw new Error(string_template("Label {name} defined twice", l));
}
labels.set(l.name, l);
descend();
labels.del(l.name);
return true; // no descend again
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope)
@@ -131,6 +137,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node);
}
@@ -145,13 +155,21 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
var def = defun.def_variable(node);
def.constant = node instanceof AST_SymbolConst;
def.init = tw.parent().value;
}
else if (node instanceof AST_SymbolCatch) {
(options.screw_ie8 ? scope : defun)
.def_variable(node);
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
});
self.walk(tw);
@@ -166,31 +184,26 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
func = prev_func;
return true;
}
if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node);
return true;
}
if (node instanceof AST_SymbolRef) {
var name = node.name;
var sym = node.scope.find_variable(name);
if (!sym) {
var g;
if (globals.has(name)) {
g = globals.get(name);
} else {
g = new SymbolDef(self, globals.size(), node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
if (name == "eval" && tw.parent() instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
s.uses_eval = true;
}
node.thedef = g;
if (name == "eval" && tw.parent() instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
s.uses_eval = true;
}
if (func && name == "arguments") {
func.uses_arguments = true;
}
} else {
node.thedef = sym;
}
node.reference();
var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) {
sym = self.def_global(node);
}
node.thedef = sym;
node.reference(options);
return true;
}
});
@@ -201,8 +214,20 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
AST_Toplevel.DEFMETHOD("def_global", function(node){
var globals = this.globals, name = node.name;
if (globals.has(name)) {
return globals.get(name);
} else {
var g = new SymbolDef(this, globals.size(), node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
return g;
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(){
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
@@ -210,28 +235,31 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.parent_scope = null; // the parent scope
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
this.cname = -1; // the current index for mangling functions/variables
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
});
AST_Scope.DEFMETHOD("strict", function(){
return this.has_directive("use strict");
});
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false;
var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end });
var def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def);
});
AST_SymbolRef.DEFMETHOD("reference", function() {
AST_SymbolRef.DEFMETHOD("reference", function(options) {
var def = this.definition();
def.references.push(this);
var s = this.scope;
while (s) {
push_uniq(s.enclosed, def);
if (options.keep_fnames) {
s.functions.each(function(d) {
push_uniq(def.scope.enclosed, d);
});
}
if (s === def.scope) break;
s = s.parent_scope;
}
this.frame = this.scope.nesting - def.scope.nesting;
});
AST_Scope.DEFMETHOD("find_variable", function(name){
@@ -240,11 +268,6 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("has_directive", function(value){
return this.parent_scope && this.parent_scope.has_directive(value)
|| (this.directives.indexOf(value) >= 0 ? this : null);
});
AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol));
});
@@ -290,18 +313,17 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
// a function expression's argument cannot shadow the function expression's name
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
// the function's mangled_name is null when keep_fnames is true
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
while (true) {
var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
if (!(tricky_def && tricky_def.mangled_name == name))
if (!tricky_name || tricky_name != name)
return name;
}
});
AST_Scope.DEFMETHOD("references", function(sym){
if (sym instanceof AST_Symbol) sym = sym.definition();
return this.enclosed.indexOf(sym) < 0 ? null : sym;
});
AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options);
});
@@ -345,15 +367,19 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
except : [],
eval : false,
sort : false,
sort : false, // Ignored. Flag retained for backwards compatibility.
toplevel : false,
screw_ie8 : false,
screw_ie8 : true,
keep_fnames : false
});
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options);
// Never mangle arguments
options.except.push('arguments');
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of
@@ -384,9 +410,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
a.push(symbol);
}
});
if (options.sort) a.sort(function(a, b){
return b.references.length - a.references.length;
});
to_mangle.push.apply(to_mangle, a);
return;
}

View File

@@ -53,16 +53,21 @@ function SourceMap(options) {
orig_line_diff : 0,
dest_line_diff : 0,
});
var generator = new MOZ_SourceMap.SourceMapGenerator({
file : options.file,
sourceRoot : options.root
});
var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig);
var generator;
if (orig_map) {
generator = MOZ_SourceMap.SourceMapGenerator.fromSourceMap(orig_map);
} else {
generator = new MOZ_SourceMap.SourceMapGenerator({
file : options.file,
sourceRoot : options.root
if (orig_map && Array.isArray(options.orig.sources)) {
orig_map._sources.toArray().forEach(function(source) {
var sourceContent = orig_map.sourceContentFor(source, true);
if (sourceContent) {
generator.setSourceContent(source, sourceContent);
}
});
}
function add(source, gen_line, gen_col, orig_line, orig_col, name) {
if (orig_map) {
var info = orig_map.originalPositionFor({
@@ -83,7 +88,7 @@ function SourceMap(options) {
source : source,
name : name
});
}
};
return {
add : add,
get : function() { return generator },

View File

@@ -64,13 +64,13 @@ TreeTransformer.prototype = new TreeWalker;
x = this;
descend(x, tw);
} else {
tw.stack[tw.stack.length - 1] = x = this.clone();
tw.stack[tw.stack.length - 1] = x = this;
descend(x, tw);
y = tw.after(x, in_list);
if (y !== undefined) x = y;
}
}
tw.pop();
tw.pop(this);
return x;
});
};

View File

@@ -59,10 +59,7 @@ function characters(str) {
};
function member(name, array) {
for (var i = array.length; --i >= 0;)
if (array[i] == name)
return true;
return false;
return array.indexOf(name) >= 0;
};
function find_if(func, array) {
@@ -81,13 +78,28 @@ function repeat_string(str, i) {
return d;
};
function configure_error_stack(fn) {
Object.defineProperty(fn.prototype, "stack", {
get: function() {
var err = new Error(this.message);
err.name = this.name;
try {
throw err;
} catch(e) {
return e.stack;
}
}
});
}
function DefaultsError(msg, defs) {
Error.call(this, msg);
this.msg = msg;
this.message = msg;
this.defs = defs;
};
DefaultsError.prototype = Object.create(Error.prototype);
DefaultsError.prototype.constructor = DefaultsError;
DefaultsError.prototype.name = "DefaultsError";
configure_error_stack(DefaultsError);
DefaultsError.croak = function(msg, defs) {
throw new DefaultsError(msg, defs);
@@ -97,17 +109,17 @@ function defaults(args, defs, croak) {
if (args === true)
args = {};
var ret = args || {};
if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i))
DefaultsError.croak("`" + i + "` is not a supported option", defs);
for (var i in defs) if (defs.hasOwnProperty(i)) {
ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
}
return ret;
};
function merge(obj, ext) {
var count = 0;
for (var i in ext) if (ext.hasOwnProperty(i)) {
for (var i in ext) if (HOP(ext, i)) {
obj[i] = ext[i];
count++;
}
@@ -115,6 +127,8 @@ function merge(obj, ext) {
};
function noop() {};
function return_false() { return false; }
function return_true() { return true; }
var MAP = (function(){
function MAP(a, f, backwards) {
@@ -150,7 +164,7 @@ var MAP = (function(){
}
}
else {
for (i in a) if (a.hasOwnProperty(i)) if (doit()) break;
for (i in a) if (HOP(a, i)) if (doit()) break;
}
return top.concat(ret);
};
@@ -171,7 +185,7 @@ function push_uniq(array, el) {
function string_template(text, props) {
return text.replace(/\{(.+?)\}/g, function(str, p){
return props[p];
return props && props[p];
});
};
@@ -230,10 +244,19 @@ function makePredicate(words) {
}
cats.push([words[i]]);
}
function quote(word) {
return JSON.stringify(word).replace(/[\u2028\u2029]/g, function(s) {
switch (s) {
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
}
return s;
});
}
function compareTo(arr) {
if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
if (arr.length == 1) return f += "return str === " + quote(arr[0]) + ";";
f += "switch(str){";
for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
for (var i = 0; i < arr.length; ++i) f += "case " + quote(arr[i]) + ":";
f += "return true}return false;";
}
// When there are more than three length categories, an outer
@@ -308,3 +331,30 @@ Dictionary.fromObject = function(obj) {
dict._size = merge(dict._values, obj);
return dict;
};
function HOP(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(stack) {
var node = stack.parent(-1);
for (var i = 0, p; p = stack.parent(i); i++) {
if (p instanceof AST_Statement && p.body === node)
return true;
if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) ||
(p instanceof AST_Conditional && p.condition === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_UnaryPostfix && p.expression === node ))
{
node = p;
} else {
return false;
}
}
}

128
npm-shrinkwrap.json generated
View File

@@ -1,128 +0,0 @@
{
"name": "uglify-js",
"version": "2.4.24",
"dependencies": {
"abbrev": {
"version": "1.0.7",
"from": "abbrev@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz"
},
"amdefine": {
"version": "1.0.0",
"from": "amdefine@>=0.0.4",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
},
"async": {
"version": "0.2.10",
"from": "async@>=0.2.6 <0.3.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
},
"camelcase": {
"version": "1.2.1",
"from": "camelcase@>=1.0.2 <2.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
},
"decamelize": {
"version": "1.0.0",
"from": "decamelize@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz"
},
"deep-is": {
"version": "0.1.3",
"from": "deep-is@>=0.1.2 <0.2.0",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz"
},
"esprima": {
"version": "1.1.1",
"from": "esprima@>=1.1.1 <1.2.0",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz"
},
"estraverse": {
"version": "1.5.1",
"from": "estraverse@>=1.5.1 <1.6.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz"
},
"esutils": {
"version": "1.0.0",
"from": "esutils@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz"
},
"fast-levenshtein": {
"version": "1.0.7",
"from": "fast-levenshtein@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz"
},
"levn": {
"version": "0.2.5",
"from": "levn@>=0.2.5 <0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz"
},
"nopt": {
"version": "2.1.2",
"from": "nopt@>=2.1.2 <2.2.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz"
},
"optionator": {
"version": "0.5.0",
"from": "optionator@>=0.5.0 <0.6.0",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz"
},
"prelude-ls": {
"version": "1.1.2",
"from": "prelude-ls@>=1.1.1 <1.2.0",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz"
},
"reflect": {
"version": "0.1.3",
"from": "git://github.com/zaach/reflect.js.git",
"resolved": "git://github.com/zaach/reflect.js.git#286bcd79661c96ecc404357d3c0e35fdb54a6967"
},
"source-map": {
"version": "0.5.1",
"from": "source-map@>=0.5.1 <0.6.0",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.1.tgz"
},
"type-check": {
"version": "0.3.1",
"from": "type-check@>=0.3.1 <0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.1.tgz"
},
"uglify-js": {
"version": "2.4.24",
"from": "git://github.com/mishoo/UglifyJS2.git",
"resolved": "git://github.com/mishoo/UglifyJS2.git#2a06c7758e24a64740473c8031eafbb7fefa213f",
"dependencies": {
"source-map": {
"version": "0.1.34",
"from": "source-map@0.1.34",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz"
}
}
},
"uglify-to-browserify": {
"version": "1.0.2",
"from": "uglify-to-browserify@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz"
},
"window-size": {
"version": "0.1.0",
"from": "window-size@0.1.0",
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
},
"wordwrap": {
"version": "0.0.2",
"from": "wordwrap@0.0.2",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
},
"yargs": {
"version": "3.5.4",
"from": "yargs@>=3.5.4 <3.6.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz"
},
"zeparser": {
"version": "0.0.7",
"from": "git://github.com/qfox/ZeParser.git",
"resolved": "git://github.com/qfox/ZeParser.git#c99240c5ba7054c467733800ff38265958a2dda9"
}
}
}

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "2.5.0",
"version": "2.8.4",
"engines": {
"node": ">=0.8.0"
},
@@ -32,13 +32,14 @@
"async": "~0.2.6",
"source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.5.4"
"yargs": "~3.10.0"
},
"devDependencies": {
"acorn": "~0.6.0",
"escodegen": "~1.3.3",
"esfuzz": "~0.3.1",
"estraverse": "~1.5.1"
"estraverse": "~1.5.1",
"mocha": "~2.3.4"
},
"browserify": {
"transform": [
@@ -48,5 +49,6 @@
"scripts": {
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
"test": "node test/run-tests.js"
}
},
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
}

49
test/benchmark.js Normal file
View File

@@ -0,0 +1,49 @@
#! /usr/bin/env node
// -*- js -*-
"use strict";
var createHash = require("crypto").createHash;
var fork = require("child_process").fork;
var args = process.argv.slice(2);
if (!args.length) {
args.push("-mc", "warnings=false");
}
args.push("--stats");
var urls = [
"https://code.jquery.com/jquery-3.1.1.js",
"https://code.angularjs.org/1.6.1/angular.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.9.0/math.js",
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js",
"https://unpkg.com/react@15.3.2/dist/react.js",
"http://builds.emberjs.com/tags/v2.11.0/ember.prod.js",
"https://cdn.jsdelivr.net/lodash/4.17.4/lodash.js",
"https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js",
];
var results = {};
var remaining = 2 * urls.length;
function done() {
if (!--remaining) {
urls.forEach(function(url) {
console.log();
console.log(url);
console.log(results[url].time);
console.log("SHA1:", results[url].sha1);
});
}
}
urls.forEach(function(url) {
results[url] = { time: "" };
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
var uglifyjs = fork("bin/uglifyjs", args, { silent: true });
res.pipe(uglifyjs.stdin);
uglifyjs.stdout.pipe(createHash("sha1")).on("data", function(data) {
results[url].sha1 = data.toString("hex");
done();
});
uglifyjs.stderr.setEncoding("utf8");
uglifyjs.stderr.on("data", function(data) {
results[url].time += data;
}).on("end", done)
});
});

View File

@@ -21,10 +21,19 @@ constant_join: {
input: {
var a = [ "foo", "bar", "baz" ].join("");
var a1 = [ "foo", "bar", "baz" ].join();
var a2 = [ "foo", "bar", "baz" ].join(null);
var a3 = [ "foo", "bar", "baz" ].join(void 0);
var a4 = [ "foo", , "baz" ].join();
var a5 = [ "foo", null, "baz" ].join();
var a6 = [ "foo", void 0, "baz" ].join();
var b = [ "foo", 1, 2, 3, "bar" ].join("");
var c = [ boo(), "foo", 1, 2, 3, "bar", bar() ].join("");
var c1 = [ boo(), bar(), "foo", 1, 2, 3, "bar", bar() ].join("");
var c2 = [ 1, 2, "foo", "bar", baz() ].join("");
var c3 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join("");
var c4 = [ 1, 2, null, undefined, "foo", "bar", baz() ].join("");
var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join();
var c6 = [ 1, 2, null, undefined, "foo", "bar", baz() ].join();
var d = [ "foo", 1 + 2 + "bar", "baz" ].join("-");
var e = [].join(foo + bar);
var f = [].join("");
@@ -33,10 +42,19 @@ constant_join: {
expect: {
var a = "foobarbaz";
var a1 = "foo,bar,baz";
var a2 = "foonullbarnullbaz";
var a3 = "foo,bar,baz";
var a4 = "foo,,baz";
var a5 = "foo,,baz";
var a6 = "foo,,baz";
var b = "foo123bar";
var c = boo() + "foo123bar" + bar();
var c1 = "" + boo() + bar() + "foo123bar" + bar();
var c2 = "12foobar" + baz();
var c3 = boo() + bar() + "foo123bar" + bar() + "foo";
var c4 = "12foobar" + baz();
var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join();
var c6 = [ "1,2,,,foo,bar", baz() ].join();
var d = "foo-3bar-baz";
var e = [].join(foo + bar);
var f = "";
@@ -72,3 +90,88 @@ constant_join_2: {
var f = "strstr" + variable + "foobarmoo" + foo;
}
}
constant_join_3: {
options = {
unsafe: true,
evaluate: true,
};
input: {
var a = [ null ].join();
var b = [ , ].join();
var c = [ , 1, , 3 ].join();
var d = [ foo ].join();
var e = [ foo, null, undefined, bar ].join("-");
var f = [ foo, bar ].join("");
var g = [ null, "foo", null, bar + "baz" ].join("");
var h = [ null, "foo", null, bar + "baz" ].join("-");
var i = [ "foo" + bar, null, baz + "moo" ].join("");
var j = [ foo + "bar", baz ].join("");
var k = [ foo, "bar" + baz ].join("");
var l = [ foo, bar + "baz" ].join("");
}
expect: {
var a = "";
var b = "";
var c = ",1,,3";
var d = "" + foo;
var e = [ foo, "-", bar ].join("-");
var f = "" + foo + bar;
var g = "foo" + bar + "baz";
var h = [ "-foo-", bar + "baz" ].join("-");
var i = "foo" + bar + baz + "moo";
var j = foo + "bar" + baz;
var k = foo + "bar" + baz;
var l = foo + (bar + "baz");
}
}
for_loop: {
options = {
unsafe : true,
unused : true,
evaluate : true,
reduce_vars : true
};
input: {
function f0() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
console.log(a[i]);
}
}
function f1() {
var a = [1, 2, 3];
for (var i = 0, len = a.length; i < len; i++) {
console.log(a[i]);
}
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
a[i]++;
}
}
}
expect: {
function f0() {
var a = [1, 2, 3];
for (var i = 0; i < 3; i++)
console.log(a[i]);
}
function f1() {
var a = [1, 2, 3];
for (var i = 0; i < 3; i++)
console.log(a[i]);
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++)
a[i]++;
}
}
}

36
test/compress/ascii.js Normal file
View File

@@ -0,0 +1,36 @@
ascii_only_true: {
options = {}
beautify = {
ascii_only : true,
screw_ie8 : true,
beautify : false,
}
input: {
function f() {
return "\x000\x001\x007\x008\x00" +
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
"\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\'}'
}
ascii_only_false: {
options = {}
beautify = {
ascii_only : false,
screw_ie8 : true,
beautify : false,
}
input: {
function f() {
return "\x000\x001\x007\x008\x00" +
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
"\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\'}'
}

View File

@@ -90,13 +90,13 @@ asm_mixed: {
}
function no_asm_GeometricMean(stdlib, foreign, buffer) {
function logSum(start, end) {
start = 0 | start, end = 0 | end;
start |= 0, end |= 0;
var sum = 0, p = 0, q = 0;
for (p = start << 3, q = end << 3; (0 | q) > (0 | p); p = p + 8 | 0) sum += +log(values[p >> 3]);
for (p = start << 3, q = end << 3; (0 | p) < (0 | q); p = p + 8 | 0) sum += +log(values[p >> 3]);
return +sum;
}
function geometricMean(start, end) {
return start = 0 | start, end = 0 | end, +exp(+logSum(start, end) / +(end - start | 0));
return start |= 0, end |= 0, +exp(+logSum(start, end) / +(end - start | 0));
}
var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
return { geometricMean: geometricMean };

238
test/compress/assignment.js Normal file
View File

@@ -0,0 +1,238 @@
op_equals_left_local_var: {
options = {
evaluate: true,
}
input: {
var x;
x = x + 3;
x = x - 3;
x = x / 3;
x = x * 3;
x = x >> 3;
x = x << 3;
x = x >>> 3;
x = x | 3;
x = x ^ 3;
x = x % 3;
x = x & 3;
x = x + g();
x = x - g();
x = x / g();
x = x * g();
x = x >> g();
x = x << g();
x = x >>> g();
x = x | g();
x = x ^ g();
x = x % g();
x = x & g();
}
expect: {
var x;
x += 3;
x -= 3;
x /= 3;
x *= 3;
x >>= 3;
x <<= 3;
x >>>= 3;
x |= 3;
x ^= 3;
x %= 3;
x &= 3;
x += g();
x -= g();
x /= g();
x *= g();
x >>= g();
x <<= g();
x >>>= g();
x |= g();
x ^= g();
x %= g();
x &= g();
}
}
op_equals_right_local_var: {
options = {
evaluate: true,
}
input: {
var x;
x = (x -= 2) ^ x;
x = 3 + x;
x = 3 - x;
x = 3 / x;
x = 3 * x;
x = 3 >> x;
x = 3 << x;
x = 3 >>> x;
x = 3 | x;
x = 3 ^ x;
x = 3 % x;
x = 3 & x;
x = g() + x;
x = g() - x;
x = g() / x;
x = g() * x;
x = g() >> x;
x = g() << x;
x = g() >>> x;
x = g() | x;
x = g() ^ x;
x = g() % x;
x = g() & x;
}
expect: {
var x;
x = (x -= 2) ^ x;
x = 3 + x;
x = 3 - x;
x = 3 / x;
x *= 3;
x = 3 >> x;
x = 3 << x;
x = 3 >>> x;
x |= 3;
x ^= 3;
x = 3 % x;
x &= 3;
x = g() + x;
x = g() - x;
x = g() / x;
x = g() * x;
x = g() >> x;
x = g() << x;
x = g() >>> x;
x = g() | x;
x = g() ^ x;
x = g() % x;
x = g() & x;
}
}
op_equals_left_global_var: {
options = {
evaluate: true,
}
input: {
x = x + 3;
x = x - 3;
x = x / 3;
x = x * 3;
x = x >> 3;
x = x << 3;
x = x >>> 3;
x = x | 3;
x = x ^ 3;
x = x % 3;
x = x & 3;
x = x + g();
x = x - g();
x = x / g();
x = x * g();
x = x >> g();
x = x << g();
x = x >>> g();
x = x | g();
x = x ^ g();
x = x % g();
x = x & g();
}
expect: {
x += 3;
x -= 3;
x /= 3;
x *= 3;
x >>= 3;
x <<= 3;
x >>>= 3;
x |= 3;
x ^= 3;
x %= 3;
x &= 3;
x += g();
x -= g();
x /= g();
x *= g();
x >>= g();
x <<= g();
x >>>= g();
x |= g();
x ^= g();
x %= g();
x &= g();
}
}
op_equals_right_global_var: {
options = {
evaluate: true,
}
input: {
x = (x -= 2) ^ x;
x = 3 + x;
x = 3 - x;
x = 3 / x;
x = 3 * x;
x = 3 >> x;
x = 3 << x;
x = 3 >>> x;
x = 3 | x;
x = 3 ^ x;
x = 3 % x;
x = 3 & x;
x = g() + x;
x = g() - x;
x = g() / x;
x = g() * x;
x = g() >> x;
x = g() << x;
x = g() >>> x;
x = g() | x;
x = g() ^ x;
x = g() % x;
x = g() & x;
}
expect: {
x = (x -= 2) ^ x;
x = 3 + x;
x = 3 - x;
x = 3 / x;
x *= 3;
x = 3 >> x;
x = 3 << x;
x = 3 >>> x;
x |= 3;
x ^= 3;
x = 3 % x;
x &= 3;
x = g() + x;
x = g() - x;
x = g() / x;
x = g() * x;
x = g() >> x;
x = g() << x;
x = g() >>> x;
x = g() | x;
x = g() ^ x;
x = g() % x;
x = g() & x;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
keep_comparisons: {
options = {
comparisons: true,
unsafe_comps: false
}
input: {
var obj1 = {
valueOf: function() {triggeredFirst();}
}
var obj2 = {
valueOf: function() {triggeredSecond();}
}
var result1 = obj1 <= obj2;
var result2 = obj1 < obj2;
var result3 = obj1 >= obj2;
var result4 = obj1 > obj2;
}
expect: {
var obj1 = {
valueOf: function() {triggeredFirst();}
}
var obj2 = {
valueOf: function() {triggeredSecond();}
}
var result1 = obj1 <= obj2;
var result2 = obj1 < obj2;
var result3 = obj1 >= obj2;
var result4 = obj1 > obj2;
}
}
keep_comparisons_with_unsafe_comps: {
options = {
comparisons: true,
unsafe_comps: true
}
input: {
var obj1 = {
valueOf: function() {triggeredFirst();}
}
var obj2 = {
valueOf: function() {triggeredSecond();}
}
var result1 = obj1 <= obj2;
var result2 = obj1 < obj2;
var result3 = obj1 >= obj2;
var result4 = obj1 > obj2;
}
expect: {
var obj1 = {
valueOf: function() {triggeredFirst();}
}
var obj2 = {
valueOf: function() {triggeredSecond();}
}
var result1 = obj2 >= obj1;
var result2 = obj2 > obj1;
var result3 = obj1 >= obj2;
var result4 = obj1 > obj2;
}
}
dont_change_in_or_instanceof_expressions: {
input: {
1 in 1;
null in null;
1 instanceof 1;
null instanceof null;
}
expect: {
1 in 1;
null in null;
1 instanceof 1;
null instanceof null;
}
}

View File

@@ -11,6 +11,9 @@ concat_1: {
var d = 1 + x() + 2 + 3 + "boo";
var e = 1 + x() + 2 + "X" + 3 + "boo";
// be careful with concatentation with "\0" with octal-looking strings.
var f = "\0" + 360 + "\0" + 8 + "\0";
}
expect: {
var a = "foobar" + x() + "moofoo" + y() + "xyz" + q();
@@ -18,5 +21,146 @@ 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";
}
}
concat_2: {
options = {};
input: {
console.log(
1 + (2 + 3),
1 + (2 + "3"),
1 + ("2" + 3),
1 + ("2" + "3"),
"1" + (2 + 3),
"1" + (2 + "3"),
"1" + ("2" + 3),
"1" + ("2" + "3")
);
}
expect: {
console.log(
1 + (2 + 3),
1 + (2 + "3"),
1 + "2" + 3,
1 + "2" + "3",
"1" + (2 + 3),
"1" + 2 + "3",
"1" + "2" + 3,
"1" + "2" + "3"
);
}
}
concat_3: {
options = {};
input: {
console.log(
1 + 2 + (3 + 4 + 5),
1 + 2 + (3 + 4 + "5"),
1 + 2 + (3 + "4" + 5),
1 + 2 + (3 + "4" + "5"),
1 + 2 + ("3" + 4 + 5),
1 + 2 + ("3" + 4 + "5"),
1 + 2 + ("3" + "4" + 5),
1 + 2 + ("3" + "4" + "5")
);
}
expect: {
console.log(
1 + 2 + (3 + 4 + 5),
1 + 2 + (3 + 4 + "5"),
1 + 2 + (3 + "4") + 5,
1 + 2 + (3 + "4") + "5",
1 + 2 + "3" + 4 + 5,
1 + 2 + "3" + 4 + "5",
1 + 2 + "3" + "4" + 5,
1 + 2 + "3" + "4" + "5"
);
}
}
concat_4: {
options = {};
input: {
console.log(
1 + "2" + (3 + 4 + 5),
1 + "2" + (3 + 4 + "5"),
1 + "2" + (3 + "4" + 5),
1 + "2" + (3 + "4" + "5"),
1 + "2" + ("3" + 4 + 5),
1 + "2" + ("3" + 4 + "5"),
1 + "2" + ("3" + "4" + 5),
1 + "2" + ("3" + "4" + "5")
);
}
expect: {
console.log(
1 + "2" + (3 + 4 + 5),
1 + "2" + (3 + 4) + "5",
1 + "2" + 3 + "4" + 5,
1 + "2" + 3 + "4" + "5",
1 + "2" + "3" + 4 + 5,
1 + "2" + "3" + 4 + "5",
1 + "2" + "3" + "4" + 5,
1 + "2" + "3" + "4" + "5"
);
}
}
concat_5: {
options = {};
input: {
console.log(
"1" + 2 + (3 + 4 + 5),
"1" + 2 + (3 + 4 + "5"),
"1" + 2 + (3 + "4" + 5),
"1" + 2 + (3 + "4" + "5"),
"1" + 2 + ("3" + 4 + 5),
"1" + 2 + ("3" + 4 + "5"),
"1" + 2 + ("3" + "4" + 5),
"1" + 2 + ("3" + "4" + "5")
);
}
expect: {
console.log(
"1" + 2 + (3 + 4 + 5),
"1" + 2 + (3 + 4) + "5",
"1" + 2 + 3 + "4" + 5,
"1" + 2 + 3 + "4" + "5",
"1" + 2 + "3" + 4 + 5,
"1" + 2 + "3" + 4 + "5",
"1" + 2 + "3" + "4" + 5,
"1" + 2 + "3" + "4" + "5"
);
}
}
concat_6: {
options = {};
input: {
console.log(
"1" + "2" + (3 + 4 + 5),
"1" + "2" + (3 + 4 + "5"),
"1" + "2" + (3 + "4" + 5),
"1" + "2" + (3 + "4" + "5"),
"1" + "2" + ("3" + 4 + 5),
"1" + "2" + ("3" + 4 + "5"),
"1" + "2" + ("3" + "4" + 5),
"1" + "2" + ("3" + "4" + "5")
);
}
expect: {
console.log(
"1" + "2" + (3 + 4 + 5),
"1" + "2" + (3 + 4) + "5",
"1" + "2" + 3 + "4" + 5,
"1" + "2" + 3 + "4" + "5",
"1" + "2" + "3" + 4 + 5,
"1" + "2" + "3" + 4 + "5",
"1" + "2" + "3" + "4" + 5,
"1" + "2" + "3" + "4" + "5"
);
}
}

View File

@@ -50,7 +50,8 @@ ifs_3_should_warn: {
conditionals : true,
dead_code : true,
evaluate : true,
booleans : true
booleans : true,
side_effects : true,
};
input: {
var x, y;
@@ -135,16 +136,28 @@ ifs_6: {
comparisons: true
};
input: {
var x;
var x, y;
if (!foo && !bar && !baz && !boo) {
x = 10;
} else {
x = 20;
}
if (y) {
x[foo] = 10;
} else {
x[foo] = 20;
}
if (foo) {
x[bar] = 10;
} else {
x[bar] = 20;
}
}
expect: {
var x;
var x, y;
x = foo || bar || baz || boo ? 20 : 10;
x[foo] = y ? 10 : 20;
foo ? x[bar] = 10 : x[bar] = 20;
}
}
@@ -159,10 +172,16 @@ cond_1: {
} else {
do_something(y);
}
if (some_condition()) {
side_effects(x);
} else {
side_effects(y);
}
}
expect: {
var do_something;
do_something(some_condition() ? x : y);
some_condition() ? side_effects(x) : side_effects(y);
}
}
@@ -213,10 +232,16 @@ cond_4: {
} else {
do_something();
}
if (some_condition()) {
side_effects();
} else {
side_effects();
}
}
expect: {
var do_something;
some_condition(), do_something();
some_condition(), side_effects();
}
}
@@ -250,7 +275,8 @@ cond_5: {
cond_7: {
options = {
conditionals: true,
evaluate : true
evaluate : true,
side_effects: true,
};
input: {
var x, y, z, a, b;
@@ -332,215 +358,442 @@ cond_7_1: {
cond_8: {
options = {
conditionals: true,
evaluate : true
evaluate : true,
booleans : false
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
if (condition) {
a = true;
} else {
a = false;
}
if (condition) {
a = !0;
} else {
a = !1;
}
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
// don't compress these
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !!condition;
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = condition ? 1 : false;
a = condition ? 0 : true;
a = !!condition;
a = !condition();
a = !condition;
a = !condition;
a = !!condition && 1;
a = !condition || 0;
a = condition ? 1 : 0;
}
}
conditional_and: {
cond_8b: {
options = {
conditionals: true,
evaluate : true
evaluate : true,
booleans : true
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = true && condition;
a = 1 && console.log("a");
a = 2 * 3 && 2 * condition;
a = 5 == 5 && condition + 3;
a = "string" && 4 - condition;
a = 5 + "" && condition / 5;
a = -4.5 && 6 << condition;
a = 6 && 7;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
a = false && condition;
a = NaN && console.log("b");
a = 0 && console.log("c");
a = undefined && 2 * condition;
a = null && condition + 3;
a = 2 * 3 - 6 && 4 - condition;
a = 10 == 7 && condition / 5;
a = !"string" && 6 % condition;
a = 0 && 7;
if (condition) {
a = true;
} else {
a = false;
}
// don't compress these
if (condition) {
a = !0;
} else {
a = !1;
}
a = condition && true;
a = console.log("a") && 2;
a = 4 - condition && "string";
a = 6 << condition && -4.5;
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition && false;
a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && undefined;
a = condition + 3 && null;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = condition;
a = console.log("a");
a = 2 * condition;
a = condition + 3;
a = 4 - condition;
a = condition / 5;
a = 6 << condition;
a = 7;
a = !!condition;
a = !condition;
a = !!condition();
a = false;
a = NaN;
a = 0;
a = void 0;
a = null;
a = 0;
a = false;
a = false;
a = 0;
a = !!condition;
a = !!condition;
a = condition && true;
a = console.log("a") && 2;
a = 4 - condition && "string";
a = 6 << condition && -4.5;
a = !condition;
a = !!condition;
a = !condition();
a = condition && false;
a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && void 0;
a = condition + 3 && null;
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !condition;
a = !!condition && 1;
a = !condition || 0;
a = condition ? 1 : 0;
}
}
conditional_or: {
cond_8c: {
options = {
conditionals: true,
evaluate : true
evaluate : false,
booleans : false
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = true || condition;
a = 1 || console.log("a");
a = 2 * 3 || 2 * condition;
a = 5 == 5 || condition + 3;
a = "string" || 4 - condition;
a = 5 + "" || condition / 5;
a = -4.5 || 6 << condition;
a = 6 || 7;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
a = false || condition;
a = 0 || console.log("b");
a = NaN || console.log("c");
a = undefined || 2 * condition;
a = null || condition + 3;
a = 2 * 3 - 6 || 4 - condition;
a = 10 == 7 || condition / 5;
a = !"string" || 6 % condition;
a = null || 7;
if (condition) {
a = true;
} else {
a = false;
}
a = console.log(undefined && condition || null);
a = console.log(undefined || condition && null);
if (condition) {
a = !0;
} else {
a = !1;
}
// don't compress these
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition || true;
a = console.log("a") || 2;
a = 4 - condition || "string";
a = 6 << condition || -4.5;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
a = condition || false;
a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || undefined;
a = condition + 3 || null;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = true;
a = 1;
a = 6;
a = true;
a = "string";
a = "5";
a = -4.5;
a = 6;
a = !!condition;
a = !condition;
a = !!condition() || !-3.5;
a = condition;
a = console.log("b");
a = console.log("c");
a = 2 * condition;
a = condition + 3;
a = 4 - condition;
a = condition / 5;
a = 6 % condition;
a = 7;
a = !!condition;
a = !!condition;
a = console.log(null);
a = console.log(condition && null);
a = !condition;
a = !!condition;
a = !condition();
a = condition || true;
a = console.log("a") || 2;
a = 4 - condition || "string";
a = 6 << condition || -4.5;
a = !condition;
a = !!condition;
a = !condition();
a = condition || false;
a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || void 0;
a = condition + 3 || null;
a = !condition;
a = !condition;
a = !!condition && 1;
a = !condition || 0;
a = condition ? 1 : 0;
}
}
ternary_boolean_consequent: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f1() { return a == b ? true : x; }
function f2() { return a == b ? false : x; }
function f3() { return a < b ? !0 : x; }
function f4() { return a < b ? !1 : x; }
function f5() { return c ? !0 : x; }
function f6() { return c ? false : x; }
function f7() { return !c ? true : x; }
function f8() { return !c ? !1 : x; }
}
expect: {
function f1() { return a == b || x; }
function f2() { return a != b && x; }
function f3() { return a < b || x; }
function f4() { return !(a < b) && x; }
function f5() { return !!c || x; }
function f6() { return !c && x; }
function f7() { return !c || x; }
function f8() { return !!c && x; }
}
}
ternary_boolean_alternative: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f1() { return a == b ? x : true; }
function f2() { return a == b ? x : false; }
function f3() { return a < b ? x : !0; }
function f4() { return a < b ? x : !1; }
function f5() { return c ? x : true; }
function f6() { return c ? x : !1; }
function f7() { return !c ? x : !0; }
function f8() { return !c ? x : false; }
}
expect: {
function f1() { return a != b || x; }
function f2() { return a == b && x; }
function f3() { return !(a < b) || x; }
function f4() { return a < b && x; }
function f5() { return !c || x; }
function f6() { return !!c && x; }
function f7() { return !!c || x; }
function f8() { return !c && x; }
}
}
trivial_boolean_ternary_expressions : {
options = {
conditionals: true,
evaluate : true,
booleans : true
};
input: {
f('foo' in m ? true : false);
f('foo' in m ? false : true);
f(g ? true : false);
f(foo() ? true : false);
f("bar" ? true : false);
f(5 ? true : false);
f(5.7 ? true : false);
f(x - y ? true : false);
f(x == y ? true : false);
f(x === y ? !0 : !1);
f(x < y ? !0 : false);
f(x <= y ? true : false);
f(x > y ? true : !1);
f(x >= y ? !0 : !1);
f(g ? false : true);
f(foo() ? false : true);
f("bar" ? false : true);
f(5 ? false : true);
f(5.7 ? false : true);
f(x - y ? false : true);
f(x == y ? !1 : !0);
f(x === y ? false : true);
f(x < y ? false : true);
f(x <= y ? false : !0);
f(x > y ? !1 : true);
f(x >= y ? !1 : !0);
}
expect: {
f('foo' in m);
f(!('foo' in m));
f(!!g);
f(!!foo());
f(!0);
f(!0);
f(!0);
f(!!(x - y));
f(x == y);
f(x === y);
f(x < y);
f(x <= y);
f(x > y);
f(x >= y);
f(!g);
f(!foo());
f(!1);
f(!1);
f(!1);
f(!(x - y));
f(x != y);
f(x !== y);
f(!(x < y));
f(!(x <= y));
f(!(x > y));
f(!(x >= y));
}
}
issue_1154: {
options = {
conditionals: true,
evaluate : true,
booleans : true,
side_effects: true,
};
input: {
function f1(x) { return x ? -1 : -1; }
function f2(x) { return x ? +2 : +2; }
function f3(x) { return x ? ~3 : ~3; }
function f4(x) { return x ? !4 : !4; }
function f5(x) { return x ? void 5 : void 5; }
function f6(x) { return x ? typeof 6 : typeof 6; }
function g1() { return g() ? -1 : -1; }
function g2() { return g() ? +2 : +2; }
function g3() { return g() ? ~3 : ~3; }
function g4() { return g() ? !4 : !4; }
function g5() { return g() ? void 5 : void 5; }
function g6() { return g() ? typeof 6 : typeof 6; }
}
expect: {
function f1(x) { return -1; }
function f2(x) { return 2; }
function f3(x) { return -4; }
function f4(x) { return !1; }
function f5(x) { return; }
function f6(x) { return "number"; }
function g1() { return g(), -1; }
function g2() { return g(), 2; }
function g3() { return g(), -4; }
function g4() { return g(), !1; }
function g5() { return void g(); }
function g6() { return g(), "number"; }
}
}
no_evaluate: {
options = {
conditionals: true,
evaluate : false,
side_effects: true,
}
input: {
function f(b) {
a = b ? !0 : !0;
a = b ? ~1 : ~1;
a = b ? -2 : -2;
a = b ? +3 : +3;
}
}
expect: {
function f(b) {
a = !0;
a = ~1;
a = -2;
a = +3;
}
}
}

165
test/compress/const.js Normal file
View File

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

View File

@@ -1,89 +1,209 @@
dead_code_1: {
options = {
dead_code: true
};
input: {
function f() {
a();
b();
x = 10;
return;
if (x) {
y();
}
}
}
expect: {
function f() {
a();
b();
x = 10;
return;
}
}
}
dead_code_2_should_warn: {
options = {
dead_code: true
};
input: {
function f() {
g();
x = 10;
throw "foo";
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
if (x) {
y();
var x;
function g(){};
// but nested declarations should not be kept.
(function(){
var q;
function y(){};
})();
}
}
}
expect: {
function f() {
g();
x = 10;
throw "foo";
var x;
function g(){};
}
}
}
dead_code_constant_boolean_should_warn_more: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
while (!((foo && bar) || (x + "0"))) {
console.log("unreachable");
var foo;
function bar() {}
}
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
}
}
expect: {
var foo;
function bar() {}
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo;
}
}
dead_code_1: {
options = {
dead_code: true
};
input: {
function f() {
a();
b();
x = 10;
return;
if (x) {
y();
}
}
}
expect: {
function f() {
a();
b();
x = 10;
return;
}
}
}
dead_code_2_should_warn: {
options = {
dead_code: true
};
input: {
function f() {
g();
x = 10;
throw "foo";
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
if (x) {
y();
var x;
function g(){};
// but nested declarations should not be kept.
(function(){
var q;
function y(){};
})();
}
}
}
expect: {
function f() {
g();
x = 10;
throw "foo";
var x;
function g(){};
}
}
}
dead_code_constant_boolean_should_warn_more: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
side_effects : true,
};
input: {
while (!((foo && bar) || (x + "0"))) {
console.log("unreachable");
var foo;
function bar() {}
}
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
}
}
expect: {
var foo;
function bar() {}
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo;
}
}
dead_code_const_declaration: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
};
input: {
var unused;
const CONST_FOO = false;
if (CONST_FOO) {
console.log("unreachable");
var moo;
function bar() {}
}
}
expect: {
var unused;
const CONST_FOO = !1;
var moo;
function bar() {}
}
}
dead_code_const_annotation: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
};
input: {
var unused;
/** @const */ var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
var moo;
function bar() {}
}
}
dead_code_const_annotation_regex: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
var unused;
// @constraint this shouldn't be a constant
var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("reachable");
}
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
CONST_FOO_ANN && console.log('reachable');
}
}
dead_code_const_annotation_complex_scope: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
};
input: {
var unused_var;
/** @const */ var test = 'test';
// @const
var CONST_FOO_ANN = false;
var unused_var_2;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
if (test === 'test') {
var beef = 'good';
/** @const */ var meat = 'beef';
var pork = 'bad';
if (meat === 'pork') {
console.log('also unreachable');
} else if (pork === 'good') {
console.log('reached, not const');
}
}
}
expect: {
var unused_var;
var test = 'test';
var CONST_FOO_ANN = !1;
var unused_var_2;
var moo;
function bar() {}
var beef = 'good';
var meat = 'beef';
var pork = 'bad';
}
}

View File

@@ -1,24 +1,24 @@
drop_console_1: {
options = {};
input: {
console.log('foo');
console.log.apply(console, arguments);
}
expect: {
console.log('foo');
console.log.apply(console, arguments);
}
}
drop_console_1: {
options = { drop_console: true };
input: {
console.log('foo');
console.log.apply(console, arguments);
}
expect: {
// with regular compression these will be stripped out as well
void 0;
void 0;
}
}
drop_console_1: {
options = {};
input: {
console.log('foo');
console.log.apply(console, arguments);
}
expect: {
console.log('foo');
console.log.apply(console, arguments);
}
}
drop_console_2: {
options = { drop_console: true };
input: {
console.log('foo');
console.log.apply(console, arguments);
}
expect: {
// with regular compression these will be stripped out as well
void 0;
void 0;
}
}

View File

@@ -177,3 +177,505 @@ keep_fnames: {
}
}
}
drop_assign: {
options = { unused: true };
input: {
function f1() {
var a;
a = 1;
}
function f2() {
var a = 1;
a = 2;
}
function f3(a) {
a = 1;
}
function f4() {
var a;
return a = 1;
}
function f5() {
var a;
return function() {
a = 1;
}
}
}
expect: {
function f1() {
1;
}
function f2() {
2;
}
function f3(a) {
1;
}
function f4() {
return 1;
}
function f5() {
var a;
return function() {
a = 1;
}
}
}
}
keep_assign: {
options = { unused: "keep_assign" };
input: {
function f1() {
var a;
a = 1;
}
function f2() {
var a = 1;
a = 2;
}
function f3(a) {
a = 1;
}
function f4() {
var a;
return a = 1;
}
function f5() {
var a;
return function() {
a = 1;
}
}
}
expect: {
function f1() {
var a;
a = 1;
}
function f2() {
var a = 1;
a = 2;
}
function f3(a) {
a = 1;
}
function f4() {
var a;
return a = 1;
}
function f5() {
var a;
return function() {
a = 1;
}
}
}
}
drop_toplevel_funcs: {
options = { toplevel: "funcs", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, b = 1, c = g;
a = 2;
function g() {}
console.log(b = 3);
}
}
drop_toplevel_vars: {
options = { toplevel: "vars", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var c = g;
function f(d) {
return function() {
c = 2;
}
}
2;
function g() {}
function h() {}
console.log(3);
}
}
drop_toplevel_vars_fargs: {
options = { keep_fargs: false, toplevel: "vars", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var c = g;
function f() {
return function() {
c = 2;
}
}
2;
function g() {}
function h() {}
console.log(3);
}
}
drop_toplevel_all: {
options = { toplevel: true, unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
2;
console.log(3);
}
}
drop_toplevel_retain: {
options = { top_retain: "f,a,o", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_retain_array: {
options = { top_retain: [ "f", "a", "o" ], unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_retain_regex: {
options = { top_retain: /^[fao]$/, unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_all_retain: {
options = { toplevel: true, top_retain: "f,a,o", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_funcs_retain: {
options = { toplevel: "funcs", top_retain: "f,a,o", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
console.log(b = 3);
}
}
drop_toplevel_vars_retain: {
options = { toplevel: "vars", top_retain: "f,a,o", unused: true };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(3);
}
}
drop_toplevel_keep_assign: {
options = { toplevel: true, unused: "keep_assign" };
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
}
a = 2;
function g() {}
function h() {}
console.log(b = 3);
}
expect: {
var a, b = 1;
a = 2;
console.log(b = 3);
}
}
drop_fargs: {
options = {
keep_fargs: false,
unused: true,
}
input: {
function f(a) {
var b = a;
}
}
expect: {
function f() {}
}
}
drop_fnames: {
options = {
keep_fnames: false,
unused: true,
}
input: {
function f() {
return function g() {
var a = g;
};
}
}
expect: {
function f() {
return function() {};
}
}
}
global_var: {
options = {
side_effects: true,
unused: true,
}
input: {
var a;
function foo(b) {
a;
b;
c;
typeof c === "undefined";
c + b + a;
b && b.ar();
return b;
}
}
expect: {
var a;
function foo(b) {
c;
c;
b && b.ar();
return b;
}
}
}
iife: {
options = {
side_effects: true,
unused: true,
}
input: {
function f() {
var a;
~function() {}(b);
}
}
expect: {
function f() {
~function() {}(b);
}
}
}
drop_value: {
options = {
side_effects: true,
}
input: {
(1, [2, foo()], 3, {a:1, b:bar()});
}
expect: {
foo(), bar();
}
}
const_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
const b = 2;
return 1 + b;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
expect: {
function f() {
return 3;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
}

684
test/compress/evaluate.js Normal file
View File

@@ -0,0 +1,684 @@
and: {
options = {
evaluate: true
}
input: {
var a;
// compress these
a = true && condition;
a = 1 && console.log("a");
a = 2 * 3 && 2 * condition;
a = 5 == 5 && condition + 3;
a = "string" && 4 - condition;
a = 5 + "" && condition / 5;
a = -4.5 && 6 << condition;
a = 6 && 7;
a = false && condition;
a = NaN && console.log("b");
a = 0 && console.log("c");
a = undefined && 2 * condition;
a = null && condition + 3;
a = 2 * 3 - 6 && 4 - condition;
a = 10 == 7 && condition / 5;
a = !"string" && 6 % condition;
a = 0 && 7;
// don't compress these
a = condition && true;
a = console.log("a") && 2;
a = 4 - condition && "string";
a = 6 << condition && -4.5;
a = condition && false;
a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && undefined;
a = condition + 3 && null;
}
expect: {
var a;
a = condition;
a = console.log("a");
a = 2 * condition;
a = condition + 3;
a = 4 - condition;
a = condition / 5;
a = 6 << condition;
a = 7;
a = false;
a = NaN;
a = 0;
a = void 0;
a = null;
a = 0;
a = false;
a = false;
a = 0;
a = condition && true;
a = console.log("a") && 2;
a = 4 - condition && "string";
a = 6 << condition && -4.5;
a = condition && false;
a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && void 0;
a = condition + 3 && null;
}
}
or: {
options = {
evaluate: true
}
input: {
var a;
// compress these
a = true || condition;
a = 1 || console.log("a");
a = 2 * 3 || 2 * condition;
a = 5 == 5 || condition + 3;
a = "string" || 4 - condition;
a = 5 + "" || condition / 5;
a = -4.5 || 6 << condition;
a = 6 || 7;
a = false || condition;
a = 0 || console.log("b");
a = NaN || console.log("c");
a = undefined || 2 * condition;
a = null || condition + 3;
a = 2 * 3 - 6 || 4 - condition;
a = 10 == 7 || condition / 5;
a = !"string" || 6 % condition;
a = null || 7;
a = console.log(undefined && condition || null);
a = console.log(undefined || condition && null);
// don't compress these
a = condition || true;
a = console.log("a") || 2;
a = 4 - condition || "string";
a = 6 << condition || -4.5;
a = condition || false;
a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || undefined;
a = condition + 3 || null;
}
expect: {
var a;
a = true;
a = 1;
a = 6;
a = true;
a = "string";
a = "5";
a = -4.5;
a = 6;
a = condition;
a = console.log("b");
a = console.log("c");
a = 2 * condition;
a = condition + 3;
a = 4 - condition;
a = condition / 5;
a = 6 % condition;
a = 7;
a = console.log(null);
a = console.log(condition && null);
a = condition || true;
a = console.log("a") || 2;
a = 4 - condition || "string";
a = 6 << condition || -4.5;
a = condition || false;
a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || void 0;
a = condition + 3 || null;
}
}
unary_prefix: {
options = {
evaluate: true
}
input: {
a = !0 && b;
a = !0 || b;
a = ~1 && b;
a = ~1 || b;
a = -2 && b;
a = -2 || b;
a = +3 && b;
a = +3 || b;
}
expect: {
a = b;
a = !0;
a = b;
a = -2;
a = b;
a = -2;
a = b;
a = 3;
}
}
negative_zero: {
options = { evaluate: true }
input: {
console.log(
-"",
- -"",
1 / (-0),
1 / (-"")
);
}
expect: {
console.log(
-0,
0,
1 / (-0),
1 / (-0)
);
}
}
positive_zero: {
options = { evaluate: true }
input: {
console.log(
+"",
+ -"",
1 / (+0),
1 / (+"")
);
}
expect: {
console.log(
0,
-0,
1 / (0),
1 / (0)
);
}
}
unsafe_constant: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
true.a,
false.a,
null.a,
undefined.a
);
}
expect: {
console.log(
true.a,
false.a,
null.a,
(void 0).a
);
}
}
unsafe_object: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({a:1}) + 1,
({a:1}).a + 1,
({a:1}).b + 1,
({a:1}).a.b + 1
);
}
expect: {
console.log(
({a:1}) + 1,
2,
({a:1}).b + 1,
1..b + 1
);
}
}
unsafe_object_nested: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({a:{b:1}}) + 1,
({a:{b:1}}).a + 1,
({a:{b:1}}).b + 1,
({a:{b:1}}).a.b + 1
);
}
expect: {
console.log(
({a:{b:1}}) + 1,
({a:{b:1}}).a + 1,
({a:{b:1}}).b + 1,
2
);
}
}
unsafe_object_complex: {
options = {
evaluate : true,
unsafe : true
}
input: {
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
);
}
expect: {
console.log(
({a:{b:1},b:1}) + 1,
({a:{b:1},b:1}).a + 1,
2,
2
);
}
}
unsafe_object_repeated: {
options = {
evaluate : true,
unsafe : true
}
input: {
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
);
}
expect: {
console.log(
({a:{b:1},a:1}) + 1,
2,
({a:{b:1},a:1}).b + 1,
1..b + 1
);
}
}
unsafe_object_accessor: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
}
input: {
function f() {
var a = {
get b() {},
set b() {}
};
return {a:a};
}
}
expect: {
function f() {
var a = {
get b() {},
set b() {}
};
return {a:a};
}
}
}
unsafe_function: {
options = {
evaluate : true,
unsafe : true
}
input: {
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
);
}
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
);
}
}
unsafe_integer_key: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({0:1}) + 1,
({0:1})[0] + 1,
({0:1})["0"] + 1,
({0:1})[1] + 1,
({0:1})[0][1] + 1,
({0:1})[0]["1"] + 1
);
}
expect: {
console.log(
({0:1}) + 1,
2,
2,
({0:1})[1] + 1,
1[1] + 1,
1["1"] + 1
);
}
}
unsafe_integer_key_complex: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({0:{1:1},1:1}) + 1,
({0:{1:1},1:1})[0] + 1,
({0:{1:1},1:1})["0"] + 1,
({0:{1:1},1:1})[1] + 1,
({0:{1:1},1:1})[0][1] + 1,
({0:{1:1},1:1})[0]["1"] + 1
);
}
expect: {
console.log(
({0:{1:1},1:1}) + 1,
"[object Object]1",
"[object Object]1",
2,
2,
2
);
}
}
unsafe_float_key: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({2.72:1}) + 1,
({2.72:1})[2.72] + 1,
({2.72:1})["2.72"] + 1,
({2.72:1})[3.14] + 1,
({2.72:1})[2.72][3.14] + 1,
({2.72:1})[2.72]["3.14"] + 1
);
}
expect: {
console.log(
({2.72:1}) + 1,
2,
2,
({2.72:1})[3.14] + 1,
1[3.14] + 1,
1["3.14"] + 1
);
}
}
unsafe_float_key_complex: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
({2.72:{3.14:1},3.14:1}) + 1,
({2.72:{3.14:1},3.14:1})[2.72] + 1,
({2.72:{3.14:1},3.14:1})["2.72"] + 1,
({2.72:{3.14:1},3.14:1})[3.14] + 1,
({2.72:{3.14:1},3.14:1})[2.72][3.14] + 1,
({2.72:{3.14:1},3.14:1})[2.72]["3.14"] + 1
);
}
expect: {
console.log(
"[object Object]1",
"[object Object]1",
"[object Object]1",
2,
2,
2
);
}
}
unsafe_array: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
[1, , 3][1],
[1, 2, 3, a] + 1,
[1, 2, 3, 4] + 1,
[1, 2, 3, a][0] + 1,
[1, 2, 3, 4][0] + 1,
[1, 2, 3, 4][6 - 5] + 1,
[1, , 3, 4][6 - 5] + 1,
[[1, 2], [3, 4]][0] + 1,
[[1, 2], [3, 4]][6 - 5][1] + 1,
[[1, 2], , [3, 4]][6 - 5][1] + 1
);
}
expect: {
console.log(
void 0,
[1, 2, 3, a] + 1,
"1,2,3,41",
[1, 2, 3, a][0] + 1,
2,
3,
NaN,
"1,21",
5,
(void 0)[1] + 1
);
}
}
unsafe_string: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234" + 1,
"1234"[0] + 1,
"1234"[6 - 5] + 1,
("12" + "34")[0] + 1,
("12" + "34")[6 - 5] + 1,
[1, 2, 3, 4].join("")[0] + 1
);
}
expect: {
console.log(
"12341",
"11",
"21",
"11",
"21",
"11"
);
}
}
unsafe_array_bad_index: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
[1, 2, 3, 4].a + 1,
[1, 2, 3, 4]["a"] + 1,
[1, 2, 3, 4][3.14] + 1
);
}
expect: {
console.log(
[1, 2, 3, 4].a + 1,
[1, 2, 3, 4]["a"] + 1,
[1, 2, 3, 4][3.14] + 1
);
}
}
unsafe_string_bad_index: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234".a + 1,
"1234"["a"] + 1,
"1234"[3.14] + 1
);
}
expect: {
console.log(
"1234".a + 1,
"1234"["a"] + 1,
"1234"[3.14] + 1
);
}
}
unsafe_prototype_function: {
options = {
evaluate : true,
unsafe : true
}
input: {
var a = ({valueOf: 0}) < 1;
var b = ({toString: 0}) < 1;
var c = ({valueOf: 0}) + "";
var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2];
var g = ({valueOf: 0}).valueOf();
var h = ({toString: 0}).toString();
}
expect: {
var a = ({valueOf: 0}) < 1;
var b = ({toString: 0}) < 1;
var c = ({valueOf: 0}) + "";
var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2];
var g = ({valueOf: 0}).valueOf();
var h = "" + ({toString: 0});
}
}
call_args: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
const a = 1;
console.log(a);
+function(a) {
return a;
}(a);
}
expect: {
const a = 1;
console.log(1);
+function(a) {
return 1;
}(1);
}
}
in_boolean_context: {
options = {
booleans: true,
evaluate: true,
sequences: true,
side_effects: true,
}
input: {
console.log(
!42,
!"foo",
![1, 2],
!/foo/,
!b(42),
!b("foo"),
!b([1, 2]),
!b(/foo/),
![1, foo()],
![1, foo(), 2]
);
}
expect: {
console.log(
!1,
!1,
!1,
!1,
!b(42),
!b("foo"),
!b([1, 2]),
!b(/foo/),
![1, foo()],
(foo(), !1)
);
}
}

View File

@@ -0,0 +1,8 @@
non_ascii_function_identifier_name: {
input: {
function fooλ(δλ) {}
function λ(δλ) {}
(function λ(δλ) {})()
}
expect_exact: "function fooλ(δλ){}function λ(δλ){}(function λ(δλ){})();"
}

View File

@@ -0,0 +1,147 @@
must_replace: {
options = {
global_defs: {
D: "foo bar",
}
}
input: {
console.log(D);
}
expect: {
console.log("foo bar");
}
}
keyword: {
options = {
global_defs: {
undefined: 0,
NaN: 1,
Infinity: 2,
},
}
input: {
console.log(undefined, NaN, Infinity);
}
expect: {
console.log(0, 1, 2);
}
}
object: {
options = {
evaluate: true,
global_defs: {
CONFIG: {
DEBUG: [ 0 ],
VALUE: 42,
},
},
unsafe: true,
}
input: {
function f(CONFIG) {
// CONFIG not global - do not replace
return CONFIG.VALUE;
}
function g() {
var CONFIG = { VALUE: 1 };
// CONFIG not global - do not replace
return CONFIG.VALUE;
}
function h() {
return CONFIG.VALUE;
}
if (CONFIG.DEBUG[0])
console.debug("foo");
}
expect: {
function f(CONFIG) {
return CONFIG.VALUE;
}
function g() {
var CONFIG = { VALUE: 1 };
return CONFIG.VALUE;
}
function h() {
return 42;
}
if (0)
console.debug("foo");
}
}
expanded: {
options = {
global_defs: {
"CONFIG.DEBUG": [ 0 ],
"CONFIG.VALUE": 42,
},
}
input: {
function f(CONFIG) {
// CONFIG not global - do not replace
return CONFIG.VALUE;
}
function g() {
var CONFIG = { VALUE: 1 };
// CONFIG not global - do not replace
return CONFIG.VALUE;
}
function h() {
return CONFIG.VALUE;
}
if (CONFIG.DEBUG[0])
console.debug("foo");
}
expect: {
function f(CONFIG) {
return CONFIG.VALUE;
}
function g() {
var CONFIG = { VALUE: 1 };
return CONFIG.VALUE;
}
function h() {
return 42;
}
if ([0][0])
console.debug("foo");
}
}
mixed: {
options = {
evaluate: true,
global_defs: {
"CONFIG.VALUE": 42,
"FOO.BAR": "moo",
},
properties: true,
}
input: {
const FOO = { BAR: 0 };
console.log(FOO.BAR);
console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE);
console.log(++CONFIG["VAL" + "UE"]);
console.log(++DEBUG[CONFIG.VALUE]);
CONFIG.VALUE.FOO = "bar";
console.log(CONFIG);
}
expect: {
const FOO = { BAR: 0 };
console.log("moo");
console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE);
console.log(++CONFIG.VALUE);
console.log(++DEBUG[42]);
CONFIG.VALUE.FOO = "bar";
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]',
]
}

View File

@@ -0,0 +1,90 @@
statements: {
options = {
hoist_funs: false,
hoist_vars: true,
}
input: {
function f() {
var a = 1;
var b = 2;
var c = 3;
function g() {}
return g(a, b, c);
}
}
expect: {
function f() {
var a = 1, b = 2, c = 3;
function g() {}
return g(a, b, c);
}
}
}
statements_funs: {
options = {
hoist_funs: true,
hoist_vars: true,
}
input: {
function f() {
var a = 1;
var b = 2;
var c = 3;
function g() {}
return g(a, b, c);
}
}
expect: {
function f() {
function g() {}
var a = 1, b = 2, c = 3;
return g(a, b, c);
}
}
}
sequences: {
options = {
hoist_funs: false,
hoist_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
function g() {}
var c = 3;
return g(a, b, c);
}
}
expect: {
function f() {
var c, a = 1, b = 2;
function g() {}
c = 3;
return g(a, b, c);
}
}
}
sequences_funs: {
options = {
hoist_funs: true,
hoist_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
function g() {}
var c = 3;
return g(a, b, c);
}
}
expect: {
function f() {
function g() {}
var a = 1, b = 2, c = 3;
return g(a, b, c);
}
}
}

View File

@@ -0,0 +1,71 @@
html_comment_in_expression: {
input: {
function f(a, b, x, y) { return a < !--b && x-- > y; }
}
expect_exact: "function f(a,b,x,y){return a< !--b&&x-- >y}";
}
html_comment_in_less_than: {
input: {
function f(a, b) { return a < !--b; }
}
expect_exact: "function f(a,b){return a< !--b}";
}
html_comment_in_left_shift: {
input: {
function f(a, b) { return a << !--b; }
}
expect_exact: "function f(a,b){return a<< !--b}";
}
html_comment_in_right_shift: {
input: {
function f(a, b) { return a-- >> b; }
}
expect_exact: "function f(a,b){return a-- >>b}";
}
html_comment_in_zero_fill_right_shift: {
input: {
function f(a, b) { return a-- >>> b; }
}
expect_exact: "function f(a,b){return a-- >>>b}";
}
html_comment_in_greater_than: {
input: {
function f(a, b) { return a-- > b; }
}
expect_exact: "function f(a,b){return a-- >b}";
}
html_comment_in_greater_than_or_equal: {
input: {
function f(a, b) { return a-- >= b; }
}
expect_exact: "function f(a,b){return a-- >=b}";
}
html_comment_in_right_shift_assign: {
input: {
// Note: illegal javascript
function f(a, b) { return a-- >>= b; }
}
expect_exact: "function f(a,b){return a-- >>=b}";
}
html_comment_in_zero_fill_right_shift_assign: {
input: {
// Note: illegal javascript
function f(a, b) { return a-- >>>= b; }
}
expect_exact: "function f(a,b){return a-- >>>=b}";
}
html_comment_in_string_literal: {
input: {
function f() { return "<!--HTML-->comment in<!--string literal-->"; }
}
expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}';
}

304
test/compress/if_return.js Normal file
View File

@@ -0,0 +1,304 @@
if_return_1: {
options = {
if_return : true,
sequences : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
unused : true,
side_effects : true,
dead_code : true,
}
input: {
function f(x) {
if (x) {
return true;
}
}
}
expect: {
function f(x){if(x)return!0}
}
}
if_return_2: {
options = {
if_return : true,
sequences : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
unused : true,
side_effects : true,
dead_code : true,
}
input: {
function f(x, y) {
if (x)
return 3;
if (y)
return c();
}
}
expect: {
function f(x,y){return x?3:y?c():void 0}
}
}
if_return_3: {
options = {
if_return : true,
sequences : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
unused : true,
side_effects : true,
dead_code : true,
}
input: {
function f(x) {
a();
if (x) {
b();
return false;
}
}
}
expect: {
function f(x){if(a(),x)return b(),!1}
}
}
if_return_4: {
options = {
if_return : true,
sequences : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
unused : true,
side_effects : true,
dead_code : true,
}
input: {
function f(x, y) {
a();
if (x) return 3;
b();
if (y) return c();
}
}
expect: {
function f(x,y){return a(),x?3:(b(),y?c():void 0)}
}
}
if_return_5: {
options = {
if_return : true,
sequences : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
unused : true,
side_effects : true,
dead_code : true,
}
input: {
function f() {
if (x)
return;
return 7;
if (y)
return j;
}
}
expect: {
function f(){if(!x)return 7}
}
}
if_return_6: {
options = {
if_return : true,
sequences : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
unused : true,
side_effects : true,
dead_code : true,
}
input: {
function f(x) {
return x ? true : void 0;
return y;
}
}
expect: {
// suboptimal
function f(x){return!!x||void 0}
}
}
if_return_7: {
options = {
if_return : true,
sequences : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
unused : true,
side_effects : true,
dead_code : true,
}
input: {
function f(x) {
if (x) {
return true;
}
foo();
bar();
}
}
expect: {
function f(x){if(x)return!0;foo(),bar()}
}
}
if_return_8: {
options = {
if_return: true,
sequences: true,
conditionals: true,
side_effects : true,
}
input: {
function f(e) {
if (2 == e) return foo();
if (3 == e) return bar();
if (4 == e) return baz();
fail(e);
}
function g(e) {
if (a(e)) return foo();
if (b(e)) return bar();
if (c(e)) return baz();
fail(e);
}
function h(e) {
if (a(e)) return foo();
else if (b(e)) return bar();
else if (c(e)) return baz();
else fail(e);
}
function i(e) {
if (a(e)) return foo();
else if (b(e)) return bar();
else if (c(e)) return baz();
fail(e);
}
}
expect: {
function f(e){return 2==e?foo():3==e?bar():4==e?baz():void fail(e)}
function g(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)}
function h(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)}
function i(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)}
}
}
issue_1089: {
options = {
if_return : true,
sequences : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
unused : true,
side_effects : true,
dead_code : true,
}
input: {
function x() {
var f = document.getElementById("fname");
if (f.files[0].size > 12345) {
alert("alert");
f.focus();
return false;
}
}
}
expect: {
function x() {
var f = document.getElementById("fname");
if (f.files[0].size > 12345)
return alert("alert"), f.focus(), !1;
}
}
}
issue_1437: {
options = {
if_return : true,
sequences : true,
conditionals : false
}
input: {
function x() {
if (a())
return b();
if (c())
return d();
else
e();
f();
}
}
expect: {
function x() {
if (a())
return b();
if (c())
return d();
else
e()
f();
}
}
}
issue_1437_conditionals: {
options = {
conditionals : true,
if_return : true,
sequences : true
}
input: {
function x() {
if (a())
return b();
if (c())
return d();
else
e();
f();
}
}
expect: {
function x() {
return a() ? b() : c() ? d() : (e(), f(), void 0);
}
}
}

121
test/compress/issue-1034.js Normal file
View File

@@ -0,0 +1,121 @@
non_hoisted_function_after_return: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true
}
input: {
function foo(x) {
if (x) {
return bar();
not_called1();
} else {
return baz();
not_called2();
}
function bar() { return 7; }
return not_reached;
function UnusedFunction() {}
function baz() { return 8; }
}
}
expect: {
function foo(x) {
return x ? bar() : baz();
function bar() { return 7 }
function baz() { return 8 }
}
}
expect_warnings: [
'WARN: Dropping unreachable code [test/compress/issue-1034.js:11,16]',
"WARN: Dropping unreachable code [test/compress/issue-1034.js:14,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:17,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:18,21]"
]
}
non_hoisted_function_after_return_2a: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2
}
input: {
function foo(x) {
if (x) {
return bar(1);
var a = not_called(1);
} else {
return bar(2);
var b = not_called(2);
}
var c = bar(3);
function bar(x) { return 7 - x; }
function nope() {}
return b || c;
}
}
expect: {
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) {
return 7 - x;
}
}
}
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:48,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:48,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:51,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:51,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:48,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:55,21]",
"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]"
]
}
non_hoisted_function_after_return_2b: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false
}
input: {
function foo(x) {
if (x) {
return bar(1);
} else {
return bar(2);
var b;
}
var c = bar(3);
function bar(x) {
return 7 - x;
}
return b || c;
}
}
expect: {
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) { return 7 - x; }
}
}
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 unused variable b [test/compress/issue-1034.js:95,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:97,16]"
]
}

View File

@@ -0,0 +1,41 @@
const_declaration: {
options = {
evaluate: true
};
input: {
const goog = goog || {};
}
expect: {
const goog = goog || {};
}
}
const_pragma: {
options = {
evaluate: true,
reduce_vars: true,
};
input: {
/** @const */ var goog = goog || {};
}
expect: {
var goog = goog || {};
}
}
// for completeness' sake
not_const: {
options = {
evaluate: true,
reduce_vars: true,
};
input: {
var goog = goog || {};
}
expect: {
var goog = goog || {};
}
}

View File

@@ -1,25 +0,0 @@
typeof_eq_undefined: {
options = {
comparisons: true
};
input: { a = typeof b.c != "undefined" }
expect: { a = "undefined" != typeof b.c }
}
typeof_eq_undefined_unsafe: {
options = {
comparisons: true,
unsafe: true
};
input: { a = typeof b.c != "undefined" }
expect: { a = void 0 !== b.c }
}
typeof_eq_undefined_unsafe2: {
options = {
comparisons: true,
unsafe: true
};
input: { a = "undefined" != typeof b.c }
expect: { a = void 0 !== b.c }
}

View File

@@ -0,0 +1,96 @@
multiple_functions: {
options = { if_return: true, hoist_funs: false };
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
function g() {}
} )();
}
expect: {
( function() {
function f() {}
function g() {}
// NOTE: other compression steps will reduce this
// down to just `window`.
if ( window );
} )();
}
}
single_function: {
options = { if_return: true, hoist_funs: false };
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
} )();
}
expect: {
( function() {
function f() {}
if ( window );
} )();
}
}
deeply_nested: {
options = { if_return: true, hoist_funs: false };
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
function g() {}
if ( !document ) {
return;
}
function h() {}
} )();
}
expect: {
( function() {
function f() {}
function g() {}
function h() {}
// NOTE: other compression steps will reduce this
// down to just `window`.
if ( window )
if (document);
} )();
}
}
not_hoisted_when_already_nested: {
options = { if_return: true, hoist_funs: false };
input: {
( function() {
if ( !window ) {
return;
}
if ( foo ) function f() {}
} )();
}
expect: {
( function() {
if ( window )
if ( foo ) function f() {}
} )();
}
}

240
test/compress/issue-1105.js Normal file
View File

@@ -0,0 +1,240 @@
with_in_global_scope: {
options = {
unused: true
}
input: {
var o = 42;
with(o) {
var foo = 'something'
}
doSomething(o);
}
expect: {
var o=42;
with(o)
var foo = "something";
doSomething(o);
}
}
with_in_function_scope: {
options = {
unused: true
}
input: {
function foo() {
var o = 42;
with(o) {
var foo = "something"
}
doSomething(o);
}
}
expect: {
function foo() {
var o=42;
with(o)
var foo = "something";
doSomething(o)
}
}
}
compress_with_with_in_other_scope: {
options = {
unused: true
}
input: {
function foo() {
var o = 42;
with(o) {
var foo = "something"
}
doSomething(o);
}
function bar() {
var unused = 42;
return something();
}
}
expect: {
function foo() {
var o = 42;
with(o)
var foo = "something";
doSomething(o)
}
function bar() {
return something()
}
}
}
with_using_existing_variable_outside_scope: {
options = {
unused: true
}
input: {
function f() {
var o = {};
var unused = {}; // Doesn't get removed because upper scope uses with
function foo() {
with(o) {
var foo = "something"
}
doSomething(o);
}
foo()
}
}
expect: {
function f() {
var o = {};
var unused = {};
function foo() {
with(o)
var foo = "something";
doSomething(o)
}
foo()
}
}
}
check_drop_unused_in_peer_function: {
options = {
unused: true
}
input: {
function outer() {
var o = {};
var unused = {}; // should be kept
function foo() { // should be kept
function not_in_use() {
var nested_unused = "foo"; // should be dropped
return 24;
}
var unused = {}; // should be kept
with (o) {
var foo = "something";
}
doSomething(o);
}
function bar() {
var unused = {}; // should be dropped
doSomethingElse();
}
foo();
bar();
}
}
expect: {
function outer() {
var o = {};
var unused = {}; // should be kept
function foo() { // should be kept
function not_in_use() {
return 24;
}
var unused = {}; // should be kept
with (o)
var foo = "something";
doSomething(o);
}
function bar() {
doSomethingElse();
}
foo();
bar();
}
}
}
Infinity_not_in_with_scope: {
options = {
unused: true
}
input: {
var o = { Infinity: 'oInfinity' };
var vInfinity = "Infinity";
vInfinity = Infinity;
}
expect: {
var o = { Infinity: 'oInfinity' }
var vInfinity = "Infinity"
vInfinity = 1/0
}
}
Infinity_in_with_scope: {
options = {
unused: true
}
input: {
var o = { Infinity: 'oInfinity' };
var vInfinity = "Infinity";
with (o) { vInfinity = Infinity; }
}
expect: {
var o = { Infinity: 'oInfinity' }
var vInfinity = "Infinity"
with (o) vInfinity = Infinity
}
}
assorted_Infinity_NaN_undefined_in_with_scope: {
options = {
unused: true,
evaluate: true,
dead_code: true,
conditionals: true,
comparisons: true,
booleans: true,
hoist_funs: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
sequences: false,
}
input: {
var o = {
undefined : 3,
NaN : 4,
Infinity : 5,
}
if (o) {
f(undefined, void 0);
f(NaN, 0/0);
f(Infinity, 1/0);
f(-Infinity, -(1/0));
f(2 + 7 + undefined, 2 + 7 + void 0);
}
with (o) {
f(undefined, void 0);
f(NaN, 0/0);
f(Infinity, 1/0);
f(-Infinity, -(1/0));
f(2 + 7 + undefined, 2 + 7 + void 0);
}
}
expect: {
var o = {
undefined : 3,
NaN : 4,
Infinity : 5
}
if (o) {
f(void 0, void 0);
f(NaN, NaN);
f(1/0, 1/0);
f(-(1/0), -(1/0));
f(NaN, NaN);
}
with (o) {
f(undefined, void 0);
f(NaN, 0/0);
f(Infinity, 1/0);
f(-Infinity, -(1/0));
f(9 + undefined, 9 + void 0);
}
}
}

View File

@@ -9,3 +9,50 @@ keep_name_of_setter: {
input: { a = { set foo () {} } }
expect: { a = { set foo () {} } }
}
setter_with_operator_keys: {
input: {
var tokenCodes = {
get instanceof(){
return test0;
},
set instanceof(value){
test0 = value;
},
set typeof(value){
test1 = value;
},
get typeof(){
return test1;
},
set else(value){
test2 = value;
},
get else(){
return test2;
}
};
}
expect: {
var tokenCodes = {
get instanceof(){
return test0;
},
set instanceof(value){
test0 = value;
},
set typeof(value){
test1 = value;
},
get typeof(){
return test1;
},
set else(value){
test2 = value;
},
get else(){
return test2;
}
};
}
}

View File

@@ -0,0 +1,52 @@
mangle_keep_fnames_false: {
options = {
keep_fnames : true,
keep_fargs : true,
}
mangle = {
keep_fnames : false,
}
input: {
"use strict";
function total() {
return function n(a, b, c) {
return a + b + c;
};
}
}
expect: {
"use strict";
function total() {
return function t(n, r, u) {
return n + r + u;
};
}
}
}
mangle_keep_fnames_true: {
options = {
keep_fnames : true,
keep_fargs : true,
}
mangle = {
keep_fnames : true,
}
input: {
"use strict";
function total() {
return function n(a, b, c) {
return a + b + c;
};
}
}
expect: {
"use strict";
function total() {
return function n(t, r, u) {
return t + r + u;
};
}
}
}

176
test/compress/issue-1261.js Normal file
View File

@@ -0,0 +1,176 @@
pure_function_calls: {
options = {
evaluate : true,
conditionals : true,
comparisons : true,
side_effects : true,
booleans : true,
unused : true,
if_return : true,
join_vars : true,
cascade : true,
negate_iife : true,
}
input: {
// pure top-level IIFE will be dropped
// @__PURE__ - comment
(function() {
console.log("iife0");
})();
// pure top-level IIFE assigned to unreferenced var will not be dropped
var iife1 = /*@__PURE__*/(function() {
console.log("iife1");
function iife1() {}
return iife1;
})();
(function(){
// pure IIFE in function scope assigned to unreferenced var will be dropped
var iife2 = /*#__PURE__*/(function() {
console.log("iife2");
function iife2() {}
return iife2;
})();
})();
// comment #__PURE__ comment
bar(), baz(), quux();
a.b(), /* @__PURE__ */ c.d.e(), f.g();
}
expect: {
var iife1 = function() {
console.log("iife1");
function iife1() {}
return iife1;
}();
baz(), quux();
a.b(), f.g();
}
expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:17,8]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:17,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:30,37]",
"WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:30,16]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:28,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:38,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:39,31]",
]
}
pure_function_calls_toplevel: {
options = {
evaluate : true,
conditionals : true,
comparisons : true,
side_effects : true,
booleans : true,
unused : true,
if_return : true,
join_vars : true,
cascade : true,
negate_iife : true,
toplevel : true,
}
input: {
// pure top-level IIFE will be dropped
// @__PURE__ - comment
(function() {
console.log("iife0");
})();
// pure top-level IIFE assigned to unreferenced var will be dropped
var iife1 = /*@__PURE__*/(function() {
console.log("iife1");
function iife1() {}
return iife1;
})();
(function(){
// pure IIFE in function scope assigned to unreferenced var will be dropped
var iife2 = /*#__PURE__*/(function() {
console.log("iife2");
function iife2() {}
return iife2;
})();
})();
// comment #__PURE__ comment
bar(), baz(), quux();
a.b(), /* @__PURE__ */ c.d.e(), f.g();
}
expect: {
baz(), quux();
a.b(), f.g();
}
expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:79,8]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:79,8]",
"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:84,33]",
"WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]",
]
}
should_warn: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
side_effects: true,
}
input: {
/* @__PURE__ */(function(){x})(), void/* @__PURE__ */(function(){y})();
/* @__PURE__ */(function(){x})() || true ? foo() : bar();
true || /* @__PURE__ */(function(){y})() ? foo() : bar();
/* @__PURE__ */(function(){x})() && false ? foo() : bar();
false && /* @__PURE__ */(function(){y})() ? foo() : bar();
/* @__PURE__ */(function(){x})() + "foo" ? bar() : baz();
"foo" + /* @__PURE__ */(function(){y})() ? bar() : baz();
/* @__PURE__ */(function(){x})() ? foo() : foo();
[/* @__PURE__ */(function(){x})()] ? foo() : bar();
!{ foo: /* @__PURE__ */(function(){x})() } ? bar() : baz();
}
expect: {
foo();
foo();
bar();
bar();
bar();
bar();
foo();
foo();
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: Boolean || 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: Boolean && 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]",
]
}

View File

@@ -0,0 +1,49 @@
string_plus_optimization: {
options = {
side_effects : true,
evaluate : true,
conditionals : true,
comparisons : true,
dead_code : true,
booleans : true,
unused : true,
if_return : true,
join_vars : true,
cascade : true,
hoist_funs : true,
};
input: {
function foo(anything) {
function throwing_function() {
throw "nope";
}
try {
console.log('0' + throwing_function() ? "yes" : "no");
} catch (ex) {
console.log(ex);
}
console.log('0' + anything ? "yes" : "no");
console.log(anything + '0' ? "Yes" : "No");
console.log('' + anything);
console.log(anything + '');
}
foo();
}
expect: {
function foo(anything) {
function throwing_function() {
throw "nope";
}
try {
console.log((throwing_function(), "yes"));
} catch (ex) {
console.log(ex);
}
console.log("yes");
console.log("Yes");
console.log('' + anything);
console.log(anything + '');
}
foo();
}
}

View File

@@ -0,0 +1,54 @@
issue_1321_no_debug: {
mangle_props = {
ignore_quoted: true
}
input: {
var x = {};
x.foo = 1;
x["a"] = 2 * x.foo;
console.log(x.foo, x["a"]);
}
expect: {
var x = {};
x.b = 1;
x["a"] = 2 * x.b;
console.log(x.b, x["a"]);
}
}
issue_1321_debug: {
mangle_props = {
ignore_quoted: true,
debug: ""
}
input: {
var x = {};
x.foo = 1;
x["_$foo$_"] = 2 * x.foo;
console.log(x.foo, x["_$foo$_"]);
}
expect: {
var x = {};
x.a = 1;
x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]);
}
}
issue_1321_with_quoted: {
mangle_props = {
ignore_quoted: false
}
input: {
var x = {};
x.foo = 1;
x["a"] = 2 * x.foo;
console.log(x.foo, x["a"]);
}
expect: {
var x = {};
x.a = 1;
x["b"] = 2 * x.a;
console.log(x.a, x["b"]);
}
}

151
test/compress/issue-1431.js Normal file
View File

@@ -0,0 +1,151 @@
level_zero: {
options = {
keep_fnames: true
}
mangle = {
keep_fnames: true
}
input: {
function f(x) {
function n(a) {
return a * a;
}
return function() {
return x;
};
}
}
expect: {
function f(r) {
function n(n) {
return n * n;
}
return function() {
return r;
};
}
}
}
level_one: {
options = {
keep_fnames: true
}
mangle = {
keep_fnames: true
}
input: {
function f(x) {
return function() {
function n(a) {
return a * a;
}
return x(n);
};
}
}
expect: {
function f(r) {
return function() {
function n(n) {
return n * n;
}
return r(n);
};
}
}
}
level_two: {
options = {
keep_fnames: true
}
mangle = {
keep_fnames: true
}
input: {
function f(x) {
return function() {
function r(a) {
return a * a;
}
return function() {
function n(a) {
return a * a;
}
return x(n);
};
};
}
}
expect: {
function f(t) {
return function() {
function r(n) {
return n * n;
}
return function() {
function n(n) {
return n * n;
}
return t(n);
};
};
}
}
}
level_three: {
options = {
keep_fnames: true
}
mangle = {
keep_fnames: true
}
input: {
function f(x) {
return function() {
function r(a) {
return a * a;
}
return [
function() {
function t(a) {
return a * a;
}
return t;
},
function() {
function n(a) {
return a * a;
}
return x(n);
}
];
};
}
}
expect: {
function f(t) {
return function() {
function r(n) {
return n * n;
}
return [
function() {
function t(n) {
return n * n;
}
return t;
},
function() {
function n(n) {
return n * n;
}
return t(n);
}
];
};
}
}
}

View File

@@ -0,0 +1,69 @@
// tests assume that variable `undefined` not redefined and has `void 0` as value
unsafe_undefined: {
options = {
if_return: true,
unsafe: true
}
mangle = {}
input: {
function f(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}
}
expect: {
function f(n) {
return function() {
if (a)
return b;
if (c)
return d;
else
return n;
};
}
}
}
keep_fnames: {
options = {
if_return: true,
unsafe: true
}
mangle = {
keep_fnames: true
}
input: {
function f(undefined) {
return function() {
function n(a) {
return a * a;
}
if (a)
return b;
if (c)
return d;
};
}
}
expect: {
function f(r) {
return function() {
function n(n) {
return n * n;
}
if (a)
return b;
if (c)
return d;
else
return r;
};
}
}
}

View File

@@ -0,0 +1,71 @@
typeof_eq_undefined: {
options = {
comparisons: true
}
input: {
var a = typeof b != "undefined";
b = typeof a != "undefined";
var c = typeof d.e !== "undefined";
var f = "undefined" === typeof g;
g = "undefined" === typeof f;
var h = "undefined" == typeof i.j;
}
expect: {
var a = "undefined" != typeof b;
b = void 0 !== a;
var c = void 0 !== d.e;
var f = "undefined" == typeof g;
g = void 0 === f;
var h = void 0 === i.j;
}
}
typeof_eq_undefined_ie8: {
options = {
comparisons: true,
screw_ie8: false
}
input: {
var a = typeof b != "undefined";
b = typeof a != "undefined";
var c = typeof d.e !== "undefined";
var f = "undefined" === typeof g;
g = "undefined" === typeof f;
var h = "undefined" == typeof i.j;
}
expect: {
var a = "undefined" != typeof b;
b = void 0 !== a;
var c = "undefined" != typeof d.e;
var f = "undefined" == typeof g;
g = void 0 === f;
var h = "undefined" == typeof i.j;
}
}
undefined_redefined: {
options = {
comparisons: true
}
input: {
function f(undefined) {
var n = 1;
return typeof n == "undefined";
}
}
expect_exact: "function f(undefined){var n=1;return void 0===n}"
}
undefined_redefined_mangle: {
options = {
comparisons: true
}
mangle = {}
input: {
function f(undefined) {
var n = 1;
return typeof n == "undefined";
}
}
expect_exact: "function f(n){var r=1;return void 0===r}"
}

View File

@@ -0,0 +1,45 @@
else_with_empty_block: {
options = {}
input: {
if (x)
yes();
else {
}
}
expect_exact: "if(x)yes();"
}
else_with_empty_statement: {
options = {}
input: {
if (x)
yes();
else
;
}
expect_exact: "if(x)yes();"
}
conditional_false_stray_else_in_loop: {
options = {
evaluate : true,
comparisons : true,
booleans : true,
unused : true,
loops : true,
side_effects : true,
dead_code : true,
hoist_vars : true,
join_vars : true,
if_return : true,
cascade : true,
conditionals : false,
}
input: {
for (var i = 1; i <= 4; ++i) {
if (i <= 2) continue;
console.log(i);
}
}
expect_exact: "for(var i=1;i<=4;++i)if(!(i<=2))console.log(i);"
}

View File

@@ -1,11 +1,70 @@
do_not_update_lhs: {
options = { global_defs: { DEBUG: false } };
input: { DEBUG = false; }
expect: { DEBUG = false; }
options = {
global_defs: { DEBUG: 0 }
}
input: {
DEBUG++;
DEBUG += 1;
DEBUG = 1;
}
expect: {
DEBUG++;
DEBUG += 1;
DEBUG = 1;
}
}
do_update_rhs: {
options = { global_defs: { DEBUG: false } };
input: { MY_DEBUG = DEBUG; }
expect: { MY_DEBUG = false; }
options = {
global_defs: { DEBUG: 0 }
}
input: {
MY_DEBUG = DEBUG;
MY_DEBUG += DEBUG;
}
expect: {
MY_DEBUG = 0;
MY_DEBUG += 0;
}
}
mixed: {
options = {
evaluate: true,
global_defs: {
DEBUG: 0,
ENV: 1,
FOO: 2,
}
}
input: {
const ENV = 3;
var FOO = 4;
f(ENV * 10);
--FOO;
DEBUG = 1;
DEBUG++;
DEBUG += 1;
f(DEBUG);
x = DEBUG;
}
expect: {
const ENV = 3;
var FOO = 4;
f(10);
--FOO;
DEBUG = 1;
DEBUG++;
DEBUG += 1;
f(0);
x = 0;
}
expect_warnings: [
'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,14]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:46,8]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:47,8]',
]
}

View File

@@ -1,23 +1,27 @@
remove_redundant_sequence_items: {
options = { side_effects: true };
input: {
(0, 1, eval)();
(0, 1, logThis)();
(0, 1, _decorators.logThis)();
}
expect: {
(0, logThis)();
(0, eval)();
logThis();
(0, _decorators.logThis)();
}
}
dont_remove_lexical_binding_sequence: {
dont_remove_this_binding_sequence: {
options = { side_effects: true };
input: {
(0, eval)();
(0, logThis)();
(0, _decorators.logThis)();
}
expect: {
(0, logThis)();
(0, eval)();
logThis();
(0, _decorators.logThis)();
}
}

View File

@@ -0,0 +1,32 @@
dont_mangle_arguments: {
mangle = {
};
options = {
sequences : true,
properties : true,
dead_code : true,
drop_debugger : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
loops : true,
unused : true,
hoist_funs : true,
keep_fargs : true,
keep_fnames : false,
hoist_vars : true,
if_return : true,
join_vars : true,
cascade : true,
side_effects : true,
negate_iife : false
};
input: {
(function(){
var arguments = arguments, not_arguments = 9;
console.log(not_arguments, arguments);
})(5,6,7);
}
expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);"
}

View File

@@ -0,0 +1,20 @@
keep_var_for_in: {
options = {
hoist_vars: true,
unused: true
};
input: {
(function(obj){
var foo = 5;
for (var i in obj)
return foo;
})();
}
expect: {
(function(obj){
var i, foo = 5;
for (i in obj)
return foo;
})();
}
}

View File

@@ -0,0 +1,96 @@
this_binding_conditionals: {
options = {
conditionals: true,
evaluate : true
};
input: {
(1 && a)();
(0 || a)();
(0 || 1 && a)();
(1 ? a : 0)();
(1 && a.b)();
(0 || a.b)();
(0 || 1 && a.b)();
(1 ? a.b : 0)();
(1 && a[b])();
(0 || a[b])();
(0 || 1 && a[b])();
(1 ? a[b] : 0)();
(1 && eval)();
(0 || eval)();
(0 || 1 && eval)();
(1 ? eval : 0)();
}
expect: {
a();
a();
a();
a();
(0, a.b)();
(0, a.b)();
(0, a.b)();
(0, a.b)();
(0, a[b])();
(0, a[b])();
(0, a[b])();
(0, a[b])();
(0, eval)();
(0, eval)();
(0, eval)();
(0, eval)();
}
}
this_binding_collapse_vars: {
options = {
collapse_vars: true,
};
input: {
var c = a; c();
var d = a.b; d();
var e = eval; e();
}
expect: {
a();
(0, a.b)();
(0, eval)();
}
}
this_binding_side_effects: {
options = {
side_effects : true
};
input: {
(function (foo) {
(0, foo)();
(0, foo.bar)();
(0, eval)('console.log(foo);');
}());
(function (foo) {
var eval = console;
(0, foo)();
(0, foo.bar)();
(0, eval)('console.log(foo);');
}());
}
expect: {
(function (foo) {
foo();
(0, foo.bar)();
(0, eval)('console.log(foo);');
}());
(function (foo) {
var eval = console;
foo();
(0, foo.bar)();
(0, eval)('console.log(foo);');
}());
}
}

View File

@@ -0,0 +1,88 @@
eval_collapse_vars: {
options = {
collapse_vars:true, sequences:false, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
};
input: {
function f1() {
var e = 7;
var s = "abcdef";
var i = 2;
var eval = console.log.bind(console);
var x = s.charAt(i++);
var y = s.charAt(i++);
var z = s.charAt(i++);
eval(x, y, z, e);
}
function p1() { var a = foo(), b = bar(), eval = baz(); return a + b + eval; }
function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
(function f2(eval) {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})(eval);
}
expect: {
function f1() {
var e = 7,
s = "abcdef",
i = 2,
eval = console.log.bind(console),
x = s.charAt(i++),
y = s.charAt(i++),
z = s.charAt(i++);
eval(x, y, z, e);
}
function p1() { return foo() + bar() + baz(); }
function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
(function f2(eval) {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})(eval);
}
}
eval_unused: {
options = { unused: true, keep_fargs: false };
input: {
function f1(a, eval, c, d, e) {
return a('c') + eval;
}
function f2(a, b, c, d, e) {
return a + eval('c');
}
function f3(a, eval, c, d, e) {
return a + eval('c');
}
}
expect: {
function f1(a, eval) {
return a('c') + eval;
}
function f2(a, b, c, d, e) {
return a + eval('c');
}
function f3(a, eval, c, d, e) {
return a + eval('c');
}
}
}
eval_mangle: {
mangle = {
};
input: {
function f1(a, eval, c, d, e) {
return a('c') + eval;
}
function f2(a, b, c, d, e) {
return a + eval('c');
}
function f3(a, eval, c, d, e) {
return a + eval('c');
}
}
expect_exact: 'function f1(n,c,e,a,f){return n("c")+c}function f2(a,b,c,d,e){return a+eval("c")}function f3(a,eval,c,d,e){return a+eval("c")}'
}

View File

@@ -0,0 +1,89 @@
issue979_reported: {
options = {
sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f1() {
if (a == 1 || b == 2) {
foo();
}
}
function f2() {
if (!(a == 1 || b == 2)) {
}
else {
foo();
}
}
}
expect: {
function f1() {
1!=a&&2!=b||foo();
}
function f2() {
1!=a&&2!=b||foo();
}
}
}
issue979_test_negated_is_best: {
options = {
sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f3() {
if (a == 1 | b == 2) {
foo();
}
}
function f4() {
if (!(a == 1 | b == 2)) {
}
else {
foo();
}
}
function f5() {
if (a == 1 && b == 2) {
foo();
}
}
function f6() {
if (!(a == 1 && b == 2)) {
}
else {
foo();
}
}
function f7() {
if (a == 1 || b == 2) {
foo();
}
else {
return bar();
}
}
}
expect: {
function f3() {
1==a|2==b&&foo();
}
function f4() {
1==a|2==b&&foo();
}
function f5() {
1==a&&2==b&&foo();
}
function f6() {
1!=a||2!=b||foo();
}
function f7() {
if(1!=a&&2!=b)return bar();foo()
}
}
}

View File

@@ -121,3 +121,98 @@ drop_if_else_break_4: {
for (; bar() && (x(), y(), foo());) baz(), z(), k();
}
}
parse_do_while_with_semicolon: {
options = { loops: false };
input: {
do {
x();
} while (false);y()
}
expect: {
do x(); while (false);y();
}
}
parse_do_while_without_semicolon: {
options = { loops: false };
input: {
do {
x();
} while (false)y()
}
expect: {
do x(); while (false);y();
}
}
keep_collapse_const_in_own_block_scope: {
options = {
join_vars: true,
loops: true
}
input: {
var i=2;
const c=5;
while(i--)
console.log(i);
console.log(c);
}
expect: {
var i=2;
const c=5;
for(;i--;)
console.log(i);
console.log(c);
}
}
keep_collapse_const_in_own_block_scope_2: {
options = {
join_vars: true,
loops: true
}
input: {
const c=5;
var i=2; // Moves to loop, while it did not in previous test
while(i--)
console.log(i);
console.log(c);
}
expect: {
const c=5;
for(var i=2;i--;)
console.log(i);
console.log(c);
}
}
evaluate: {
options = {
loops: true,
dead_code: true,
evaluate: true,
};
input: {
while (true) {
a();
}
while (false) {
b();
}
do {
c();
} while (true);
do {
d();
} while (false);
}
expect: {
for(;;)
a();
for(;;)
c();
d();
}
}

View File

@@ -0,0 +1,28 @@
too_short: {
beautify = {
max_line_len: 10,
}
input: {
function f(a) {
return { c: 42, d: a(), e: "foo"};
}
}
expect_exact: 'function f(a){\nreturn{\nc:42,\nd:a(),\ne:"foo"}}'
expect_warnings: [
"WARN: Output exceeds 10 characters"
]
}
just_enough: {
beautify = {
max_line_len: 14,
}
input: {
function f(a) {
return { c: 42, d: a(), e: "foo"};
}
}
expect_exact: 'function f(a){\nreturn{c:42,\nd:a(),e:"foo"}\n}'
expect_warnings: [
]
}

View File

@@ -10,6 +10,16 @@ negate_iife_1: {
}
}
negate_iife_1_off: {
options = {
negate_iife: false,
};
input: {
(function(){ stuff() })();
}
expect_exact: '(function(){stuff()})();'
}
negate_iife_2: {
options = {
negate_iife: true
@@ -25,6 +35,7 @@ negate_iife_2: {
negate_iife_3: {
options = {
negate_iife: true,
conditionals: true
};
input: {
(function(){ return true })() ? console.log(true) : console.log(false);
@@ -34,9 +45,23 @@ negate_iife_3: {
}
}
negate_iife_3: {
negate_iife_3_off: {
options = {
negate_iife: false,
conditionals: true,
};
input: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
!function(){ return true }() ? console.log(false) : console.log(true);
}
}
negate_iife_4: {
options = {
negate_iife: true,
conditionals: true,
sequences: true
};
input: {
@@ -52,7 +77,42 @@ negate_iife_3: {
}
}
negate_iife_4: {
sequence_off: {
options = {
negate_iife: false,
conditionals: true,
sequences: true,
passes: 2,
};
input: {
function f() {
(function(){ return true })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
function g() {
(function(){
console.log("something");
})();
(function(){ return true })() ? console.log(true) : console.log(false);
}
}
expect: {
function f() {
!function(){ return true }() ? console.log(false) : console.log(true), function(){
console.log("something");
}();
}
function g() {
(function(){
console.log("something");
})(), function(){ return true }() ? console.log(true) : console.log(false);
}
}
}
negate_iife_5: {
options = {
negate_iife: true,
sequences: true,
@@ -74,3 +134,189 @@ negate_iife_4: {
}();
}
}
negate_iife_5_off: {
options = {
negate_iife: false,
sequences: true,
conditionals: true,
};
input: {
if ((function(){ return true })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
!function(){ return true }() ? bar(false) : foo(true), function(){
console.log("something");
}();
}
}
negate_iife_nested: {
options = {
negate_iife: true,
sequences: true,
conditionals: true,
};
input: {
function Foo(f) {
this.f = f;
}
new Foo(function() {
(function(x) {
(function(y) {
console.log(y);
})(x);
})(7);
}).f();
}
expect: {
function Foo(f) {
this.f = f;
}
new Foo(function() {
!function(x) {
!function(y) {
console.log(y);
}(x);
}(7);
}).f();
}
}
negate_iife_nested_off: {
options = {
negate_iife: false,
sequences: true,
conditionals: true,
};
input: {
function Foo(f) {
this.f = f;
}
new Foo(function() {
(function(x) {
(function(y) {
console.log(y);
})(x);
})(7);
}).f();
}
expect: {
function Foo(f) {
this.f = f;
}
new Foo(function() {
(function(x) {
(function(y) {
console.log(y);
})(x);
})(7);
}).f();
}
}
negate_iife_issue_1073: {
options = {
negate_iife: true,
sequences: true,
conditionals: true,
};
input: {
new (function(a) {
return function Foo() {
this.x = a;
console.log(this);
};
}(7))();
}
expect: {
new (function(a) {
return function Foo() {
this.x = a,
console.log(this);
};
}(7))();
}
}
issue_1254_negate_iife_false: {
options = {
negate_iife: false,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: '(function(){return function(){console.log("test")}})()();'
}
issue_1254_negate_iife_true: {
options = {
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: '!function(){return function(){console.log("test")}}()();'
}
issue_1254_negate_iife_nested: {
options = {
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '!function(){return function(){console.log("test")}}()()()()();'
}
issue_1288: {
options = {
negate_iife: true,
conditionals: true,
};
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w || function f() {}();
x || function() {
x = {};
}();
y ? function() {}() : function(z) {
return z;
}(0);
}
}

View File

@@ -10,3 +10,75 @@ new_statement: {
}
expect_exact: "new x(1);new x(1)(2);new x(1)(2)(3);new new x(1);new new x(1)(2);new new x(1)(2);(new new x(1))(2);"
}
new_statements_2: {
input: {
new x;
new new x;
new new new x;
new true;
new (0);
new (!0);
new (bar = function(foo) {this.foo=foo;})(123);
new (bar = function(foo) {this.foo=foo;})();
}
expect_exact: "new x;new(new x);new(new(new x));new true;new 0;new(!0);new(bar=function(foo){this.foo=foo})(123);new(bar=function(foo){this.foo=foo});"
}
new_statements_3: {
input: {
new (function(foo){this.foo=foo;})(1);
new (function(foo){this.foo=foo;})();
new (function test(foo){this.foo=foo;})(1);
new (function test(foo){this.foo=foo;})();
}
expect_exact: "new function(foo){this.foo=foo}(1);new function(foo){this.foo=foo};new function test(foo){this.foo=foo}(1);new function test(foo){this.foo=foo};"
}
new_with_rewritten_true_value: {
options = { booleans: true }
input: {
new true;
}
expect_exact: "new(!0);"
}
new_with_many_parameters: {
input: {
new foo.bar("baz");
new x(/123/, 456);
}
expect_exact: 'new foo.bar("baz");new x(/123/,456);'
}
new_constructor_with_unary_arguments: {
input: {
new x();
new x(-1);
new x(-1, -2);
new x(void 1, +2, -3, ~4, !5, --a, ++b, c--, d++, typeof e, delete f);
new (-1); // should parse despite being invalid at runtime.
new (-1)(); // should parse despite being invalid at runtime.
new (-1)(-2); // should parse despite being invalid at runtime.
}
expect_exact: "new x;new x(-1);new x(-1,-2);new x(void 1,+2,-3,~4,!5,--a,++b,c--,d++,typeof e,delete f);new(-1);new(-1);new(-1)(-2);"
}
call_with_unary_arguments: {
input: {
x();
x(-1);
x(-1, -2);
x(void 1, +2, -3, ~4, !5, --a, ++b, c--, d++, typeof e, delete f);
(-1)(); // should parse despite being invalid at runtime.
(-1)(-2); // should parse despite being invalid at runtime.
}
expect_exact: "x();x(-1);x(-1,-2);x(void 1,+2,-3,~4,!5,--a,++b,c--,d++,typeof e,delete f);(-1)();(-1)(-2);"
}
new_with_unary_prefix: {
input: {
var bar = (+new Date()).toString(32);
}
expect_exact: 'var bar=(+new Date).toString(32);';
}

19
test/compress/numbers.js Normal file
View File

@@ -0,0 +1,19 @@
hex_numbers_in_parentheses_for_prototype_functions: {
input: {
(-2);
(-2).toFixed(0);
(2);
(2).toFixed(0);
(0.2);
(0.2).toFixed(0);
(0.00000002);
(0.00000002).toFixed(0);
(1000000000000000128);
(1000000000000000128).toFixed(0);
}
expect_exact: "-2;(-2).toFixed(0);2;2..toFixed(0);.2;.2.toFixed(0);2e-8;2e-8.toFixed(0);0xde0b6b3a7640080;(0xde0b6b3a7640080).toFixed(0);"
}

View File

@@ -12,7 +12,8 @@ keep_properties: {
dot_properties: {
options = {
properties: true
properties: true,
screw_ie8: false
};
input: {
a["foo"] = "bar";
@@ -53,7 +54,56 @@ dot_properties_es5: {
}
}
evaluate_length: {
sub_properties: {
options = {
evaluate: true,
properties: true
};
input: {
a[0] = 0;
a["0"] = 1;
a[3.14] = 2;
a["3" + ".14"] = 3;
a["i" + "f"] = 4;
a["foo" + " bar"] = 5;
a[0 / 0] = 6;
a[null] = 7;
a[undefined] = 8;
}
expect: {
a[0] = 0;
a[0] = 1;
a[3.14] = 2;
a[3.14] = 3;
a.if = 4;
a["foo bar"] = 5;
a[NaN] = 6;
a[null] = 7;
a[void 0] = 8;
}
}
evaluate_array_length: {
options = {
properties: true,
unsafe: true,
evaluate: true
};
input: {
a = [1, 2, 3].length;
a = [1, 2, 3].join()["len" + "gth"];
a = [1, 2, b].length;
a = [1, 2, 3].join(b).length;
}
expect: {
a = 3;
a = 5;
a = [1, 2, b].length;
a = [1, 2, 3].join(b).length;
}
}
evaluate_string_length: {
options = {
properties: true,
unsafe: true,
@@ -72,3 +122,436 @@ evaluate_length: {
a = ("foo" + b).length;
}
}
mangle_properties: {
mangle_props = {
ignore_quoted: false
};
input: {
a["foo"] = "bar";
a.color = "red";
x = {"bar": 10};
a.run(x.bar, a.foo);
a['run']({color: "blue", foo: "baz"});
}
expect: {
a["a"] = "bar";
a.b = "red";
x = {c: 10};
a.d(x.c, a.a);
a['d']({b: "blue", a: "baz"});
}
}
mangle_unquoted_properties: {
options = {
properties: false
}
mangle_props = {
ignore_quoted: true
}
beautify = {
beautify: false,
quote_style: 3,
keep_quoted_props: true,
}
input: {
a.top = 1;
function f1() {
a["foo"] = "bar";
a.color = "red";
a.stuff = 2;
x = {"bar": 10, size: 7};
a.size = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, size: 7};
a.size = 9;
a.stuff = 3;
}
}
expect: {
a.a = 1;
function f1() {
a["foo"] = "bar";
a.color = "red";
a.b = 2;
x = {"bar": 10, c: 7};
a.c = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, c: 7};
a.c = 9;
a.b = 3;
}
}
}
mangle_debug: {
mangle_props = {
debug: ""
};
input: {
a.foo = "bar";
x = { baz: "ban" };
}
expect: {
a._$foo$_ = "bar";
x = { _$baz$_: "ban" };
}
}
mangle_debug_true: {
mangle_props = {
debug: true
};
input: {
a.foo = "bar";
x = { baz: "ban" };
}
expect: {
a._$foo$_ = "bar";
x = { _$baz$_: "ban" };
}
}
mangle_debug_suffix: {
mangle_props = {
debug: "XYZ"
};
input: {
a.foo = "bar";
x = { baz: "ban" };
}
expect: {
a._$foo$XYZ_ = "bar";
x = { _$baz$XYZ_: "ban" };
}
}
mangle_debug_suffix_ignore_quoted: {
options = {
properties: false
}
mangle_props = {
ignore_quoted: true,
debug: "XYZ",
reserved: []
}
beautify = {
beautify: false,
quote_style: 3,
keep_quoted_props: true,
}
input: {
a.top = 1;
function f1() {
a["foo"] = "bar";
a.color = "red";
a.stuff = 2;
x = {"bar": 10, size: 7};
a.size = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, size: 7};
a.size = 9;
a.stuff = 3;
}
}
expect: {
a._$top$XYZ_ = 1;
function f1() {
a["foo"] = "bar";
a.color = "red";
a._$stuff$XYZ_ = 2;
x = {"bar": 10, _$size$XYZ_: 7};
a._$size$XYZ_ = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, _$size$XYZ_: 7};
a._$size$XYZ_ = 9;
a._$stuff$XYZ_ = 3;
}
}
}
first_256_chars_as_properties: {
beautify = {
ascii_only: true,
}
input: {
// Note: some of these unicode character keys are not visible on github.com
var o = {
"\0":0,"":1,"":2,"":3,"":4,"":5,"":6,"":7,"\b":8,
"\t":9,"\n":10,"\v":11,"\f":12,"\r":13,"":14,"":15,"":16,"":17,
"":18,"":19,"":20,"":21,"":22,"":23,"":24,"":25,"":26,
"":27,"":28,"":29,"":30,"":31," ":32,"!":33,'"':34,"#":35,
$:36,"%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,
"-":45,".":46,"/":47,"0":48,"1":49,"2":50,"3":51,"4":52,"5":53,"6":54,"7":55,
"8":56,"9":57,":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,A:65,
B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,
O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,"[":91,
"\\":92,"]":93,"^":94,_:95,"`":96,a:97,b:98,c:99,d:100,e:101,
f:102,g:103,h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112,
q:113,r:114,s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,"{":123,
"|":124,"}":125,"~":126,"":127,"€":128,"":129,"‚":130,"ƒ":131,
"„":132,"…":133,"†":134,"‡":135,"ˆ":136,"‰":137,"Š":138,"‹":139,
"Œ":140,"":141,"Ž":142,"":143,"":144,"‘":145,"’":146,"“":147,
"”":148,"•":149,"–":150,"—":151,"˜":152,"™":153,"š":154,"›":155,
"œ":156,"":157,"ž":158,"Ÿ":159," ":160,"¡":161,"¢":162,"£":163,
"¤":164,"¥":165,"¦":166,"§":167,"¨":168,"©":169,"ª":170,"«":171,
"¬":172,"­":173,"®":174,"¯":175,"°":176,"±":177,"²":178,"³":179,
"´":180,"µ":181,"¶":182,"·":183,"¸":184,"¹":185,"º":186,"»":187,
"¼":188,"½":189,"¾":190,"¿":191,"À":192,"Á":193,"Â":194,"Ã":195,
"Ä":196,"Å":197,"Æ":198,"Ç":199,"È":200,"É":201,"Ê":202,"Ë":203,
"Ì":204,"Í":205,"Î":206,"Ï":207,"Ð":208,"Ñ":209,"Ò":210,"Ó":211,
"Ô":212,"Õ":213,"Ö":214,"×":215,"Ø":216,"Ù":217,"Ú":218,"Û":219,
"Ü":220,"Ý":221,"Þ":222,"ß":223,"à":224,"á":225,"â":226,"ã":227,
"ä":228,"å":229,"æ":230,"ç":231,"è":232,"é":233,"ê":234,"ë":235,
"ì":236,"í":237,"î":238,"ï":239,"ð":240,"ñ":241,"ò":242,"ó":243,
"ô":244,"õ":245,"ö":246,"÷":247,"ø":248,"ù":249,"ú":250,"û":251,
"ü":252,"ý":253,"þ":254,"ÿ":255
};
}
expect: {
var o = {
"\0":0,"\x01":1,"\x02":2,"\x03":3,"\x04":4,"\x05":5,"\x06":6,
"\x07":7,"\b":8,"\t":9,"\n":10,"\v":11,"\f":12,"\r":13,"\x0e":14,
"\x0f":15,"\x10":16,"\x11":17,"\x12":18,"\x13":19,"\x14":20,"\x15":21,
"\x16":22,"\x17":23,"\x18":24,"\x19":25,"\x1a":26,"\x1b":27,"\x1c":28,
"\x1d":29,"\x1e":30,"\x1f":31," ":32,"!":33,'"':34,"#":35,$:36,
"%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,"-":45,
".":46,"/":47,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,
":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,A:65,B:66,C:67,
D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,
Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,"[":91,"\\":92,
"]":93,"^":94,_:95,"`":96,a:97,b:98,c:99,d:100,e:101,f:102,g:103,
h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112,q:113,r:114,
s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,"{":123,"|":124,
"}":125,"~":126,"\x7f":127,"\x80":128,"\x81":129,"\x82":130,"\x83":131,
"\x84":132,"\x85":133,"\x86":134,"\x87":135,"\x88":136,"\x89":137,
"\x8a":138,"\x8b":139,"\x8c":140,"\x8d":141,"\x8e":142,"\x8f":143,
"\x90":144,"\x91":145,"\x92":146,"\x93":147,"\x94":148,"\x95":149,
"\x96":150,"\x97":151,"\x98":152,"\x99":153,"\x9a":154,"\x9b":155,
"\x9c":156,"\x9d":157,"\x9e":158,"\x9f":159,"\xa0":160,"\xa1":161,
"\xa2":162,"\xa3":163,"\xa4":164,"\xa5":165,"\xa6":166,"\xa7":167,
"\xa8":168,"\xa9":169,"\xaa":170,"\xab":171,"\xac":172,"\xad":173,
"\xae":174,"\xaf":175,"\xb0":176,"\xb1":177,"\xb2":178,"\xb3":179,
"\xb4":180,"\xb5":181,"\xb6":182,"\xb7":183,"\xb8":184,"\xb9":185,
"\xba":186,"\xbb":187,"\xbc":188,"\xbd":189,"\xbe":190,"\xbf":191,
"\xc0":192,"\xc1":193,"\xc2":194,"\xc3":195,"\xc4":196,"\xc5":197,
"\xc6":198,"\xc7":199,"\xc8":200,"\xc9":201,"\xca":202,"\xcb":203,
"\xcc":204,"\xcd":205,"\xce":206,"\xcf":207,"\xd0":208,"\xd1":209,
"\xd2":210,"\xd3":211,"\xd4":212,"\xd5":213,"\xd6":214,"\xd7":215,
"\xd8":216,"\xd9":217,"\xda":218,"\xdb":219,"\xdc":220,"\xdd":221,
"\xde":222,"\xdf":223,"\xe0":224,"\xe1":225,"\xe2":226,"\xe3":227,
"\xe4":228,"\xe5":229,"\xe6":230,"\xe7":231,"\xe8":232,"\xe9":233,
"\xea":234,"\xeb":235,"\xec":236,"\xed":237,"\xee":238,"\xef":239,
"\xf0":240,"\xf1":241,"\xf2":242,"\xf3":243,"\xf4":244,"\xf5":245,
"\xf6":246,"\xf7":247,"\xf8":248,"\xf9":249,"\xfa":250,"\xfb":251,
"\xfc":252,"\xfd":253,"\xfe":254,"\xff":255
};
}
}
first_256_unicode_chars_as_properties: {
input: {
var o = {
"\u0000": 0, "\u0001": 1, "\u0002": 2, "\u0003": 3, "\u0004": 4, "\u0005": 5,
"\u0006": 6, "\u0007": 7, "\u0008": 8, "\u0009": 9, "\u000A": 10, "\u000B": 11,
"\u000C": 12, "\u000D": 13, "\u000E": 14, "\u000F": 15, "\u0010": 16, "\u0011": 17,
"\u0012": 18, "\u0013": 19, "\u0014": 20, "\u0015": 21, "\u0016": 22, "\u0017": 23,
"\u0018": 24, "\u0019": 25, "\u001A": 26, "\u001B": 27, "\u001C": 28, "\u001D": 29,
"\u001E": 30, "\u001F": 31, "\u0020": 32, "\u0021": 33, "\u0022": 34, "\u0023": 35,
"\u0024": 36, "\u0025": 37, "\u0026": 38, "\u0027": 39, "\u0028": 40, "\u0029": 41,
"\u002A": 42, "\u002B": 43, "\u002C": 44, "\u002D": 45, "\u002E": 46, "\u002F": 47,
"\u0030": 48, "\u0031": 49, "\u0032": 50, "\u0033": 51, "\u0034": 52, "\u0035": 53,
"\u0036": 54, "\u0037": 55, "\u0038": 56, "\u0039": 57, "\u003A": 58, "\u003B": 59,
"\u003C": 60, "\u003D": 61, "\u003E": 62, "\u003F": 63, "\u0040": 64, "\u0041": 65,
"\u0042": 66, "\u0043": 67, "\u0044": 68, "\u0045": 69, "\u0046": 70, "\u0047": 71,
"\u0048": 72, "\u0049": 73, "\u004A": 74, "\u004B": 75, "\u004C": 76, "\u004D": 77,
"\u004E": 78, "\u004F": 79, "\u0050": 80, "\u0051": 81, "\u0052": 82, "\u0053": 83,
"\u0054": 84, "\u0055": 85, "\u0056": 86, "\u0057": 87, "\u0058": 88, "\u0059": 89,
"\u005A": 90, "\u005B": 91, "\u005C": 92, "\u005D": 93, "\u005E": 94, "\u005F": 95,
"\u0060": 96, "\u0061": 97, "\u0062": 98, "\u0063": 99, "\u0064": 100, "\u0065": 101,
"\u0066": 102, "\u0067": 103, "\u0068": 104, "\u0069": 105, "\u006A": 106, "\u006B": 107,
"\u006C": 108, "\u006D": 109, "\u006E": 110, "\u006F": 111, "\u0070": 112, "\u0071": 113,
"\u0072": 114, "\u0073": 115, "\u0074": 116, "\u0075": 117, "\u0076": 118, "\u0077": 119,
"\u0078": 120, "\u0079": 121, "\u007A": 122, "\u007B": 123, "\u007C": 124, "\u007D": 125,
"\u007E": 126, "\u007F": 127, "\u0080": 128, "\u0081": 129, "\u0082": 130, "\u0083": 131,
"\u0084": 132, "\u0085": 133, "\u0086": 134, "\u0087": 135, "\u0088": 136, "\u0089": 137,
"\u008A": 138, "\u008B": 139, "\u008C": 140, "\u008D": 141, "\u008E": 142, "\u008F": 143,
"\u0090": 144, "\u0091": 145, "\u0092": 146, "\u0093": 147, "\u0094": 148, "\u0095": 149,
"\u0096": 150, "\u0097": 151, "\u0098": 152, "\u0099": 153, "\u009A": 154, "\u009B": 155,
"\u009C": 156, "\u009D": 157, "\u009E": 158, "\u009F": 159, "\u00A0": 160, "\u00A1": 161,
"\u00A2": 162, "\u00A3": 163, "\u00A4": 164, "\u00A5": 165, "\u00A6": 166, "\u00A7": 167,
"\u00A8": 168, "\u00A9": 169, "\u00AA": 170, "\u00AB": 171, "\u00AC": 172, "\u00AD": 173,
"\u00AE": 174, "\u00AF": 175, "\u00B0": 176, "\u00B1": 177, "\u00B2": 178, "\u00B3": 179,
"\u00B4": 180, "\u00B5": 181, "\u00B6": 182, "\u00B7": 183, "\u00B8": 184, "\u00B9": 185,
"\u00BA": 186, "\u00BB": 187, "\u00BC": 188, "\u00BD": 189, "\u00BE": 190, "\u00BF": 191,
"\u00C0": 192, "\u00C1": 193, "\u00C2": 194, "\u00C3": 195, "\u00C4": 196, "\u00C5": 197,
"\u00C6": 198, "\u00C7": 199, "\u00C8": 200, "\u00C9": 201, "\u00CA": 202, "\u00CB": 203,
"\u00CC": 204, "\u00CD": 205, "\u00CE": 206, "\u00CF": 207, "\u00D0": 208, "\u00D1": 209,
"\u00D2": 210, "\u00D3": 211, "\u00D4": 212, "\u00D5": 213, "\u00D6": 214, "\u00D7": 215,
"\u00D8": 216, "\u00D9": 217, "\u00DA": 218, "\u00DB": 219, "\u00DC": 220, "\u00DD": 221,
"\u00DE": 222, "\u00DF": 223, "\u00E0": 224, "\u00E1": 225, "\u00E2": 226, "\u00E3": 227,
"\u00E4": 228, "\u00E5": 229, "\u00E6": 230, "\u00E7": 231, "\u00E8": 232, "\u00E9": 233,
"\u00EA": 234, "\u00EB": 235, "\u00EC": 236, "\u00ED": 237, "\u00EE": 238, "\u00EF": 239,
"\u00F0": 240, "\u00F1": 241, "\u00F2": 242, "\u00F3": 243, "\u00F4": 244, "\u00F5": 245,
"\u00F6": 246, "\u00F7": 247, "\u00F8": 248, "\u00F9": 249, "\u00FA": 250, "\u00FB": 251,
"\u00FC": 252, "\u00FD": 253, "\u00FE": 254, "\u00FF": 255
};
}
expect: {
var o = {
"\0":0,"\x01":1,"\x02":2,"\x03":3,"\x04":4,"\x05":5,"\x06":6,
"\x07":7,"\b":8,"\t":9,"\n":10,"\v":11,"\f":12,"\r":13,"\x0e":14,
"\x0f":15,"\x10":16,"\x11":17,"\x12":18,"\x13":19,"\x14":20,"\x15":21,
"\x16":22,"\x17":23,"\x18":24,"\x19":25,"\x1a":26,"\x1b":27,"\x1c":28,
"\x1d":29,"\x1e":30,"\x1f":31," ":32,"!":33,'"':34,"#":35,$:36,
"%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,"-":45,
".":46,"/":47,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,
":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,A:65,B:66,C:67,
D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,
Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,"[":91,"\\":92,
"]":93,"^":94,_:95,"`":96,a:97,b:98,c:99,d:100,e:101,f:102,g:103,
h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112,q:113,r:114,
s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,"{":123,"|":124,
"}":125,"~":126,"\x7f":127,"\x80":128,"\x81":129,"\x82":130,"\x83":131,
"\x84":132,"\x85":133,"\x86":134,"\x87":135,"\x88":136,"\x89":137,
"\x8a":138,"\x8b":139,"\x8c":140,"\x8d":141,"\x8e":142,"\x8f":143,
"\x90":144,"\x91":145,"\x92":146,"\x93":147,"\x94":148,"\x95":149,
"\x96":150,"\x97":151,"\x98":152,"\x99":153,"\x9a":154,"\x9b":155,
"\x9c":156,"\x9d":157,"\x9e":158,"\x9f":159,"\xa0":160,"\xa1":161,
"\xa2":162,"\xa3":163,"\xa4":164,"\xa5":165,"\xa6":166,"\xa7":167,
"\xa8":168,"\xa9":169,"\xaa":170,"\xab":171,"\xac":172,"\xad":173,
"\xae":174,"\xaf":175,"\xb0":176,"\xb1":177,"\xb2":178,"\xb3":179,
"\xb4":180,"\xb5":181,"\xb6":182,"\xb7":183,"\xb8":184,"\xb9":185,
"\xba":186,"\xbb":187,"\xbc":188,"\xbd":189,"\xbe":190,"\xbf":191,
"\xc0":192,"\xc1":193,"\xc2":194,"\xc3":195,"\xc4":196,"\xc5":197,
"\xc6":198,"\xc7":199,"\xc8":200,"\xc9":201,"\xca":202,"\xcb":203,
"\xcc":204,"\xcd":205,"\xce":206,"\xcf":207,"\xd0":208,"\xd1":209,
"\xd2":210,"\xd3":211,"\xd4":212,"\xd5":213,"\xd6":214,"\xd7":215,
"\xd8":216,"\xd9":217,"\xda":218,"\xdb":219,"\xdc":220,"\xdd":221,
"\xde":222,"\xdf":223,"\xe0":224,"\xe1":225,"\xe2":226,"\xe3":227,
"\xe4":228,"\xe5":229,"\xe6":230,"\xe7":231,"\xe8":232,"\xe9":233,
"\xea":234,"\xeb":235,"\xec":236,"\xed":237,"\xee":238,"\xef":239,
"\xf0":240,"\xf1":241,"\xf2":242,"\xf3":243,"\xf4":244,"\xf5":245,
"\xf6":246,"\xf7":247,"\xf8":248,"\xf9":249,"\xfa":250,"\xfb":251,
"\xfc":252,"\xfd":253,"\xfe":254,"\xff":255
};
}
}
first_256_hex_chars_as_properties: {
input: {
var o = {
"\x00": 0, "\x01": 1, "\x02": 2, "\x03": 3, "\x04": 4, "\x05": 5,
"\x06": 6, "\x07": 7, "\x08": 8, "\x09": 9, "\x0A": 10, "\x0B": 11,
"\x0C": 12, "\x0D": 13, "\x0E": 14, "\x0F": 15, "\x10": 16, "\x11": 17,
"\x12": 18, "\x13": 19, "\x14": 20, "\x15": 21, "\x16": 22, "\x17": 23,
"\x18": 24, "\x19": 25, "\x1A": 26, "\x1B": 27, "\x1C": 28, "\x1D": 29,
"\x1E": 30, "\x1F": 31, "\x20": 32, "\x21": 33, "\x22": 34, "\x23": 35,
"\x24": 36, "\x25": 37, "\x26": 38, "\x27": 39, "\x28": 40, "\x29": 41,
"\x2A": 42, "\x2B": 43, "\x2C": 44, "\x2D": 45, "\x2E": 46, "\x2F": 47,
"\x30": 48, "\x31": 49, "\x32": 50, "\x33": 51, "\x34": 52, "\x35": 53,
"\x36": 54, "\x37": 55, "\x38": 56, "\x39": 57, "\x3A": 58, "\x3B": 59,
"\x3C": 60, "\x3D": 61, "\x3E": 62, "\x3F": 63, "\x40": 64, "\x41": 65,
"\x42": 66, "\x43": 67, "\x44": 68, "\x45": 69, "\x46": 70, "\x47": 71,
"\x48": 72, "\x49": 73, "\x4A": 74, "\x4B": 75, "\x4C": 76, "\x4D": 77,
"\x4E": 78, "\x4F": 79, "\x50": 80, "\x51": 81, "\x52": 82, "\x53": 83,
"\x54": 84, "\x55": 85, "\x56": 86, "\x57": 87, "\x58": 88, "\x59": 89,
"\x5A": 90, "\x5B": 91, "\x5C": 92, "\x5D": 93, "\x5E": 94, "\x5F": 95,
"\x60": 96, "\x61": 97, "\x62": 98, "\x63": 99, "\x64": 100, "\x65": 101,
"\x66": 102, "\x67": 103, "\x68": 104, "\x69": 105, "\x6A": 106, "\x6B": 107,
"\x6C": 108, "\x6D": 109, "\x6E": 110, "\x6F": 111, "\x70": 112, "\x71": 113,
"\x72": 114, "\x73": 115, "\x74": 116, "\x75": 117, "\x76": 118, "\x77": 119,
"\x78": 120, "\x79": 121, "\x7A": 122, "\x7B": 123, "\x7C": 124, "\x7D": 125,
"\x7E": 126, "\x7F": 127, "\x80": 128, "\x81": 129, "\x82": 130, "\x83": 131,
"\x84": 132, "\x85": 133, "\x86": 134, "\x87": 135, "\x88": 136, "\x89": 137,
"\x8A": 138, "\x8B": 139, "\x8C": 140, "\x8D": 141, "\x8E": 142, "\x8F": 143,
"\x90": 144, "\x91": 145, "\x92": 146, "\x93": 147, "\x94": 148, "\x95": 149,
"\x96": 150, "\x97": 151, "\x98": 152, "\x99": 153, "\x9A": 154, "\x9B": 155,
"\x9C": 156, "\x9D": 157, "\x9E": 158, "\x9F": 159, "\xA0": 160, "\xA1": 161,
"\xA2": 162, "\xA3": 163, "\xA4": 164, "\xA5": 165, "\xA6": 166, "\xA7": 167,
"\xA8": 168, "\xA9": 169, "\xAA": 170, "\xAB": 171, "\xAC": 172, "\xAD": 173,
"\xAE": 174, "\xAF": 175, "\xB0": 176, "\xB1": 177, "\xB2": 178, "\xB3": 179,
"\xB4": 180, "\xB5": 181, "\xB6": 182, "\xB7": 183, "\xB8": 184, "\xB9": 185,
"\xBA": 186, "\xBB": 187, "\xBC": 188, "\xBD": 189, "\xBE": 190, "\xBF": 191,
"\xC0": 192, "\xC1": 193, "\xC2": 194, "\xC3": 195, "\xC4": 196, "\xC5": 197,
"\xC6": 198, "\xC7": 199, "\xC8": 200, "\xC9": 201, "\xCA": 202, "\xCB": 203,
"\xCC": 204, "\xCD": 205, "\xCE": 206, "\xCF": 207, "\xD0": 208, "\xD1": 209,
"\xD2": 210, "\xD3": 211, "\xD4": 212, "\xD5": 213, "\xD6": 214, "\xD7": 215,
"\xD8": 216, "\xD9": 217, "\xDA": 218, "\xDB": 219, "\xDC": 220, "\xDD": 221,
"\xDE": 222, "\xDF": 223, "\xE0": 224, "\xE1": 225, "\xE2": 226, "\xE3": 227,
"\xE4": 228, "\xE5": 229, "\xE6": 230, "\xE7": 231, "\xE8": 232, "\xE9": 233,
"\xEA": 234, "\xEB": 235, "\xEC": 236, "\xED": 237, "\xEE": 238, "\xEF": 239,
"\xF0": 240, "\xF1": 241, "\xF2": 242, "\xF3": 243, "\xF4": 244, "\xF5": 245,
"\xF6": 246, "\xF7": 247, "\xF8": 248, "\xF9": 249, "\xFA": 250, "\xFB": 251,
"\xFC": 252, "\xFD": 253, "\xFE": 254, "\xFF": 255
};
}
expect: {
var o = {
"\0":0,"\x01":1,"\x02":2,"\x03":3,"\x04":4,"\x05":5,"\x06":6,
"\x07":7,"\b":8,"\t":9,"\n":10,"\v":11,"\f":12,"\r":13,"\x0e":14,
"\x0f":15,"\x10":16,"\x11":17,"\x12":18,"\x13":19,"\x14":20,"\x15":21,
"\x16":22,"\x17":23,"\x18":24,"\x19":25,"\x1a":26,"\x1b":27,"\x1c":28,
"\x1d":29,"\x1e":30,"\x1f":31," ":32,"!":33,'"':34,"#":35,$:36,
"%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,"-":45,
".":46,"/":47,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,
":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,A:65,B:66,C:67,
D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,
Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,"[":91,"\\":92,
"]":93,"^":94,_:95,"`":96,a:97,b:98,c:99,d:100,e:101,f:102,g:103,
h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112,q:113,r:114,
s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,"{":123,"|":124,
"}":125,"~":126,"\x7f":127,"\x80":128,"\x81":129,"\x82":130,"\x83":131,
"\x84":132,"\x85":133,"\x86":134,"\x87":135,"\x88":136,"\x89":137,
"\x8a":138,"\x8b":139,"\x8c":140,"\x8d":141,"\x8e":142,"\x8f":143,
"\x90":144,"\x91":145,"\x92":146,"\x93":147,"\x94":148,"\x95":149,
"\x96":150,"\x97":151,"\x98":152,"\x99":153,"\x9a":154,"\x9b":155,
"\x9c":156,"\x9d":157,"\x9e":158,"\x9f":159,"\xa0":160,"\xa1":161,
"\xa2":162,"\xa3":163,"\xa4":164,"\xa5":165,"\xa6":166,"\xa7":167,
"\xa8":168,"\xa9":169,"\xaa":170,"\xab":171,"\xac":172,"\xad":173,
"\xae":174,"\xaf":175,"\xb0":176,"\xb1":177,"\xb2":178,"\xb3":179,
"\xb4":180,"\xb5":181,"\xb6":182,"\xb7":183,"\xb8":184,"\xb9":185,
"\xba":186,"\xbb":187,"\xbc":188,"\xbd":189,"\xbe":190,"\xbf":191,
"\xc0":192,"\xc1":193,"\xc2":194,"\xc3":195,"\xc4":196,"\xc5":197,
"\xc6":198,"\xc7":199,"\xc8":200,"\xc9":201,"\xca":202,"\xcb":203,
"\xcc":204,"\xcd":205,"\xce":206,"\xcf":207,"\xd0":208,"\xd1":209,
"\xd2":210,"\xd3":211,"\xd4":212,"\xd5":213,"\xd6":214,"\xd7":215,
"\xd8":216,"\xd9":217,"\xda":218,"\xdb":219,"\xdc":220,"\xdd":221,
"\xde":222,"\xdf":223,"\xe0":224,"\xe1":225,"\xe2":226,"\xe3":227,
"\xe4":228,"\xe5":229,"\xe6":230,"\xe7":231,"\xe8":232,"\xe9":233,
"\xea":234,"\xeb":235,"\xec":236,"\xed":237,"\xee":238,"\xef":239,
"\xf0":240,"\xf1":241,"\xf2":242,"\xf3":243,"\xf4":244,"\xf5":245,
"\xf6":246,"\xf7":247,"\xf8":248,"\xf9":249,"\xfa":250,"\xfb":251,
"\xfc":252,"\xfd":253,"\xfe":254,"\xff":255
};
}
}
native_prototype: {
options = {
unsafe_proto: true,
}
input: {
Array.prototype.splice.apply(a, [1, 2, b, c]);
Object.prototype.hasOwnProperty.call(d, "foo");
String.prototype.indexOf.call(e, "bar");
}
expect: {
[].splice.apply(a, [1, 2, b, c]);
({}).hasOwnProperty.call(d, "foo");
"".indexOf.call(e, "bar");
}
}

295
test/compress/pure_funcs.js Normal file
View File

@@ -0,0 +1,295 @@
array: {
options = {
pure_funcs: [ "Math.floor" ],
side_effects: true,
}
input: {
var a;
function f(b) {
Math.floor(a / b);
Math.floor(c / b);
}
}
expect: {
var a;
function f(b) {
c;
}
}
}
func: {
options = {
pure_funcs: function(node) {
return !~node.args[0].print_to_string().indexOf("a");
},
side_effects: true,
}
input: {
function f(a, b) {
Math.floor(a / b);
Math.floor(c / b);
}
}
expect: {
function f(a, b) {
Math.floor(c / b);
}
}
}
side_effects: {
options = {
pure_funcs: [ "console.log" ],
side_effects: true,
}
input: {
function f(a, b) {
console.log(a());
console.log(b);
}
}
expect: {
function f(a, b) {
a();
}
}
}
unused: {
options = {
pure_funcs: [ "pure" ],
side_effects: true,
unused: true,
}
input: {
function foo() {
var u = pure(1);
var x = pure(2);
var y = pure(x);
var z = pure(pure(side_effects()));
return pure(3);
}
}
expect: {
function foo() {
side_effects();
return pure(3);
}
}
}
babel: {
options = {
pure_funcs: [ "_classCallCheck" ],
side_effects: true,
unused: true,
}
input: {
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor))
throw new TypeError("Cannot call a class as a function");
}
var Foo = function Foo() {
_classCallCheck(this, Foo);
};
}
expect: {
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor))
throw new TypeError("Cannot call a class as a function");
}
var Foo = function() {
};
}
}
conditional: {
options = {
pure_funcs: [ "pure" ],
side_effects: true,
}
input: {
pure(1 | a() ? 2 & b() : 7 ^ c());
pure(1 | a() ? 2 & b() : 5);
pure(1 | a() ? 4 : 7 ^ c());
pure(1 | a() ? 4 : 5);
pure(3 ? 2 & b() : 7 ^ c());
pure(3 ? 2 & b() : 5);
pure(3 ? 4 : 7 ^ c());
pure(3 ? 4 : 5);
}
expect: {
1 | a() ? b() : c();
1 | a() && b();
1 | a() || c();
a();
3 ? b() : c();
3 && b();
3 || c();
}
}
relational: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
foo() in foo();
foo() instanceof bar();
foo() < "bar";
bar() > foo();
bar() != bar();
bar() !== "bar";
"bar" == foo();
"bar" === bar();
"bar" >= "bar";
}
expect: {
bar();
bar();
bar(), bar();
bar();
bar();
}
}
arithmetic: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
foo() + foo();
foo() - bar();
foo() * "bar";
bar() / foo();
bar() & bar();
bar() | "bar";
"bar" >> foo();
"bar" << bar();
"bar" >>> "bar";
}
expect: {
bar();
bar();
bar(), bar();
bar();
bar();
}
}
boolean_and: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
foo() && foo();
foo() && bar();
foo() && "bar";
bar() && foo();
bar() && bar();
bar() && "bar";
"bar" && foo();
"bar" && bar();
"bar" && "bar";
}
expect: {
foo() && bar();
bar();
bar() && bar();
bar();
"bar" && bar();
}
}
boolean_or: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
foo() || foo();
foo() || bar();
foo() || "bar";
bar() || foo();
bar() || bar();
bar() || "bar";
"bar" || foo();
"bar" || bar();
"bar" || "bar";
}
expect: {
foo() || bar();
bar();
bar() || bar();
bar();
"bar" || bar();
}
}
assign: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
var a;
function f(b) {
a = foo();
b *= 4 + foo();
c >>= 0 | foo();
}
}
expect: {
var a;
function f(b) {
a = foo();
b *= 4 + foo();
c >>= 0 | foo();
}
}
}
unary: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
}
input: {
typeof foo();
typeof bar();
typeof "bar";
void foo();
void bar();
void "bar";
delete a[foo()];
delete a[bar()];
delete a["bar"];
a[foo()]++;
a[bar()]++;
a["bar"]++;
--a[foo()];
--a[bar()];
--a["bar"];
~foo();
~bar();
~"bar";
}
expect: {
bar();
bar();
delete a[foo()];
delete a[bar()];
delete a["bar"];
a[foo()]++;
a[bar()]++;
a["bar"]++;
--a[foo()];
--a[bar()];
--a["bar"];
bar();
}
}

View File

@@ -0,0 +1,591 @@
reduce_vars: {
options = {
conditionals : true,
evaluate : true,
global_defs : {
C : 0
},
reduce_vars : true,
unused : true
}
input: {
var A = 1;
(function f0() {
var a = 2;
console.log(a - 5);
console.log(A - 5);
})();
(function f1() {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})();
(function f2(eval) {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})(eval);
(function f3() {
var b = typeof C !== "undefined";
var c = 4;
if (b) {
return 'yes';
} else {
return 'no';
}
})();
console.log(A + 1);
}
expect: {
var A = 1;
(function() {
console.log(-3);
console.log(-4);
})();
(function f1() {
var a = 2;
console.log(-3);
eval("console.log(a);");
})();
(function f2(eval) {
var a = 2;
console.log(-3);
eval("console.log(a);");
})(eval);
(function() {
return "yes";
})();
console.log(2);
}
}
modified: {
options = {
conditionals : true,
evaluate : true,
reduce_vars : true,
unused : true
}
input: {
function f0() {
var a = 1, b = 2;
b++;
console.log(a + 1);
console.log(b + 1);
}
function f1() {
var a = 1, b = 2;
--b;
console.log(a + 1);
console.log(b + 1);
}
function f2() {
var a = 1, b = 2, c = 3;
b = c;
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f3() {
var a = 1, b = 2, c = 3;
b *= c;
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f4() {
var a = 1, b = 2, c = 3;
if (a) {
b = c;
} else {
c = b;
}
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
}
}
expect: {
function f0() {
var b = 2;
b++;
console.log(2);
console.log(b + 1);
}
function f1() {
var b = 2;
--b;
console.log(2);
console.log(b + 1);
}
function f2() {
var b = 2;
b = 3;
console.log(1 + b);
console.log(b + 3);
console.log(4);
console.log(1 + b + 3);
}
function f3() {
var b = 2;
b *= 3;
console.log(1 + b);
console.log(b + 3);
console.log(4);
console.log(1 + b + 3);
}
function f4() {
var b = 2, c = 3;
b = c;
console.log(1 + b);
console.log(b + c);
console.log(1 + c);
console.log(1 + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
}
}
}
unsafe_evaluate: {
options = {
evaluate : true,
reduce_vars : true,
unsafe : true,
unused : true
}
input: {
function f0(){
var a = {
b:1
};
console.log(a.b + 3);
}
function f1(){
var a = {
b:{
c:1
},
d:2
};
console.log(a.b + 3, a.d + 4, a.b.c + 5, a.d.c + 6);
}
}
expect: {
function f0(){
console.log(4);
}
function f1(){
var a = {
b:{
c:1
},
d:2
};
console.log(a.b + 3, 6, 6, 2..c + 6);
}
}
}
unsafe_evaluate_object: {
options = {
evaluate : true,
reduce_vars : true,
unsafe : true
}
input: {
function f0(){
var a = 1;
var b = {};
b[a] = 2;
console.log(a + 3);
}
function f1(){
var a = {
b:1
};
a.b = 2;
console.log(a.b + 3);
}
}
expect: {
function f0(){
var a = 1;
var b = {};
b[1] = 2;
console.log(4);
}
function f1(){
var a = {
b:1
};
a.b = 2;
console.log(a.b + 3);
}
}
}
unsafe_evaluate_array: {
options = {
evaluate : true,
reduce_vars : true,
unsafe : true
}
input: {
function f0(){
var a = 1;
var b = [];
b[a] = 2;
console.log(a + 3);
}
function f1(){
var a = [1];
a[2] = 3;
console.log(a.length);
}
function f2(){
var a = [1];
a.push(2);
console.log(a.length);
}
}
expect: {
function f0(){
var a = 1;
var b = [];
b[1] = 2;
console.log(4);
}
function f1(){
var a = [1];
a[2] = 3;
console.log(a.length);
}
function f2(){
var a = [1];
a.push(2);
console.log(a.length);
}
}
}
unsafe_evaluate_equality: {
options = {
evaluate : true,
reduce_vars : true,
unsafe : true,
unused : true
}
input: {
function f0(){
var a = {};
console.log(a === a);
}
function f1(){
var a = [];
console.log(a === a);
}
function f2(){
var a = {a:1, b:2};
var b = a;
var c = a;
console.log(b === c);
}
function f3(){
var a = [1, 2, 3];
var b = a;
var c = a;
console.log(b === c);
}
}
expect: {
function f0(){
console.log(true);
}
function f1(){
console.log(true);
}
function f2(){
console.log(true);
}
function f3(){
console.log(true);
}
}
}
passes: {
options = {
conditionals: true,
evaluate: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
function f() {
var a = 1, b = 2, c = 3;
if (a) {
b = c;
} else {
c = b;
}
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
}
expect: {
function f() {
var b = 2;
b = 3;
console.log(1 + b);
console.log(b + 3);
console.log(4);
console.log(1 + b + 3);
}
}
}
iife: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
!function(a, b, c) {
b++;
console.log(a - 1, b * 1, c + 2);
}(1, 2, 3);
}
expect: {
!function(a, b, c) {
b++;
console.log(0, 1 * b, 5);
}(1, 2, 3);
}
}
iife_new: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var A = new function(a, b, c) {
b++;
console.log(a - 1, b * 1, c + 2);
}(1, 2, 3);
}
expect: {
var A = new function(a, b, c) {
b++;
console.log(0, 1 * b, 5);
}(1, 2, 3);
}
}
multi_def: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
function f(a) {
if (a)
var b = 1;
else
var b = 2
console.log(b + 1);
}
}
expect: {
function f(a) {
if (a)
var b = 1;
else
var b = 2
console.log(b + 1);
}
}
}
multi_def_2: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
if (code == 16)
var bitsLength = 2, bitsOffset = 3, what = len;
else if (code == 17)
var bitsLength = 3, bitsOffset = 3, what = (len = 0);
else if (code == 18)
var bitsLength = 7, bitsOffset = 11, what = (len = 0);
var repeatLength = this.getBits(bitsLength) + bitsOffset;
}
expect: {
if (16 == code)
var bitsLength = 2, bitsOffset = 3, what = len;
else if (17 == code)
var bitsLength = 3, bitsOffset = 3, what = (len = 0);
else if (18 == code)
var bitsLength = 7, bitsOffset = 11, what = (len = 0);
var repeatLength = this.getBits(bitsLength) + bitsOffset;
}
}
use_before_var: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(t);
var t = 1;
}
expect: {
console.log(t);
var t = 1;
}
}
inner_var_if: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
function f(){
return 0;
}
if (f())
var t = 1;
if (!t)
console.log(t);
}
expect: {
function f(){
return 0;
}
if (f())
var t = 1;
if (!t)
console.log(t);
}
}
inner_var_label: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
function f(){
return 1;
}
l: {
if (f()) break l;
var t = 1;
}
console.log(t);
}
expect: {
function f(){
return 1;
}
l: {
if (f()) break l;
var t = 1;
}
console.log(t);
}
}
inner_var_for: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var a = 1;
x(a, b, d);
for (var b = 2, c = 3; x(a, b, c, d); x(a, b, c, d)) {
var d = 4, e = 5;
x(a, b, c, d, e);
}
x(a, b, c, d, e)
}
expect: {
var a = 1;
x(1, b, d);
for (var b = 2, c = 3; x(1, b, 3, d); x(1, b, 3, d)) {
var d = 4, e = 5;
x(1, b, 3, d, e);
}
x(1, b, 3, d, e);
}
}
inner_var_for_in: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var a = 1, b = 2;
for (b in (function() {
return x(a, b, c);
})()) {
var c = 3, d = 4;
x(a, b, c, d);
}
x(a, b, c, d);
}
expect: {
var a = 1, b = 2;
for (b in (function() {
return x(1, b, c);
})()) {
var c = 3, d = 4;
x(1, b, c, d);
}
x(1, b, c, d);
}
}

View File

@@ -0,0 +1,124 @@
return_undefined: {
options = {
sequences : false,
if_return : true,
evaluate : true,
dead_code : true,
conditionals : true,
comparisons : true,
booleans : true,
unused : true,
side_effects : true,
properties : true,
drop_debugger : true,
loops : true,
hoist_funs : true,
keep_fargs : true,
keep_fnames : false,
hoist_vars : true,
join_vars : true,
cascade : true,
negate_iife : true
};
input: {
function f0() {
}
function f1() {
return undefined;
}
function f2() {
return void 0;
}
function f3() {
return void 123;
}
function f4() {
return;
}
function f5(a, b) {
console.log(a, b);
baz(a);
return;
}
function f6(a, b) {
console.log(a, b);
if (a) {
foo(b);
baz(a);
return a + b;
}
return undefined;
}
function f7(a, b) {
console.log(a, b);
if (a) {
foo(b);
baz(a);
return void 0;
}
return a + b;
}
function f8(a, b) {
foo(a);
bar(b);
return void 0;
}
function f9(a, b) {
foo(a);
bar(b);
return undefined;
}
function f10() {
return false;
}
function f11() {
return null;
}
function f12() {
return 0;
}
}
expect: {
function f0() {}
function f1() {}
function f2() {}
function f3() {}
function f4() {}
function f5(a, b) {
console.log(a, b);
baz(a);
}
function f6(a, b) {
console.log(a, b);
if (a) {
foo(b);
baz(a);
return a + b;
}
}
function f7(a, b) {
console.log(a, b);
if (!a)
return a + b;
foo(b);
baz(a);
}
function f8(a, b) {
foo(a);
bar(b);
}
function f9(a, b) {
foo(a);
bar(b);
}
function f10() {
return !1;
}
function f11() {
return null;
}
function f12() {
return 0;
}
}
}

134
test/compress/screw-ie8.js Normal file
View File

@@ -0,0 +1,134 @@
do_screw: {
options = { screw_ie8: true };
beautify = {
screw_ie8: true,
ascii_only: true
};
input: f("\v");
expect_exact: 'f("\\v");';
}
dont_screw: {
options = { screw_ie8: false };
beautify = { screw_ie8: false, ascii_only: true };
input: f("\v");
expect_exact: 'f("\\x0B");';
}
do_screw_try_catch: {
options = { screw_ie8: true };
mangle = { screw_ie8: true };
beautify = { screw_ie8: true };
input: {
good = function(e){
return function(error){
try{
e()
} catch(e) {
error(e)
}
}
};
}
expect: {
good = function(n){
return function(t){
try{
n()
} catch(n) {
t(n)
}
}
};
}
}
dont_screw_try_catch: {
// This test is known to generate incorrect code for screw_ie8=false.
// Update expected result in the event this bug is ever fixed.
options = { screw_ie8: false };
mangle = { screw_ie8: false };
beautify = { screw_ie8: false };
input: {
bad = function(e){
return function(error){
try{
e()
} catch(e) {
error(e)
}
}
};
}
expect: {
bad = function(n){
return function(n){
try{
t()
} catch(t) {
n(t)
}
}
};
}
}
do_screw_try_catch_undefined: {
options = { screw_ie8: true };
mangle = { screw_ie8: true };
beautify = { screw_ie8: true };
input: {
function a(b){
try {
throw 'Stuff';
} catch (undefined) {
console.log('caught: ' + undefined);
}
console.log('undefined is ' + undefined);
return b === undefined;
};
}
expect: {
function a(o){
try{
throw "Stuff"
} catch (o) {
console.log("caught: "+o)
}
console.log("undefined is " + void 0);
return void 0===o
}
}
}
dont_screw_try_catch_undefined: {
// This test is known to generate incorrect code for screw_ie8=false.
// Update expected result in the event this bug is ever fixed.
options = { screw_ie8: false };
mangle = { screw_ie8: false };
beautify = { screw_ie8: false };
input: {
function a(b){
try {
throw 'Stuff';
} catch (undefined) {
console.log('caught: ' + undefined);
}
console.log('undefined is ' + undefined);
return b === undefined;
};
}
expect: {
function a(o){
try{
throw "Stuff"
} catch (n) {
console.log("caught: "+n)
}
console.log("undefined is " + n);
return o === n
}
}
}

View File

@@ -169,3 +169,85 @@ for_sequences: {
for (y = 5; false;);
}
}
limit_1: {
options = {
sequences: 3,
};
input: {
a;
b;
c;
d;
e;
f;
g;
h;
i;
j;
k;
}
expect: {
a, b, c;
d, e, f;
g, h, i;
j, k;
}
}
limit_2: {
options = {
sequences: 3,
};
input: {
a, b;
c, d;
e, f;
g, h;
i, j;
k;
}
expect: {
a, b, c, d;
e, f, g, h;
i, j, k;
}
}
negate_iife_for: {
options = {
sequences: true,
negate_iife: true,
};
input: {
(function() {})();
for (i = 0; i < 5; i++) console.log(i);
(function() {})();
for (; i < 5; i++) console.log(i);
}
expect: {
for (!function() {}(), i = 0; i < 5; i++) console.log(i);
for (function() {}(); i < 5; i++) console.log(i);
}
}
iife: {
options = {
sequences: true,
};
input: {
x = 42;
(function a() {})();
!function b() {}();
~function c() {}();
+function d() {}();
-function e() {}();
void function f() {}();
typeof function g() {}();
}
expect: {
x = 42, function a() {}(), function b() {}(), function c() {}(),
function d() {}(), function e() {}(), function f() {}(), function g() {}()
}
}

View File

@@ -0,0 +1,10 @@
octal_escape_sequence: {
input: {
var boundaries = "\0\7\00\07\70\77\000\077\300\377";
var border_check = "\400\700\0000\3000";
}
expect: {
var boundaries = "\x00\x07\x00\x07\x38\x3f\x00\x3f\xc0\xff";
var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30";
}
}

View File

@@ -23,3 +23,28 @@ typeof_evaluation: {
h='undefined';
}
}
typeof_in_boolean_context: {
options = {
booleans : true,
evaluate : true,
conditionals : true,
side_effects : true,
};
input: {
function f1(x) { return typeof x ? "yes" : "no"; }
function f2() { return typeof g()? "Yes" : "No"; }
typeof 0 ? foo() : bar();
!typeof console.log(1);
var a = !typeof console.log(2);
if (typeof (1 + foo()));
}
expect: {
function f1(x) { return "yes"; }
function f2() { return g(), "Yes"; }
foo();
console.log(1);
var a = !(console.log(2), !0);
foo();
}
}

View File

@@ -0,0 +1,48 @@
wrap_iife: {
options = {
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: '(function(){return function(){console.log("test")}})()();'
}
wrap_iife_in_expression: {
options = {
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
foo = (function () {
return bar();
})();
}
expect_exact: 'foo=(function(){return bar()})();'
}
wrap_iife_in_return_call: {
options = {
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return (function() {
console.log('test')
})();
})()();
}
expect_exact: '(function(){return(function(){console.log("test")})()})()();'
}

View File

@@ -0,0 +1,3 @@
// foo
/*@preserve*/
// bar

View File

@@ -0,0 +1 @@
console.log(C.V, C.D);

View File

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

View File

@@ -0,0 +1 @@
foo, bar(

View File

@@ -0,0 +1 @@
function f(a{}

View File

@@ -0,0 +1 @@
foo( xyz, 0abc);

View File

@@ -0,0 +1,8 @@
"use strict";
var foo = function foo(x) {
return "foo " + x;
};
console.log(foo("bar"));
//# sourceMappingURL=simple.js.map

View File

@@ -0,0 +1,8 @@
{
"version": 3,
"sources": ["index.js"],
"names": [],
"mappings": ";;AAAA,IAAI,MAAM,SAAN,GAAM;AAAA,SAAK,SAAS,CAAd;AAAA,CAAV;AACA,QAAQ,GAAR,CAAY,IAAI,KAAJ,CAAZ",
"file": "simple.js",
"sourcesContent": ["let foo = x => \"foo \" + x;\nconsole.log(foo(\"bar\"));"]
}

View File

@@ -0,0 +1,4 @@
function bar(x) {
var triple = x * (2 + 1);
return triple;
}

View File

@@ -0,0 +1,4 @@
function baz(x) {
var half = x / 2;
return half;
}

View File

@@ -0,0 +1,5 @@
var print = console.log.bind(console);
function foo(x) {
var twice = x * 2;
print('Foo:', twice);
}

View File

@@ -0,0 +1,4 @@
var a = bar(1+2);
var b = baz(3+9);
print('q' + 'u' + 'x', a, b);
foo(5+6);

View File

@@ -0,0 +1,7 @@
var bar = (function () {
function foo (bar) {
return bar;
}
return foo;
})();

View File

@@ -0,0 +1,14 @@
function f(x) {
return function() {
function n(a) {
return a * a;
}
return x(n);
};
}
function g(op) {
return op(1) + op(2);
}
console.log(f(g)() == 5);

View File

@@ -0,0 +1,73 @@
if (x) {
foo();
}
if (x) {
foo();
} else {
baz();
}
if (x) {
foo();
} else if (y) {
bar();
} else {
baz();
}
if (x) {
if (y) {
foo();
} else {
bar();
}
} else {
baz();
}
if (x) {
foo();
} else if (y) {
bar();
} else if (z) {
baz();
} else {
moo();
}
function f() {
if (x) {
foo();
}
if (x) {
foo();
} else {
baz();
}
if (x) {
foo();
} else if (y) {
bar();
} else {
baz();
}
if (x) {
if (y) {
foo();
} else {
bar();
}
} else {
baz();
}
if (x) {
foo();
} else if (y) {
bar();
} else if (z) {
baz();
} else {
moo();
}
}

View File

@@ -0,0 +1,17 @@
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
function f() {
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
}

View File

@@ -0,0 +1,12 @@
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
function f() {
if (x) foo();
if (x) foo(); else baz();
if (x) foo(); else if (y) bar(); else baz();
if (x) if (y) foo(); else bar(); else baz();
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
}

View File

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

View File

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

87
test/jetstream.js Normal file
View File

@@ -0,0 +1,87 @@
#! /usr/bin/env node
// -*- js -*-
"use strict";
var site = "http://browserbench.org/JetStream/";
if (typeof phantom == "undefined") {
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
var args = process.argv.slice(2);
if (!args.length) {
args.push("-mc", "warnings=false");
}
args.push("--stats");
var child_process = require("child_process");
try {
require("phantomjs-prebuilt");
} catch(e) {
child_process.execSync("npm install phantomjs-prebuilt@2.1.14");
}
var http = require("http");
var server = http.createServer(function(request, response) {
request.resume();
var url = decodeURIComponent(request.url.slice(1));
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.indexOf(site) == 0 ? url.slice(site.length) : url, args.join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});
uglifyjs.stderr.on("data", function(data) {
stderr += data;
}).setEncoding("utf8");
uglifyjs.stdout.pipe(response);
http.get(url, function(res) {
res.pipe(uglifyjs.stdin);
});
}).listen().on("listening", function() {
var phantomjs = require("phantomjs-prebuilt");
var program = phantomjs.exec(process.argv[1], server.address().port);
program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr);
program.on("exit", function(code) {
server.close();
if (code) throw new Error("JetStream failed!");
console.log("JetStream completed successfully.");
});
});
server.timeout = 0;
} else {
var page = require("webpage").create();
page.onError = function(msg, trace) {
var body = [ msg ];
if (trace) trace.forEach(function(t) {
body.push(" " + (t.function || "Anonymous function") + " (" + t.file + ":" + t.line + ")");
});
console.error(body.join("\n"));
phantom.exit(1);
};
var url = "http://localhost:" + require("system").args[1] + "/";
page.onResourceRequested = function(requestData, networkRequest) {
if (/\.js$/.test(requestData.url))
networkRequest.changeUrl(url + encodeURIComponent(requestData.url));
}
page.onConsoleMessage = function(msg) {
if (/Error:/i.test(msg)) {
console.error(msg);
phantom.exit(1);
}
console.log(msg);
if (~msg.indexOf("Raw results:")) {
phantom.exit();
}
};
page.open(site, function(status) {
if (status != "success") phantomjs.exit(1);
page.evaluate(function() {
JetStream.switchToQuick();
JetStream.start();
});
});
}

29
test/mocha.js Normal file
View File

@@ -0,0 +1,29 @@
var Mocha = require('mocha'),
fs = require('fs'),
path = require('path');
// Instantiate a Mocha instance.
var mocha = new Mocha({});
var testDir = __dirname + '/mocha/';
// Add each .js file to the mocha instance
fs.readdirSync(testDir).filter(function(file){
// Only keep the .js files
return file.substr(-3) === '.js';
}).forEach(function(file){
mocha.addFile(
path.join(testDir, file)
);
});
module.exports = function() {
mocha.run(function(failures) {
if (failures !== 0) {
process.on('exit', function () {
process.exit(failures);
});
}
});
};

View File

@@ -0,0 +1,32 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("Accessor tokens", function() {
it("Should fill the token information for accessors (issue #1492)", function() {
// location 0 1 2 3 4
// 01234567890123456789012345678901234567890123456789
var ast = UglifyJS.parse("var obj = { get latest() { return undefined; } }");
// test all AST_ObjectProperty tokens are set as expected
var checkedAST_ObjectProperty = false;
var checkWalker = new UglifyJS.TreeWalker(function(node, descend) {
if (node instanceof UglifyJS.AST_ObjectProperty) {
checkedAST_ObjectProperty = true;
assert.equal(node.start.pos, 12);
assert.equal(node.end.endpos, 46);
assert(node.key instanceof UglifyJS.AST_SymbolRef);
assert.equal(node.key.start.pos, 16);
assert.equal(node.key.end.endpos, 22);
assert(node.value instanceof UglifyJS.AST_Accessor);
assert.equal(node.value.start.pos, 22);
assert.equal(node.value.end.endpos, 46);
}
});
ast.walk(checkWalker);
assert(checkedAST_ObjectProperty, "AST_ObjectProperty not found");
});
});

30
test/mocha/arguments.js Normal file
View File

@@ -0,0 +1,30 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("arguments", function() {
it("Should known that arguments in functions are local scoped", function() {
var ast = UglifyJS.parse("var arguments; var f = function() {arguments.length}");
ast.figure_out_scope();
// Test scope of `var arguments`
assert.strictEqual(ast.find_variable("arguments").global, true);
// Select arguments symbol in function
var symbol = ast.body[1].definitions[0].value.find_variable("arguments");
assert.strictEqual(symbol.global, false);
assert.strictEqual(symbol.scope, ast. // From ast
body[1]. // Select 2nd statement (equals to `var f ...`)
definitions[0]. // First definition of selected statement
value // Select function as scope
);
});
it("Should recognize when a function uses arguments", function() {
var ast = UglifyJS.parse("function a(){function b(){function c(){}; return arguments[0];}}");
ast.figure_out_scope();
assert.strictEqual(ast.body[0].uses_arguments, false);
assert.strictEqual(ast.body[0].body[0].uses_arguments, true);
assert.strictEqual(ast.body[0].body[0].body[0].uses_arguments, false);
});
});

241
test/mocha/cli.js Normal file
View File

@@ -0,0 +1,241 @@
var assert = require("assert");
var exec = require("child_process").exec;
var readFileSync = require("fs").readFileSync;
describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) {
this.timeout(15000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
exec(command, function (err, stdout) {
if (err) throw err;
eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object');
assert.strictEqual(true, WrappedUglifyJS.parse('foo;') instanceof WrappedUglifyJS.AST_Node);
done();
});
});
it("Should be able to filter comments correctly with `--comment all`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments all';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "// foo\n/*@preserve*/\n// bar\n\n");
done();
});
});
it("Should be able to filter comments correctly with `--comment <RegExp>`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments /r/';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n// bar\n\n");
done();
});
});
it("Should be able to filter comments correctly with just `--comment`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n\n");
done();
});
});
it("Should append source map to output when using --source-map-inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js --source-map-inline';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n");
done();
});
});
it("should not append source map to output when not using --source-map-inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n");
done();
});
});
it("Should work with --keep-fnames (mangle only)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --keep-fnames (mangle & compress)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(5==f(g)());\n");
done();
});
});
it("Should work with keep_fnames under mangler options", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep_fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --define (simple)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define D=5 -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(5);\n");
done();
});
});
it("Should work with --define (nested)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/nested.js --define C.D=5,C.V=3 -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(3,5);\n");
done();
});
});
it("Should work with --define (AST_Node)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define console.log=stdout.println -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "stdout.println(D);\n");
done();
});
});
it("Should work with `--beautify`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-1482/default.js", "utf8"));
done();
});
});
it("Should work with `--beautify bracketize`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-1482/bracketize.js", "utf8"));
done();
});
});
it("Should process inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js -cm toplevel --in-source-map inline --source-map-inline';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-520/output.js", "utf8"));
done();
});
});
it("Should warn for missing inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js --in-source-map inline';
exec(command, function (err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n");
assert.strictEqual(stderr, "WARN: inline source map not found\n");
done();
});
});
it("Should fail with multiple input and inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js test/input/issue-520/output.js --in-source-map inline --source-map-inline';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: Inline source map only works with singular input\n");
done();
});
});
it("Should fail with acorn and inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --acorn';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n");
done();
});
});
it("Should fail with SpiderMonkey and inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --spidermonkey';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n");
done();
});
});
it("Should fail with invalid syntax", function(done) {
var command = uglifyjscmd + ' test/input/invalid/simple.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12");
assert.strictEqual(lines[1], "function f(a{}");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "SyntaxError: Unexpected token punc «{», expected punc «,»");
done();
});
});
it("Should fail with correct marking of tabs", function(done) {
var command = uglifyjscmd + ' test/input/invalid/tab.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12");
assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);");
assert.strictEqual(lines[2], "\t\t \t ^");
assert.strictEqual(lines[3], "SyntaxError: Invalid syntax: 0abc");
done();
});
});
it("Should fail with correct marking at start of line", function(done) {
var command = uglifyjscmd + ' test/input/invalid/eof.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0");
assert.strictEqual(lines[1], "foo, bar(");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done();
});
});
});

View File

@@ -0,0 +1,75 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("comment filters", function() {
it("Should be able to filter comments by passing regexp", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
});
it("Should be able to filter comments with the 'all' option", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
});
it("Should be able to filter commments with the 'some' option", function() {
var ast = UglifyJS.parse("// foo\n/*@preserve*/\n// bar\n/*@license*/\n//@license with the wrong comment type\n/*@cc_on something*/");
assert.strictEqual(ast.print_to_string({comments: "some"}), "/*@preserve*/\n/*@license*/\n/*@cc_on something*/\n");
});
it("Should be able to filter comments by passing a function", function() {
var ast = UglifyJS.parse("/*TEST 123*/\n//An other comment\n//8 chars.");
var f = function(node, comment) {
return comment.value.length === 8;
};
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.\n");
});
it("Should be able to filter comments by passing regex in string format", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: "/^!/"}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
});
it("Should be able to get the comment and comment type when using a function", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
var f = function(node, comment) {
return comment.type == "comment1" || comment.type == "comment3";
};
assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6\n");
});
it("Should be able to filter comments by passing a boolean", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
assert.strictEqual(ast.print_to_string({comments: false}), "");
});
it("Should never be able to filter comment5 (shebangs)", function() {
var ast = UglifyJS.parse("#!Random comment\n//test1\n/*test2*/");
var f = function(node, comment) {
assert.strictEqual(comment.type === "comment5", false);
return true;
};
assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/\n");
});
it("Should never be able to filter comment5 when using 'some' as filter", function() {
var ast = UglifyJS.parse("#!foo\n//foo\n/*@preserve*/\n/* please hide me */");
assert.strictEqual(ast.print_to_string({comments: "some"}), "#!foo\n/*@preserve*/\n");
});
it("Should have no problem on multiple calls", function() {
const options = {
comments: /ok/
};
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}");
});
});

50
test/mocha/comment.js Normal file
View File

@@ -0,0 +1,50 @@
var assert = require("assert");
var uglify = require("../../");
describe("Comment", function() {
it("Should recognize eol of single line comments", function() {
var tests = [
"//Some comment 1\n>",
"//Some comment 2\r>",
"//Some comment 3\r\n>",
"//Some comment 4\u2028>",
"//Some comment 5\u2029>"
];
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: operator (>)" &&
e.line === 2 &&
e.col === 0;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(function() {
uglify.parse(tests[i], {fromString: true})
}, fail, tests[i]);
}
});
it("Should update the position of a multiline comment correctly", function() {
var tests = [
"/*Some comment 1\n\n\n*/\n>\n\n\n\n\n\n",
"/*Some comment 2\r\n\r\n\r\n*/\r\n>\n\n\n\n\n\n",
"/*Some comment 3\r\r\r*/\r>\n\n\n\n\n\n",
"/*Some comment 4\u2028\u2028\u2028*/\u2028>\n\n\n\n\n\n",
"/*Some comment 5\u2029\u2029\u2029*/\u2029>\n\n\n\n\n\n"
];
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: operator (>)" &&
e.line === 5 &&
e.col === 0;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(function() {
uglify.parse(tests[i], {fromString: true})
}, fail, tests[i]);
}
});
});

View File

@@ -0,0 +1,27 @@
var Uglify = require('../../');
var assert = require("assert");
describe("comment before constant", function() {
var js = 'function f() { /*c1*/ var /*c2*/ foo = /*c3*/ false; return foo; }';
it("Should test comment before constant is retained and output after mangle.", function() {
var result = Uglify.minify(js, {
fromString: true,
compress: { collapse_vars: false, reduce_vars: false },
mangle: {},
output: { comments: true },
});
assert.strictEqual(result.code, 'function f(){/*c1*/var/*c2*/n=/*c3*/!1;return n}');
});
it("Should test code works when comments disabled.", function() {
var result = Uglify.minify(js, {
fromString: true,
compress: { collapse_vars: false, reduce_vars: false },
mangle: {},
output: { comments: false },
});
assert.strictEqual(result.code, 'function f(){var n=!1;return n}');
});
});

370
test/mocha/directives.js Normal file
View File

@@ -0,0 +1,370 @@
var assert = require("assert");
var uglify = require("../../");
describe("Directives", function() {
it ("Should allow tokenizer to store directives state", function() {
var tokenizer = uglify.tokenizer("", "foo.js");
// Stack level 0
assert.strictEqual(tokenizer.has_directive("use strict"), false);
assert.strictEqual(tokenizer.has_directive("use asm"), false);
assert.strictEqual(tokenizer.has_directive("use thing"), false);
// Stack level 2
tokenizer.push_directives_stack();
tokenizer.push_directives_stack();
tokenizer.add_directive("use strict");
assert.strictEqual(tokenizer.has_directive("use strict"), true);
assert.strictEqual(tokenizer.has_directive("use asm"), false);
assert.strictEqual(tokenizer.has_directive("use thing"), false);
// Stack level 3
tokenizer.push_directives_stack();
tokenizer.add_directive("use strict");
tokenizer.add_directive("use asm");
assert.strictEqual(tokenizer.has_directive("use strict"), true);
assert.strictEqual(tokenizer.has_directive("use asm"), true);
assert.strictEqual(tokenizer.has_directive("use thing"), false);
// Stack level 2
tokenizer.pop_directives_stack();
assert.strictEqual(tokenizer.has_directive("use strict"), true);
assert.strictEqual(tokenizer.has_directive("use asm"), false);
assert.strictEqual(tokenizer.has_directive("use thing"), false);
// Stack level 3
tokenizer.push_directives_stack();
tokenizer.add_directive("use thing");
tokenizer.add_directive("use\\\nasm");
assert.strictEqual(tokenizer.has_directive("use strict"), true);
assert.strictEqual(tokenizer.has_directive("use asm"), false); // Directives are strict!
assert.strictEqual(tokenizer.has_directive("use thing"), true);
// Stack level 2
tokenizer.pop_directives_stack();
assert.strictEqual(tokenizer.has_directive("use strict"), true);
assert.strictEqual(tokenizer.has_directive("use asm"), false);
assert.strictEqual(tokenizer.has_directive("use thing"), false);
// Stack level 1
tokenizer.pop_directives_stack();
assert.strictEqual(tokenizer.has_directive("use strict"), false);
assert.strictEqual(tokenizer.has_directive("use asm"), false);
assert.strictEqual(tokenizer.has_directive("use thing"), false);
// Stack level 0
tokenizer.pop_directives_stack();
assert.strictEqual(tokenizer.has_directive("use strict"), false);
assert.strictEqual(tokenizer.has_directive("use asm"), false);
assert.strictEqual(tokenizer.has_directive("use thing"), false);
});
it("Should know which strings are directive and which ones are not", function() {
var test_directive = function(tokenizer, test) {
test.directives.map(function(directive) {
assert.strictEqual(tokenizer.has_directive(directive), true, directive + " in " + test.input);
});
test.non_directives.map(function(fake_directive) {
assert.strictEqual(tokenizer.has_directive(fake_directive), false, fake_directive + " in " + test.input);
});
}
var tests = [
{
input: '"use strict"\n',
directives: ["use strict"],
non_directives: ["use asm"]
},
{
input: '"use\\\nstrict";',
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
{
input: '"use strict"\n"use asm"\n"use bar"\n',
directives: ["use strict", "use asm", "use bar"],
non_directives: ["use foo", "use\\x20strict"]
},
{
input: '"use \\\nstrict";"use strict";',
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
{
input: '"\\76";',
directives: [],
non_directives: [">", "\\76"]
},
{
input: '"use strict"', // no ; or newline
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
{
input: ';"use strict"',
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
// Duplicate above code but put it in a function
{
input: 'function foo() {"use strict"\n',
directives: ["use strict"],
non_directives: ["use asm"]
},
{
input: 'function foo() {"use\\\nstrict";',
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
{
input: 'function foo() {"use strict"\n"use asm"\n"use bar"\n',
directives: ["use strict", "use asm", "use bar"],
non_directives: ["use foo", "use\\x20strict"]
},
{
input: 'function foo() {"use \\\nstrict";"use strict";',
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
{
input: 'var foo = function() {"\\76";',
directives: [],
non_directives: [">", "\\76"]
},
{
input: 'var foo = function() {"use strict"', // no ; or newline
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
{
input: 'var foo = function() {;"use strict"',
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
// Special cases
{
input: '"1";"2";"3";"4";;"5"',
directives: ["1", "2", "3", "4"],
non_directives: ["5", "6", "use strict", "use asm"]
},
{
input: 'if(1){"use strict";',
directives: [],
non_directives: ["use strict", "use\nstrict", "use \nstrict", "use asm"]
},
{
input: '"use strict";try{"use asm";',
directives: ["use strict"],
non_directives: ["use\nstrict", "use \nstrict", "use asm"]
}
];
for (var i = 0; i < tests.length; i++) {
// Fail parser deliberately to get state at failure
var tokenizer = uglify.tokenizer(tests[i].input + "]", "foo.js");
try {
var parser = uglify.parse(tokenizer);
throw new Error("Expected parser to fail");
} catch (e) {
assert.strictEqual(e instanceof uglify.JS_Parse_Error, true);
assert.strictEqual(e.message, "Unexpected token: punc (])");
}
test_directive(tokenizer, tests[i]);
}
});
it("Should test EXPECT_DIRECTIVE RegExp", function() {
var tests = [
["", true],
["'test';", true],
["'test';;", true],
["'tests';\n", true],
["'tests'", false],
["'tests'; \n\t", true],
["'tests';\n\n", true],
["\n\n\"use strict\";\n\n", true]
];
for (var i = 0; i < tests.length; i++) {
assert.strictEqual(uglify.EXPECT_DIRECTIVE.test(tests[i][0]), tests[i][1], tests[i][0]);
}
});
it("Should only print 2 semicolons spread over 2 lines in beautify mode", function() {
assert.strictEqual(
uglify.minify(
'"use strict";\'use strict\';"use strict";"use strict";;\'use strict\';console.log(\'use strict\');',
{fromString: true, output: {beautify: true, quote_style: 3}, compress: false}
).code,
'"use strict";\n\n\'use strict\';\n\n"use strict";\n\n"use strict";\n\n;\'use strict\';\n\nconsole.log(\'use strict\');'
);
});
it("Should not add double semicolons in non-scoped block statements to avoid strings becoming directives", function() {
var tests = [
[
'{"use\x20strict"}',
'{"use strict"}'
],
[
'function foo(){"use\x20strict";}', // Valid place for directives
'function foo(){"use strict"}'
],
[
'try{"use\x20strict"}catch(e){}finally{"use\x20strict"}',
'try{"use strict"}catch(e){}finally{"use strict"}'
],
[
'if(1){"use\x20strict"} else {"use strict"}',
'if(1){"use strict"}else{"use strict"}'
]
];
for (var i = 0; i < tests.length; i++) {
assert.strictEqual(
uglify.minify(tests[i][0], {fromString: true, quote_style: 3, compress: false, mangle: false}).code,
tests[i][1],
tests[i][0]
);
}
});
it("Should add double semicolon when relying on automatic semicolon insertion", function() {
var code = uglify.minify('"use strict";"use\\x20strict";',
{fromString: true, output: {semicolons: false}, compress: false}
).code;
assert.strictEqual(code, '"use strict";;"use strict"\n');
});
it("Should check quote style of directives", function() {
var tests = [
// 0. Prefer double quotes, unless string contains more double quotes than single quotes
[
'"testing something";',
0,
'"testing something";'
],
[
"'use strict';",
0,
'"use strict";'
],
[
'"\\\'use strict\\\'";', // Not a directive as it contains quotes
0,
';"\'use strict\'";',
],
[
"'\"use strict\"';",
0,
"'\"use strict\"';",
],
// 1. Always use single quote
[
'"testing something";',
1,
"'testing something';"
],
[
"'use strict';",
1,
"'use strict';"
],
[
'"\'use strict\'";',
1,
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
"'\\'use strict\\'';",
],
[
"'\\'use strict\\'';", // Not a valid directive
1,
"'\\'use strict\\'';" // But no ; necessary as directive stays invalid
],
[
"'\"use strict\"';",
1,
"'\"use strict\"';",
],
// 2. Always use double quote
[
'"testing something";',
2,
'"testing something";'
],
[
"'use strict';",
2,
'"use strict";'
],
[
'"\'use strict\'";',
2,
"\"'use strict'\";",
],
[
"'\"use strict\"';",
2,
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
'"\\\"use strict\\\"";',
],
[
'"\\"use strict\\"";', // Not a valid directive
2,
'"\\"use strict\\"";' // But no ; necessary as directive stays invalid
],
// 3. Always use original
[
'"testing something";',
3,
'"testing something";'
],
[
"'use strict';",
3,
"'use strict';",
],
[
'"\'use strict\'";',
3,
'"\'use strict\'";',
],
[
"'\"use strict\"';",
3,
"'\"use strict\"';",
],
];
for (var i = 0; i < tests.length; i++) {
assert.strictEqual(
uglify.minify(tests[i][0], {fromString: true, output:{quote_style: tests[i][1]}, compress: false}).code,
tests[i][2],
tests[i][0] + " using mode " + tests[i][1]
);
}
});
it("Should be able to compress without side effects", function() {
// NOTE: the "use asm" directive disables any optimisation after being defined
var tests = [
[
'"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");',
'"use strict";"use foo";doSomething("foo");'
],
[
// Nothing gets optimised in the compressor because "use asm" is the first statement
'"use asm";"use\\x20strict";1+1;',
'"use asm";;"use strict";1+1;' // Yet, the parser noticed that "use strict" wasn't a directive
]
];
for (var i = 0; i < tests.length; i++) {
assert.strictEqual(
uglify.minify(tests[i][0], {fromString: true, compress: {collapse_vars: true, side_effects: true}}).code,
tests[i][1],
tests[i][0]
);
}
});
});

View File

@@ -0,0 +1,89 @@
var UglifyJS = require('../../');
var assert = require("assert");
describe("Getters and setters", function() {
it("Should not accept operator symbols as getter/setter name", function() {
var illegalOperators = [
"++",
"--",
"+",
"-",
"!",
"~",
"&",
"|",
"^",
"*",
"/",
"%",
">>",
"<<",
">>>",
"<",
">",
"<=",
">=",
"==",
"===",
"!=",
"!==",
"?",
"=",
"+=",
"-=",
"/=",
"*=",
"%=",
">>=",
"<<=",
">>>=",
"|=",
"^=",
"&=",
"&&",
"||"
];
var generator = function() {
var results = [];
for (var i in illegalOperators) {
results.push({
code: "var obj = { get " + illegalOperators[i] + "() { return test; }};",
operator: illegalOperators[i],
method: "get"
});
results.push({
code: "var obj = { set " + illegalOperators[i] + "(value) { test = value}};",
operator: illegalOperators[i],
method: "set"
});
}
return results;
};
var testCase = function(data) {
return function() {
UglifyJS.parse(data.code);
};
};
var fail = function(data) {
return function (e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Invalid getter/setter name: " + data.operator;
};
};
var errorMessage = function(data) {
return "Expected but didn't get a syntax error while parsing following line:\n" + data.code;
};
var tests = generator();
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
assert.throws(testCase(test), fail(test), errorMessage(test));
}
});
});

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