Compare commits

..

182 Commits

Author SHA1 Message Date
Alex Lam S.L
c8e61448cb Merge pull request #1541 from alexlamsl/harmony-v2.8.5
Merging from master for 2.8.5
2017-03-03 07:30:28 +08:00
alexlamsl
f704e9b65c fix destructing crash in reduce_vars
fixes #1531
2017-03-03 07:23:46 +08:00
alexlamsl
0b77d861a8 Merge branch 'master' into harmony-v2.8.5 2017-03-03 07:17:52 +08:00
Alex Lam S.L
e27dab7e7c Merge pull request #1528 from alexlamsl/harmony-v2.8.4
Merging from master for 2.8.4
2017-03-02 11:22:06 +08:00
alexlamsl
80f3ad3ce0 Merge branch 'master' into harmony-v2.8.4 2017-03-02 11:16:55 +08:00
Alex Lam S.L
22f7af205d Merge pull request #1521 from alexlamsl/harmony-v2.8.2
Merging from master for 2.8.2
2017-03-01 11:13:32 +08:00
alexlamsl
8a7a4749c7 Merge branch 'master' into harmony-v2.8.2 2017-03-01 11:06:33 +08:00
Alex Lam S.L
514fc68f4f Merge pull request #1509 from alexlamsl/harmony-2.8.0
Merging from master for 2.8.0
2017-02-28 23:14:25 +08:00
alexlamsl
478aaab469 fix parser test
not sure if `start.pos` is correct, but oh well
2017-02-28 14:08:31 +08:00
alexlamsl
ab217539e9 restore sourceMappingURL in test 2017-02-28 13:23:02 +08:00
alexlamsl
a942dc07c4 fix parser tests
update exception messages
2017-02-28 03:58:01 +08:00
kzc
a0eaff750d fix parsing of arrow function with bind
fixes #1510
2017-02-28 02:47:33 +08:00
alexlamsl
57777b6cfa Merge branch 'master' into harmony-2.8.0 2017-02-28 02:28:58 +08:00
kzc
8d205f7f39 fix parser handling of comments 2017-02-27 14:54:47 +08:00
alexlamsl
bc9bfd15a2 fix test 2017-02-27 11:39:48 +08:00
alexlamsl
9fc1c4b3b5 update test
top-level block-variables not within blocks are global variables
2017-02-27 06:23:12 +08:00
alexlamsl
c59bf5e8d8 fix SymbolDef.global
properly compute for top-level block-variables
2017-02-27 06:20:08 +08:00
alexlamsl
d2d3a6e065 fix double-descend()
for `AST_Export` in `AST_Toplevel.figure_out_scope()`
2017-02-27 06:06:09 +08:00
alexlamsl
7755733716 fix "Starting destructuring."
commit 32f76f7ff does not take into account `AST_SymbolFunarg` is also `AST_SymbolVar`, so don't need to call `AST_Scope.def_variable()` twice
2017-02-27 05:30:18 +08:00
alexlamsl
b3a987b0df fix up drop_unused() 2017-02-27 05:11:01 +08:00
alexlamsl
88a338f29e Merge branch 'master' into harmony-2.8.0 2017-02-27 04:37:48 +08:00
Anthony Van de Gejuchte
07734b000a Destructuring consistency fixes (#1417)
- Use AST_Destructuring for lhf assignment patterns
- Use AST_DefaultAssign for default assignments
- Add more checks for lhs expressions
- Add lots of testing
- Cleanup ast (e.g. remove default property)
- Fix #1402 based on a patch from @kzc
- Refine spread allowance in array destructring pattern
- Add destructuring AST tree checker
2017-02-24 08:49:19 +08:00
Anthony Van de Gejuchte
85c1cba760 Remove duplicated code (#1456)
[ES6] Remove duplicated code
2017-02-24 08:48:13 +08:00
Richard van Velzen
4bd31607f6 Merge branch 'master' into harmony 2017-01-26 13:02:22 +01:00
Anthony Van de Gejuchte
4728bc73ad Allow parsing regexp after arrow token (#1439) 2017-01-26 12:06:46 +01:00
Anthony Van de Gejuchte
52ce9a333c Fix compression with unused containing destructuring 2017-01-19 21:04:28 +01:00
kzc
abbeb266b5 [ES6] output parens for yield when parented by AST_Dot or AST_Sub (#1419) 2017-01-19 17:15:59 +01:00
Anthony Van de Gejuchte
b11c5151bc Fix regression with non-ascii function identifiers
Regression since 110a1ac885
2017-01-19 16:47:37 +01:00
Richard van Velzen
962b1f3d40 Merge branch 'master' into harmony 2016-11-30 18:59:32 +01:00
Richard van Velzen
3ee46e91e8 Merge branch 'master' into harmony 2016-11-29 22:32:49 +01:00
Richard van Velzen
ee26e7f11b Merge branch 'master' into harmony 2016-11-29 20:50:27 +01:00
Anthony Van de Gejuchte
937f534392 Fix flag name in readme 2016-11-29 20:44:22 +01:00
Anthony Van de Gejuchte
1b2c02c944 Fix nlb property for template strings tokens starting with nlb
Also add .gitattributes to checkout lf eol style
2016-11-29 20:36:00 +01:00
Anthony Van de Gejuchte
0aa526e72c Do not allow arrow functions in the middle of an expression 2016-11-29 20:32:05 +01:00
Anthony Van de Gejuchte
c2112d5886 Fix case where a lonely var is used as computed property 2016-11-29 20:19:01 +01:00
Anthony Van de Gejuchte
5f6825f9ec Introduce is_block_scope to AST_Node to determine block scope.
Will return false if AST_Node is instance of AST_Scope for now.
2016-10-24 21:28:32 +02:00
Anthony Van de Gejuchte
7e80a979a7 Remove AST_ObjectComputedKeyVal 2016-10-23 22:12:30 +02:00
Richard van Velzen
b7bb706150 Merge branch 'master' into harmony 2016-10-23 22:11:27 +02:00
Anthony Van de Gejuchte
32c2cc33bb Improve binding patterns for arrow functions 2016-10-23 21:13:12 +02:00
Anthony Van de Gejuchte
947b8750e8 Make classes implicitly strict mode 2016-10-17 20:24:38 +02:00
kzc
88f6ff38d1 [ES6] fix template string escaping of \${...} 2016-10-01 11:32:36 +02:00
kzc
4198095a9c [ES6] fix parsing spread arguments that are expressions 2016-09-29 13:34:22 -04:00
Anthony Van de Gejuchte
13ed445607 Improve support for binding pattern
Including improvements for parameters, variable assignment and
catch parameter.
2016-09-05 17:48:48 +02:00
Richard van Velzen
1db50c3b16 Don't parenthesize arrow functions in parameter lists 2016-09-02 09:35:31 +02:00
Richard van Velzen
7f6b5d662b Merge branch 'master' into harmony 2016-08-30 15:16:23 +02:00
Anthony Van de Gejuchte
1c15d0db45 Fix quoting of properties
- Make AST_ConciseMethod child of AST_ObjectProperty.
- Fix some typos.
2016-08-26 15:06:24 +02:00
Richard van Velzen
67461666dc Merge branch 'master' into harmony 2016-08-17 21:30:35 +02:00
Richard van Velzen
45d81f881b Merge branch 'master' into harmony 2016-08-17 20:21:38 +02:00
Richard van Velzen
8c7d23dfb1 Merge branch 'master' into harmony 2016-08-17 08:50:31 +02:00
Richard van Velzen
7fa0dbdeb9 Ignore default reserved properties in compress test 2016-08-15 09:54:15 +02:00
Richard van Velzen
c644c1292d Merge branch 'master' into harmony 2016-08-15 09:09:04 +02:00
kzc
d224d71b8d [ES6] Fix handling of semicolons in export parse. 2016-08-14 21:43:24 +02:00
Anthony Van de Gejuchte
27d3669800 Don't allow escaped surrogated identifiers + introduce ascii_identifiers
Don't use 2 characters for surrogates in identifiers because there is
support for the \u{} syntax when escaped identifiers were introduced.

Also catch eof errors while reading identifier names

Introduce ascii_identifiers:

By setting ascii_identifiers to undefined (default value),
ascii_identifiers will print identifiers using the same setting as
ascii_only within the limits of the ecmascript 6 grammar.

ascii_identifiers accept true and false, allowing identifiers to be
printed under different settings than strings with the ascii_only setting.
2016-08-14 21:36:06 +02:00
Anthony Van de Gejuchte
110a1ac885 Make distinction between * method and * operator
Also add quotes to properties when necessary,
this might be the case if the name isn't a valid
identifier
2016-07-29 21:31:08 +02:00
Anthony Van de Gejuchte
3f8fc3a316 Fix computed getters + cleanup AST 2016-07-21 18:21:46 +02:00
Anthony Van de Gejuchte
88384cf351 Add more globals, whereof most defined after es5.1
Also do not pollute env with mocks replacing standard globals
2016-07-21 16:52:04 +02:00
Anthony Van de Gejuchte
72a9d799b6 Various property fixes
* Implement getter/setter with computed value
* Fix parsing getter/setter after static or generator token
* Allow storing expressions for computed expression in AST_SymbolMethod
* Allow get and set in shorthand properties in object literals

Fixes #1094, #1146 and #1221
2016-07-21 16:50:32 +02:00
Anthony Van de Gejuchte
766fafda8b Don't remove empty generators passed as parameter 2016-07-19 18:13:07 +02:00
kzc
842ac27efb [ES6] Get compress and global_defs working for AST_Class 2016-07-17 19:56:02 +02:00
Anthony Van de Gejuchte
0af42d1831 Template fixes
* Fixes #1147: template strings not obeying -b ascii_only true
* Allow evaluation of template expressions by adding optimizers and
  walkers
* Make sure tagged templates are never changed
* Remove template tokenizer in parser, add template tokenizer in
  tokenizer. It is using a brace counter to track brace position of
  templates
* Add tokens `template_head` and `template_substitution` but parsing
  tokens stays mostly the same
* Do not output strings anymore in AST_TemplateString, instead use
  AST_TemplateSegment
* Fix parsing tagged templates, allowing multiple templates behind
  as spec allows this

These changes don't influence tagged templates because raw content
may influence code execution, however they are safe to do in normal
templates:
* Allow basic string concatenation of templates where possible
* Allow custom character escape style similar to strings, except in
  tagged templates

Note that expressions are still compressed in tagged templates.

Optional things that may be improved later:
* Custom quote style for templates if it doesn't have expressions.
  Making it obey the quote_style option if this is the case.
2016-07-17 00:36:42 +02:00
Anthony Van de Gejuchte
ff7f6139ba Improve multi-line comment parsing
* Make sure comments are skipped correctly with surrogates
* Fix regression in multiline comments with nlb
2016-07-12 00:08:35 +02:00
Richard van Velzen
0db7caf13b Merge branch 'master' into harmony 2016-07-03 21:39:00 +02:00
Anthony Van de Gejuchte
d9bc6f303c Fix output arrow function with 1 param with default value
Fixes #1090
2016-07-03 21:37:53 +02:00
Anthony Van de Gejuchte
6fd9b338dd Merge branch 'master' into harmony 2016-07-03 21:35:00 +02:00
Anthony Van de Gejuchte
d8d4e71b9e Fix uses_with/uses_eval/directives state in block scope 2016-07-01 15:43:17 +02:00
Anthony Van de Gejuchte
fb2f8d1a51 Add reserved words to list unescapable keywords
Additionals:
* Update list reserved keywords
2016-06-30 22:47:44 +02:00
Anthony Van de Gejuchte
54a783ba84 Add ecma5 flag for codegen 2016-06-30 22:47:44 +02:00
Anthony Van de Gejuchte
63c432f4fa Extend unicode support
* Support \u{xxxx} syntax
 * Add support for surrogate pairs
 * Allow identifiers to have unicode escape sequence
2016-06-30 22:47:44 +02:00
Anthony Van de Gejuchte
07785d0003 Throw error if new.target is like new.foo 2016-06-25 19:32:53 +02:00
Anthony Van de Gejuchte
6eaeb19a4a Add exponentiation operator 2016-06-22 12:23:37 +02:00
Anthony Van de Gejuchte
2246c79318 Merge branch 'master' into fix-harmony 2016-06-20 19:21:25 +02:00
Anthony Van de Gejuchte
dda58244b6 Fixes to prevent failing tests after merging master
* Add missing quote properties to AST_ObjectKeyVal
 * Avoid test results being interpret as directives
2016-06-20 15:02:09 +02:00
Anthony Van de Gejuchte
ca04508cd1 Restrict yield outside generators in strict mode
* Move some yield/generic tests from compress/harmony.js to
  compress/yield.js
 * Adjust error messages to conform ecmascript standards
2016-06-19 21:03:36 +02:00
Anthony Van de Gejuchte
6b03b800b3 Only last parameter between parentheses can have spread 2016-06-19 20:51:29 +02:00
Anthony Van de Gejuchte
f9cab7ad61 Allow expand in array literals 2016-06-19 20:49:18 +02:00
Richard van Velzen
0a3d780327 Merge branch 'master' into harmony 2016-06-12 17:29:42 +02:00
Anthony Van de Gejuchte
b0555a123a Fix newline handling after yield
YieldExpressions can only be defined as:
 * `yield`
 * `yield` [no nlb] AssignmentExpression
 * `yield` [no nlb] `*` AssignmentExpression
2016-06-11 21:41:16 +02:00
Richard van Velzen
fa29344781 Merge branch 'master' into harmony 2016-06-09 22:29:52 +02:00
Anthony Van de Gejuchte
dcfc514c38 Improve yield support and restrict usage of strict
- Partially reverting 91cdb93e57 and eaf3911c31 and reimplement
- Add generators support for objects and classes
- Only classes can have static methods so restrict use of it

Special thanks to @rvanvelzen and @kzc for reviewing this patch and
providing constructive feedback over and over again.
2016-06-09 22:22:15 +02:00
Anthony Van de Gejuchte
8ad8d7b717 Add Symbol to builtins 2016-05-24 17:57:18 +02:00
Richard van Velzen
0357e5923f Merge branch 'master' into harmony 2016-05-24 17:56:20 +02:00
Richard van Velzen
f63803e3e3 Merge branch 'master' into harmony 2016-05-16 09:53:13 +02:00
Richard van Velzen
63be1f3a4d Only allow var definitions to be moved into the for-init clause
Fixes #1079
2016-05-15 21:00:51 +02:00
Richard van Velzen
e36e07fa77 Merge branch 'master' into harmony 2016-05-04 20:13:35 +02:00
Richard van Velzen
d1b4f61f93 Merge pull request #1059 from not-an-aardvark/harmony
Avoid syntax error in yield assignments
2016-04-26 11:51:13 +02:00
not-an-aardvark
5b893c8ec3 Avoid syntax error in yield assignments (fixes #1054) 2016-04-25 19:14:44 -04:00
Richard van Velzen
8571a08a93 Do not attempt evaluating class expressions
Broadly the same as function expressions - these actually are statements but
we're limited by the inheritance tree.

Fixes #1044
2016-04-19 20:01:26 +02:00
Richard van Velzen
68cc14f846 Fixups after merge 2016-04-18 15:51:32 +02:00
Richard van Velzen
f94497d1d6 Merge branch 'master' into harmony 2016-04-18 15:50:35 +02:00
Richard van Velzen
eaf3911c31 Consider yield expressions as having side-effects
See #1043
2016-04-13 14:39:49 +02:00
Darío Javier Cravero
91cdb93e57 Implement harmony generators and yield
Uses #716's implementation and adds tests.

Fixes #716.
2016-04-13 14:22:08 +02:00
Fábio Santos
634f231b78 First class block scope
- Make let, const, and class symbols be declared in a block scope.
- Piggy back on existing catch symbol implementation to get block-aware mangling working
- Make sure unused block-scoped declarations can be dropped
- Don't eliminate a block if it has a block-scoped declaration
- Remove silly empty anonymous blocks left over from drop_unused
- AST_Toplevel now gets to call drop_unused too, since block-scoped variables aren't global!
- Don't consider block declarations global
2016-03-27 19:40:20 +02:00
Fábio Santos
6702cae918 fix #1021 2016-03-27 12:21:39 +01:00
Fábio Santos
6d2f77c180 fix #1003 by removing AST_ObjectSymbol and using AST_ObjectKeyVal for the same effect 2016-03-14 13:42:50 +01:00
Fábio Santos
accca2445f fix crash: Import statements don't abort 2016-03-14 12:54:05 +01:00
Fábio Santos
0bc4f6edb4 Don't mangle exported symbols 2016-03-14 12:54:05 +01:00
Fábio Santos
ce84a706a3 Implement the export statement 2016-03-14 12:54:05 +01:00
Fábio Santos
86b5248837 Mangling externally imported names by using aliasing 2016-03-14 12:54:05 +01:00
Fábio Santos
59e1601fb8 importing names in the modules, not just default imports 2016-03-14 12:54:05 +01:00
Fábio Santos
d35a9e7839 Importing names from places 2016-03-14 12:54:05 +01:00
Fábio Santos
0465bd270d Starting out the import statement 2016-03-14 12:54:05 +01:00
viclm
6780d0906c Fix eager parsing of arrow functions for non-punc tokens 2016-03-10 22:18:48 +01:00
Darío Javier Cravero
0b303379c0 fix: don't fail if definition is undefined
Running `uglifyjs --verbose --compress --mangle --screw-ie8 class.js`
with
`class.js`:
```
class Foo {
  bar() {
  }
}
```
Fails with:
```
undefined:4041
  return this.definition().unmangleable(options);
TypeError: Cannot read property 'unmangleable' of undefined
...
```
2016-02-10 10:17:32 +01:00
Fábio Santos
d7ec2ecc12 Fix #931: Create arrow functions in maybe_assign so that they can be used in assignments 2016-02-09 00:02:23 +00:00
Richard van Velzen
2827fa8699 Merge branch 'harmony' of github.com:mishoo/UglifyJS2 into harmony 2016-01-19 19:22:35 +01:00
Richard van Velzen
c80ec625ec Add test for bad template string parsing 2016-01-19 19:22:22 +01:00
Fugiman
5e78f20f1c Remove duplicate error message 2016-01-19 19:22:22 +01:00
Fugiman
1f75232062 Fix template string parsing 2016-01-19 19:22:22 +01:00
Fábio Santos
028ff64e9a Default values inside destructurings 2016-01-19 19:22:22 +01:00
Fábio Santos
7a8cffd631 Move the idea of a symbol having a default value up the class chain. 2016-01-19 19:22:22 +01:00
Fábio Santos
5b553aafe2 Destructuring parameters with defaults. function x({ foo, bar } = {}) { } 2016-01-19 19:22:22 +01:00
Fábio Santos
084437bc6d Non-destructuring default parameters 2016-01-19 19:22:22 +01:00
Richard van Velzen
1cd9a2df9a Merge pull request #872 from fabiosantoscode/feature/harmony-defaults
Feature/harmony defaults
2016-01-05 21:39:23 +01:00
Richard van Velzen
7f3dbb6df7 Merge branch 'master' into harmony 2015-12-26 17:59:38 +01:00
Richard van Velzen
e4d73d8b7c Merge pull request #870 from fabiosantoscode/feature/harmony-class
Harmony: classes
2015-12-07 18:59:07 +01:00
Fábio Santos
8220dbbea0 Default values inside destructurings 2015-11-22 19:04:42 +00:00
Fábio Santos
59e999597e Move the idea of a symbol having a default value up the class chain. 2015-11-22 19:00:54 +00:00
Fábio Santos
cbcb3ac44e Destructuring parameters with defaults. function x({ foo, bar } = {}) { } 2015-11-22 18:02:51 +00:00
Fábio Santos
f07ab4666f Non-destructuring default parameters 2015-11-22 17:40:05 +00:00
Fábio Santos
a800356ad0 Implement new.target 2015-11-21 14:48:23 +00:00
Fábio Santos
e076abdbf2 Mangle class names correctly 2015-11-21 13:59:18 +00:00
Fábio Santos
425613b0d2 mangle class names 2015-11-21 12:20:20 +00:00
Fábio Santos
69da8e53e0 Separate class expressions from class declarations and their symbols like defuns 2015-11-21 09:17:32 +00:00
Fábio Santos
bb6b3a773a Make AST_Class inherit AST_Scope instead of AST_Object
This is one of those days I'd love to use multiple inheritance.

An AST_Class has lots of common with AST_Object, but unfortunately
`instanceof AST_Scope` is used very, very much, and a class has its name
inside its own special pocket scope. This compels me to make AST_Class
inherit Scope instead.

It looks like, although there is much in common with AST_Object,
`instanceof AST_Object` seldom are made, perhaps because it is less
often necessary to traverse an object than a scope.
2015-11-20 19:34:10 +00:00
Fábio Santos
364d20f8fb Add mangle = { ...mangleopts } option to tests. 2015-11-20 18:11:17 +00:00
Richard van Velzen
392ac4ff31 Merge branch 'master' into harmony 2015-10-28 20:38:07 +01:00
Fábio Santos
9ffed2bea6 static properties 2015-10-27 12:24:37 +00:00
Fábio Santos
5f7cb6939c Starting ES6 classes 2015-10-27 12:24:37 +00:00
Fábio Santos
64e7a00399 Accept keyword names as concise method names 2015-10-27 09:31:16 +01:00
Fábio Santos
c99eaae360 Make concise methods work with propmangle 2015-10-27 09:31:16 +01:00
Fábio Santos
34213ea2f8 Create a new symbol for methods' names 2015-10-27 09:31:16 +01:00
Fábio Santos
da8c428a07 Just making sure that concise methods are separated by commas. When classes come, they won't be necessary. 2015-10-27 09:31:16 +01:00
Fábio Santos
0d8dea9538 start concise methods 2015-10-27 09:31:16 +01:00
Richard van Velzen
2babe737e0 Merge pull request #844 from fabiosantoscode/harmony-allow-of
Harmony: allow use of `of` as a name.
2015-10-27 09:26:51 +01:00
Fábio Santos
2cce61c564 Allow 'of' to be a name. 2015-10-26 20:56:59 +00:00
Richard van Velzen
246ec416c0 Merge branch 'master' into harmony 2015-10-20 21:58:58 +02:00
Richard van Velzen
6be9c752d5 Merge branch 'master' into harmony 2015-10-20 20:12:10 +02:00
Fábio Santos
76ed083e47 Using single quotes 2015-10-12 21:39:19 +01:00
Fábio Santos
b31918bbf0 computed properties 2015-10-12 21:38:20 +01:00
Richard van Velzen
b14496c742 Merge branch 'master' into harmony
Conflicts:
	lib/compress.js
2015-10-12 08:54:44 +02:00
Richard van Velzen
991fa99655 Merge branch 'master' into harmony 2015-09-13 14:17:45 +02:00
Fábio Santos
3d7f73114d Add a test to make sure future generations don't hoist lets 2015-09-13 14:15:53 +02:00
Fábio Santos
dde9e293df parse, output the let statement 2015-09-13 14:15:53 +02:00
Richard van Velzen
34685a6f55 Merge pull request #794 from fabiosantoscode/feature/harmony-template-strings-2
Harmony: template strings
2015-09-08 21:23:41 +02:00
Richard van Velzen
9812c826e0 Merge pull request #795 from fabiosantoscode/harmony-fix-cannot-destructure-crash
Fix crash, remove unused code and state variable.
2015-09-06 17:05:34 +02:00
Fábio Santos
2fac2bbfe4 Remove unused state variable in_parameters, and also remove unreachable code (try_an_object always returned an object!) 2015-09-05 23:01:29 +01:00
Fábio Santos
242c61be94 prefixed template strings, like "String.rawfoo\nbar". 2015-09-05 22:48:38 +01:00
Fábio Santos
e1cb1a0e3c Parse and output ES6 template strings. Yikes! 2015-09-05 22:32:57 +01:00
Richard van Velzen
af22b9c657 Merge pull request #768 from fabiosantoscode/feature/harmony-destructuring-expression
Feature/harmony destructuring expression
2015-08-25 19:00:36 +02:00
Fábio Santos
adee5023c0 What about --mangle-props being on and --mangle being off? 2015-08-25 17:52:51 +01:00
Fábio Santos
7ee8f3512e play nice with propmangle 2015-08-25 17:49:29 +01:00
Fábio Santos
dc5db9b6ca Starting destructuring expressions 2015-08-25 17:49:27 +01:00
Fábio Santos
079aaa0d48 Tolerate expansions in vardefs, too! 2015-08-21 12:04:26 +02:00
Fábio Santos
d4f17f29ae Destructuring vardef in for..of and for..in 2015-08-21 12:04:26 +02:00
Fábio Santos
e99bc914ca Do not mangle a name if it is in a destructuring vardef. 2015-08-21 12:04:26 +02:00
Fábio Santos
025d34bfa2 Add holes in destructuring defs, also make them nestable 2015-08-21 12:04:26 +02:00
Fábio Santos
c44c2d6c21 Parse and compress destructuring VarDefs 2015-08-21 12:04:26 +02:00
Fábio Santos
824ecfb8a2 A little refactoring. Add a new function to get all symbols in a destructuring. 2015-08-21 12:04:26 +02:00
Fábio Santos
ceebc466b9 prepare AST_Destructuring for the Ents 2015-08-21 12:04:26 +02:00
Richard van Velzen
35b31bdd4e Merge branch 'master' into harmony 2015-08-21 11:58:35 +02:00
Richard van Velzen
c8b82583d2 Merge pull request #773 from fabiosantoscode/harmony-typeof-arrows
Fix evaluating the typeof an arrow function.
2015-08-21 11:51:42 +02:00
Richard van Velzen
9f9179ba1a Merge pull request #774 from fabiosantoscode/feature/harmony-symbol
remove Symbol's argument when we're unsafe and Symbol is undeclared
2015-08-21 11:51:02 +02:00
Richard van Velzen
872231a0ca Merge pull request #775 from fabiosantoscode/feature/binary-literals
Parse ES6 number literals, round 2
2015-08-21 11:50:39 +02:00
Fábio Santos
36420183fd s/binary/number/g 2015-08-17 16:23:43 +01:00
Fábio Santos
a8f8aa518b Add new-style octal literals and make the B and the O case insensitive. 2015-08-17 11:50:56 +01:00
Fábio Santos
4c12cccff9 remove Symbol's argument when we're unsafe and it's undeclared 2015-08-14 22:44:16 +01:00
Fábio Santos
dcce4e5c66 Fix evaluating the typeof an arrow function. Using evaluate on used to cause a crash. 2015-08-14 22:05:42 +01:00
Richard van Velzen
56c0b834d6 Merge pull request #771 from fabiosantoscode/feature/binary-literals
Parse binary number literals
2015-08-14 14:59:55 +02:00
Fábio Santos
6f864402d3 Parse binary number literals 2015-08-14 03:24:54 +01:00
Richard van Velzen
8909e9e1cb Merge pull request #763 from fabiosantoscode/feature/harmony-super
Feature/harmony super
2015-08-07 08:54:20 +02:00
Fábio Santos
e80ed38772 Super! 2015-08-07 02:44:53 +01:00
Fábio Santos
9863f0efa3 expand parameters
Conflicts:
	test/compress/harmony.js
2015-08-05 21:15:23 +02:00
Richard van Velzen
e8664e63ef Merge branch 'master' into harmony 2015-08-05 21:13:11 +02:00
Fábio Santos
ddd30eeaaa Uglifyjs already supports super as an implicit global! Just adding a test to indicate that. 2015-08-05 11:49:37 +01:00
Fábio Santos
a68953c491 => with destructuring arguments. Requires a lot of parser changes 2015-08-04 00:57:53 +01:00
Fábio Santos
fa5c4f2d03 Adding arrow functions 2015-08-04 00:57:53 +01:00
Fábio Santos
9d7d365c2b for...of 2015-07-29 15:10:54 +02:00
Fábio Santos
ad344c5be3 Add a test to verify that destructuring arguments work with #203 code 2015-07-29 14:54:45 +02:00
Fábio Santos
96b89e34a3 test that names used in destructurings don't get hoisted 2015-07-29 14:54:45 +02:00
Fábio Santos
4644becb9b do not support destructuring arguments and ngInject 2015-07-29 14:54:45 +02:00
Fábio Santos
f7460166dd remove trace statement 2015-07-29 14:54:45 +02:00
Fábio Santos
32f76f7ff8 Starting destructuring. 2015-07-29 14:54:45 +02:00
158 changed files with 16461 additions and 18480 deletions

View File

@@ -1,20 +0,0 @@
**Bug report or feature request?**
<!-- Note: sub-optimal but correct code is not a bug -->
**ES5 or ES6+ input?**
<!-- Note: for ES6 see: https://github.com/mishoo/UglifyJS2/tree/harmony#harmony -->
**`uglify-js` version (`uglifyjs -V`)**
**JavaScript input - ideally as small as possible.**
**The `uglifyjs` CLI command executed or `minify()` options used.**
**JavaScript output produced and/or the error or warning.**
<!--
Note: the release version of uglify-js only supports ES5. Those wishing
to minify ES6 should use the experimental harmony branch.
-->

View File

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

600
README.md
View File

@@ -1,15 +1,17 @@
UglifyJS 3
UglifyJS 2
==========
[![Build Status](https://travis-ci.org/mishoo/UglifyJS2.svg)](https://travis-ci.org/mishoo/UglifyJS2)
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
#### Note:
- **`uglify-js@3.x` has a new API and CLI and is not backwards compatible with [`uglify-js@2.x`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- Release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
This page documents the command line utility. For
[API and internals documentation see my website](http://lisperator.net/uglifyjs/).
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.
- Node 7 has a known performance regression and runs `uglify-js` twice as slow.
Install
-------
@@ -36,13 +38,14 @@ Usage
uglifyjs [input files] [options]
UglifyJS can take multiple input files. It's recommended that you pass the
UglifyJS2 can take multiple input files. It's recommended that you pass the
input files first, then pass the options. UglifyJS will parse input files
in sequence and apply any compression options. The files are parsed in the
same global scope, that is, a reference from a file to some
variable/function declared in another file will be matched properly.
If no input file is specified, UglifyJS will read from STDIN.
If you want to read from STDIN instead, pass a single dash instead of input
files.
If you wish to pass your options before the input files, separate the two with
a double dash to prevent input files being used as option arguments:
@@ -52,53 +55,49 @@ a double dash to prevent input files being used as option arguments:
The available options are:
```
-h, --help Print usage information.
-V, --version Print version number.
-p, --parse <options> Specify parser options:
`acorn` Use Acorn for parsing.
`bare_returns` Allow return outside of functions.
Useful when minifying CommonJS
modules and Userscripts that may
be anonymous function wrapped (IIFE)
by the .user.js engine `caller`.
`expression` Parse a single expression, rather than
a program (for parsing JSON).
`spidermonkey` Assume input files are SpiderMonkey
AST format (as JSON).
-c, --compress [options] Enable compressor/specify compressor options:
`pure_funcs` List of functions that can be safely
removed when their return values are
not used.
-m, --mangle [options] Mangle names/specify mangler options:
`reserved` List of names that should not be mangled.
--mangle-props [options] Mangle properties/specify mangler options:
`builtins` Mangle property names that overlaps
with standard JavaScript globals.
`debug` Add debug prefix and suffix.
`domprops` Mangle property names that overlaps
with DOM properties.
`keep_quoted` Only mangle unquoted properies.
`regex` Only mangle matched property names.
`reserved` List of names that should not be mangled.
-b, --beautify [options] Beautify output/specify output options:
`beautify` Enabled with `--beautify` by default.
`preamble` Preamble to prepend to the output. You
can use this to insert a comment, for
example for licensing information.
This will not be parsed, but the source
map will adjust for its presence.
`quote_style` Quote style:
0 - auto
1 - single
2 - double
3 - original
`wrap_iife` Wrap IIFEs in parenthesis. Note: you may
want to disable `negate_iife` under
compressor options.
-o, --output <file> Output file path (default STDOUT). Specify `ast` or
`spidermonkey` to write UglifyJS or SpiderMonkey AST
as JSON to STDOUT respectively.
--comments [filter] Preserve copyright comments in the output. By
--source-map Specify an output file where to generate source
map.
--source-map-root The path to the original source to be included
in the source map.
--source-map-url The path to the source map to be added in //#
sourceMappingURL. Defaults to the value passed
with --source-map.
--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 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
in source maps. For example -p 3 will drop 3
directories from file names and ensure they are
relative paths. You can also specify -p
relative, which will make UglifyJS figure out
itself the relative paths between original
sources, the source map and the output file.
-o, --output Output file (default STDOUT).
-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, 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
configurable parameter/argument list.
--comments Preserve copyright comments in the output. By
default this works like Google Closure, keeping
JSDoc-style comments that contain "@license" or
"@preserve". You can optionally pass one of the
@@ -110,39 +109,54 @@ The available options are:
kept when compression is on, because of dead
code removal or cascading statements into
sequences.
--config-file <file> Read `minify()` options from JSON file.
-d, --define <expr>[=value] Global definitions.
--ie8 Support non-standard Internet Explorer 8.
Equivalent to setting `ie8: true` in `minify()`
for `compress`, `mangle` and `output` options.
By default UglifyJS will not try to be IE-proof.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--name-cache File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options:
`base` Path to compute relative paths from input files.
`content` Input source map, useful if you're compressing
JS that was generated from some other original
code. Specify "inline" if the source map is
included within the sources.
`filename` Name and/or location of the output source.
`includeSources` Pass this flag if you want to include
the content of source files in the
source map as sourcesContent property.
`root` Path to the original source to be included in
the source map.
`url` If specified, path to the source map to append in
`//# sourceMappingURL`.
--stats Display operations run time on STDERR.
--toplevel Compress and/or mangle variables in toplevel scope.
--verbose Print diagnostic messages.
--warn Print warning messages.
--wrap <name> Embed everything in a big function, making the
--preamble Preamble to prepend to the output. You can use
this to insert a comment, for example for
licensing information. This will not be
parsed, but the source map will adjust for its
presence.
--stats Display operations run time on STDERR.
--acorn Use Acorn for parsing.
--spidermonkey Assume input files are SpiderMonkey AST format
(as JSON).
--self Build itself (UglifyJS2) as a library (implies
--wrap=UglifyJS --export-all)
--wrap Embed everything in a big function, making the
“exports” and “global” variables available. You
need to pass an argument to this option to
specify the name that your module will take
when included in, say, a browser.
--export-all Only used when --wrap, this tells UglifyJS to
add code to automatically export all globals.
--lint Display some scope warnings
-v, --verbose Verbose
-V, --version Print version number and exit.
--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 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 (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
@@ -150,21 +164,25 @@ goes to STDOUT.
## Source map options
UglifyJS can generate a source map file, which is highly useful for
UglifyJS2 can generate a source map file, which is highly useful for
debugging your compressed JavaScript. To get a source map, pass
`--source-map --output output.js` (source map will be written out to
`output.js.map`).
`--source-map output.js.map` (full path to the file where you want the
source map dumped).
Additionally you might need `--source-map root=<URL>` to pass the URL where
the original files can be found. Use `--source-map url=<URL>` to specify
the URL where the source map can be found.
Additionally you might need `--source-map-root` to pass the URL where the
original files can be found. In case you are passing full paths to input
files to UglifyJS, you can use `--prefix` (`-p`) to specify the number of
directories to drop from the path prefix when declaring files in the source
map.
For example:
uglifyjs /home/doe/work/foo/src/js/file1.js \
/home/doe/work/foo/src/js/file2.js \
-o foo.min.js -c -m \
--source-map base="/home/doe/work/foo/src",root="http://foo.com/src"
-o foo.min.js \
--source-map foo.min.js.map \
--source-map-root http://foo.com/src \
-p 5 -c -m
The above will compress and mangle `file1.js` and `file2.js`, will drop the
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
@@ -184,10 +202,9 @@ compressed JS by mapping every token in the compiled JS to its original
location.
To use this feature you need to pass `--in-source-map
/path/to/input/source.map` or `--in-source-map inline` if the source map is
included inline with the sources. Normally the input source map should also
point to the file containing the generated JS, so if that's correct you can
omit input files from the command line.
/path/to/input/source.map`. Normally the input source map should also point
to the file containing the generated JS, so if that's correct you can omit
input files from the command line.
## Mangler options
@@ -201,10 +218,10 @@ To enable the mangler you need to pass `--mangle` (`-m`). The following
(disabled by default).
When mangling is enabled but you want to prevent certain names from being
mangled, you can declare those names with `--mangle reserved` — pass a
mangled, you can declare those names with `--reserved` (`-r`) — pass a
comma-separated list of names. For example:
uglifyjs ... -m reserved=[$,require,exports]
uglifyjs ... -m -r '$,require,exports'
to prevent the `require`, `exports` and `$` names from being changed.
@@ -229,22 +246,39 @@ console.log(x.something());
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
with single characters, while `something()` will be left as is.
In order for this to be of any use, we avoid mangling standard JS names by
default (`--mangle-props builtins` to override).
In order for this to be of any use, we should avoid mangling standard JS
names. For instance, if your code would contain `x.length = 10`, then
`length` becomes a candidate for mangling and it will be mangled throughout
the code, regardless if it's being used as part of your own objects or
accessing an array's length. To avoid that, you can use `--reserved-file`
to pass a filename that should contain the names to be excluded from
mangling. This file can be used both for excluding variable names and
property names. It could look like this, for example:
```js
{
"vars": [ "define", "require", ... ],
"props": [ "length", "prototype", ... ]
}
```
`--reserved-file` can be an array of file names (either a single
comma-separated argument, or you can pass multiple `--reserved-file`
arguments) — in this case it will exclude names from all those files.
A default exclusion file is provided in `tools/domprops.json` which should
cover most standard JS and DOM properties defined in various browsers. Pass
`--mangle-props domprops` to disable this feature.
`--reserve-domprops` to read that in.
You can also use a regular expression to define which property names should be
mangled. For example, `--mangle-props regex=/^_/` will only mangle property
names that start with an underscore.
mangled. For example, `--mangle-regex="/^_/"` will only mangle property names
that start with an underscore.
When you compress multiple files using this option, in order for them to
work together in the end we need to ensure somehow that one property gets
mangled to the same name in all of them. For this, pass `--name-cache filename.json`
and UglifyJS will maintain these mappings in a file which can then be reused.
It should be initially empty. Example:
mangled to the same name in all of them. For this, pass `--name-cache
filename.json` and UglifyJS will maintain these mappings in a file which can
then be reused. It should be initially empty. Example:
```
rm -f /tmp/cache.json # start fresh
@@ -258,26 +292,26 @@ of mangled property names.
Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS.
#### Mangling unquoted names (`--mangle-props keep_quoted`)
#### Mangling unquoted names (`--mangle-props=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 keep_quoted -mc
var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo);
$ 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
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
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
@@ -316,9 +350,6 @@ to set `true`; it's effectively a shortcut for `foo=true`).
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
@@ -359,11 +390,11 @@ 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` -- Collapse single-use `var` and `const` definitions
when possible.
- `collapse_vars` -- default `false`. Collapse single-use `var` and `const`
definitions when possible.
- `reduce_vars` -- Improve optimization on variables assigned with and
used as constant values.
- `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.
@@ -375,8 +406,6 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `pure_getters` -- the default is `false`. If you pass `true` for
this, UglifyJS will assume that object property access
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `pure_funcs` -- default `null`. You can pass an array of names and
UglifyJS will assume that those functions do not produce side
@@ -394,9 +423,6 @@ to set `true`; it's effectively a shortcut for `foo=true`).
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `expression` -- default `false`. Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
@@ -409,9 +435,6 @@ to set `true`; it's effectively a shortcut for `foo=true`).
integer argument larger than 1 to further reduce code size in some cases.
Note: raising the number of passes will increase uglify compress time.
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
### The `unsafe` option
It enables some transformations that *might* break code logic in certain
@@ -465,6 +488,8 @@ 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:
@@ -525,6 +550,9 @@ can pass additional arguments that control the code output:
- `3` -- always use the original quotes
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
- `ecma` (default `5`) -- set output printing mode. This will only change the
output in direct control of the beautifier. Non-compatible features in the
abstract syntax tree will still be outputted as is.
### Keeping copyright notices or other comments
@@ -532,8 +560,8 @@ You can pass `--comments` to retain certain comments in the output. By
default it will keep JSDoc-style comments that contain "@preserve",
"@license" or "@cc_on" (conditional compilation for IE). You can pass
`--comments all` to keep all the comments, or a valid JavaScript regexp to
keep only comments that match this regexp. For example `--comments /^!/`
will keep comments like `/*! Copyright Notice */`.
keep only comments that match this regexp. For example `--comments
'/foo|bar/'` will keep only comments that contain "foo" or "bar".
Note, however, that there might be situations where comments are lost. For
example:
@@ -556,7 +584,7 @@ needs to be kept in the output) are comments attached to toplevel nodes.
## Support for the SpiderMonkey AST
UglifyJS has its own abstract syntax tree format; for
UglifyJS2 has its own abstract syntax tree format; for
[practical reasons](http://lisperator.net/blog/uglifyjs-why-not-switching-to-spidermonkey-ast/)
we can't easily change to using the SpiderMonkey AST internally. However,
UglifyJS now has a converter which can import a SpiderMonkey AST.
@@ -566,22 +594,54 @@ SpiderMonkey AST. It has a small CLI utility that parses one file and dumps
the AST in JSON on the standard output. To use UglifyJS to mangle and
compress that:
acorn file.js | uglifyjs -p spidermonkey -m -c
acorn file.js | uglifyjs --spidermonkey -m -c
The `-p spidermonkey` option tells UglifyJS that all input files are not
The `--spidermonkey` option tells UglifyJS that all input files are not
JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we
don't use our own parser in this case, but just transform that AST into our
internal AST.
### Use Acorn for parsing
More for fun, I added the `-p acorn` option which will use Acorn to do all
More for fun, I added the `--acorn` option which will use Acorn to do all
the parsing. If you pass this option, UglifyJS will `require("acorn")`.
Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
in total it's a bit more than just using UglifyJS's own parser.
### Using UglifyJS to transform SpiderMonkey AST
Now you can use UglifyJS as any other intermediate tool for transforming
JavaScript ASTs in SpiderMonkey format.
Example:
```javascript
function uglify(ast, options, mangle) {
// Conversion from SpiderMonkey AST to internal format
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
// Compression
uAST.figure_out_scope();
uAST = UglifyJS.Compressor(options).compress(uAST);
// Mangling (optional)
if (mangle) {
uAST.figure_out_scope();
uAST.compute_char_frequency();
uAST.mangle_names();
}
// Back-conversion to SpiderMonkey AST
return uAST.to_mozilla_ast();
}
```
Check out
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
for details.
API Reference
-------------
@@ -591,72 +651,107 @@ like this:
var UglifyJS = require("uglify-js");
```
There is a single toplevel function, `minify(files, options)`, which will
performs all the steps in a configurable manner.
It exports a lot of names, but I'll discuss here the basics that are needed
for parsing, mangling and compressing a piece of code. The sequence is (1)
parse, (2) compress, (3) mangle, (4) generate output code.
### The simple way
There's a single toplevel function which combines all the steps. If you
don't need additional customization, you might want to go with `minify`.
Example:
```javascript
var result = UglifyJS.minify("var b = function() {};");
var result = UglifyJS.minify("/path/to/file.js");
console.log(result.code); // minified output
// if you need to pass code instead of file name
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
```
You can also compress multiple files:
```javascript
var result = UglifyJS.minify({
"file1.js": "var a = function() {};",
"file2.js": "var b = function() {};"
});
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
console.log(result.code);
```
To generate a source map:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
sourceMap: {
filename: "out.js",
url: "out.js.map"
}
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
outSourceMap: "out.js.map"
});
console.log(result.code); // minified output
console.log(result.map); // source map
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 `sourceMap.url` is only used to set
`result.map`. The value passed for `outSourceMap` is only used to set
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
in source map file.
`outFileName` is only used to set `file` attribute in source map file.
You can set option `sourceMap.url` to be `"inline"` and source map will
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
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
sourceMap: {
root: "http://example.com/src",
url: "out.js.map"
}
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
outSourceMap: "out.js.map",
sourceRoot: "http://example.com/src"
});
```
If you're compressing compiled JavaScript and have a source map for it, you
can use `sourceMap.content`:
can use the `inSourceMap` argument:
```javascript
var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
sourceMap: {
content: "content from compiled.js.map",
url: "minified.js.map"
}
var result = UglifyJS.minify("compiled.js", {
inSourceMap: "compiled.js.map",
outSourceMap: "minified.js.map"
});
// same as before, it returns `code` and `map`
```
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`.
If your input source map is not in a file, you can pass it in as an object
using the `inSourceMap` argument:
```javascript
var result = UglifyJS.minify("compiled.js", {
inSourceMap: JSON.parse(my_source_map_string),
outSourceMap: "minified.js.map"
});
```
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.
- `fromString` (default `false`) — if you pass `true` then you can pass
JavaScript source code, rather than file names.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
an object to specify mangling options (see below).
@@ -664,18 +759,18 @@ Other options:
mangle property options.
- `output` (default `null`) — pass an object if you wish to specify
additional [output options](#beautifier-options). The defaults are optimized
additional [output options][codegen]. The defaults are optimized
for best compression.
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compressor options](#compressor-options).
Pass an object to specify custom [compressor options][compressor].
- `parse` (default {}) — pass an object if you wish to specify some
additional [parser options](#the-parser).
additional [parser options][parser]. (not all options available... see below)
##### mangle
- `reserved` - pass an array of identifiers that should be excluded from mangling
- `except` - pass an array of identifiers that should be excluded from mangling
- `toplevel` — mangle names declared in the toplevel scope (disabled by
default).
@@ -700,22 +795,183 @@ Other options:
UglifyJS.minify("tst.js").code;
// 'function funcName(a,n){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { reserved: ['firstLongName'] } }).code;
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;'
```
##### mangle.properties options
##### mangleProperties options
- `regex` — Pass a RegExp to only mangle certain names
- `keep_quoted` Only mangle unquoted property names
- `debug` Mangle names with the original name still present. Defaults to `false`.
Pass an empty string to enable, or a non-empty string to set the suffix.
- `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!
### The hard way
Following there's more detailed API info, in case the `minify` function is
too simple for your needs.
#### The parser
```javascript
var toplevel_ast = UglifyJS.parse(code, options);
```
`options` is optional and if present it must be an object. The following
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`)
The last two options are useful when you'd like to minify multiple files and
get a single file as the output and a proper source map. Our CLI tool does
something like this:
```javascript
var toplevel = null;
files.forEach(function(file){
var code = fs.readFileSync(file, "utf8");
toplevel = UglifyJS.parse(code, {
filename: file,
toplevel: toplevel
});
});
```
After this, we have in `toplevel` a big AST containing all our files, with
each token having proper information about where it came from.
#### Scope information
UglifyJS contains a scope analyzer that you need to call manually before
compressing or mangling. Basically it augments various nodes in the AST
with information about where is a name defined, how many times is a name
referenced, if it is a global or not, if a function is using `eval` or the
`with` statement etc. I will discuss this some place else, for now what's
important to know is that you need to call the following before doing
anything with the tree:
```javascript
toplevel.figure_out_scope()
```
#### Compression
Like this:
```javascript
var compressor = UglifyJS.Compressor(options);
var compressed_ast = compressor.compress(toplevel);
```
The `options` can be missing. Available options are discussed above in
“Compressor options”. Defaults should lead to best compression in most
scripts.
The compressor is destructive, so don't rely that `toplevel` remains the
original tree.
#### Mangling
After compression it is a good idea to call again `figure_out_scope` (since
the compressor might drop unused variables / unreachable code and this might
change the number of identifiers or their position). Optionally, you can
call a trick that helps after Gzip (counting character frequency in
non-mangleable words). Example:
```javascript
compressed_ast.figure_out_scope();
compressed_ast.compute_char_frequency();
compressed_ast.mangle_names();
```
#### Generating output
AST nodes have a `print` method that takes an output stream. Essentially,
to generate code you do this:
```javascript
var stream = UglifyJS.OutputStream(options);
compressed_ast.print(stream);
var code = stream.toString(); // this is your minified code
```
or, for a shortcut you can do:
```javascript
var code = compressed_ast.print_to_string(options);
```
As usual, `options` is optional. The output stream accepts a lot of options,
most of them documented above in section “Beautifier options”. The two
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 (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.
The comment token has these properties:
- `type`: "comment1" for single-line comments or "comment2" for multi-line
comments
- `value`: the comment body
- `pos` and `endpos`: the start/end positions (zero-based indexes) in the
original code where this comment appears
- `line` and `col`: the line and column where this comment appears in the
original code
- `file` — the file name of the original file
- `nlb` — true if there was a newline before this comment in the original
code, or if this comment contains a newline.
Your function should return `true` to keep the comment, or a falsy value
otherwise.
#### Generating a source mapping
You need to pass the `source_map` argument when calling `print`. It needs
to be a `SourceMap` object (which is a thin wrapper on top of the
[source-map][source-map] library).
Example:
```javascript
var source_map = UglifyJS.SourceMap(source_map_options);
var stream = UglifyJS.OutputStream({
...
source_map: source_map
});
compressed_ast.print(stream);
var code = stream.toString();
var map = source_map.toString(); // json output for your source map
```
The `source_map_options` (optional) can contain the following properties:
- `file`: the name of the JavaScript output file that this mapping refers to
- `root`: the `sourceRoot` property (see the [spec][sm-spec])
- `orig`: the "original source map", handy when you compress generated JS
and want to map the minified output back to the original code where it
came from. It can be simply a string in JSON, or a JSON object containing
the original source map.
[acorn]: https://github.com/ternjs/acorn
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
[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

77
bin/extract-props.js Executable file
View File

@@ -0,0 +1,77 @@
#! /usr/bin/env node
"use strict";
var U2 = require("../tools/node");
var fs = require("fs");
var yargs = require("yargs");
var ARGS = yargs
.describe("o", "Output file")
.argv;
var files = ARGS._.slice();
var output = {
vars: {},
props: {}
};
if (ARGS.o) try {
output = JSON.parse(fs.readFileSync(ARGS.o, "utf8"));
} catch(ex) {}
files.forEach(getProps);
if (ARGS.o) {
fs.writeFileSync(ARGS.o, JSON.stringify(output, null, 2), "utf8");
} else {
console.log("%s", JSON.stringify(output, null, 2));
}
function getProps(filename) {
var code = fs.readFileSync(filename, "utf8");
var ast = U2.parse(code);
ast.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_ObjectKeyVal) {
add(node.key);
}
else if (node instanceof U2.AST_ObjectProperty) {
add(node.key.name);
}
else if (node instanceof U2.AST_Dot) {
add(node.property);
}
else if (node instanceof U2.AST_Sub) {
addStrings(node.property);
}
}));
function addStrings(node) {
var out = {};
try {
(function walk(node){
node.walk(new U2.TreeWalker(function(node){
if (node instanceof U2.AST_Seq) {
walk(node.cdr);
return true;
}
if (node instanceof U2.AST_String) {
add(node.value);
return true;
}
if (node instanceof U2.AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function add(name) {
output.props[name] = true;
}
}

View File

@@ -3,382 +3,629 @@
"use strict";
// 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 fs = require("fs");
var info = require("../package.json");
var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node");
var sys = require("util");
var yargs = require("yargs");
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.\
\n\n\
NOTE: by default there is no mangling/compression.\n\
Without [options] it will simply parse input files and dump the AST\n\
with whitespace and comments discarded. To achieve compression and\n\
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", "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. \
You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \
the source map and the output file.")
.describe("o", "Output file (default STDOUT).")
.describe("b", "Beautify output/specify output options.")
.describe("m", "Mangle names/pass mangler options.")
.describe("r", "Reserved names to exclude from mangling.")
.describe("c", "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 options.")
.describe("d", "Global definitions")
.describe("e", "Embed everything in a big function, with a configurable parameter/argument list.")
var skip_keys = [ "cname", "enclosed", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ];
var files = {};
var options = {
compress: false,
mangle: false
};
program._name = info.name;
program.version(info.version);
program.parseArgv = program.parse;
program.parse = undefined;
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true));
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true));
program.option("-o, --output <file>", "Output file (default STDOUT).");
program.option("--comments [filter]", "Preserve copyright comments in the output.");
program.option("--config-file <file>", "Read minify() options from JSON file.");
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
program.option("--ie8", "Support non-standard Internet Explorer 8.");
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
program.option("--name-cache <file>", "File to hold mangled name mappings.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
program.option("--stats", "Display operations run time on STDERR.")
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
program.option("--verbose", "Print diagnostic messages.");
program.option("--warn", "Print warning messages.");
program.option("--wrap <name>", "Embed everything as a function with “exports” corresponding to “name” globally.");
program.arguments("[files...]").parseArgv(process.argv);
if (program.configFile) {
options = JSON.parse(read_file(program.configFile));
.describe("comments", "Preserve copyright comments in the output. \
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 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.")
.describe("preamble", "Preamble to prepend to the output. You can use this to insert a \
comment, for example for licensing information. This will not be \
parsed, but the source map will adjust for its presence.")
.describe("stats", "Display operations run time on STDERR.")
.describe("acorn", "Use Acorn for parsing.")
.describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
.describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
.describe("lint", "Display some scope warnings")
.describe("v", "Verbose")
.describe("V", "Print version number and exit.")
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
.describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.")
.describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.")
.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 (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")
.alias("v", "verbose")
.alias("b", "beautify")
.alias("m", "mangle")
.alias("c", "compress")
.alias("d", "define")
.alias("r", "reserved")
.alias("V", "version")
.alias("e", "enclose")
.alias("q", "quotes")
.string("source-map")
.string("source-map-root")
.string("source-map-url")
.string("b")
.string("beautify")
.string("m")
.string("mangle")
.string("mangle-props-debug")
.string("c")
.string("compress")
.string("d")
.string("define")
.string("e")
.string("enclose")
.string("comments")
.string("wrap")
.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")
.boolean("verbose")
.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("reserve-domprops")
.boolean("wrap-iife")
.wrap(80)
.argv
;
normalize(ARGS);
if (ARGS.noerr) {
UglifyJS.DefaultsError.croak = function(msg, defs) {
print_error("WARN: " + msg);
};
}
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
fatal("ERROR: cannot write source map to STDOUT");
if (ARGS.version || ARGS.V) {
var json = require("../package.json");
print(json.name + ' ' + json.version);
process.exit(0);
}
[
"compress",
"ie8",
"mangle",
"sourceMap",
"toplevel",
"wrap"
].forEach(function(name) {
if (name in program) {
options[name] = program[name];
}
if (ARGS.ast_help) {
var desc = UglifyJS.describe_ast();
print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
process.exit(0);
}
if (ARGS.h || ARGS.help) {
print(yargs.help());
process.exit(0);
}
if (ARGS.acorn) {
acorn = require("acorn");
}
var COMPRESS = getOptions("c", true);
var MANGLE = getOptions("m", true);
var BEAUTIFY = getOptions("b", true);
var RESERVED = null;
if (ARGS.reserved_file) ARGS.reserved_file.forEach(function(filename){
RESERVED = UglifyJS.readReservedFile(filename, RESERVED);
});
if (program.beautify) {
options.output = typeof program.beautify == "object" ? program.beautify : {};
if (!("beautify" in options.output)) {
options.output.beautify = true;
}
if (ARGS.reserve_domprops) {
RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
}
if (program.comments) {
if (typeof options.output != "object") options.output = {};
options.output.comments = typeof program.comments == "string" ? program.comments : "some";
if (ARGS.d) {
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
}
if (program.define) {
if (typeof options.compress != "object") options.compress = {};
if (typeof options.compress.global_defs != "object") options.compress.global_defs = {};
for (var expr in program.define) {
options.compress.global_defs[expr] = program.define[expr];
}
if (ARGS.pure_funcs) {
if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
}
if (program.keepFnames) {
options.keep_fnames = true;
if (ARGS.r) {
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
}
if (program.mangleProps) {
if (program.mangleProps.domprops) {
delete program.mangleProps.domprops;
} else {
if (typeof program.mangleProps != "object") program.mangleProps = {};
if (!Array.isArray(program.mangleProps.reserved)) program.mangleProps.reserved = [];
require("../tools/domprops").forEach(function(name) {
UglifyJS._push_uniq(program.mangleProps.reserved, name);
});
}
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps;
if (RESERVED && MANGLE) {
if (!MANGLE.except) MANGLE.except = RESERVED.vars;
else MANGLE.except = MANGLE.except.concat(RESERVED.vars);
}
var cache;
if (program.nameCache) {
cache = JSON.parse(read_file(program.nameCache, "{}"));
if (options.mangle) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.cache = to_cache("vars");
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
options.mangle.properties.cache = to_cache("props");
}
}
function readNameCache(key) {
return UglifyJS.readNameCache(ARGS.name_cache, key);
}
if (program.output == "ast") {
options.output = {
ast: true,
code: false
};
function writeNameCache(key, cache) {
return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
}
if (program.parse) {
if (program.parse.acorn || program.parse.spidermonkey) {
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser");
} else {
options.parse = program.parse;
}
function extractRegex(str) {
if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
var regex_pos = str.lastIndexOf("/");
return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
} else {
throw new Error("Invalid regular expression: " + str);
}
}
var convert_path = function(name) {
return name;
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,
max_line_len : 32000,
preamble : ARGS.preamble || null,
quote_style : ARGS.quotes != null ? ARGS.quotes : 0,
};
if (typeof program.sourceMap == "object" && "base" in program.sourceMap) {
convert_path = function() {
var base = program.sourceMap.base;
delete options.sourceMap.base;
return function(name) {
return path.relative(base, name);
};
}();
if (ARGS.mangle_props == 2) {
OUTPUT_OPTIONS.keep_quoted_props = true;
if (COMPRESS && !("properties" in COMPRESS))
COMPRESS.properties = false;
}
if (program.verbose) {
options.warnings = "verbose";
} else if (program.warn) {
options.warnings = true;
if (ARGS.support_ie8 === true && ARGS.screw_ie8 !== true) {
screw_ie8 = false;
}
if (program.self) {
if (program.args.length) {
console.error("WARN: Ignoring input files since --self was passed");
}
if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) {
files[convert_path(name)] = read_file(name);
});
run();
} else if (program.args.length) {
simple_glob(program.args).forEach(function(name) {
files[convert_path(name)] = read_file(name);
});
run();
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 === "") {
OUTPUT_OPTIONS.comments = "some";
} else {
var chunks = [];
process.stdin.setEncoding("utf8");
process.stdin.on("data", function(chunk) {
chunks.push(chunk);
}).on("end", function() {
files = [ chunks.join("") ];
run();
});
process.stdin.resume();
OUTPUT_OPTIONS.comments = ARGS.comments;
}
function convert_ast(fn) {
return UglifyJS.AST_Node.from_mozilla_ast(Object.keys(files).reduce(fn, null));
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");
}
files = UglifyJS.FILES;
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
}
function run() {
UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg);
};
if (program.stats) program.stats = Date.now();
try {
if (program.parse) {
if (program.parse.acorn) {
files = convert_ast(function(toplevel, name) {
return require("acorn").parse(files[name], {
locations: true,
program: toplevel,
sourceFile: name
});
});
} else if (program.parse.spidermonkey) {
files = convert_ast(function(toplevel, name) {
var obj = JSON.parse(files[name]);
if (!toplevel) return obj;
toplevel.body = toplevel.body.concat(obj.body);
return toplevel;
});
}
}
} catch (ex) {
fatal("ERROR: " + ex.message);
var ORIG_MAP = ARGS.in_source_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 ];
}
var result = UglifyJS.minify(files, options);
if (result.error) {
var ex = result.error;
if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/);
var line = lines[ex.line - 1];
if (!line && !col) {
line = lines[ex.line - 2];
col = line.length;
}
if (line) {
if (col > 40) {
line = line.slice(col - 40);
col = 40;
}
console.error(line.slice(0, 80));
console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
}
}
if (ex.defs) {
console.error("Supported options:");
console.error(ex.defs);
}
fatal("ERROR: " + ex.message);
} else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) {
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
if (value instanceof UglifyJS.AST_Node) {
var result = {
_class: "AST_" + value.TYPE
};
value.CTOR.PROPS.forEach(function(prop) {
result[prop] = value[prop];
});
return result;
}
return value;
}, 2));
} else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
ast: true,
code: false
}
}).ast.to_mozilla_ast(), null, 2));
} else if (program.output) {
fs.writeFileSync(program.output, result.code);
if (result.map) {
fs.writeFileSync(program.output + ".map", result.map);
}
} else {
console.log(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
}
if (program.stats) console.error("Elapsed:", Date.now() - program.stats);
}
function fatal(message) {
console.error(message);
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);
}
// A file glob function that only supports "*" and "?" wildcards in the basename.
// Example: "foo/bar/*baz??.*.js"
// Argument `glob` may be a string or an array of strings.
// Returns an array of strings. Garbage in, garbage out.
function simple_glob(glob) {
if (Array.isArray(glob)) {
return [].concat.apply([], glob.map(simple_glob));
}
if (glob.match(/\*|\?/)) {
var dir = path.dirname(glob);
try {
var entries = fs.readdirSync(dir);
} catch (ex) {}
if (entries) {
var pattern = "^" + path.basename(glob)
.replace(/[.+^$[\]\\(){}]/g, "\\$&")
.replace(/\*/g, "[^/\\\\]*")
.replace(/\?/g, "[^/\\\\]") + "$";
var mod = process.platform === "win32" ? "i" : "";
var rx = new RegExp(pattern, mod);
var results = entries.filter(function(name) {
return rx.test(name);
}).map(function(name) {
return path.join(dir, name);
});
if (results.length) return results;
if (files.filter(function(el){ return el == "-" }).length > 1) {
print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
process.exit(1);
}
var STATS = {};
var TOPLEVEL = null;
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
var SOURCES_CONTENT = {};
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);
}
}
return [ glob ];
}
function read_file(path, default_value) {
try {
return fs.readFileSync(path, "utf8");
} catch (ex) {
if (ex.code == "ENOENT" && default_value != null) return default_value;
fatal("ERROR: " + ex.message);
}
}
function parse_js(flag, constants) {
return function(value, options) {
options = options || {};
try {
UglifyJS.minify(value, {
parse: {
expression: true
},
compress: false,
mangle: false,
output: {
ast: true,
code: false
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, '/');
} else {
var p = parseInt(ARGS.p, 10);
if (!isNaN(p)) {
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
}
}).ast.walk(new UglifyJS.TreeWalker(function(node) {
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string();
var value = node.right;
if (!constants) {
options[name] = value;
} else if (value instanceof UglifyJS.AST_Array) {
options[name] = value.elements.map(to_string);
} else {
options[name] = to_string(value);
}
return true;
}
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_PropAccess) {
var name = node.print_to_string();
options[name] = true;
return true;
}
if (!(node instanceof UglifyJS.AST_Sequence)) throw node;
function to_string(value) {
return value instanceof UglifyJS.AST_Constant ? value.getValue() : value.print_to_string({
quote_keys: true
}
}
SOURCES_CONTENT[file] = code;
time_it("parse", function(){
if (ARGS.spidermonkey) {
var program = JSON.parse(code);
if (!TOPLEVEL) TOPLEVEL = program;
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
}
else if (ARGS.acorn) {
TOPLEVEL = acorn.parse(code, {
locations : true,
sourceFile : file,
program : TOPLEVEL
});
}
else {
try {
TOPLEVEL = UglifyJS.parse(code, {
filename : file,
toplevel : TOPLEVEL,
expression : ARGS.expr,
bare_returns : ARGS.bare_returns,
});
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
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);
}
throw ex;
}
}));
} catch(ex) {
fatal("Error parsing arguments for '" + flag + "': " + value);
}
return options;
}
}
};
});
cb();
});
}, function () {
var OUTPUT_FILE = ARGS.o;
function parse_source_map() {
var parse = parse_js("sourceMap", true);
return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap;
var settings = parse(value, options);
if (!hasContent && settings.content && settings.content != "inline") {
console.error("INFO: Using input source map:", settings.content);
settings.content = read_file(settings.content, settings.content);
}
return settings;
}
}
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;
function to_cache(key) {
if (cache[key]) {
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
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);
});
if (ARGS.wrap != null) {
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
}
if (ARGS.enclose != null) {
var arg_parameter_list = ARGS.enclose;
if (arg_parameter_list === true) {
arg_parameter_list = [];
}
else if (!(arg_parameter_list instanceof Array)) {
arg_parameter_list = [arg_parameter_list];
}
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
}
if (ARGS.mangle_props || ARGS.name_cache) (function(){
var reserved = RESERVED ? RESERVED.props : null;
var cache = readNameCache("props");
var regex;
try {
regex = ARGS.mangle_regex ? extractRegex(ARGS.mangle_regex) : null;
} catch (e) {
print_error("ERROR: Invalid --mangle-regex: " + e.message);
process.exit(1);
}
TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, {
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;
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();
}
});
}
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(){
TOPLEVEL.mangle_names(MANGLE);
});
writeNameCache("vars", TL_CACHE);
if (ARGS.source_map_include_sources) {
for (var file in SOURCES_CONTENT) {
if (SOURCES_CONTENT.hasOwnProperty(file)) {
SOURCE_MAP.get().setSourceContent(file, SOURCES_CONTENT[file]);
}
}
}
if (ARGS.dump_spidermonkey_ast) {
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
} else {
cache[key] = {
cname: -1,
props: new UglifyJS.Dictionary()
};
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) {
print_error(UglifyJS.string_template("Timing information (compressed {count} files):", {
count: files.length
}));
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
print_error(UglifyJS.string_template("- {name}: {time}s", {
name: i,
time: (STATS[i] / 1000).toFixed(3)
}));
}
}
});
/* -----[ functions ]----- */
function normalize(o) {
for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
o[i.replace(/-/g, "_")] = o[i];
delete o[i];
}
return cache[key];
}
function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
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 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().replace(/-/g, "_");
var value = node.right;
if (constants)
value = new Function("return (" + value.print_to_string() + ")")();
ret[name] = value;
return true; // no descend
}
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
var name = node.print_to_string().replace(/-/g, "_");
ret[name] = true;
return true; // no descend
}
print_error(node.TYPE)
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}));
}
return ret;
}
function read_whole_file(filename, cb) {
if (filename == "-") {
var chunks = [];
process.stdin.setEncoding('utf-8');
process.stdin.on('data', function (chunk) {
chunks.push(chunk);
}).on('end', function () {
cb(null, chunks.join(""));
});
process.openStdin();
} else {
fs.readFile(filename, "utf-8", 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();
if (ARGS.stats) {
var spent = new Date().getTime() - t1;
if (STATS[name]) STATS[name] += spent;
else STATS[name] = spent;
}
return ret;
}
function print_error(msg) {
console.error("%s", msg);
}
function print(txt) {
console.log("%s", txt);
}

View File

@@ -91,20 +91,9 @@ var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos
}, null);
var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) {
if (deep) {
var self = this.clone();
return self.transform(new TreeTransformer(function(node) {
if (node !== self) {
return node.clone(true);
}
}));
}
clone: function() {
return new this.CTOR(this);
},
clone: function(deep) {
return this._clone(deep);
},
$documentation: "Base class of all AST nodes",
$propdoc: {
start: "[AST_Token] The first token of this node",
@@ -157,7 +146,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
function walk_body(node, visitor) {
var body = node.body;
if (body instanceof AST_Statement) {
if (body instanceof AST_Node) {
body._walk(visitor);
}
else for (var i = 0, len = body.length; i < len; i++) {
@@ -182,13 +171,21 @@ var AST_BlockStatement = DEFNODE("BlockStatement", null, {
}, AST_Block);
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
$documentation: "The empty statement (empty block or simply a semicolon)"
$documentation: "The empty statement (empty block or simply a semicolon)",
_walk: function(visitor) {
return visitor._visit(this);
}
}, AST_Statement);
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
$propdoc: {
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.body._walk(visitor);
});
}
}, AST_Statement);
@@ -202,21 +199,6 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
this.label._walk(visitor);
this.body._walk(visitor);
});
},
clone: function(deep) {
var node = this._clone(deep);
if (deep) {
var label = node.label;
var def = this.label;
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_LoopControl
&& node.label && node.label.thedef === def) {
node.label.thedef = label;
label.references.push(node);
}
}));
}
return node;
}
}, AST_StatementWithBody);
@@ -284,6 +266,10 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", {
}
}, AST_IterationStatement);
var AST_ForOf = DEFNODE("ForOf", null, {
$documentation: "A `for ... of` statement",
}, AST_ForIn);
var AST_With = DEFNODE("With", "expression", {
$documentation: "A `with` statement",
$propdoc: {
@@ -311,6 +297,13 @@ var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
},
get_defun_scope: function () {
var self = this;
while (self.is_block_scope() && self.parent_scope) {
self = self.parent_scope;
}
return self;
}
}, AST_Block);
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
@@ -318,26 +311,169 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
$propdoc: {
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
},
wrap_commonjs: function(name) {
var body = this.body;
var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");";
wrap_enclose: function(arg_parameter_pairs) {
var self = this;
var args = [];
var parameters = [];
arg_parameter_pairs.forEach(function(pair) {
var splitAt = pair.lastIndexOf(":");
args.push(pair.substr(0, splitAt));
parameters.push(pair.substr(splitAt + 1));
});
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
wrapped_tl = parse(wrapped_tl);
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
if (node instanceof AST_Directive && node.value == "$ORIG") {
return MAP.splice(body);
return MAP.splice(self.body);
}
}));
return wrapped_tl;
},
wrap_commonjs: function(name, export_all) {
var self = this;
var to_export = [];
if (export_all) {
self.figure_out_scope();
self.walk(new TreeWalker(function(node){
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
if (!find_if(function(n){ return n.name == node.name }, to_export))
to_export.push(node);
}
}));
}
var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))";
wrapped_tl = parse(wrapped_tl);
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
if (node instanceof AST_Directive) {
switch (node.value) {
case "$ORIG":
return MAP.splice(self.body);
case "$EXPORTS":
var body = [];
to_export.forEach(function(sym){
body.push(new AST_SimpleStatement({
body: new AST_Assign({
left: new AST_Sub({
expression: new AST_SymbolRef({ name: "exports" }),
property: new AST_String({ value: sym.name }),
}),
operator: "=",
right: new AST_SymbolRef(sym),
}),
}));
});
return MAP.splice(body);
}
}
}));
return wrapped_tl;
}
}, AST_Scope);
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
var AST_Expansion = DEFNODE("Expansion", "expression", {
$documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list",
$propdoc: {
expression: "AST_Symbol the thing to be expanded"
},
_walk: function(visitor) {
var self = this;
return visitor._visit(this, function(){
self.expression.walk(visitor);
});
}
});
var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
$documentation: "A set of arrow function parameters or a sequence expression. This is used because when the parser sees a \"(\" it could be the start of a seq, or the start of a parameter list of an arrow function.",
$propdoc: {
expressions: "[AST_Expression|AST_Destructuring|AST_Expansion*] array of expressions or argument names or destructurings."
},
as_params: function (croak) {
// We don't want anything which doesn't belong in a destructuring
var root = this;
return this.expressions.map(function to_fun_args(ex, _, __, default_seen_above) {
var insert_default = function(ex, default_value) {
if (default_value) {
return new AST_DefaultAssign({
start: ex.start,
left: ex,
operator: "=",
right: default_value,
end: default_value.end
});
}
return ex;
}
if (ex instanceof AST_Object) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: false,
names: ex.properties.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_ObjectKeyVal) {
if (ex.key instanceof AST_SymbolRef) {
ex.key = to_fun_args(ex.key, 0, [ex.key]);
}
ex.value = to_fun_args(ex.value, 0, [ex.key]);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Hole) {
return ex;
} else if (ex instanceof AST_Destructuring) {
if (ex.names.length == 0)
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
ex.names = ex.names.map(to_fun_args);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_SymbolRef) {
return insert_default(new AST_SymbolFunarg({
name: ex.name,
start: ex.start,
end: ex.end
}), default_seen_above);
} else if (ex instanceof AST_Expansion) {
ex.expression = to_fun_args(ex.expression);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Array) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: true,
names: ex.elements.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_Assign) {
return insert_default(to_fun_args(ex.left, undefined, undefined, ex.right), default_seen_above);
} else {
croak("Invalid function parameter", ex.start.line, ex.start.col);
}
});
},
as_expr: function (croak) {
return AST_Seq.from_array(this.expressions);
}
});
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", {
$documentation: "Base class for functions",
$propdoc: {
is_generator: "[boolean] is generatorFn or not",
name: "[AST_SymbolDeclaration?] the name of this function",
argnames: "[AST_SymbolFunarg*] array of function arguments",
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
},
args_as_names: function () {
var out = [];
for (var i = 0; i < this.argnames.length; i++) {
if (this.argnames[i] instanceof AST_Destructuring) {
out = out.concat(this.argnames[i].all_symbols());
} else {
out.push(this.argnames[i]);
}
}
return out;
},
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.name) this.name._walk(visitor);
@@ -358,10 +494,78 @@ var AST_Function = DEFNODE("Function", null, {
$documentation: "A function expression"
}, AST_Lambda);
var AST_Arrow = DEFNODE("Arrow", null, {
$documentation: "An ES6 Arrow function ((a) => b)"
}, AST_Lambda);
var AST_Defun = DEFNODE("Defun", null, {
$documentation: "A function definition"
}, AST_Lambda);
/* -----[ DESTRUCTURING ]----- */
var AST_Destructuring = DEFNODE("Destructuring", "names is_array", {
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
$propdoc: {
"names": "[AST_Node*] Array of properties or elements",
"is_array": "[Boolean] Whether the destructuring represents an object or array"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.names.forEach(function(name){
name._walk(visitor);
});
});
},
all_symbols: function() {
var out = [];
this.walk(new TreeWalker(function (node) {
if (node instanceof AST_Symbol) {
out.push(node);
}
if (node instanceof AST_Expansion) {
out.push(node.expression);
}
}));
return out;
}
});
var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_string prefix", {
$documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`",
$propdoc: {
template_string: "[AST_TemplateString] The template string",
prefix: "[AST_SymbolRef|AST_PropAccess] The prefix, which can be a symbol such as `foo` or a dotted expression such as `String.raw`."
},
_walk: function(visitor) {
this.prefix._walk(visitor);
this.template_string._walk(visitor);
}
})
var AST_TemplateString = DEFNODE("TemplateString", "segments", {
$documentation: "A template string literal",
$propdoc: {
segments: "[AST_TemplateSegment|AST_Expression]* One or more segments, starting with AST_TemplateSegment. AST_Expression may follow AST_TemplateSegment, but each AST_Expression must be followed by AST_TemplateSegment."
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.segments.forEach(function(seg, i){
if (i % 2 !== 0) {
seg._walk(visitor);
}
});
});
}
});
var AST_TemplateSegment = DEFNODE("TemplateSegment", "value raw", {
$documentation: "A segment of a template string literal",
$propdoc: {
value: "Content of the segment",
raw: "Raw content of the segment"
}
});
/* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, {
@@ -481,7 +685,7 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
var AST_Catch = DEFNODE("Catch", "argname", {
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
$propdoc: {
argname: "[AST_SymbolCatch] symbol for the exception"
argname: "[AST_SymbolCatch|AST_Destructuring|AST_Expansion|AST_DefaultAssign] symbol for the exception"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
@@ -516,16 +720,78 @@ var AST_Var = DEFNODE("Var", null, {
$documentation: "A `var` statement"
}, AST_Definitions);
var AST_Let = DEFNODE("Let", null, {
$documentation: "A `let` statement"
}, AST_Definitions);
var AST_Const = DEFNODE("Const", null, {
$documentation: "A `const` statement"
}, AST_Definitions);
var AST_NameImport = DEFNODE("NameImport", "foreign_name name", {
$documentation: "The part of the import statement that imports names from a module.",
$propdoc: {
foreign_name: "[AST_SymbolImportForeign] The name being imported (as specified in the module)",
name: "[AST_SymbolImport] The name as it becomes available to this module."
},
_walk: function (visitor) {
return visitor._visit(this, function() {
this.foreign_name._walk(visitor);
this.name._walk(visitor);
});
}
})
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
$documentation: "An `import` statement",
$propdoc: {
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
imported_names: "[AST_NameImport*] The names of non-default imported variables",
module_name: "[AST_String] String literal describing where this module came from",
},
_walk: function(visitor) {
return visitor._visit(this, function() {
if (this.imported_name) {
this.imported_name._walk(visitor);
}
if (this.imported_names) {
this.imported_names.forEach(function (name_import) {
name_import._walk(visitor);
});
}
this.module_name._walk(visitor);
});
}
});
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default", {
$documentation: "An `export` statement",
$propdoc: {
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
exported_value: "[AST_Node?] An exported value",
is_default: "[Boolean] Whether this is the default exported value of this module"
},
_walk: function (visitor) {
visitor._visit(this, function () {
if (this.exported_definition) {
this.exported_definition._walk(visitor);
}
if (this.exported_value) {
this.exported_value._walk(visitor);
}
});
}
}, AST_Statement);
var AST_VarDef = DEFNODE("VarDef", "name value", {
$documentation: "A variable declaration; only appears in a AST_Definitions node",
$propdoc: {
name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer"
},
is_destructuring: function() {
return this.name instanceof AST_Destructuring;
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.name._walk(visitor);
@@ -544,11 +810,11 @@ var AST_Call = DEFNODE("Call", "expression args", {
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.expression._walk(visitor);
var args = this.args;
for (var i = 0, len = args.length; i < len; i++) {
args[i]._walk(visitor);
}
this.expression._walk(visitor);
});
}
});
@@ -557,16 +823,68 @@ var AST_New = DEFNODE("New", null, {
$documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
}, AST_Call);
var AST_Sequence = DEFNODE("Sequence", "expressions", {
$documentation: "A sequence expression (comma-separated expressions)",
var AST_Seq = DEFNODE("Seq", "car cdr", {
$documentation: "A sequence expression (two comma-separated expressions)",
$propdoc: {
expressions: "[AST_Node*] array of expressions (at least two)"
car: "[AST_Node] first element in sequence",
cdr: "[AST_Node] second element in sequence"
},
$cons: function(x, y) {
var seq = new AST_Seq(x);
seq.car = x;
seq.cdr = y;
return seq;
},
$from_array: function(array) {
if (array.length == 0) return null;
if (array.length == 1) return array[0].clone();
var list = null;
for (var i = array.length; --i >= 0;) {
list = AST_Seq.cons(array[i], list);
}
var p = list;
while (p) {
if (p.cdr && !p.cdr.cdr) {
p.cdr = p.cdr.car;
break;
}
p = p.cdr;
}
return list;
},
to_array: function() {
var p = this, a = [];
while (p) {
a.push(p.car);
if (p.cdr && !(p.cdr instanceof AST_Seq)) {
a.push(p.cdr);
break;
}
p = p.cdr;
}
return a;
},
add: function(node) {
var p = this;
while (p) {
if (!(p.cdr instanceof AST_Seq)) {
var cell = AST_Seq.cons(p.cdr, node);
return p.cdr = cell;
}
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.expressions.forEach(function(node) {
node._walk(visitor);
});
this.car._walk(visitor);
if (this.cdr) this.cdr._walk(visitor);
});
}
});
@@ -619,7 +937,7 @@ var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
$documentation: "Unary postfix expression, i.e. `i++`"
}, AST_Unary);
var AST_Binary = DEFNODE("Binary", "operator left right", {
var AST_Binary = DEFNODE("Binary", "left operator right", {
$documentation: "Binary expression, i.e. `a + b`",
$propdoc: {
left: "[AST_Node] left-hand side expression",
@@ -654,6 +972,10 @@ var AST_Assign = DEFNODE("Assign", null, {
$documentation: "An assignment expression — `a = b + 5`",
}, AST_Binary);
var AST_DefaultAssign = DEFNODE("DefaultAssign", null, {
$documentation: "A default assignment expression like in `(a = 3) => a`"
}, AST_Binary);
/* -----[ LITERALS ]----- */
var AST_Array = DEFNODE("Array", "elements", {
@@ -689,11 +1011,13 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties",
$propdoc: {
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.",
value: "[AST_Node] property value. For setters and getters this is an AST_Function."
key: "[string|AST_Node] the property name converted to a string for ObjectKeyVal. For setters, getters and computed property this is an arbitrary AST_Node",
value: "[AST_Node] property value. For setters and getters this is an AST_Function."
},
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.key instanceof AST_Node)
this.key._walk(visitor);
this.value._walk(visitor);
});
}
@@ -706,21 +1030,72 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
}
}, AST_ObjectProperty);
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
var AST_ObjectSetter = DEFNODE("ObjectSetter", "quote static", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this is a static setter (classes only)"
},
$documentation: "An object setter property",
}, AST_ObjectProperty);
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this is a static getter (classes only)"
},
$documentation: "An object getter property",
}, AST_ObjectProperty);
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this method is static (classes only)",
is_generator: "[boolean] is generatorFn or not",
},
$documentation: "An ES6 concise method inside an object or class"
}, AST_ObjectProperty);
var AST_Class = DEFNODE("Class", "name extends properties", {
$propdoc: {
name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.",
extends: "[AST_Node]? optional parent class",
properties: "[AST_ObjectProperty*] array of properties"
},
$documentation: "An ES6 class",
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.name) {
this.name._walk(visitor);
}
if (this.extends) {
this.extends._walk(visitor);
}
this.properties.forEach(function(prop){
prop._walk(visitor);
});
});
},
}, AST_Scope);
var AST_DefClass = DEFNODE("DefClass", null, {
$documentation: "A class definition",
}, AST_Class);
var AST_ClassExpression = DEFNODE("ClassExpression", null, {
$documentation: "A class expression."
}, AST_Class);
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
$propdoc: {
name: "[string] name of this symbol",
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
thedef: "[SymbolDef/S] the definition of this symbol"
},
$documentation: "Base class for all symbols",
$documentation: "Base class for all symbols"
});
var AST_NewTarget = DEFNODE("NewTarget", null, {
$documentation: "A reference to new.target"
});
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
@@ -729,15 +1104,26 @@ var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
$propdoc: {
init: "[AST_Node*/S] array of initializers for this declaration."
},
}, AST_Symbol);
var AST_SymbolVar = DEFNODE("SymbolVar", null, {
$documentation: "Symbol defining a variable",
}, AST_SymbolDeclaration);
var AST_SymbolBlockDeclaration = DEFNODE("SymbolBlockDeclaration", null, {
$documentation: "Base class for block-scoped declaration symbols"
}, AST_SymbolDeclaration);
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
$documentation: "A constant declaration"
}, AST_SymbolDeclaration);
}, AST_SymbolBlockDeclaration);
var AST_SymbolLet = DEFNODE("SymbolLet", null, {
$documentation: "A block-scoped `let` declaration"
}, AST_SymbolBlockDeclaration);
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
$documentation: "Symbol naming a function argument",
@@ -747,13 +1133,33 @@ var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
$documentation: "Symbol defining a function",
}, AST_SymbolDeclaration);
var AST_SymbolMethod = DEFNODE("SymbolMethod", null, {
$documentation: "Symbol in an object defining a method",
}, AST_Symbol);
var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
$documentation: "Symbol naming a function expression",
}, AST_SymbolDeclaration);
var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
$documentation: "Symbol naming a class's name in a class declaration. Lexically scoped to its containing scope, and accessible within the class."
}, AST_SymbolBlockDeclaration);
var AST_SymbolClass = DEFNODE("SymbolClass", null, {
$documentation: "Symbol naming a class's name. Lexically scoped to the class."
}, AST_SymbolDeclaration);
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
$documentation: "Symbol naming the exception in catch",
}, AST_SymbolDeclaration);
}, AST_SymbolBlockDeclaration);
var AST_SymbolImport = DEFNODE("SymbolImport", null, {
$documentation: "Symbol refering to an imported name",
}, AST_SymbolBlockDeclaration);
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
$documentation: "A symbol imported from a module, but it is defined in the other module, and its real name is irrelevant for this module's purposes",
}, AST_Symbol);
var AST_Label = DEFNODE("Label", "references", {
$documentation: "Symbol naming a label (declaration)",
@@ -778,6 +1184,10 @@ var AST_This = DEFNODE("This", null, {
$documentation: "The `this` symbol",
}, AST_Symbol);
var AST_Super = DEFNODE("Super", null, {
$documentation: "The `super` symbol",
}, AST_Symbol);
var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants",
getValue: function() {
@@ -851,6 +1261,21 @@ var AST_True = DEFNODE("True", null, {
value: true
}, AST_Boolean);
/* -----[ Yield ]----- */
var AST_Yield = DEFNODE("Yield", "expression is_star", {
$documentation: "A `yield` statement",
$propdoc: {
expression: "[AST_Node?] the value returned or thrown by this statement; could be null (representing undefined) but only when is_star is set to false",
is_star: "[Boolean] Whether this is a yield or yield* statement"
},
_walk: function(visitor) {
return visitor._visit(this, this.expression && function(){
this.expression._walk(visitor);
});
}
});
/* -----[ TreeWalker ]----- */
function TreeWalker(callback) {
@@ -873,17 +1298,20 @@ TreeWalker.prototype = {
parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)];
},
push: function(node) {
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] = node;
} else if (node instanceof AST_Directive) {
this.directives[node.value] = this.directives[node.value] ? "up" : true;
} else if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
this.directives["use strict"] = this.directives["use strict"] ? "up" : true;
}
this.stack.push(node);
},
pop: function(node) {
this.stack.pop();
if (node instanceof AST_Lambda) {
if (node instanceof AST_Lambda || node instanceof AST_Class) {
this.directives = Object.getPrototypeOf(this.directives);
}
},
@@ -901,11 +1329,11 @@ TreeWalker.prototype = {
var dir = this.directives[type];
if (dir) return dir;
var node = this.stack[this.stack.length - 1];
if (node instanceof AST_Scope) {
if (node instanceof AST_Scope && node.body) {
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 st;
if (st.value == type) return true;
}
}
},
@@ -927,16 +1355,16 @@ TreeWalker.prototype = {
self = p;
}
},
loopcontrol_target: function(node) {
loopcontrol_target: function(label) {
var stack = this.stack;
if (node.label) for (var i = stack.length; --i >= 0;) {
if (label) for (var i = stack.length; --i >= 0;) {
var x = stack[i];
if (x instanceof AST_LabeledStatement && x.label.name == node.label.name)
if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
return x.body;
}
} else for (var i = stack.length; --i >= 0;) {
var x = stack[i];
if (x instanceof AST_IterationStatement
|| node instanceof AST_Break && x instanceof AST_Switch)
if (x instanceof AST_Switch || x instanceof AST_IterationStatement)
return x;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,154 +0,0 @@
"use strict";
var to_ascii = typeof atob == "undefined" ? function(b64) {
return new Buffer(b64, "base64").toString();
} : atob;
var to_base64 = typeof btoa == "undefined" ? function(str) {
return new Buffer(str).toString("base64");
} : btoa;
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
AST_Node.warn("inline source map not found");
return null;
}
return to_ascii(match[2]);
}
function set_shorthand(name, options, keys) {
if (options[name]) {
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
}
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
if (typeof files == "string") {
files = [ files ];
}
options = defaults(options, {
compress: {},
ie8: false,
keep_fnames: false,
mangle: {},
output: {},
parse: {},
sourceMap: false,
toplevel: false,
warnings: false,
wrap: false,
}, true);
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]);
if (options.mangle) {
options.mangle = defaults(options.mangle, {
cache: null,
eval: false,
ie8: false,
keep_fnames: false,
properties: false,
reserved: [],
toplevel: false,
}, true);
}
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
content: null,
filename: null,
includeSources: false,
root: null,
url: null,
}, true);
}
var warnings = [];
if (options.warnings && !AST_Node.warn_function) {
AST_Node.warn_function = function(warning) {
warnings.push(warning);
};
}
var toplevel;
if (files instanceof AST_Toplevel) {
toplevel = files;
} else {
options.parse = options.parse || {};
options.parse.toplevel = null;
for (var name in files) {
options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") {
if (Object.keys(files).length > 1)
throw new Error("inline source map only works with singular input");
options.sourceMap.content = read_source_map(files[name]);
}
}
toplevel = options.parse.toplevel;
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (options.compress) {
toplevel.figure_out_scope(options.mangle);
toplevel = new Compressor(options.compress).compress(toplevel);
}
if (options.mangle) {
toplevel.figure_out_scope(options.mangle);
base54.reset();
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
if (options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
}
var result = {};
if (options.output.ast) {
result.ast = toplevel;
}
if (!HOP(options.output, "code") || options.output.code) {
if (options.sourceMap) {
if (typeof options.sourceMap.content == "string") {
options.sourceMap.content = JSON.parse(options.sourceMap.content);
}
options.output.source_map = SourceMap({
file: options.sourceMap.filename,
orig: options.sourceMap.content,
root: options.sourceMap.root
});
if (options.sourceMap.includeSources) {
for (var name in files) {
options.output.source_map.get().setSourceContent(name, files[name]);
}
}
}
delete options.output.ast;
delete options.output.code;
var stream = OutputStream(options.output);
toplevel.print(stream);
result.code = stream.get();
if (options.sourceMap) {
result.map = options.output.source_map.toString();
if (options.sourceMap.url == "inline") {
result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map);
} else if (options.sourceMap.url) {
result.code += "\n//# sourceMappingURL=" + options.sourceMap.url;
}
}
}
if (warnings.length) {
result.warnings = warnings;
}
return result;
} catch (ex) {
return { error: ex };
} finally {
AST_Node.warn_function = warn_function;
}
}

View File

@@ -149,11 +149,7 @@
});
},
SequenceExpression: function(M) {
return new AST_Sequence({
start : my_start_token(M),
end : my_end_token(M),
expressions: M.expressions.map(from_moz)
});
return AST_Seq.from_array(M.expressions.map(from_moz));
},
MemberExpression: function(M) {
return new (M.computed ? AST_Sub : AST_Dot)({
@@ -336,10 +332,10 @@
};
});
def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) {
def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
return {
type: "SequenceExpression",
expressions: M.expressions.map(to_moz)
expressions: M.to_array().map(to_moz)
};
});

View File

@@ -46,38 +46,56 @@
var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
function is_some_comments(comment) {
// multiline comment
return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value);
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, {
ascii_only : false,
beautify : false,
bracketize : false,
comments : false,
ie8 : false,
indent_level : 4,
indent_start : 0,
inline_script : true,
keep_quoted_props: false,
max_line_len : false,
preamble : null,
preserve_line : false,
indent_level : 4,
quote_keys : false,
quote_style : 0,
semicolons : true,
shebang : true,
source_map : null,
space_colon : true,
ascii_only : false,
ascii_identifiers: undefined,
unescape_regexps : false,
inline_script : false,
width : 80,
max_line_len : false,
beautify : false,
source_map : null,
bracketize : false,
semicolons : true,
comments : false,
shebang : true,
preserve_line : false,
screw_ie8 : true,
preamble : null,
quote_style : 0,
keep_quoted_props: false,
shorthand : undefined,
ecma : 5,
wrap_iife : false,
}, true);
if (typeof options.ascii_identifiers === 'undefined')
options.ascii_identifiers = options.ascii_only;
if (options.shorthand === undefined)
options.shorthand = options.ecma > 5;
// Convert comment option to RegExp if neccessary and set up comments filter
var comment_filter = return_false; // Default case, throw all comments away
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)) {
@@ -89,12 +107,12 @@ function OutputStream(options) {
}
if (comments instanceof RegExp) {
comment_filter = function(comment) {
return comment.type != "comment5" && comments.test(comment.value);
return comment.type == "comment5" || comments.test(comment.value);
};
}
else if (typeof comments === "function") {
comment_filter = function(comment) {
return comment.type != "comment5" && comments(this, comment);
return comment.type == "comment5" || comments(this, comment);
};
}
else if (comments === "some") {
@@ -111,9 +129,19 @@ function OutputStream(options) {
var OUTPUT = "";
function to_ascii(str, identifier) {
return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
var code = ch.charCodeAt(0).toString(16);
if (code.length <= 2 && !identifier) {
return str.replace(/[\ud800-\udbff][\udc00-\udfff]|[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
var code = get_full_char_code(ch, 0).toString(16);
if ((identifier && code.length === 1 && options.ecma >= 6) || code.length > 4) {
if (options.ecma < 6) {
if (identifier) {
return ch; // no \u{} support
}
return "\\u" + ch.charCodeAt(0).toString(16) + "\\u"
+ ch.charCodeAt(1).toString(16);
}
return "\\u{" + code + "}";
} else if (code.length <= 2 && !identifier) {
while (code.length < 2) code = "0" + code;
return "\\x" + code;
} else {
@@ -136,12 +164,12 @@ function OutputStream(options) {
case "\t": return "\\t";
case "\b": return "\\b";
case "\f": return "\\f";
case "\x0B": return options.ie8 ? "\\x0B" : "\\v";
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 /[0-7]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
}
return s;
});
@@ -151,7 +179,22 @@ function OutputStream(options) {
function quote_double() {
return '"' + str.replace(/\x22/g, '\\"') + '"';
}
function quote_template() {
if (!options.ascii_only) {
str = str.replace(/\\(n|r|u2028|u2029)/g, function(s, c) {
switch(c) {
case "n": return "\n";
case "r": return "\r";
case "u2028": return "\u2028";
case "u2029": return "\u2029";
}
return s;
});
}
return '`' + str.replace(/`/g, '\\`') + '`';
}
if (options.ascii_only) str = to_ascii(str);
if (quote === "`") return quote_template();
switch (options.quote_style) {
case 1:
return quote_single();
@@ -176,7 +219,7 @@ function OutputStream(options) {
function make_name(name) {
name = name.toString();
if (options.ascii_only)
if (options.ascii_identifiers)
name = to_ascii(name, true);
return name;
};
@@ -190,7 +233,17 @@ function OutputStream(options) {
var might_need_space = false;
var might_need_semicolon = false;
var might_add_newline = 0;
var last = "";
var last = null;
function last_char() {
var char = last.charAt(last.length - 1);
if (is_surrogate_pair_tail(char)) {
return last.charAt(last.length - 2) + char;
}
return char;
};
var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) {
@@ -213,12 +266,11 @@ function OutputStream(options) {
function print(str) {
str = String(str);
var ch = str.charAt(0);
var prev = last.charAt(last.length - 1);
var ch = get_full_char(str, 0);
if (might_need_semicolon) {
might_need_semicolon = false;
if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") {
if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
if (options.semicolons || requireSemicolonChars(ch)) {
OUTPUT += ";";
current_col++;
@@ -255,6 +307,7 @@ function OutputStream(options) {
}
if (might_need_space) {
var prev = last_char();
if ((is_identifier_char(prev)
&& (is_identifier_char(ch) || ch == "\\"))
|| (ch == "/" && ch == prev)
@@ -278,6 +331,10 @@ function OutputStream(options) {
last = str;
};
var star = function(){
print("*");
}
var space = options.beautify ? function() {
print(" ");
} : function() {
@@ -387,6 +444,10 @@ function OutputStream(options) {
return OUTPUT;
};
if (options.preamble) {
print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
}
var stack = [];
return {
get : get,
@@ -397,6 +458,7 @@ function OutputStream(options) {
should_break : function() { return options.width && this.current_width() >= options.width },
newline : newline,
print : print,
star : star,
space : space,
comma : comma,
colon : colon,
@@ -416,6 +478,10 @@ function OutputStream(options) {
}
print(encoded);
},
print_template_string_chars: function(str) {
var encoded = encode_string(str, '`').replace(/\${/g, "\\${");
return print(encoded.substr(1, encoded.length - 2));
},
encode_string : encode_string,
next_indent : next_indent,
with_indent : with_indent,
@@ -506,17 +572,6 @@ function OutputStream(options) {
}));
}
if (output.pos() == 0) {
if (comments.length > 0 && output.option("shebang") && comments[0].type == "comment5") {
output.print("#!" + comments.shift().value + "\n");
output.indent();
}
var preamble = output.option("preamble");
if (preamble) {
output.print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
}
}
comments = comments.filter(output.comment_filter, self);
// Keep single line comments after nlb, after nlb
@@ -541,6 +596,10 @@ function OutputStream(options) {
output.space();
}
}
else if (output.pos() === 0 && c.type == "comment5" && output.option("shebang")) {
output.print("#!" + c.value + "\n");
output.indent();
}
});
}
});
@@ -576,19 +635,30 @@ function OutputStream(options) {
return false;
});
PARENS(AST_Arrow, function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this;
});
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
PARENS(AST_Object, function(output){
return first_in_statement(output);
});
PARENS(AST_Unary, function(output){
PARENS([ AST_Unary, AST_Undefined ], function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this
|| p instanceof AST_Call && p.expression === this;
|| p instanceof AST_Call && p.expression === this
|| p instanceof AST_Binary
&& p.operator === "**"
&& this instanceof AST_UnaryPrefix
&& p.left === this
&& this.operator !== "++"
&& this.operator !== "--";
});
PARENS(AST_Sequence, function(output){
PARENS(AST_Seq, function(output){
var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|| p instanceof AST_Unary // !(foo, bar, baz)
@@ -599,6 +669,8 @@ function OutputStream(options) {
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
* ==> 20 (side effect, set a := 10 and b := 20) */
|| p instanceof AST_Arrow // x => (x, x)
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
;
});
@@ -625,6 +697,24 @@ function OutputStream(options) {
}
});
PARENS(AST_Yield, function(output){
var p = output.parent();
// (yield 1) + (yield 2)
// a = yield 3
if (p instanceof AST_Binary && p.operator !== "=")
return true;
// (yield 1) ? yield 2 : yield 3
if (p instanceof AST_Conditional && p.condition === this)
return true;
// -(yield 4)
if (p instanceof AST_Unary)
return true;
// (yield x).foo
// (yield x)['foo']
if (p instanceof AST_PropAccess && p.expression === this)
return true;
});
PARENS(AST_PropAccess, function(output){
var p = output.parent();
if (p instanceof AST_New && p.expression === this) {
@@ -677,7 +767,7 @@ function OutputStream(options) {
}
});
PARENS([ AST_Assign, AST_Conditional ], function(output){
PARENS([ AST_Assign, AST_Conditional ], function (output){
var p = output.parent();
// !(a = false) → true
if (p instanceof AST_Unary)
@@ -694,6 +784,9 @@ function OutputStream(options) {
// (a = foo)["prop"] —or— (a = foo).prop
if (p instanceof AST_PropAccess && p.expression === this)
return true;
// ({a, b} = {a: 1, b: 2}), a destructuring assignment
if (this instanceof AST_Assign && this.left instanceof AST_Destructuring && this.left.is_array === false)
return true;
});
/* -----[ PRINTERS ]----- */
@@ -702,6 +795,28 @@ function OutputStream(options) {
output.print_string(self.value, self.quote);
output.semicolon();
});
DEFPRINT(AST_Expansion, function (self, output) {
output.print('...');
self.expression.print(output);
});
DEFPRINT(AST_Destructuring, function (self, output) {
output.print(self.is_array ? "[" : "{");
var first = true;
var len = self.names.length;
self.names.forEach(function (name, i) {
if (first) first = false; else { output.comma(); output.space(); }
name.print(output);
// If the final element is a hole, we need to make sure it
// doesn't look like a trailing comma, by inserting an actual
// trailing comma.
if (i === len - 1 && name instanceof AST_Hole)
output.comma();
})
output.print(self.is_array ? "]" : "}");
});
DEFPRINT(AST_Debugger, function(self, output){
output.print("debugger");
output.semicolon();
@@ -773,7 +888,7 @@ function OutputStream(options) {
DEFPRINT(AST_Do, function(self, output){
output.print("do");
output.space();
make_block(self.body, output);
self._do_print_body(output);
output.space();
output.print("while");
output.space();
@@ -795,7 +910,7 @@ function OutputStream(options) {
output.print("for");
output.space();
output.with_parens(function(){
if (self.init) {
if (self.init && !(self.init instanceof AST_EmptyStatement)) {
if (self.init instanceof AST_Definitions) {
self.init.print(output);
} else {
@@ -826,7 +941,11 @@ function OutputStream(options) {
output.with_parens(function(){
self.init.print(output);
output.space();
output.print("in");
if (self instanceof AST_ForOf) {
output.print("of");
} else {
output.print("in");
}
output.space();
self.object.print(output);
});
@@ -848,10 +967,19 @@ function OutputStream(options) {
var self = this;
if (!nokeyword) {
output.print("function");
if (this.is_generator) {
output.star();
}
if (self.name) {
output.space();
}
}
if (self.name) {
output.space();
if (self.name instanceof AST_Symbol) {
self.name.print(output);
} else if (nokeyword && self.name instanceof AST_Node) {
output.with_square(function() {
self.name.print(output); // Computed method name
});
}
output.with_parens(function(){
self.argnames.forEach(function(arg, i){
@@ -866,6 +994,56 @@ function OutputStream(options) {
self._do_print(output);
});
DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
self.prefix.print(output);
self.template_string.print(output);
});
DEFPRINT(AST_TemplateString, function(self, output) {
var is_tagged = output.parent() instanceof AST_PrefixedTemplateString;
output.print("`");
for (var i = 0; i < self.segments.length; i++) {
if (!(self.segments[i] instanceof AST_TemplateSegment)) {
output.print("${");
self.segments[i].print(output);
output.print("}");
} else if (is_tagged) {
output.print(self.segments[i].raw);
} else {
output.print_template_string_chars(self.segments[i].value);
}
}
output.print("`");
});
AST_Arrow.DEFMETHOD("_do_print", function(output){
var self = this;
var parent = output.parent();
var needs_parens = parent instanceof AST_Binary ||
parent instanceof AST_Unary ||
(parent instanceof AST_Call && self === parent.expression);
if (needs_parens) { output.print("(") }
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
self.argnames[0].print(output);
} else {
output.with_parens(function(){
self.argnames.forEach(function(arg, i){
if (i) output.comma();
arg.print(output);
});
});
}
output.space();
output.print('=>');
output.space();
if (self.body instanceof AST_Node) {
this.body.print(output);
} else {
print_bracketed(this.body, output);
}
if (needs_parens) { output.print(")") }
});
/* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
@@ -882,6 +1060,17 @@ function OutputStream(options) {
self._do_print(output, "throw");
});
/* -----[ yield ]----- */
DEFPRINT(AST_Yield, function(self, output){
var star = self.is_star ? "*" : "";
output.print("yield" + star);
if (self.expression) {
output.space();
self.expression.print(output);
}
});
/* -----[ loop control ]----- */
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
@@ -900,10 +1089,10 @@ function OutputStream(options) {
/* -----[ if ]----- */
function make_then(self, output) {
var b = self.body;
if (output.option("bracketize")
|| output.option("ie8") && b instanceof AST_Do)
return make_block(b, output);
if (output.option("bracketize")) {
make_block(self.body, output);
return;
}
// The squeezer replaces "block"-s that contain only a single
// statement with the statement itself; technically, the AST
// is correct, but this can create problems when we output an
@@ -911,7 +1100,18 @@ function OutputStream(options) {
// IF *without* an ELSE block (then the outer ELSE would refer
// to the inner IF). This function checks for this case and
// adds the block brackets if needed.
if (!b) return output.force_semicolon();
if (!self.body)
return output.force_semicolon();
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
// around do/while
make_block(self.body, output);
return;
}
var b = self.body;
while (true) {
if (b instanceof AST_If) {
if (!b.alternative) {
@@ -956,24 +1156,24 @@ function OutputStream(options) {
self.expression.print(output);
});
output.space();
var last = self.body.length - 1;
if (last < 0) output.print("{}");
else output.with_block(function(){
self.body.forEach(function(branch, i){
if (self.body.length > 0) output.with_block(function(){
self.body.forEach(function(stmt, i){
if (i) output.newline();
output.indent(true);
branch.print(output);
if (i < last && branch.body.length > 0)
output.newline();
stmt.print(output);
});
});
else output.print("{}");
});
AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
output.newline();
this.body.forEach(function(stmt){
output.indent();
stmt.print(output);
if (this.body.length > 0) {
output.newline();
});
this.body.forEach(function(stmt){
output.indent();
stmt.print(output);
output.newline();
});
}
});
DEFPRINT(AST_Default, function(self, output){
output.print("default:");
@@ -1030,12 +1230,77 @@ function OutputStream(options) {
if (!avoid_semicolon)
output.semicolon();
});
DEFPRINT(AST_Let, function(self, output){
self._do_print(output, "let");
});
DEFPRINT(AST_Var, function(self, output){
self._do_print(output, "var");
});
DEFPRINT(AST_Const, function(self, output){
self._do_print(output, "const");
});
DEFPRINT(AST_Import, function(self, output) {
output.print("import");
output.space();
if (self.imported_name) {
self.imported_name.print(output);
}
if (self.imported_name && self.imported_names) {
output.print(",");
output.space();
}
if (self.imported_names) {
output.print("{");
self.imported_names.forEach(function(name_import, i) {
output.space();
name_import.print(output);
if (i < self.imported_names.length - 1) {
output.print(",");
output.space();
}
});
output.space();
output.print("}");
}
if (self.imported_name || self.imported_names) {
output.space();
output.print("from")
output.space();
}
self.module_name.print(output);
output.semicolon();
});
DEFPRINT(AST_NameImport, function(self, output) {
var definition = self.name.definition();
var names_are_different =
(definition && definition.mangled_name || self.name.name) !==
self.foreign_name.name;
if (names_are_different) {
output.print(self.foreign_name.name);
output.space();
output.print("as");
output.space();
self.name.print(output);
} else {
self.name.print(output);
}
});
DEFPRINT(AST_Export, function(self, output) {
output.print("export");
output.space();
if (self.is_default) {
output.print("default");
output.space();
}
if (self.exported_value) {
self.exported_value.print(output);
} else if (self.exported_definition) {
self.exported_definition.print(output);
}
output.semicolon();
});
function parenthesize_for_noin(node, output, noin) {
if (!noin) node.print(output);
@@ -1083,19 +1348,18 @@ function OutputStream(options) {
AST_Call.prototype._codegen(self, output);
});
AST_Sequence.DEFMETHOD("_do_print", function(output){
this.expressions.forEach(function(node, index) {
if (index > 0) {
output.comma();
if (output.should_break()) {
output.newline();
output.indent();
}
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
if (output.should_break()) {
output.newline();
output.indent();
}
node.print(output);
});
this.cdr.print(output);
}
});
DEFPRINT(AST_Sequence, function(self, output){
DEFPRINT(AST_Seq, function(self, output){
self._do_print(output);
// var p = output.parent();
// if (p instanceof AST_Statement) {
@@ -1208,9 +1472,35 @@ function OutputStream(options) {
});
else output.print("{}");
});
DEFPRINT(AST_ObjectKeyVal, function(self, output){
var key = self.key;
var quote = self.quote;
DEFPRINT(AST_Class, function(self, output){
output.print("class");
output.space();
if (self.name) {
self.name.print(output);
output.space();
}
if (self.extends) {
output.print("extends");
output.space();
self.extends.print(output);
output.space();
}
if (self.properties.length > 0) output.with_block(function(){
self.properties.forEach(function(prop, i){
if (i) {
output.newline();
}
output.indent();
prop.print(output);
});
output.newline();
});
else output.print("{}");
});
DEFPRINT(AST_NewTarget, function(self, output) {
output.print("new.target");
});
AST_ObjectProperty.DEFMETHOD("print_property_name", function(key, quote, output) {
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
@@ -1218,7 +1508,7 @@ function OutputStream(options) {
&& +key + "" == key)
&& parseFloat(key) >= 0) {
output.print(make_num(key));
} else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) {
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) {
output.print_string(key, quote);
} else {
@@ -1227,29 +1517,108 @@ function OutputStream(options) {
} else {
output.print_string(key, quote);
}
output.colon();
self.value.print(output);
});
DEFPRINT(AST_ObjectKeyVal, function(self, output){
function get_name(self) {
var def = self.definition();
return def ? def.mangled_name || def.name : self.name;
}
var allowShortHand = output.option("shorthand");
if (allowShortHand &&
self.value instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self.value) === self.key
) {
self.print_property_name(self.key, self.quote, output);
} else if (allowShortHand &&
self.value instanceof AST_DefaultAssign &&
self.value.left instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self.value.left) === self.key
) {
self.print_property_name(self.key, self.quote, output);
output.print("=");
self.value.right.print(output);
} else {
if (!(self.key instanceof AST_Node)) {
self.print_property_name(self.key, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
output.colon();
self.value.print(output);
}
});
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) {
if (self.static) {
output.print("static");
output.space();
}
output.print(type);
output.space();
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
});
DEFPRINT(AST_ObjectSetter, function(self, output){
output.print("set");
output.space();
self.key.print(output);
self.value._do_print(output, true);
self._print_getter_setter("set", self, output);
});
DEFPRINT(AST_ObjectGetter, function(self, output){
output.print("get");
self._print_getter_setter("get", self, output);
});
DEFPRINT(AST_ConciseMethod, function(self, output){
if (self.static) {
output.print("static");
output.space();
}
if (self.is_generator) {
output.print("*");
}
output.space();
self.key.print(output);
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
});
DEFPRINT(AST_Symbol, function(self, output){
var def = self.definition();
output.print_name(def ? def.mangled_name || def.name : self.name);
AST_Symbol.DEFMETHOD("_do_print", function(output){
var def = this.definition();
output.print_name(def ? def.mangled_name || def.name : this.name);
});
DEFPRINT(AST_Symbol, function (self, output) {
self._do_print(output);
});
DEFPRINT(AST_SymbolDeclaration, function(self, output){
self._do_print(output);
});
DEFPRINT(AST_Undefined, function(self, output){
output.print("void 0");
});
DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_Infinity, function(self, output){
output.print("Infinity");
});
DEFPRINT(AST_NaN, function(self, output){
output.print("NaN");
});
DEFPRINT(AST_This, function(self, output){
output.print("this");
});
DEFPRINT(AST_Super, function(self, output){
output.print("super");
});
DEFPRINT(AST_Constant, function(self, output){
output.print(self.getValue());
});
@@ -1312,7 +1681,15 @@ function OutputStream(options) {
function force_statement(stat, output) {
if (output.option("bracketize")) {
make_block(stat, output);
if (!stat || stat instanceof AST_EmptyStatement)
output.print("{}");
else if (stat instanceof AST_BlockStatement)
stat.print(output);
else output.with_block(function(){
output.indent();
stat.print(output);
output.newline();
});
} else {
if (!stat || stat instanceof AST_EmptyStatement)
output.force_semicolon();
@@ -1361,11 +1738,11 @@ function OutputStream(options) {
};
function make_block(stmt, output) {
if (!stmt || stmt instanceof AST_EmptyStatement)
output.print("{}");
else if (stmt instanceof AST_BlockStatement)
if (stmt instanceof AST_BlockStatement) {
stmt.print(output);
else output.with_block(function(){
return;
}
output.with_block(function(){
output.indent();
stmt.print(output);
output.newline();

File diff suppressed because one or more lines are too long

View File

@@ -43,19 +43,28 @@
"use strict";
function find_builtins(reserved) {
// NaN will be included due to Number.NaN
[
"null",
"true",
"false",
"Infinity",
"-Infinity",
"undefined",
].forEach(add);
function find_builtins() {
// Compatibility fix for some standard defined globals not defined on every js environment
var new_globals = ["Symbol", "Map", "Promise", "Proxy", "Reflect", "Set", "WeakMap", "WeakSet"];
var objects = {};
new_globals.forEach(function (new_global) {
objects[new_global] = global[new_global] || new Function();
});
var a = [];
[ Object, Array, Function, Number,
String, Boolean, Error, Math,
Date, RegExp
Date, RegExp, objects.Symbol, ArrayBuffer,
DataView, decodeURI, decodeURIComponent,
encodeURI, encodeURIComponent, eval, EvalError,
Float32Array, Float64Array, Int8Array, Int16Array,
Int32Array, isFinite, isNaN, JSON, objects.Map, parseFloat,
parseInt, objects.Promise, objects.Proxy, RangeError, ReferenceError,
objects.Reflect, objects.Set, SyntaxError, TypeError, Uint8Array,
Uint8ClampedArray, Uint16Array, Uint32Array, URIError,
objects.WeakMap, objects.WeakSet
].forEach(function(ctor){
Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) {
@@ -63,23 +72,24 @@ function find_builtins(reserved) {
}
});
function add(name) {
push_uniq(reserved, name);
push_uniq(a, name);
}
return a;
}
function mangle_properties(ast, options) {
options = defaults(options, {
builtins: false,
cache: null,
debug: false,
keep_quoted: false,
only_cache: false,
regex: null,
reserved: null,
reserved : null,
cache : null,
only_cache : false,
regex : null,
ignore_quoted : false,
debug : false
});
var reserved = options.reserved || [];
if (!options.builtins) find_builtins(reserved);
var reserved = options.reserved;
if (reserved == null)
reserved = find_builtins();
var cache = options.cache;
if (cache == null) {
@@ -90,12 +100,12 @@ function mangle_properties(ast, options) {
}
var regex = options.regex;
var keep_quoted = options.keep_quoted;
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 = (options.debug !== false);
var debug_name_suffix;
if (debug) {
debug_name_suffix = (options.debug === true ? "" : options.debug);
@@ -103,12 +113,12 @@ function mangle_properties(ast, options) {
var names_to_mangle = [];
var unmangleable = [];
var to_keep = {};
var ignored = {};
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
add(node.key, keep_quoted && node.quote);
add(node.key, ignore_quoted && node.quote);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
@@ -118,14 +128,17 @@ function mangle_properties(ast, options) {
add(node.property);
}
else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted);
addStrings(node.property, ignore_quoted);
}
else if (node instanceof AST_ConciseMethod) {
add(node.name.name);
}
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) {
if (!(keep_quoted && node.quote))
if (!(ignore_quoted && node.quote))
node.key = mangle(node.key);
}
else if (node instanceof AST_ObjectProperty) {
@@ -136,9 +149,14 @@ function mangle_properties(ast, options) {
node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
if (!keep_quoted)
if (!ignore_quoted)
node.property = mangleStrings(node.property);
}
else if (node instanceof AST_ConciseMethod) {
if (should_mangle(node.name.name)) {
node.name.name = mangle(node.name.name);
}
}
// else if (node instanceof AST_String) {
// if (should_mangle(node.value)) {
// AST_Node.warn(
@@ -156,26 +174,27 @@ 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) {
return cache.props.has(name);
}
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
if (/^[0-9.]+$/.test(name)) return false;
return true;
}
function should_mangle(name) {
if (keep_quoted && name in to_keep) return false;
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, keep) {
if (keep) {
to_keep[name] = true;
function add(name, ignore) {
if (ignore) {
ignored[name] = true;
return;
}
@@ -198,19 +217,19 @@ function mangle_properties(ast, options) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) {
if (can_mangle(debug_mangled) && !(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 `to_keep` set
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this
// 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) || keep_quoted && mangled in to_keep);
} while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored));
}
cache.props.set(name, mangled);
@@ -218,17 +237,17 @@ function mangle_properties(ast, options) {
return mangled;
}
function addStrings(node, keep) {
function addStrings(node, ignore) {
var out = {};
try {
(function walk(node){
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Sequence) {
walk(node.expressions[node.expressions.length - 1]);
if (node instanceof AST_Seq) {
walk(node.cdr);
return true;
}
if (node instanceof AST_String) {
add(node.value, keep);
add(node.value, ignore);
return true;
}
if (node instanceof AST_Conditional) {
@@ -246,9 +265,8 @@ function mangle_properties(ast, options) {
function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Sequence) {
var last = node.expressions.length - 1;
node.expressions[last] = mangleStrings(node.expressions[last]);
if (node instanceof AST_Seq) {
node.cdr = mangleStrings(node.cdr);
}
else if (node instanceof AST_String) {
node.value = mangle(node.value);
@@ -260,4 +278,5 @@ function mangle_properties(ast, options) {
return node;
}));
}
}

View File

@@ -49,7 +49,9 @@ function SymbolDef(scope, index, orig) {
this.scope = scope;
this.references = [];
this.global = false;
this.export = false;
this.mangled_name = null;
this.object_destructuring_arg = false;
this.undeclared = false;
this.index = index;
this.id = SymbolDef.next_id++;
@@ -62,11 +64,17 @@ SymbolDef.prototype = {
if (!options) options = {};
return (this.global && !options.toplevel)
|| this.export
|| this.object_destructuring_arg
|| this.undeclared
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|| (options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun));
|| this.orig[0] instanceof AST_SymbolDefun))
|| this.orig[0] instanceof AST_SymbolMethod
|| (options.keep_classnames
&& (this.orig[0] instanceof AST_SymbolClass
|| this.orig[0] instanceof AST_SymbolDefClass));
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
@@ -75,14 +83,9 @@ SymbolDef.prototype = {
}
else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope;
var sym = this.orig[0];
if (options.ie8 && sym instanceof AST_SymbolLambda)
if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
this.mangled_name = s.next_mangled(options, this);
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
@@ -92,8 +95,8 @@ SymbolDef.prototype = {
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, {
cache: null,
ie8: false,
screw_ie8: true,
cache: null
});
// pass 1: setup scope chaining and handle definitions
@@ -101,18 +104,33 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var in_destructuring = null;
var in_export = false;
var in_block = 0;
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Catch) {
if (node.is_block_scope()) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
scope.init_scope_vars();
scope.parent_scope = save_scope;
if (!(node instanceof AST_Scope)) {
scope.uses_with = save_scope.uses_with;
scope.uses_eval = save_scope.uses_eval;
scope.directives = save_scope.directives;
}
descend();
scope = save_scope;
return true;
}
if (node instanceof AST_Destructuring && node.is_array === false) {
in_destructuring = node; // These don't nest
descend();
in_destructuring = null;
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
node.init_scope_vars();
var save_scope = node.parent_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
@@ -123,6 +141,22 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Export) {
in_export = true;
descend();
in_export = false;
return true;
}
if (node instanceof AST_BlockStatement
|| node instanceof AST_Switch
|| node instanceof AST_Try
|| node instanceof AST_Catch
|| node instanceof AST_Finally) {
in_block++;
descend();
in_block--;
return true;
}
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
@@ -141,12 +175,15 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_SymbolFunarg) {
node.object_destructuring_arg = !!in_destructuring;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolLambda) {
defun.def_function(node);
defun.def_function(node, in_export, in_block);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
@@ -154,22 +191,32 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
(node.scope = defun.parent_scope).def_function(node);
var parent_lambda = defun.parent_scope;
while (parent_lambda.is_block_scope()) {
parent_lambda = parent_lambda.parent_scope;
}
(node.scope = parent_lambda).def_function(node, in_export, in_block);
}
else if (node instanceof AST_SymbolClass) {
defun.def_variable(node, in_export, in_block);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node, in_export, in_block);
}
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
(node.scope = defun.parent_scope).def_function(node, in_export, in_block);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
defun.def_variable(node);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
node.reference(options);
}
}
|| node instanceof AST_SymbolConst
|| node instanceof AST_SymbolLet) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export, in_block);
def.destructuring = in_destructuring;
def.init = tw.parent().value;
}
else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
(options.screw_ie8 ? scope : defun).def_variable(node, in_export, in_block);
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
@@ -185,6 +232,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// pass 2: find back references and eval
var func = null;
var cls = null;
var globals = self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Lambda) {
@@ -194,6 +242,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
func = prev_func;
return true;
}
if (node instanceof AST_Class) {
var prev_cls = cls;
cls = node;
descend();
cls = prev_cls;
return true;
}
if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node);
return true;
@@ -219,24 +274,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
});
self.walk(tw);
// pass 3: fix up any scoping issue with IE8
if (options.ie8) {
self.walk(new TreeWalker(function(node, descend) {
if (node instanceof AST_SymbolCatch) {
var name = node.name;
var refs = node.thedef.references;
var scope = node.thedef.defun;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
refs.forEach(function(ref) {
ref.thedef = def;
ref.reference(options);
});
node.thedef = def;
return true;
}
}));
}
if (options.cache) {
this.cname = options.cache.cname;
}
@@ -255,28 +292,41 @@ AST_Toplevel.DEFMETHOD("def_global", function(node){
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
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
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
this.parent_scope = parent_scope; // 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
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
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
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
});
AST_Node.DEFMETHOD("is_block_scope", function(){
return false; // Behaviour will be overridden by AST_Block
});
AST_Block.DEFMETHOD("is_block_scope", function(){
return (
!(this instanceof AST_Lambda) &&
!(this instanceof AST_Toplevel) &&
!(this instanceof AST_Class) &&
!(this instanceof AST_SwitchBranch)
);
});
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false;
this.def_variable(new AST_SymbolConst({
name: "arguments",
start: this.start,
end: this.end
}));
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_Symbol.DEFMETHOD("mark_enclosed", function(options) {
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);
@@ -290,27 +340,30 @@ AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
}
});
AST_Symbol.DEFMETHOD("reference", function(options) {
this.definition().references.push(this);
this.mark_enclosed(options);
});
AST_Scope.DEFMETHOD("find_variable", function(name){
if (name instanceof AST_Symbol) name = name.name;
return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol));
AST_Scope.DEFMETHOD("def_function", function(symbol, in_export, in_block){
this.functions.set(symbol.name, this.def_variable(symbol, in_export, in_block));
});
AST_Scope.DEFMETHOD("def_variable", function(symbol){
AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export, in_block){
var def;
if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
def.object_destructuring_arg = symbol.object_destructuring_arg;
if (in_export) {
def.export = true;
}
if (in_block && symbol instanceof AST_SymbolBlockDeclaration) {
def.global = false;
} else {
def.global = !this.parent_scope;
}
} else {
def = this.variables.get(symbol.name);
def.orig.push(symbol);
@@ -325,8 +378,8 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
if (!is_identifier(m)) continue; // skip over "do"
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name reserved from mangling.
if (options.reserved.indexOf(m) >= 0) continue;
// shadow a name excepted from mangling.
if (options.except.indexOf(m) >= 0) continue;
// we must ensure that the mangled name does not shadow a name
// from some parent scope that is referenced in this or in
@@ -358,7 +411,8 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
});
AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options);
var def = this.definition();
return def && def.unmangleable(options);
});
// property accessors are not mangleable
@@ -398,11 +452,13 @@ AST_Symbol.DEFMETHOD("global", function(){
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
except : [],
eval : false,
ie8 : false,
keep_fnames : false,
reserved : [],
sort : false, // Ignored. Flag retained for backwards compatibility.
toplevel : false,
screw_ie8 : true,
keep_fnames : false,
keep_classnames : false
});
});
@@ -410,7 +466,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options);
// Never mangle arguments
options.reserved.push('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
@@ -421,7 +477,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (options.cache) {
this.globals.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) {
if (options.except.indexOf(symbol.name) < 0) {
to_mangle.push(symbol);
}
});
@@ -438,7 +494,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (node instanceof AST_Scope) {
var p = tw.parent(), a = [];
node.variables.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) {
if (options.except.indexOf(symbol.name) < 0) {
a.push(symbol);
}
});
@@ -451,13 +507,19 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.mangled_name = name;
return true;
}
if (!options.ie8 && node instanceof AST_SymbolCatch) {
var mangle_with_block_scope =
(options.screw_ie8 && node instanceof AST_SymbolCatch) ||
node instanceof AST_SymbolBlockDeclaration;
if (mangle_with_block_scope) {
to_mangle.push(node.definition());
return;
}
});
this.walk(tw);
to_mangle.forEach(function(def){ def.mangle(options) });
to_mangle.forEach(function(def){
if (def.destructuring && !def.destructuring.is_array) return;
def.mangle(options);
});
if (options.cache) {
options.cache.cname = this.cname;
@@ -508,21 +570,27 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
else if (node instanceof AST_With)
base54.consider("with");
else if (node instanceof AST_ObjectSetter)
base54.consider("set" + node.key);
base54.consider("set" + (typeof node.key === "string" ? node.key : ""));
else if (node instanceof AST_ObjectGetter)
base54.consider("get" + node.key);
else if (node instanceof AST_ObjectKeyVal)
base54.consider("get" + (typeof node.key === "string" ? node.key : ""));
else if (node instanceof AST_ObjectKeyVal && typeof node.key === "string")
base54.consider(node.key);
else if (node instanceof AST_ConciseMethod && typeof node.key === "string")
base54.consider(node.key);
else if (node instanceof AST_New)
base54.consider("new");
else if (node instanceof AST_This)
base54.consider("this");
else if (node instanceof AST_Super)
base54.consider("super");
else if (node instanceof AST_Try)
base54.consider("try");
else if (node instanceof AST_Catch)
base54.consider("catch");
else if (node instanceof AST_Finally)
base54.consider("finally");
else if (node instanceof AST_Yield)
base54.consider("yield");
else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)
@@ -572,3 +640,89 @@ var base54 = (function() {
};
return base54;
})();
AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
options = defaults(options, {
undeclared : false, // this makes a lot of noise
unreferenced : true,
assign_to_global : true,
func_arguments : true,
nested_defuns : true,
eval : true
});
var tw = new TreeWalker(function(node){
if (options.undeclared
&& node instanceof AST_SymbolRef
&& node.undeclared())
{
// XXX: this also warns about JS standard names,
// i.e. Object, Array, parseInt etc. Should add a list of
// exceptions.
AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.assign_to_global)
{
var sym = null;
if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef)
sym = node.left;
else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef)
sym = node.init;
if (sym
&& (sym.undeclared()
|| (sym.global() && sym.scope !== sym.definition().scope))) {
AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
name: sym.name,
file: sym.start.file,
line: sym.start.line,
col: sym.start.col
});
}
}
if (options.eval
&& node instanceof AST_SymbolRef
&& node.undeclared()
&& node.name == "eval") {
AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
}
if (options.unreferenced
&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
&& !(node instanceof AST_SymbolCatch)
&& node.unreferenced()) {
AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
type: node instanceof AST_Label ? "Label" : "Symbol",
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.func_arguments
&& node instanceof AST_Lambda
&& node.uses_arguments) {
AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
name: node.name ? node.name.name : "anonymous",
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.nested_defuns
&& node instanceof AST_Defun
&& !(tw.parent() instanceof AST_Scope)) {
AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
name: node.name.name,
type: tw.parent().TYPE,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
});
this.walk(tw);
});

View File

@@ -163,10 +163,18 @@ TreeTransformer.prototype = new TreeWalker;
if (self.value) self.value = self.value.transform(tw);
});
_(AST_Destructuring, function(self, tw) {
self.names = do_list(self.names, tw);
});
_(AST_Lambda, function(self, tw){
if (self.name) self.name = self.name.transform(tw);
self.argnames = do_list(self.argnames, tw);
self.body = do_list(self.body, tw);
if (self.body instanceof AST_Node) {
self.body = self.body.transform(tw);
} else {
self.body = do_list(self.body, tw);
}
});
_(AST_Call, function(self, tw){
@@ -174,8 +182,9 @@ TreeTransformer.prototype = new TreeWalker;
self.args = do_list(self.args, tw);
});
_(AST_Sequence, function(self, tw){
self.expressions = do_list(self.expressions, tw);
_(AST_Seq, function(self, tw){
self.car = self.car.transform(tw);
self.cdr = self.cdr.transform(tw);
});
_(AST_Dot, function(self, tw){
@@ -187,6 +196,10 @@ TreeTransformer.prototype = new TreeWalker;
self.property = self.property.transform(tw);
});
_(AST_Yield, function(self, tw){
if (self.expression) self.expression = self.expression.transform(tw);
});
_(AST_Unary, function(self, tw){
self.expression = self.expression.transform(tw);
});
@@ -211,7 +224,32 @@ TreeTransformer.prototype = new TreeWalker;
});
_(AST_ObjectProperty, function(self, tw){
if (self.key instanceof AST_Node) {
self.key = self.key.transform(tw);
}
self.value = self.value.transform(tw);
});
_(AST_Class, function(self, tw){
if (self.name) self.name = self.name.transform(tw);
if (self.extends) self.extends = self.extends.transform(tw);
self.properties = do_list(self.properties, tw);
});
_(AST_Expansion, function(self, tw){
self.expression = self.expression.transform(tw);
});
_(AST_TemplateString, function(self, tw) {
for (var i = 0; i < self.segments.length; i++) {
if (!(self.segments[i] instanceof AST_TemplateSegment)) {
self.segments[i] = self.segments[i].transform(tw);
}
}
});
_(AST_PrefixedTemplateString, function(self, tw) {
self.template_string = self.template_string.transform(tw);
});
})();

View File

@@ -126,11 +126,9 @@ function merge(obj, ext) {
return count;
};
function noop() {}
function noop() {};
function return_false() { return false; }
function return_true() { return true; }
function return_this() { return this; }
function return_null() { return null; }
var MAP = (function(){
function MAP(a, f, backwards) {
@@ -346,7 +344,7 @@ function first_in_statement(stack) {
for (var i = 0, p; p = stack.parent(i); i++) {
if (p instanceof AST_Statement && p.body === node)
return true;
if ((p instanceof AST_Sequence && p.expressions[0] === node) ||
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 ) ||

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": "3.0.1",
"version": "2.8.5",
"engines": {
"node": ">=0.8.0"
},
@@ -29,8 +29,10 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.9.0",
"source-map": "~0.5.1"
"async": "~0.2.6",
"source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.10.0"
},
"devDependencies": {
"acorn": "~0.6.0",
@@ -39,15 +41,13 @@
"estraverse": "~1.5.1",
"mocha": "~2.3.4"
},
"optionalDependencies": {
"uglify-to-browserify": "~1.0.0"
},
"browserify": {
"transform": [
"uglify-to-browserify"
]
},
"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"]

View File

@@ -7,12 +7,12 @@ var createHash = require("crypto").createHash;
var fork = require("child_process").fork;
var args = process.argv.slice(2);
if (!args.length) {
args.push("-mc");
args.push("-mc", "warnings=false");
}
args.push("--stats");
var urls = [
"https://code.jquery.com/jquery-3.2.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js",
"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",
@@ -24,57 +24,26 @@ var results = {};
var remaining = 2 * urls.length;
function done() {
if (!--remaining) {
var failures = [];
urls.forEach(function(url) {
var info = results[url];
console.log();
console.log(url);
var elapsed = 0;
console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) {
elapsed += 1e-3 * parseInt(time);
return "";
}));
console.log("Run-time:", elapsed.toFixed(3), "s");
console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes");
console.log("SHA1 sum:", info.sha1);
if (info.code) {
failures.push(url);
}
console.log(results[url].time);
console.log("SHA1:", results[url].sha1);
});
if (failures.length) {
console.error("Benchmark failed:");
failures.forEach(function(url) {
console.error(url);
});
process.exit(1);
}
}
}
urls.forEach(function(url) {
results[url] = {
input: 0,
output: 0,
log: ""
};
results[url] = { time: "" };
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
var uglifyjs = fork("bin/uglifyjs", args, { silent: true });
res.on("data", function(data) {
results[url].input += data.length;
}).pipe(uglifyjs.stdin);
uglifyjs.stdout.on("data", function(data) {
results[url].output += data.length;
}).pipe(createHash("sha1")).on("data", function(data) {
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].log += data;
});
uglifyjs.on("exit", function(code) {
results[url].code = code;
done();
});
results[url].time += data;
}).on("end", done)
});
});

67
test/compress/angular-inject.js vendored Normal file
View File

@@ -0,0 +1,67 @@
ng_inject_defun: {
options = {
angular: true
};
input: {
/*@ngInject*/
function Controller(dependency) {
return dependency;
}
}
expect: {
function Controller(dependency) {
return dependency;
}
Controller.$inject=['dependency']
}
}
ng_inject_assignment: {
options = {
angular: true
};
input: {
/*@ngInject*/
var Controller = function(dependency) {
return dependency;
}
}
expect: {
var Controller = function(dependency) {
return dependency;
}
Controller.$inject=['dependency']
}
}
ng_inject_inline: {
options = {
angular: true
};
input: {
angular.module('a').
factory('b',
/*@ngInject*/
function(dependency) {
return dependency;
}).
directive('c',
/*@ngInject*/
function(anotherDependency) {
return anotherDependency;
})
}
expect: {
angular.module('a').
factory('b',[
'dependency',
function(dependency) {
return dependency;
}]).
directive('c',[
'anotherDependency',
function(anotherDependency) {
return anotherDependency;
}])
}
}

View File

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

137
test/compress/arrow.js Normal file
View File

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

View File

@@ -2,7 +2,7 @@ ascii_only_true: {
options = {}
beautify = {
ascii_only : true,
ie8 : false,
screw_ie8 : true,
beautify : false,
}
input: {
@@ -20,7 +20,7 @@ ascii_only_false: {
options = {}
beautify = {
ascii_only : false,
ie8 : false,
screw_ie8 : true,
beautify : false,
}
input: {
@@ -33,3 +33,4 @@ ascii_only_false: {
}
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

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

File diff suppressed because it is too large Load Diff

View File

@@ -51,7 +51,6 @@ concat_2: {
"1" + "2" + "3"
);
}
expect_stdout: true
}
concat_3: {
@@ -80,7 +79,6 @@ concat_3: {
1 + 2 + "3" + "4" + "5"
);
}
expect_stdout: true
}
concat_4: {
@@ -109,7 +107,6 @@ concat_4: {
1 + "2" + "3" + "4" + "5"
);
}
expect_stdout: true
}
concat_5: {
@@ -138,7 +135,6 @@ concat_5: {
"1" + 2 + "3" + "4" + "5"
);
}
expect_stdout: true
}
concat_6: {
@@ -167,7 +163,6 @@ concat_6: {
"1" + "2" + "3" + "4" + "5"
);
}
expect_stdout: true
}
concat_7: {
@@ -193,7 +188,6 @@ concat_7: {
x += "foo"
);
}
expect_stdout: true
}
concat_8: {
@@ -219,5 +213,4 @@ concat_8: {
x += "foo"
);
}
expect_stdout: true
}

View File

@@ -797,221 +797,3 @@ no_evaluate: {
}
}
}
equality_conditionals_false: {
options = {
conditionals: false,
sequences: true,
}
input: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0);
f(0, true, 0);
f(1, 2, 3);
f(1, null, 3);
f(NaN);
f(NaN, "foo");
}
expect: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0),
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
f(NaN),
f(NaN, "foo");
}
expect_stdout: true
}
equality_conditionals_true: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0);
f(0, true, 0);
f(1, 2, 3);
f(1, null, 3);
f(NaN);
f(NaN, "foo");
}
expect: {
function f(a, b, c) {
console.log(
(b, a == a),
a == (b ? a : c),
(b, a != a),
a != (b ? a : c),
(b, a === a),
a === (b ? a : c),
(b, a !== a),
a !== (b ? a : c)
);
}
f(0, 0, 0),
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
f(NaN),
f(NaN, "foo");
}
expect_stdout: true
}
issue_1645_1: {
options = {
conditionals: true,
}
input: {
var a = 100, b = 10;
(b = a) ? a++ + (b += a) ? b += a : b += a : b ^= a;
console.log(a, b);
}
expect: {
var a = 100, b = 10;
(b = a) ? (a++ + (b += a), b += a) : b ^= a;
console.log(a,b);
}
expect_stdout: true
}
issue_1645_2: {
options = {
conditionals: true,
}
input: {
var a = 0;
function f() {
return a++;
}
f() ? a += 2 : a += 4;
console.log(a);
}
expect: {
var a = 0;
function f(){
return a++;
}
f() ? a += 2 : a += 4;
console.log(a);
}
expect_stdout: true
}
condition_symbol_matches_consequent: {
options = {
conditionals: true,
}
input: {
function foo(x, y) {
return x ? x : y;
}
function bar() {
return g ? g : h;
}
var g = 4;
var h = 5;
console.log(foo(3, null), foo(0, 7), foo(true, false), bar());
}
expect: {
function foo(x, y) {
return x || y;
}
function bar() {
return g || h;
}
var g = 4;
var h = 5;
console.log(foo(3, null), foo(0, 7), foo(true, false), bar());
}
expect_stdout: "3 7 true 4"
}
delete_conditional_1: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
side_effects: true,
}
input: {
console.log(delete (1 ? undefined : x));
console.log(delete (1 ? void 0 : x));
console.log(delete (1 ? Infinity : x));
console.log(delete (1 ? 1 / 0 : x));
console.log(delete (1 ? NaN : x));
console.log(delete (1 ? 0 / 0 : x));
}
expect: {
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
}
delete_conditional_2: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
keep_infinity: true,
side_effects: true,
}
input: {
console.log(delete (0 ? x : undefined));
console.log(delete (0 ? x : void 0));
console.log(delete (0 ? x : Infinity));
console.log(delete (0 ? x : 1 / 0));
console.log(delete (0 ? x : NaN));
console.log(delete (0 ? x : 0 / 0));
}
expect: {
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
}

View File

@@ -162,5 +162,4 @@ regexp_literal_not_const: {
while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]);
})();
}
expect_stdout: true
}

View File

@@ -87,7 +87,28 @@ dead_code_constant_boolean_should_warn_more: {
var x = 10, y;
var moo;
}
expect_stdout: true
}
dead_code_block_decls_die: {
options = {
dead_code : true,
conditionals : true,
booleans : true,
evaluate : true
};
input: {
if (0) {
let foo = 6;
const bar = 12;
class Baz {};
var qux;
}
console.log(foo, bar, Baz);
}
expect: {
var qux;
console.log(foo, bar, Baz);
}
}
dead_code_const_declaration: {
@@ -114,7 +135,6 @@ dead_code_const_declaration: {
var moo;
function bar() {}
}
expect_stdout: true
}
dead_code_const_annotation: {
@@ -125,7 +145,6 @@ dead_code_const_annotation: {
conditionals : true,
evaluate : true,
reduce_vars : true,
toplevel : true,
};
input: {
var unused;
@@ -142,7 +161,6 @@ dead_code_const_annotation: {
var moo;
function bar() {}
}
expect_stdout: true
}
dead_code_const_annotation_regex: {
@@ -166,7 +184,6 @@ dead_code_const_annotation_regex: {
var CONST_FOO_ANN = !1;
CONST_FOO_ANN && console.log('reachable');
}
expect_stdout: true
}
dead_code_const_annotation_complex_scope: {
@@ -177,7 +194,6 @@ dead_code_const_annotation_complex_scope: {
conditionals : true,
evaluate : true,
reduce_vars : true,
toplevel : true,
};
input: {
var unused_var;
@@ -212,47 +228,4 @@ dead_code_const_annotation_complex_scope: {
var meat = 'beef';
var pork = 'bad';
}
expect_stdout: true
}
try_catch_finally: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
var a = 1;
!function() {
try {
if (false) throw x;
} catch (a) {
var a = 2;
console.log("FAIL");
} finally {
a = 3;
console.log("PASS");
}
}();
try {
console.log(a);
} finally {
}
}
expect: {
var a = 1;
!function() {
var a;
a = 3;
console.log("PASS");
}();
try {
console.log(a);
} finally {
}
}
expect_stdout: [
"PASS",
"1",
]
}

View File

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

View File

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

View File

@@ -164,6 +164,87 @@ used_var_in_catch: {
}
}
unused_block_decls_in_catch: {
options = { unused: true };
input: {
function foo() {
try {
foo();
} catch(ex) {
let x = 10;
const y = 10;
class Zee {};
}
}
}
expect: {
function foo() {
try {
foo();
} catch(ex) {}
}
}
}
used_block_decls_in_catch: {
options = { unused: true };
input: {
function foo() {
try {
foo();
} catch(ex) {
let x = 10;
const y = 10;
class Zee {};
}
console.log(x, y, Zee);
}
}
expect: {
function foo() {
try {
foo();
} catch(ex) {}
console.log(x, y, Zee);
}
}
}
unused_block_decls: {
options = { unused: true };
input: {
function foo() {
{
const x;
}
{
let y;
}
console.log(x, y);
}
}
expect: {
function foo() {
console.log(x, y);
}
}
}
unused_keep_harmony_destructuring: {
options = { unused: true };
input: {
function foo() {
var {x, y} = foo;
var a = foo;
}
}
expect: {
function foo() {
var {x, y} = foo;
}
}
}
keep_fnames: {
options = { unused: true, keep_fnames: true, unsafe: true };
input: {
@@ -632,7 +713,7 @@ iife: {
}
expect: {
function f() {
b;
~function() {}(b);
}
}
}
@@ -700,450 +781,3 @@ issue_1539: {
}
}
}
vardef_value: {
options = {
keep_fnames: false,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function g(){
return x();
}
var a = g();
return a(42);
}
}
expect: {
function f() {
var a = function(){
return x();
}();
return a(42);
}
}
}
assign_binding: {
options = {
cascade: true,
side_effects: true,
unused: true,
}
input: {
function f() {
var a;
a = f.g, a();
}
}
expect: {
function f() {
(0, f.g)();
}
}
}
assign_chain: {
options = {
unused: true,
}
input: {
function f() {
var a, b;
x = a = y = b = 42;
}
}
expect: {
function f() {
x = y = 42;
}
}
}
issue_1583: {
options = {
keep_fargs: true,
reduce_vars: true,
unused: true,
}
input: {
function m(t) {
(function(e) {
t = e();
})(function() {
return (function(a) {
return a;
})(function(a) {});
});
}
}
expect: {
function m(t) {
(function(e) {
t = (function() {
return (function(a) {
return a;
})(function(a) {});
})();
})();
}
}
}
issue_1656: {
options = {
toplevel: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
for(var a=0;;);
}
expect_exact: "for (;;) ;"
}
issue_1709: {
options = {
unused: true,
}
input: {
console.log(
function x() {
var x = 1;
return x;
}(),
function y() {
const y = 2;
return y;
}(),
function z() {
function z() {}
return z;
}()
);
}
expect: {
console.log(
function() {
var x = 1;
return x;
}(),
function() {
const y = 2;
return y;
}(),
function() {
function z() {}
return z;
}()
);
}
expect_stdout: true
}
issue_1715_1: {
options = {
unused: true,
}
input: {
var a = 1;
function f() {
a++;
try {
x();
} catch (a) {
var a;
}
}
f();
console.log(a);
}
expect: {
var a = 1;
function f() {
a++;
try {
x();
} catch (a) {
var a;
}
}
f();
console.log(a);
}
expect_stdout: "1"
}
issue_1715_2: {
options = {
unused: true,
}
input: {
var a = 1;
function f() {
a++;
try {
x();
} catch (a) {
var a = 2;
}
}
f();
console.log(a);
}
expect: {
var a = 1;
function f() {
a++;
try {
x();
} catch (a) {
var a;
}
}
f();
console.log(a);
}
expect_stdout: "1"
}
issue_1715_3: {
options = {
unused: true,
}
input: {
var a = 1;
function f() {
a++;
try {
console;
} catch (a) {
var a = 2 + x();
}
}
f();
console.log(a);
}
expect: {
var a = 1;
function f() {
a++;
try {
console;
} catch (a) {
var a;
x();
}
}
f();
console.log(a);
}
expect_stdout: "1"
}
issue_1715_4: {
options = {
unused: true,
}
input: {
var a = 1;
!function a() {
a++;
try {
x();
} catch (a) {
var a;
}
}();
console.log(a);
}
expect: {
var a = 1;
!function() {
a++;
try {
x();
} catch (a) {
var a;
}
}();
console.log(a);
}
expect_stdout: "1"
}
delete_assign_1: {
options = {
booleans: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
console.log(delete (a = undefined));
console.log(delete (a = void 0));
console.log(delete (a = Infinity));
console.log(delete (a = 1 / 0));
console.log(delete (a = NaN));
console.log(delete (a = 0 / 0));
}
expect: {
console.log((void 0, !0));
console.log((void 0, !0));
console.log((1 / 0, !0));
console.log((1 / 0, !0));
console.log((NaN, !0));
console.log((0 / 0, !0));
}
expect_stdout: true
}
delete_assign_2: {
options = {
booleans: true,
keep_infinity: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
console.log(delete (a = undefined));
console.log(delete (a = void 0));
console.log(delete (a = Infinity));
console.log(delete (a = 1 / 0));
console.log(delete (a = NaN));
console.log(delete (a = 0 / 0));
}
expect: {
console.log((void 0, !0));
console.log((void 0, !0));
console.log((Infinity, !0));
console.log((1 / 0, !0));
console.log((NaN, !0));
console.log((0 / 0, !0));
}
expect_stdout: true
}
drop_var: {
options = {
toplevel: true,
unused: true,
}
input: {
var a;
console.log(a, b);
var a = 1, b = 2;
console.log(a, b);
var a = 3;
console.log(a, b);
}
expect: {
console.log(a, b);
var a = 1, b = 2;
console.log(a, b);
a = 3;
console.log(a, b);
}
expect_stdout: [
"undefined undefined",
"1 2",
"3 2",
]
}
issue_1830_1: {
options = {
unused: true,
}
input: {
!function() {
L: for (var b = console.log(1); !1;) continue L;
}();
}
expect: {
!function() {
L: for (console.log(1); !1;) continue L;
}();
}
expect_stdout: "1"
}
issue_1830_2: {
options = {
unused: true,
}
input: {
!function() {
L: for (var a = 1, b = console.log(a); --a;) continue L;
}();
}
expect: {
!function() {
var a = 1;
L: for (console.log(a); --a;) continue L;
}();
}
expect_stdout: "1"
}
issue_1838: {
options = {
join_vars: true,
loops: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
function f() {
var b = a;
while (c);
}
}
expect_exact: [
"function f() {",
" for (a; c; ) ;",
"}",
]
}
var_catch_toplevel: {
options = {
conditionals: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f() {
a--;
try {
a++;
} catch(a) {
if (a) var a;
var a = 10;
}
}
f();
}
expect: {
!function() {
a--;
try {
a++;
} catch(a) {
var a;
}
}();
}
}

View File

@@ -196,11 +196,10 @@ negative_zero: {
console.log(
-0,
0,
-1/0,
-1/0
1 / (-0),
1 / (-0)
);
}
expect_stdout: true
}
positive_zero: {
@@ -217,11 +216,104 @@ positive_zero: {
console.log(
0,
-0,
1/0,
1/0
1 / (0),
1 / (0)
);
}
expect_stdout: true
}
pow: {
options = { evaluate: true }
input: {
var a = 5 ** 3;
}
expect: {
var a = 125;
}
}
pow_sequence: {
options = {
evaluate: true
}
input: {
var a = 2 ** 3 ** 2;
}
expect: {
var a = 512;
}
}
pow_mixed: {
options = {
evaluate: true
}
input: {
var a = 5 + 2 ** 3 + 5;
var b = 5 * 3 ** 2;
var c = 5 ** 3 * 2;
var d = 5 ** +3;
}
expect: {
var a = 18;
var b = 45;
var c = 250;
var d = 125;
}
}
pow_with_right_side_evaluating_to_unary: {
options = {
evaluate: true
}
input: {
var a = (4 - 7) ** foo;
var b = ++bar ** 3;
var c = --baz ** 2;
}
expect_exact: "var a=(-3)**foo;var b=++bar**3;var c=--baz**2;"
}
pow_with_number_constants: {
options = {
evaluate: true
}
input: {
var a = 5 ** NaN;
/* NaN exponent results to NaN */
var b = 42 ** +0;
/* +0 exponent results to NaN */
var c = 42 ** -0;
/* -0 exponent results to NaN */
var d = NaN ** 1;
/* NaN with non-zero exponent is NaN */
var e = 2 ** Infinity;
/* abs(base) > 1 with Infinity as exponent is Infinity */
var f = 2 ** -Infinity;
/* abs(base) > 1 with -Infinity as exponent is +0 */
var g = (-7) ** (0.5);
var h = 2324334 ** 34343443;
var i = (-2324334) ** 34343443;
var j = 2 ** (-3);
var k = 2.0 ** -3;
var l = 2.0 ** (5 - 7);
var m = 3 ** -10; // Result will be 0.000016935087808430286, which is too long
}
expect: {
var a = NaN;
var b = 1;
var c = 1;
var d = NaN;
var e = Infinity;
var f = 0;
var g = NaN;
var h = Infinity;
var i = -Infinity;
var j = .125;
var k = .125;
var l = .25;
var m = 3 ** -10;
}
}
unsafe_constant: {
@@ -245,7 +337,6 @@ unsafe_constant: {
(void 0).a
);
}
expect_stdout: true
}
unsafe_object: {
@@ -269,7 +360,6 @@ unsafe_object: {
1..b + 1
);
}
expect_stdout: true
}
unsafe_object_nested: {
@@ -293,7 +383,6 @@ unsafe_object_nested: {
2
);
}
expect_stdout: true
}
unsafe_object_complex: {
@@ -317,7 +406,6 @@ unsafe_object_complex: {
2
);
}
expect_stdout: true
}
unsafe_object_repeated: {
@@ -341,7 +429,6 @@ unsafe_object_repeated: {
1..b + 1
);
}
expect_stdout: true
}
unsafe_object_accessor: {
@@ -391,7 +478,6 @@ unsafe_function: {
({a:{b:1},b:function(){}}).a.b + 1
);
}
expect_stdout: true
}
unsafe_integer_key: {
@@ -419,7 +505,6 @@ unsafe_integer_key: {
1["1"] + 1
);
}
expect_stdout: true
}
unsafe_integer_key_complex: {
@@ -447,7 +532,6 @@ unsafe_integer_key_complex: {
2
);
}
expect_stdout: true
}
unsafe_float_key: {
@@ -475,7 +559,6 @@ unsafe_float_key: {
1["3.14"] + 1
);
}
expect_stdout: true
}
unsafe_float_key_complex: {
@@ -503,7 +586,6 @@ unsafe_float_key_complex: {
2
);
}
expect_stdout: true
}
unsafe_array: {
@@ -539,7 +621,6 @@ unsafe_array: {
(void 0)[1] + 1
);
}
expect_stdout: true
}
unsafe_string: {
@@ -567,7 +648,6 @@ unsafe_string: {
"11"
);
}
expect_stdout: true
}
unsafe_array_bad_index: {
@@ -589,7 +669,6 @@ unsafe_array_bad_index: {
[1, 2, 3, 4][3.14] + 1
);
}
expect_stdout: true
}
unsafe_string_bad_index: {
@@ -611,7 +690,6 @@ unsafe_string_bad_index: {
"1234"[3.14] + 1
);
}
expect_stdout: true
}
unsafe_prototype_function: {
@@ -656,9 +734,10 @@ call_args: {
expect: {
const a = 1;
console.log(1);
+(1, 1);
+function(a) {
return 1;
}(1);
}
expect_stdout: true
}
call_args_drop_param: {
@@ -678,9 +757,10 @@ call_args_drop_param: {
expect: {
const a = 1;
console.log(1);
+(b, 1);
+function() {
return 1;
}(b);
}
expect_stdout: true
}
in_boolean_context: {
@@ -718,274 +798,4 @@ in_boolean_context: {
(foo(), !1)
);
}
expect_stdout: true
}
unsafe_charAt: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234" + 1,
"1234".charAt(0) + 1,
"1234".charAt(6 - 5) + 1,
("12" + "34").charAt(0) + 1,
("12" + "34").charAt(6 - 5) + 1,
[1, 2, 3, 4].join("").charAt(0) + 1
);
}
expect: {
console.log(
"12341",
"11",
"21",
"11",
"21",
"11"
);
}
expect_stdout: true
}
unsafe_charAt_bad_index: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234".charAt() + 1,
"1234".charAt("a") + 1,
"1234".charAt(3.14) + 1
);
}
expect: {
console.log(
"11",
"11",
"41"
);
}
expect_stdout: true
}
unsafe_charAt_noop: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
s.charAt(0),
"string".charAt(x)
);
}
expect: {
console.log(
s.charAt(0),
"string".charAt(x)
);
}
}
issue_1649: {
options = {
evaluate: true,
}
input: {
console.log(-1 + -1);
}
expect: {
console.log(-2);
}
expect_stdout: "-2";
}
issue_1760_1: {
options = {
evaluate: true,
}
input: {
!function(a) {
try {
throw 0;
} catch (NaN) {
a = +"foo";
}
console.log(a);
}();
}
expect: {
!function(a) {
try {
throw 0;
} catch (NaN) {
a = 0 / 0;
}
console.log(a);
}();
}
expect_stdout: "NaN"
}
issue_1760_2: {
options = {
evaluate: true,
keep_infinity: true,
}
input: {
!function(a) {
try {
throw 0;
} catch (Infinity) {
a = 123456789 / 0;
}
console.log(a);
}();
}
expect: {
!function(a) {
try {
throw 0;
} catch (Infinity) {
a = 1 / 0;
}
console.log(a);
}();
}
expect_stdout: "Infinity"
}
delete_expr_1: {
options = {
booleans: true,
evaluate: true,
}
input: {
console.log(delete undefined);
console.log(delete void 0);
console.log(delete Infinity);
console.log(delete (1 / 0));
console.log(delete NaN);
console.log(delete (0 / 0));
}
expect: {
console.log(delete undefined);
console.log((void 0, !0));
console.log(delete Infinity);
console.log((1 / 0, !0));
console.log(delete NaN);
console.log((0 / 0, !0));
}
expect_stdout: true
}
delete_expr_2: {
options = {
booleans: true,
evaluate: true,
keep_infinity: true,
}
input: {
console.log(delete undefined);
console.log(delete void 0);
console.log(delete Infinity);
console.log(delete (1 / 0));
console.log(delete NaN);
console.log(delete (0 / 0));
}
expect: {
console.log(delete undefined);
console.log((void 0, !0));
console.log(delete Infinity);
console.log((1 / 0, !0));
console.log(delete NaN);
console.log((0 / 0, !0));
}
expect_stdout: true
}
delete_binary_1: {
options = {
booleans: true,
evaluate: true,
side_effects: true,
}
input: {
console.log(delete (true && undefined));
console.log(delete (true && void 0));
console.log(delete (true && Infinity));
console.log(delete (true && (1 / 0)));
console.log(delete (true && NaN));
console.log(delete (true && (0 / 0)));
}
expect: {
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
}
delete_binary_2: {
options = {
booleans: true,
evaluate: true,
keep_infinity: true,
side_effects: true,
}
input: {
console.log(delete (false || undefined));
console.log(delete (false || void 0));
console.log(delete (false || Infinity));
console.log(delete (false || (1 / 0)));
console.log(delete (false || NaN));
console.log(delete (false || (0 / 0)));
}
expect: {
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
}
Infinity_NaN_undefined_LHS: {
beautify = {
beautify: true,
}
input: {
function f() {
Infinity = Infinity;
++Infinity;
Infinity--;
NaN *= NaN;
++NaN;
NaN--;
undefined |= undefined;
++undefined;
undefined--;
}
}
expect_exact: [
"function f() {",
" Infinity = 1 / 0;",
" ++Infinity;",
" Infinity--;",
" NaN *= NaN;",
" ++NaN;",
" NaN--;",
" undefined |= void 0;",
" ++undefined;",
" undefined--;",
"}",
]
}

View File

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

View File

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

View File

@@ -35,11 +35,10 @@ iifes_returning_constants_keep_fargs_true: {
console.log("okay");
console.log(123);
console.log(void 0);
console.log(2);
console.log(6);
console.log((a(), b(), 6));
console.log(function(x,y,z){return 2}(1,2,3));
console.log(function(x,y){return 6}(2,3));
console.log(function(x, y){return 6}(2,3,a(),b()));
}
expect_stdout: true
}
iifes_returning_constants_keep_fargs_false: {
@@ -72,98 +71,6 @@ iifes_returning_constants_keep_fargs_false: {
console.log(void 0);
console.log(2);
console.log(6);
console.log((a(), b(), 6));
}
expect_stdout: true
}
issue_485_crashing_1530: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
(function(a) {
if (true) return;
var b = 42;
})(this);
}
expect: {
this, void 0;
console.log(function(){return 6}(a(),b()));
}
}
issue_1841_1: {
options = {
keep_fargs: false,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
var b = 10;
!function(arg) {
for (var key in "hi")
var n = arg.baz, n = [ b = 42 ];
}(--b);
console.log(b);
}
expect: {
var b = 10;
!function() {
for (var key in "hi")
b = 42;
}(--b);
console.log(b);
}
expect_exact: "42"
}
issue_1841_2: {
options = {
keep_fargs: false,
pure_getters: false,
reduce_vars: true,
unused: true,
}
input: {
var b = 10;
!function(arg) {
for (var key in "hi")
var n = arg.baz, n = [ b = 42 ];
}(--b);
console.log(b);
}
expect: {
var b = 10;
!function(arg) {
for (var key in "hi")
arg.baz, b = 42;
}(--b);
console.log(b);
}
expect_exact: "42"
}
function_returning_constant_literal: {
options = {
reduce_vars: true,
unsafe: true,
toplevel: true,
evaluate: true,
cascade: true,
unused: true,
}
input: {
function greeter() {
return { message: 'Hello there' };
}
var greeting = greeter();
console.log(greeting.message);
}
expect: {
console.log("Hello there");
}
expect_stdout: "Hello there"
}

View File

@@ -145,18 +145,3 @@ mixed: {
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]',
]
}
issue_1801: {
options = {
booleans: true,
global_defs: {
"CONFIG.FOO.BAR": true,
},
}
input: {
console.log(CONFIG.FOO.BAR);
}
expect: {
console.log(!0);
}
}

288
test/compress/harmony.js Normal file
View File

@@ -0,0 +1,288 @@
arrow_function_parens: {
input: {
something && (() => {});
}
expect_exact: "something&&(()=>{});"
}
arrow_function_parens_2: {
input: {
(() => null)();
}
expect_exact: "(()=>null)();"
}
typeof_arrow_functions: {
options = {
evaluate: true
}
input: {
var foo = typeof (x) => null;
}
expect_exact: "var foo=\"function\";"
}
classes: {
input: {
class SomeClass {
constructor() {
};
foo() {};
};
class NoSemi {
constructor(...args) {
}
foo() {}
};
class ChildClass extends SomeClass {};
var asExpression = class AsExpression {};
var nameless = class {};
}
expect_exact: "class SomeClass{constructor(){}foo(){}}class NoSemi{constructor(...args){}foo(){}}class ChildClass extends SomeClass{}var asExpression=class AsExpression{};var nameless=class{};"
}
class_statics: {
input: {
x = class {
static staticMethod() {}
static get foo() {}
static set bar() {}
static() { /* "static" can be a method name! */ }
get() { /* "get" can be a method name! */ }
set() { /* "set" can be a method name! */ }
}
}
expect_exact: "x=class{static staticMethod(){}static get foo(){}static set bar(){}static(){}get(){}set(){}};"
}
class_name_can_be_mangled: {
mangle = { };
input: {
function x() {
class Foo {
}
var class1 = Foo
var class2 = class Bar {}
}
}
expect: {
function x() {
class a { }
var n = a
var r = class a {}
}
}
}
class_name_can_be_preserved: {
mangle = {
keep_classnames: true
}
input: {
function x() {
(class Baz { });
class Foo {};
}
}
expect: {
function x() {
(class Baz { });
class Foo {};
}
}
}
classes_can_have_generators: {
input: {
class Foo {
*bar() {}
static *baz() {}
}
}
expect: {
class Foo {
*bar() {}
static *baz() {}
}
}
}
classes_can_have_computed_generators: {
input: {
class C4 {
*['constructor']() {}
}
}
expect: {
class C4 {
*['constructor']() {}
}
}
}
classes_can_have_computed_static: {
input: {
class C4 {
static ['constructor']() {}
}
}
expect: {
class C4 {
static ['constructor']() {}
}
}
}
class_methods_and_getters_with_keep_quoted_props_enabled: {
beautify = {
quote_style: 3,
keep_quoted_props: true,
}
input: {
class clss {
a() {}
"b"() {}
get c() { return "c"}
get "d"() { return "d"}
set e(a) { doSomething(a); }
set 'f'(a) { doSomething(b); }
static g() {}
static "h"() {}
}
}
expect_exact: 'class clss{a(){}"b"(){}get c(){return"c"}get"d"(){return"d"}set e(a){doSomething(a)}set\'f\'(a){doSomething(b)}static g(){}static"h"(){}}'
}
new_target: {
input: {
new.target;
new.target.name;
}
expect_exact: "new.target;new.target.name;"
}
number_literals: {
input: {
0b1001;
0B1001;
0o11;
0O11;
}
expect: {
9;
9;
9;
9;
}
}
import_statement: {
input: {
import "mod-name";
import Foo from "bar";
import { Bar, Baz } from 'lel';
import Bar, { Foo } from 'lel';
import { Bar as kex, Baz as food } from 'lel';
}
expect_exact: "import\"mod-name\";import Foo from\"bar\";import{Bar,Baz}from\"lel\";import Bar,{Foo}from\"lel\";import{Bar as kex,Baz as food}from\"lel\";"
}
export_statement: {
input: {
export default 1;
export var foo = 4;
export let foo = 6;
export const foo = 6;
export function foo() {};
export class foo { };
}
expect_exact: "export default 1;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
}
import_statement_mangling: {
mangle = { toplevel: true };
input: {
import Foo from "foo";
import Bar, {Food} from "lel";
import {What as Whatever} from "lel";
Foo();
Bar();
Food();
Whatever();
}
expect: {
import l from "foo";
import e, {Food as o} from "lel";
import {What as f} from "lel";
l();
e();
o();
f();
}
}
export_statement_mangling: {
mangle = { };
input: {
export var foo = 6;
export function bar() { }
export class Baz { }
bar(foo, Baz)
}
expect: {
export var foo = 6;
export function bar() { }
export class Baz { }
bar(foo, Baz)
}
}
// https://github.com/mishoo/UglifyJS2/issues/1021
regression_for_of_const: {
input: {
for (const x of y) {}
for (const x in y) {}
}
expect: {
for (const x of y);for (const x in y);
}
}
// Fabio: My patches accidentally caused a crash whenever
// there's an extraneous set of parens around an object.
regression_cannot_destructure: {
input: {
var x = ({ x : 3 });
x(({ x: 3 }));
}
expect_exact: "var x={x:3};x({x:3});";
}
regression_cannot_use_of: {
input: {
function of() {
}
var of = "is a valid variable name";
of = { of: "is ok" };
x.of;
of: foo()
}
expect: {
function of(){}
var of="is a valid variable name";
of={of:"is ok"};
x.of;
foo(); /* Label statement missing? No prob. */
}
}
fat_arrow_as_param: {
input: {
foo(x => x);
foo(x => x, y => y);
foo(x => (x, x));
foo(x => (x, x), y => (y, y));
}
expect_exact: "foo(x=>x);foo(x=>x,y=>y);foo(x=>(x,x));foo(x=>(x,x),y=>(y,y));"
}

85
test/compress/hoist.js Normal file
View File

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

View File

@@ -47,9 +47,34 @@ html_comment_in_greater_than_or_equal: {
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"}';
}
html_comment_after_multiline_comment: {
input: {
var foo; /*
*/--> var bar;
var foobar;
}
expect_exact: "var foo;var foobar;"
}

View File

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

View File

@@ -39,7 +39,7 @@ non_hoisted_function_after_return_2a: {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2, warnings: "verbose"
collapse_vars: false, passes: 2
}
input: {
function foo(x) {
@@ -75,7 +75,7 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]"
]
}
@@ -114,5 +114,8 @@ non_hoisted_function_after_return_2b: {
"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,30 @@
issue_1043: {
options = {
side_effects: true
};
input: {
function* range(start = 0, end = null, step = 1) {
if (end == null) {
end = start;
start = 0;
}
for (let i = start; i < end; i += step) {
yield i;
}
}
}
expect: {
function* range(start = 0, end = null, step = 1) {
if (null == end) {
end = start;
start = 0;
}
for (let i = start; i < end; i += step)
yield i;
}
}
}

View File

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

View File

@@ -193,15 +193,13 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
cascade: true,
side_effects: true,
sequences: false,
keep_infinity: false,
}
input: {
var f = console.log;
var o = {
undefined : 3,
NaN : 4,
Infinity : 5,
};
}
if (o) {
f(undefined, void 0);
f(NaN, 0/0);
@@ -218,88 +216,25 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
}
}
expect: {
var f = console.log, o = {
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(-(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(-Infinity, -(1/0));
f(9 + undefined, 9 + void 0);
}
}
expect_stdout: true
}
assorted_Infinity_NaN_undefined_in_with_scope_keep_infinity: {
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,
keep_infinity: true,
}
input: {
var f = console.log;
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 f = console.log, o = {
undefined : 3,
NaN : 4,
Infinity : 5
};
if (o) {
f(void 0, void 0);
f(NaN, NaN);
f(Infinity, 1/0);
f(-Infinity, -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);
}
}
expect_stdout: true
}

View File

@@ -49,3 +49,4 @@ mangle_keep_fnames_true: {
}
}
}

View File

@@ -0,0 +1,76 @@
issue_1212_debug_false: {
options = {
global_defs : { DEBUG: false },
sequences : true,
properties : true,
dead_code : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
loops : true,
unused : true,
hoist_funs : true,
keep_fargs : true,
if_return : true,
join_vars : true,
cascade : true,
side_effects : true,
}
input: {
class foo {
bar() {
if (DEBUG)
console.log("DEV");
else
console.log("PROD");
}
}
new foo().bar();
}
expect: {
class foo{
bar() { console.log("PROD") }
}
(new foo).bar();
}
}
issue_1212_debug_true: {
options = {
global_defs : { DEBUG: true },
sequences : true,
properties : true,
dead_code : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
loops : true,
unused : true,
hoist_funs : true,
keep_fargs : true,
if_return : true,
join_vars : true,
cascade : true,
side_effects : true,
}
input: {
class foo {
bar() {
if (DEBUG)
console.log("DEV");
else
console.log("PROD");
}
}
new foo().bar();
}
expect: {
class foo{
bar() { console.log("DEV") }
}
(new foo).bar();
}
}

View File

@@ -154,12 +154,12 @@ should_warn: {
"WARN: Boolean || always true [test/compress/issue-1261.js:129,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:129,23]",
"WARN: Condition always true [test/compress/issue-1261.js:129,23]",
"WARN: Condition left of || always true [test/compress/issue-1261.js:130,8]",
"WARN: 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: Condition left of && always false [test/compress/issue-1261.js:132,8]",
"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]",

View File

@@ -46,5 +46,4 @@ string_plus_optimization: {
}
foo();
}
expect_stdout: true
}

View File

@@ -1,6 +1,6 @@
issue_1321_no_debug: {
mangle_props = {
keep_quoted: true
ignore_quoted: true
}
input: {
var x = {};
@@ -14,12 +14,11 @@ issue_1321_no_debug: {
x["a"] = 2 * x.b;
console.log(x.b, x["a"]);
}
expect_stdout: true
}
issue_1321_debug: {
mangle_props = {
keep_quoted: true,
ignore_quoted: true,
debug: ""
}
input: {
@@ -34,12 +33,11 @@ issue_1321_debug: {
x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]);
}
expect_stdout: true
}
issue_1321_with_quoted: {
mangle_props = {
keep_quoted: false
ignore_quoted: false
}
input: {
var x = {};
@@ -53,5 +51,4 @@ issue_1321_with_quoted: {
x["b"] = 2 * x.a;
console.log(x.a, x["b"]);
}
expect_stdout: true
}

View File

@@ -2,7 +2,6 @@
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true
}
@@ -20,7 +19,12 @@ unsafe_undefined: {
expect: {
function f(n) {
return function() {
return a ? b : c ? d : n;
if (a)
return b;
if (c)
return d;
else
return n;
};
}
}
@@ -28,7 +32,6 @@ unsafe_undefined: {
keep_fnames: {
options = {
conditionals: true,
if_return: true,
unsafe: true
}
@@ -54,7 +57,12 @@ keep_fnames: {
function n(n) {
return n * n;
}
return a ? b : c ? d : r;
if (a)
return b;
if (c)
return d;
else
return r;
};
}
}

View File

@@ -23,7 +23,7 @@ typeof_eq_undefined: {
typeof_eq_undefined_ie8: {
options = {
comparisons: true,
ie8: true,
screw_ie8: false
}
input: {
var a = typeof b != "undefined";

View File

@@ -42,5 +42,4 @@ conditional_false_stray_else_in_loop: {
}
}
expect_exact: "for(var i=1;i<=4;++i)if(!(i<=2))console.log(i);"
expect_stdout: true
}

View File

@@ -1,19 +0,0 @@
inner_reference: {
options = {
side_effects: true,
}
input: {
!function f(a) {
return a && f(a - 1) + a;
}(42);
!function g(a) {
return a;
}(42);
}
expect: {
!function f(a) {
return a && f(a - 1) + a;
}(42);
!void 0;
}
}

View File

@@ -1,99 +0,0 @@
screw_ie8: {
options = {
ie8: false,
}
mangle = {
ie8: false,
}
input: {
try { throw "foo"; } catch (x) { console.log(x); }
}
expect_exact: 'try{throw"foo"}catch(o){console.log(o)}'
expect_stdout: [
"foo"
]
}
support_ie8: {
options = {
ie8: true,
}
mangle = {
ie8: true,
}
input: {
try { throw "foo"; } catch (x) { console.log(x); }
}
expect_exact: 'try{throw"foo"}catch(x){console.log(x)}'
expect_stdout: "foo"
}
safe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: false,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}(1)());
}
expect: {
var a, c;
console.log(function(n) {
return function() {
return a ? b : c ? d : void 0;
};
}(1)());
}
expect_stdout: true
}
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}()());
}
expect: {
var a, c;
console.log(function(n) {
return function() {
return a ? b : c ? d : n;
};
}()());
}
expect_stdout: true
}
runtime_error: {
input: {
const a = 1;
console.log(a++);
}
expect: {
const a = 1;
console.log(a++);
}
expect_stdout: true
}

View File

@@ -1,52 +0,0 @@
chained_evaluation_1: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = 1;
(function() {
var b = a, c;
c = f(b);
c.bar = b;
})();
})();
}
expect: {
(function() {
(function() {
f(1).bar = 1;
})();
})();
}
}
chained_evaluation_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = "long piece of string";
(function() {
var b = a, c;
c = f(b);
c.bar = b;
})();
})();
}
expect: {
(function() {
(function() {
var b = "long piece of string";
f(b).bar = b;
})();
})();
}
}

View File

@@ -1,88 +0,0 @@
issue_1639_1: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
join_vars: true,
loops: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
var L1 = 5;
while (--L1 > 0) {
if ((--b), false) {
if (b) {
var ignore = 0;
}
}
}
console.log(a, b);
}
expect: {
for (var a = 100, b = 10, L1 = 5; --L1 > 0;)
if (--b, !1) var ignore = 0;
console.log(a, b);
}
expect_stdout: true
}
issue_1639_2: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
function f19() {
if (++a, false)
if (a)
if (++a);
}
f19();
console.log(a, b);
}
expect: {
var a = 100, b = 10;
function f19() {
++a, 1;
}
f19(),
console.log(a, b);
}
expect_stdout: true
}
issue_1639_3: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
a++ && false && a ? 0 : 0;
console.log(a, b);
}
expect: {
var a = 100, b = 10;
a++,
console.log(a, b);
}
expect_stdout: true
}

View File

@@ -1,45 +0,0 @@
f7: {
options = {
booleans: true,
cascade: true,
collapse_vars: true,
comparisons: true,
conditionals: true,
dead_code: true,
drop_debugger: true,
evaluate: true,
hoist_funs: true,
if_return: true,
join_vars: true,
loops: true,
negate_iife: true,
passes: 3,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
var a = 100, b = 10;
function f22464() {
var brake146670 = 5;
while (((b = a) ? !a : ~a ? null : b += a) && --brake146670 > 0) {
}
}
f22464();
console.log(a, b);
}
expect_exact: [
"var b = 10;",
"",
"!function() {",
" for (;b = 100, !1; ) ;",
"}(), console.log(100, b);",
]
expect_stdout: true
}

View File

@@ -1,161 +0,0 @@
side_effects_catch: {
options = {
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f() {
function g() {
try {
throw 0;
} catch (e) {
console.log("PASS");
}
}
g();
}
f();
}
expect: {
function f() {
(function() {
try {
throw 0;
} catch (e) {
console.log("PASS");
}
})();
}
f();
}
expect_stdout: "PASS"
}
side_effects_else: {
options = {
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f(x) {
function g() {
if (x);
else console.log("PASS");
}
g();
}
f(0);
}
expect: {
function f(x) {
(function() {
if (x);
else console.log("PASS");
})();
}
f(0);
}
expect_stdout: "PASS"
}
side_effects_finally: {
options = {
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f() {
function g() {
try {
x();
} catch (e) {
} finally {
console.log("PASS");
}
}
g();
}
f();
}
expect: {
function f() {
(function() {
try {
x();
} catch (e) {
} finally {
console.log("PASS");
}
})();
}
f();
}
expect_stdout: "PASS"
}
side_effects_label: {
options = {
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f(x) {
function g() {
L: {
console.log("PASS");
break L;
}
}
g();
}
f(0);
}
expect: {
function f(x) {
(function() {
L: {
console.log("PASS");
break L;
}
})();
}
f(0);
}
expect_stdout: "PASS"
}
side_effects_switch: {
options = {
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f() {
function g() {
switch (0) {
default:
case console.log("PASS"):
}
}
g();
}
f();
}
expect: {
function f() {
(function() {
switch (0) {
default:
case console.log("PASS"):
}
})();
}
f();
}
expect_stdout: "PASS"
}

View File

@@ -1,347 +0,0 @@
mangle_catch: {
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(o){a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_ie8: {
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(args){a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_var: {
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(o){var a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_var_ie8: {
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(args){var a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_toplevel: {
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_ie8_toplevel: {
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_var_toplevel: {
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_var_ie8_toplevel: {
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_1: {
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_ie8: {
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_toplevel: {
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_ie8_toplevel: {
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_2: {
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_ie8: {
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_toplevel: {
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_ie8_toplevel: {
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "undefined"
}

View File

@@ -1,97 +0,0 @@
function_iife_catch: {
mangle = {
ie8: false,
}
input: {
function f(n) {
!function() {
try {
throw 0;
} catch (n) {
var a = 1;
console.log(n, a);
}
}();
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
expect_stdout: "0 1"
}
function_iife_catch_ie8: {
mangle = {
ie8: true,
}
input: {
function f(n) {
!function() {
try {
throw 0;
} catch (n) {
var a = 1;
console.log(n, a);
}
}();
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
expect_stdout: "0 1"
}
function_catch_catch: {
mangle = {
ie8: false,
}
input: {
var o = 0;
function f() {
try {
throw 1;
} catch (c) {
try {
throw 2;
} catch (o) {
var o = 3;
console.log(o);
}
}
console.log(o);
}
f();
}
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_stdout: [
"3",
"undefined",
]
}
function_catch_catch_ie8: {
mangle = {
ie8: true,
}
input: {
var o = 0;
function f() {
try {
throw 1;
} catch (c) {
try {
throw 2;
} catch (o) {
var o = 3;
console.log(o);
}
}
console.log(o);
}
f();
}
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_stdout: [
"3",
"undefined",
]
}

View File

@@ -1,54 +0,0 @@
case_1: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
var a = 0, b = 1;
switch (true) {
case a, true:
default:
b = 2;
case true:
}
console.log(a, b);
}
expect: {
var a = 0, b = 1;
switch (true) {
case a, true:
b = 2;
}
console.log(a, b);
}
expect_stdout: "0 2"
}
case_2: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
var a = 0, b = 1;
switch (0) {
default:
b = 2;
case a:
a = 3;
case 0:
}
console.log(a, b);
}
expect: {
var a = 0, b = 1;
switch (0) {
case a:
a = 3;
}
console.log(a, b);
}
expect_stdout: "3 1"
}

View File

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

View File

@@ -1,15 +0,0 @@
unary_prefix: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
var x = -(2 / 3);
return x;
}());
}
expect_exact: "console.log(-2/3);"
expect_stdout: true
}

View File

@@ -1,134 +0,0 @@
iife_for: {
options = {
negate_iife: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
function g() {
L: for (;;) break L;
}
g();
}
f();
}
expect: {
!function() {
!function() {
L: for (;;) break L;
}();
}();
}
}
iife_for_in: {
options = {
negate_iife: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
function g() {
L: for (var a in x) break L;
}
g();
}
f();
}
expect: {
!function() {
!function() {
L: for (var a in x) break L;
}();
}();
}
}
iife_do: {
options = {
negate_iife: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
function g() {
L: do {
break L;
} while (1);
}
g();
}
f();
}
expect: {
!function() {
!function() {
L: do {
break L;
} while (1);
}();
}();
}
}
iife_while: {
options = {
negate_iife: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
function g() {
L: while (1) break L;
}
g();
}
f();
}
expect: {
!function() {
!function() {
L: while (1) break L;
}();
}();
}
}
label_do: {
options = {
evaluate: true,
loops: true,
}
input: {
L: do {
continue L;
} while (0);
}
expect: {
L: do {
continue L;
} while (0);
}
}
label_while: {
options = {
evaluate: true,
dead_code: true,
loops: true,
}
input: {
function f() {
L: while (0) continue L;
}
}
expect_exact: "function f(){L:;}"
}

View File

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

View File

@@ -1,55 +0,0 @@
collapse: {
options = {
cascade: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
var a;
a = typeof b === 'function' ? b() : b;
return a !== undefined && c();
}
function f2(b) {
var a;
b = c();
a = typeof b === 'function' ? b() : b;
return 'stirng' == typeof a && d();
}
function f3(c) {
var a;
a = b(a / 2);
if (a < 0) {
a++;
++c;
return c / 2;
}
}
function f4(c) {
var a;
a = b(a / 2);
if (a < 0) {
a++;
c++;
return c / 2;
}
}
}
expect: {
function f1() {
return void 0 !== ('function' === typeof b ? b() : b) && c();
}
function f2(b) {
return b = c(), 'stirng' == typeof ('function' === typeof b ? b() : b) && d();
}
function f3(c) {
var a;
if ((a = b(a / 2)) < 0) return a++, ++c / 2;
}
function f4(c) {
var a;
if ((a = b(a / 2)) < 0) return a++, ++c / 2;
}
}
}

View File

@@ -6,7 +6,7 @@ NaN_and_Infinity_must_have_parens: {
}
expect: {
(1/0).toString();
NaN.toString();
NaN.toString(); // transformation to 0/0 dropped
}
}
@@ -23,135 +23,3 @@ NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: {
NaN.toString();
}
}
NaN_and_Infinity_must_have_parens_evaluate: {
options = {
evaluate: true,
}
input: {
(123456789 / 0).toString();
(+"foo").toString();
}
expect: {
(1/0).toString();
NaN.toString();
}
}
NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined_evaluate: {
options = {
evaluate: true,
}
input: {
var Infinity, NaN;
(123456789 / 0).toString();
(+"foo").toString();
}
expect: {
var Infinity, NaN;
(1/0).toString();
(0/0).toString();
}
}
beautify_off_1: {
options = {
evaluate: true,
}
beautify = {
beautify: false,
}
input: {
var NaN;
console.log(
null,
undefined,
Infinity,
NaN,
Infinity * undefined,
Infinity.toString(),
NaN.toString(),
(Infinity * undefined).toString()
);
}
expect_exact: "var NaN;console.log(null,void 0,1/0,NaN,0/0,(1/0).toString(),NaN.toString(),(0/0).toString());"
expect_stdout: true
}
beautify_off_2: {
options = {
evaluate: true,
}
beautify = {
beautify: false,
}
input: {
console.log(
null.toString(),
undefined.toString()
);
}
expect_exact: "console.log(null.toString(),(void 0).toString());"
}
beautify_on_1: {
options = {
evaluate: true,
}
beautify = {
beautify: true,
}
input: {
var NaN;
console.log(
null,
undefined,
Infinity,
NaN,
Infinity * undefined,
Infinity.toString(),
NaN.toString(),
(Infinity * undefined).toString()
);
}
expect_exact: [
"var NaN;",
"",
"console.log(null, void 0, 1 / 0, NaN, 0 / 0, (1 / 0).toString(), NaN.toString(), (0 / 0).toString());",
]
expect_stdout: true
}
beautify_on_2: {
options = {
evaluate: true,
}
beautify = {
beautify: true,
}
input: {
console.log(
null.toString(),
undefined.toString()
);
}
expect_exact: "console.log(null.toString(), (void 0).toString());"
}
issue_1724: {
input: {
var a = 0;
++a % Infinity | Infinity ? a++ : 0;
console.log(a);
}
expect_exact: "var a=0;++a%(1/0)|1/0?a++:0;console.log(a);"
expect_stdout: "2"
}
issue_1725: {
input: {
([].length === 0) % Infinity ? console.log("PASS") : console.log("FAIL");
}
expect_exact: '(0===[].length)%(1/0)?console.log("PASS"):console.log("FAIL");'
expect_stdout: "PASS"
}

View File

@@ -1,320 +0,0 @@
cond_5: {
options = {
conditionals: true,
expression: true,
}
input: {
if (some_condition()) {
if (some_other_condition()) {
do_something();
} else {
alternate();
}
} else {
alternate();
}
if (some_condition()) {
if (some_other_condition()) {
do_something();
}
}
}
expect: {
some_condition() && some_other_condition() ? do_something() : alternate();
if (some_condition() && some_other_condition()) do_something();
}
}
dead_code_const_annotation_regex: {
options = {
booleans : true,
conditionals : true,
dead_code : true,
evaluate : true,
expression : true,
loops : 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;
if (CONST_FOO_ANN) console.log('reachable');
}
expect_stdout: true
}
drop_console_2: {
options = {
drop_console: true,
expression: 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_value: {
options = {
expression: true,
side_effects: true,
}
input: {
(1, [2, foo()], 3, {a:1, b:bar()});
}
expect: {
foo(), {a:1, b:bar()};
}
}
wrongly_optimized: {
options = {
conditionals: true,
booleans: true,
evaluate: true,
expression: true,
}
input: {
function func() {
foo();
}
if (func() || true) {
bar();
}
}
expect: {
function func() {
foo();
}
// TODO: optimize to `func(), bar()`
if (func(), !0) bar();
}
}
negate_iife_1: {
options = {
expression: true,
negate_iife: true,
}
input: {
(function(){ stuff() })();
}
expect: {
(function(){ stuff() })();
}
}
negate_iife_3: {
options = {
conditionals: true,
expression: true,
negate_iife: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
}
negate_iife_3_off: {
options = {
conditionals: true,
expression: true,
negate_iife: false,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
}
negate_iife_4: {
options = {
conditionals: true,
expression: true,
negate_iife: true,
sequences: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
expect: {
!function(){ return t }() ? console.log(false) : console.log(true), function(){
console.log("something");
}();
}
}
negate_iife_5: {
options = {
conditionals: true,
expression: true,
negate_iife: true,
sequences: true,
}
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
!function(){ return t }() ? bar(false) : foo(true), function(){
console.log("something");
}();
}
}
negate_iife_5_off: {
options = {
conditionals: true,
expression: true,
negate_iife: false,
sequences: true,
};
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
!function(){ return t }() ? bar(false) : foo(true), function(){
console.log("something");
}();
}
}
issue_1254_negate_iife_true: {
options = {
expression: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: '(function(){return function(){console.log("test")}})()();'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
options = {
expression: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '(function(){return function(){console.log("test")}})()()()()();'
expect_stdout: true
}
conditional: {
options = {
expression: true,
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();
pure(3 ? 4 : 5);
}
}
limit_1: {
options = {
expression: true,
sequences: 3,
}
input: {
a;
b;
c;
d;
e;
f;
g;
h;
i;
j;
k;
}
expect: {
// Turned into a single return statement
// so it can no longer be split into lines
a,b,c,d,e,f,g,h,i,j,k;
}
}
iife: {
options = {
expression: true,
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() {}(), typeof function g() {}();
}
}

View File

@@ -29,5 +29,4 @@ dont_mangle_arguments: {
})(5,6,7);
}
expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);"
expect_stdout: true
}

View File

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

View File

@@ -50,7 +50,6 @@ this_binding_conditionals: {
this_binding_collapse_vars: {
options = {
collapse_vars: true,
toplevel: true,
};
input: {
var c = a; c();

View File

@@ -42,7 +42,6 @@ eval_collapse_vars: {
eval("console.log(a);");
})(eval);
}
expect_stdout: true
}
eval_unused: {

View File

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

View File

@@ -9,7 +9,6 @@ labels_1: {
expect: {
foo || console.log("bar");
}
expect_stdout: true
}
labels_2: {
@@ -41,7 +40,6 @@ labels_3: {
for (var i = 0; i < 5; ++i)
i < 3 || console.log(i);
}
expect_stdout: true
}
labels_4: {
@@ -56,7 +54,6 @@ labels_4: {
for (var i = 0; i < 5; ++i)
i < 3 || console.log(i);
}
expect_stdout: true
}
labels_5: {

View File

@@ -166,7 +166,6 @@ keep_collapse_const_in_own_block_scope: {
console.log(i);
console.log(c);
}
expect_stdout: true
}
keep_collapse_const_in_own_block_scope_2: {
@@ -187,7 +186,6 @@ keep_collapse_const_in_own_block_scope_2: {
console.log(i);
console.log(c);
}
expect_stdout: true
}
evaluate: {
@@ -215,7 +213,8 @@ evaluate: {
a();
for(;;)
c();
d();
// rule disabled due to issue_1532
do d(); while (false);
}
}
@@ -241,242 +240,3 @@ issue_1532: {
}
}
}
issue_186: {
beautify = {
beautify: false,
ie8: false,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: 'var x=3;if(foo())do{do{alert(x)}while(--x)}while(x);else bar();'
}
issue_186_ie8: {
beautify = {
beautify: false,
ie8: true,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else bar();'
}
issue_186_beautify: {
beautify = {
beautify: true,
ie8: false,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: [
'var x = 3;',
'',
'if (foo()) do {',
' do {',
' alert(x);',
' } while (--x);',
'} while (x); else bar();',
]
}
issue_186_beautify_ie8: {
beautify = {
beautify: true,
ie8: true,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else bar();',
]
}
issue_186_bracketize: {
beautify = {
beautify: false,
bracketize: true,
ie8: false,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
}
issue_186_bracketize_ie8: {
beautify = {
beautify: false,
bracketize: true,
ie8: true,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
}
issue_186_beautify_bracketize: {
beautify = {
beautify: true,
bracketize: true,
ie8: false,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else {',
' bar();',
'}',
]
}
issue_186_beautify_bracketize_ie8: {
beautify = {
beautify: true,
bracketize: true,
ie8: true,
}
input: {
var x = 3;
if (foo())
do
do
alert(x);
while (--x);
while (x);
else
bar();
}
expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else {',
' bar();',
'}',
]
}
issue_1648: {
options = {
join_vars: true,
loops: true,
passes: 2,
sequences: true,
unused: true,
}
input: {
function f() {
x();
var b = 1;
while (1);
}
}
expect_exact: "function f(){for(x();1;);}"
}
do_switch: {
options = {
evaluate: true,
loops: true,
}
input: {
do {
switch (a) {
case b:
continue;
}
} while (false);
}
expect: {
do {
switch (a) {
case b:
continue;
}
} while (false);
}
}

View File

@@ -7,13 +7,7 @@ too_short: {
return { c: 42, d: a(), e: "foo"};
}
}
expect_exact: [
'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"
]
@@ -28,12 +22,7 @@ just_enough: {
return { c: 42, d: a(), e: "foo"};
}
}
expect_exact: [
'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

@@ -25,20 +25,11 @@ negate_iife_2: {
negate_iife: true
};
input: {
(function(){ return {} })().x = 10; // should not transform this one
}
expect: {
(function(){ return {} })().x = 10;
}
expect_exact: "({}).x=10;"
}
negate_iife_2_side_effects: {
options = {
negate_iife: true,
side_effects: true,
}
input: {
(function(){ return {} })().x = 10;
}
expect_exact: "({}).x=10;"
}
negate_iife_3: {
@@ -54,35 +45,6 @@ negate_iife_3: {
}
}
negate_iife_3_evaluate: {
options = {
conditionals: true,
evaluate: true,
negate_iife: true,
}
input: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
console.log(true);
}
expect_stdout: true
}
negate_iife_3_side_effects: {
options = {
conditionals: true,
negate_iife: true,
side_effects: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
!function(){ return t }() ? console.log(false) : console.log(true);
}
}
negate_iife_3_off: {
options = {
negate_iife: false,
@@ -96,21 +58,6 @@ negate_iife_3_off: {
}
}
negate_iife_3_off_evaluate: {
options = {
conditionals: true,
evaluate: true,
negate_iife: false,
}
input: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
console.log(true);
}
expect_stdout: true
}
negate_iife_4: {
options = {
negate_iife: true,
@@ -241,7 +188,6 @@ negate_iife_nested: {
}(7);
}).f();
}
expect_stdout: true
}
negate_iife_nested_off: {
@@ -274,7 +220,6 @@ negate_iife_nested_off: {
})(7);
}).f();
}
expect_stdout: true
}
negate_iife_issue_1073: {
@@ -299,7 +244,6 @@ negate_iife_issue_1073: {
};
}(7))();
}
expect_stdout: true
}
issue_1254_negate_iife_false: {
@@ -314,7 +258,6 @@ issue_1254_negate_iife_false: {
})()();
}
expect_exact: '(function(){return function(){console.log("test")}})()();'
expect_stdout: true
}
issue_1254_negate_iife_true: {
@@ -329,7 +272,6 @@ issue_1254_negate_iife_true: {
})()();
}
expect_exact: '!function(){return function(){console.log("test")}}()();'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
@@ -344,14 +286,12 @@ issue_1254_negate_iife_nested: {
})()()()()();
}
expect_exact: '!function(){return function(){console.log("test")}}()()()()();'
expect_stdout: true
}
issue_1288: {
options = {
conditionals: true,
negate_iife: true,
side_effects: false,
conditionals: true,
};
input: {
if (w) ;
@@ -371,44 +311,12 @@ issue_1288: {
})(0);
}
expect: {
w || !function f() {}();
x || !function() {
w || function f() {}();
x || function() {
x = {};
}();
y ? !function() {}() : !function(z) {
y ? function() {}() : function(z) {
return z;
}(0);
}
}
issue_1288_side_effects: {
options = {
conditionals: true,
negate_iife: true,
side_effects: true,
}
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w;
x || function() {
x = {};
}();
y;
}
}

View File

@@ -82,3 +82,19 @@ new_with_unary_prefix: {
}
expect_exact: 'var bar=(+new Date).toString(32);';
}
new_with_assignement_expression: {
options = {
evaluate: true
}
input: {
var a;
new x(a = 5 * 2, b = [1, 2, 3], c = {a: "a", b: "b", cd: "c" + "d"});
new y([a, b] = [3, 4]);
}
expect: {
var a;
new x(a = 10, b = [1, 2, 3], c = {a: "a", b: "b", cd: "cd"});
new y([a, b] = [3, 4]);
}
}

View File

@@ -17,188 +17,3 @@ hex_numbers_in_parentheses_for_prototype_functions: {
}
expect_exact: "-2;(-2).toFixed(0);2;2..toFixed(0);.2;.2.toFixed(0);2e-8;2e-8.toFixed(0);0xde0b6b3a7640080;(0xde0b6b3a7640080).toFixed(0);"
}
comparisons: {
options = {
comparisons: true,
}
input: {
console.log(
~x === 42,
x % n === 42
);
}
expect: {
console.log(
42 == ~x,
x % n == 42
);
}
}
evaluate_1: {
options = {
evaluate: true,
unsafe_math: false,
}
input: {
console.log(
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 + (2 + ~x + 3),
-y + (2 + ~x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3)
);
}
expect: {
console.log(
x + 1 + 2,
1 * x * 2,
+x + 1 + 2,
1 + x + 2 + 3,
3 | x,
1 + x-- + 2 + 3,
x*y + 2 + 1 + 3,
1 + (2 + x + 3),
2 + ~x + 3 + 1,
-y + (2 + ~x + 3),
0 & x,
2 + (x |= 0) + 3 + 1
);
}
}
evaluate_2: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
console.log(
x + 1 + 2,
x * 1 * 2,
+x + 1 + 2,
1 + x + 2 + 3,
1 | x | 2 | 3,
1 + x-- + 2 + 3,
1 + (x*y + 2) + 3,
1 + (2 + x + 3),
1 & (2 & x & 3),
1 + (2 + (x |= 0) + 3)
);
}
expect: {
console.log(
x + 1 + 2,
2 * x,
3 + +x,
1 + x + 2 + 3,
3 | x,
6 + x--,
6 + x*y,
1 + (2 + x + 3),
0 & x,
6 + (x |= 0)
);
}
}
evaluate_3: {
options = {
evaluate: true,
unsafe: true,
unsafe_math: true,
}
input: {
console.log(1 + Number(x) + 2);
}
expect: {
console.log(3 + +x);
}
}
evaluate_4: {
options = {
evaluate: true,
}
input: {
console.log(
1+ +a,
+a+1,
1+-a,
-a+1,
+a+ +b,
+a+-b,
-a+ +b,
-a+-b
);
}
expect: {
console.log(
+a+1,
+a+1,
1-a,
1-a,
+a+ +b,
+a-b,
-a+ +b,
-a-b
);
}
}
issue_1710: {
options = {
evaluate: true,
}
input: {
var x = {};
console.log((x += 1) + -x);
}
expect: {
var x = {};
console.log((x += 1) + -x);
}
expect_stdout: true
}
unary_binary_parenthesis: {
input: {
var v = [ 0, 1, NaN, Infinity, null, undefined, true, false, "", "foo", /foo/ ];
v.forEach(function(x) {
v.forEach(function(y) {
console.log(
+(x*y),
+(x/y),
+(x%y),
-(x*y),
-(x/y),
-(x%y)
);
});
});
}
expect: {
var v = [ 0, 1, NaN, 1/0, null, void 0, true, false, "", "foo", /foo/ ];
v.forEach(function(x) {
v.forEach(function(y) {
console.log(
+x*y,
+x/y,
+x%y,
-x*y,
-x/y,
-x%y
);
});
});
}
expect_stdout: true
}

510
test/compress/object.js Normal file
View File

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

168
test/compress/parameters.js Normal file
View File

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

View File

@@ -13,7 +13,7 @@ keep_properties: {
dot_properties: {
options = {
properties: true,
ie8: true,
screw_ie8: false
};
input: {
a["foo"] = "bar";
@@ -36,7 +36,7 @@ dot_properties: {
dot_properties_es5: {
options = {
properties: true,
ie8: false,
screw_ie8: true
};
input: {
a["foo"] = "bar";
@@ -125,7 +125,7 @@ evaluate_string_length: {
mangle_properties: {
mangle_props = {
keep_quoted: false
ignore_quoted: false
};
input: {
a["foo"] = "bar";
@@ -148,7 +148,8 @@ mangle_unquoted_properties: {
properties: false
}
mangle_props = {
keep_quoted: true
ignore_quoted: true,
reserved: []
}
beautify = {
beautify: false,
@@ -233,12 +234,12 @@ mangle_debug_suffix: {
}
}
mangle_debug_suffix_keep_quoted: {
mangle_debug_suffix_ignore_quoted: {
options = {
properties: false
}
mangle_props = {
keep_quoted: true,
ignore_quoted: true,
debug: "XYZ",
reserved: []
}

View File

@@ -1,121 +0,0 @@
strict: {
options = {
pure_getters: "strict",
reduce_vars: false,
side_effects: true,
toplevel: true,
}
input: {
var a, b = null, c = {};
a.prop;
b.prop;
c.prop;
d.prop;
null.prop;
(void 0).prop;
undefined.prop;
}
expect: {
var a, b = null, c = {};
a.prop;
b.prop;
c.prop;
d.prop;
null.prop;
(void 0).prop;
(void 0).prop;
}
}
strict_reduce_vars: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a, b = null, c = {};
a.prop;
b.prop;
c.prop;
d.prop;
null.prop;
(void 0).prop;
undefined.prop;
}
expect: {
var a, b = null, c = {};
a.prop;
b.prop;
d.prop;
null.prop;
(void 0).prop;
(void 0).prop;
}
}
unsafe: {
options = {
pure_getters: true,
reduce_vars: false,
side_effects: true,
toplevel: true,
}
input: {
var a, b = null, c = {};
a.prop;
b.prop;
c.prop;
d.prop;
null.prop;
(void 0).prop;
undefined.prop;
}
expect: {
var a, b = null, c = {};
d;
null.prop;
(void 0).prop;
(void 0).prop;
}
}
unsafe_reduce_vars: {
options = {
pure_getters: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a, b = null, c = {};
a.prop;
b.prop;
c.prop;
d.prop;
null.prop;
(void 0).prop;
undefined.prop;
}
expect: {
var a, b = null, c = {};
d;
null.prop;
(void 0).prop;
(void 0).prop;
}
}
chained: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
a.b.c;
}
expect: {
a.b.c;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +1,26 @@
do_screw: {
options = {
ie8: false,
}
options = { screw_ie8: true };
beautify = {
ie8: false,
ascii_only: true,
}
input: {
f("\v");
}
expect_exact: 'f("\\v");'
screw_ie8: true,
ascii_only: true
};
input: f("\v");
expect_exact: 'f("\\v");';
}
dont_screw: {
options = {
ie8: true,
}
beautify = {
ie8: true,
ascii_only: true,
}
input: {
f("\v");
}
expect_exact: 'f("\\x0B");'
}
options = { screw_ie8: false };
beautify = { screw_ie8: false, ascii_only: true };
do_screw_constants: {
options = {
ie8: false,
}
input: {
f(undefined, Infinity);
}
expect_exact: "f(void 0,1/0);"
}
dont_screw_constants: {
options = {
ie8: true,
}
input: {
f(undefined, Infinity);
}
expect_exact: "f(undefined,Infinity);"
input: f("\v");
expect_exact: 'f("\\x0B");';
}
do_screw_try_catch: {
options = {
ie8: false,
}
mangle = {
ie8: false,
}
beautify = {
ie8: false,
}
options = { screw_ie8: true };
mangle = { screw_ie8: true };
beautify = { screw_ie8: true };
input: {
good = function(e){
return function(error){
@@ -81,15 +46,11 @@ do_screw_try_catch: {
}
dont_screw_try_catch: {
options = {
ie8: true,
}
mangle = {
ie8: true,
}
beautify = {
ie8: true,
}
// 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){
@@ -103,11 +64,11 @@ dont_screw_try_catch: {
}
expect: {
bad = function(n){
return function(t){
return function(n){
try{
n()
} catch(n) {
t(n)
t()
} catch(t) {
n(t)
}
}
};
@@ -115,15 +76,9 @@ dont_screw_try_catch: {
}
do_screw_try_catch_undefined: {
options = {
ie8: false,
}
mangle = {
ie8: false,
}
beautify = {
ie8: false,
}
options = { screw_ie8: true };
mangle = { screw_ie8: true };
beautify = { screw_ie8: true };
input: {
function a(b){
try {
@@ -146,19 +101,14 @@ do_screw_try_catch_undefined: {
return void 0===o
}
}
expect_stdout: true
}
dont_screw_try_catch_undefined: {
options = {
ie8: true,
}
mangle = {
ie8: true,
}
beautify = {
ie8: true,
}
// 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 {
@@ -171,87 +121,14 @@ dont_screw_try_catch_undefined: {
};
}
expect: {
function a(n){
function a(o){
try{
throw "Stuff"
} catch (undefined) {
console.log("caught: " + undefined)
} catch (n) {
console.log("caught: "+n)
}
console.log("undefined is " + undefined);
return n === undefined
}
}
expect_stdout: true
}
reduce_vars: {
options = {
evaluate: true,
reduce_vars: true,
ie8: true,
unused: true,
}
mangle = {
ie8: true,
}
input: {
function f() {
var a;
try {
x();
} catch (a) {
y();
}
alert(a);
}
}
expect: {
function f() {
var t;
try {
x();
} catch (t) {
y();
}
alert(t);
console.log("undefined is " + n);
return o === n
}
}
}
issue_1586_1: {
options = {
ie8: true,
}
mangle = {
ie8: true,
}
input: {
function f() {
try {
x();
} catch (err) {
console.log(err.message);
}
}
}
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
}
issue_1586_2: {
options = {
ie8: false,
}
mangle = {
ie8: false,
}
input: {
function f() {
try {
x();
} catch (err) {
console.log(err.message);
}
}
}
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
}

View File

@@ -86,7 +86,6 @@ make_sequences_4: {
switch (x = 5, y) {}
with (x = 5, obj);
}
expect_stdout: true
}
lift_sequences_1: {
@@ -104,18 +103,15 @@ lift_sequences_1: {
lift_sequences_2: {
options = { sequences: true, evaluate: true };
input: {
var foo = 1, bar;
var foo, bar;
foo.x = (foo = {}, 10);
bar = (bar = {}, 10);
console.log(foo, bar);
}
expect: {
var foo = 1, bar;
var foo, bar;
foo.x = (foo = {}, 10),
bar = {}, bar = 10,
console.log(foo, bar);
bar = {}, bar = 10;
}
expect_stdout: true
}
lift_sequences_3: {
@@ -142,23 +138,6 @@ lift_sequences_4: {
}
}
lift_sequences_5: {
options = {
sequences: true,
}
input: {
var a = 2, b;
a *= (b, a = 4, 3);
console.log(a);
}
expect: {
var a = 2, b;
b, a *= (a = 4, 3),
console.log(a);
}
expect_stdout: "6"
}
for_sequences: {
options = { sequences: true };
input: {
@@ -251,7 +230,6 @@ negate_iife_for: {
for (!function() {}(), i = 0; i < 5; i++) console.log(i);
for (function() {}(); i < 5; i++) console.log(i);
}
expect_stdout: true
}
iife: {
@@ -270,443 +248,6 @@ iife: {
}
expect: {
x = 42, function a() {}(), function b() {}(), function c() {}(),
function d() {}(), function e() {}(), function f() {}(), function g() {}();
}
}
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
unsafe: true,
}
input: {
function f(undefined) {
if (a)
return b;
if (c)
return d;
}
function g(undefined) {
if (a)
return b;
if (c)
return d;
e();
}
}
expect: {
function f(undefined) {
return a ? b : c ? d : undefined;
}
function g(undefined) {
return a ? b : c ? d : void e();
}
}
}
issue_1685: {
options = {
cascade: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
function f() {
var a = (a--, delete a && --b);
}
f();
console.log(a, b);
}
expect: {
var a = 100, b = 10;
function f() {
var a = (a--, delete a && --b);
}
f();
console.log(a, b);
}
expect_stdout: true
}
func_def_1: {
options = {
cascade: true,
side_effects: true,
}
input: {
function f() {
return f = 0, !!f;
}
console.log(f());
}
expect: {
function f() {
return !!(f = 0);
}
console.log(f());
}
expect_stdout: "false"
}
func_def_2: {
options = {
cascade: true,
side_effects: true,
}
input: {
console.log(function f() {
return f = 0, !!f;
}());
}
expect: {
console.log(function f() {
return f = 0, !!f;
}());
}
expect_stdout: "true"
}
func_def_3: {
options = {
cascade: true,
side_effects: true,
}
input: {
function f() {
function g() {}
return g = 0, !!g;
}
console.log(f());
}
expect: {
function f() {
function g() {}
return !!(g = 0);
}
console.log(f());
}
expect_stdout: "false"
}
func_def_4: {
options = {
cascade: true,
side_effects: true,
}
input: {
function f() {
function g() {
return g = 0, !!g;
}
return g();
}
console.log(f());
}
expect: {
function f() {
function g() {
return !!(g = 0);
}
return g();
}
console.log(f());
}
expect_stdout: "false"
}
func_def_5: {
options = {
cascade: true,
side_effects: true,
}
input: {
function f() {
return function g(){
return g = 0, !!g;
}();
}
console.log(f());
}
expect: {
function f() {
return function g(){
return g = 0, !!g;
}();
}
console.log(f());
}
expect_stdout: "true"
}
issue_1758: {
options = {
sequences: true,
side_effects: true,
}
input: {
console.log(function(c) {
var undefined = 42;
return function() {
c--;
c--, c.toString();
return;
}();
}());
}
expect: {
console.log(function(c) {
var undefined = 42;
return function() {
return c--, c--, void c.toString();
}();
}());
}
expect_stdout: "undefined"
}
delete_seq_1: {
options = {
booleans: true,
side_effects: true,
}
input: {
console.log(delete (1, undefined));
console.log(delete (1, void 0));
console.log(delete (1, Infinity));
console.log(delete (1, 1 / 0));
console.log(delete (1, NaN));
console.log(delete (1, 0 / 0));
}
expect: {
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
}
delete_seq_2: {
options = {
booleans: true,
side_effects: true,
}
input: {
console.log(delete (1, 2, undefined));
console.log(delete (1, 2, void 0));
console.log(delete (1, 2, Infinity));
console.log(delete (1, 2, 1 / 0));
console.log(delete (1, 2, NaN));
console.log(delete (1, 2, 0 / 0));
}
expect: {
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
}
delete_seq_3: {
options = {
booleans: true,
keep_infinity: true,
side_effects: true,
}
input: {
console.log(delete (1, 2, undefined));
console.log(delete (1, 2, void 0));
console.log(delete (1, 2, Infinity));
console.log(delete (1, 2, 1 / 0));
console.log(delete (1, 2, NaN));
console.log(delete (1, 2, 0 / 0));
}
expect: {
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
console.log(!0);
}
expect_stdout: true
}
delete_seq_4: {
options = {
booleans: true,
sequences: true,
side_effects: true,
}
input: {
function f() {}
console.log(delete (f(), undefined));
console.log(delete (f(), void 0));
console.log(delete (f(), Infinity));
console.log(delete (f(), 1 / 0));
console.log(delete (f(), NaN));
console.log(delete (f(), 0 / 0));
}
expect: {
function f() {}
console.log((f(), !0)),
console.log((f(), !0)),
console.log((f(), !0)),
console.log((f(), !0)),
console.log((f(), !0)),
console.log((f(), !0));
}
expect_stdout: true
}
delete_seq_5: {
options = {
booleans: true,
keep_infinity: true,
sequences: true,
side_effects: true,
}
input: {
function f() {}
console.log(delete (f(), undefined));
console.log(delete (f(), void 0));
console.log(delete (f(), Infinity));
console.log(delete (f(), 1 / 0));
console.log(delete (f(), NaN));
console.log(delete (f(), 0 / 0));
}
expect: {
function f() {}
console.log((f(), !0)),
console.log((f(), !0)),
console.log((f(), !0)),
console.log((f(), !0)),
console.log((f(), !0)),
console.log((f(), !0));
}
expect_stdout: true
}
delete_seq_6: {
options = {
booleans: true,
side_effects: true,
}
input: {
var a;
console.log(delete (1, a));
}
expect: {
var a;
console.log(!0);
}
expect_stdout: true
}
side_effects: {
options = {
sequences: true,
side_effects: true,
}
input: {
0, a(), 1, b(), 2, c(), 3;
}
expect: {
a(), b(), c();
}
}
side_effects_cascade_1: {
options = {
cascade: true,
conditionals: true,
sequences: true,
side_effects: true,
}
input: {
function f(a, b) {
a -= 42;
if (a < 0) a = 0;
b.a = a;
}
}
expect: {
function f(a, b) {
(a -= 42) < 0 && (a = 0), b.a = a;
}
}
}
side_effects_cascade_2: {
options = {
cascade: true,
side_effects: true,
}
input: {
function f(a, b) {
b = a,
!a + (b += a) || (b += a),
b = a,
b;
}
}
expect: {
function f(a, b) {
b = a,
!a + (b += a) || (b += a),
b = a;
}
}
}
side_effects_cascade_3: {
options = {
cascade: true,
conditionals: true,
side_effects: true,
}
input: {
function f(a, b) {
"foo" ^ (b += a),
b ? false : (b = a) ? -1 : (b -= a) - (b ^= a),
a-- || !a,
a;
}
}
expect: {
function f(a, b) {
!(b += a) && ((b = a) || (b -= a, b ^= a)),
--a;
}
}
}
issue_27: {
options = {
cascade: true,
passes: 2,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(jQuery) {
var $;
$ = jQuery;
$("body").addClass("foo");
})(jQuery);
}
expect: {
(function(jQuery) {
jQuery("body").addClass("foo");
})(jQuery);
function d() {}(), function e() {}(), function f() {}(), function g() {}()
}
}

9
test/compress/super.js Normal file
View File

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

View File

@@ -1,10 +1,5 @@
constant_switch_1: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
switch (1+1) {
case 1: foo(); break;
@@ -18,12 +13,7 @@ constant_switch_1: {
}
constant_switch_2: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
switch (1) {
case 1: foo();
@@ -38,12 +28,7 @@ constant_switch_2: {
}
constant_switch_3: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
switch (10) {
case 1: foo();
@@ -59,12 +44,7 @@ constant_switch_3: {
}
constant_switch_4: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
switch (2) {
case 1:
@@ -85,12 +65,7 @@ constant_switch_4: {
}
constant_switch_5: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
switch (1) {
case 1:
@@ -119,12 +94,7 @@ constant_switch_5: {
}
constant_switch_6: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
OUT: {
foo();
@@ -153,12 +123,7 @@ constant_switch_6: {
}
constant_switch_7: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
OUT: {
foo();
@@ -196,12 +161,7 @@ constant_switch_7: {
}
constant_switch_8: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
OUT: switch (1) {
case 1:
@@ -225,12 +185,7 @@ constant_switch_8: {
}
constant_switch_9: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
options = { dead_code: true, evaluate: true };
input: {
OUT: switch (1) {
case 1:
@@ -255,10 +210,7 @@ constant_switch_9: {
}
drop_default_1: {
options = {
dead_code: true,
switches: true,
}
options = { dead_code: true };
input: {
switch (foo) {
case 'bar': baz();
@@ -273,10 +225,7 @@ drop_default_1: {
}
drop_default_2: {
options = {
dead_code: true,
switches: true,
}
options = { dead_code: true };
input: {
switch (foo) {
case 'bar': baz(); break;
@@ -292,10 +241,7 @@ drop_default_2: {
}
keep_default: {
options = {
dead_code: true,
switches: true,
}
options = { dead_code: true };
input: {
switch (foo) {
case 'bar': baz();
@@ -312,507 +258,3 @@ keep_default: {
}
}
}
issue_1663: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
input: {
var a = 100, b = 10;
function f() {
switch (1) {
case 1:
b = a++;
return ++b;
default:
var b;
}
}
f();
console.log(a, b);
}
expect: {
var a = 100, b = 10;
function f() {
var b;
b = a++;
return ++b;
}
f();
console.log(a, b);
}
expect_stdout: true
}
drop_case: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (foo) {
case 'bar': baz(); break;
case 'moo':
break;
}
}
expect: {
switch (foo) {
case 'bar': baz();
}
}
}
keep_case: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (foo) {
case 'bar': baz(); break;
case moo:
break;
}
}
expect: {
switch (foo) {
case 'bar': baz(); break;
case moo:
}
}
}
issue_376: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
switch (true) {
case boolCondition:
console.log(1);
break;
case false:
console.log(2);
break;
}
}
expect: {
switch (true) {
case boolCondition:
console.log(1);
}
}
}
issue_441_1: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (foo) {
case bar:
qux();
break;
case baz:
qux();
break;
default:
qux();
break;
}
}
expect: {
switch (foo) {
case bar:
case baz:
default:
qux();
}
}
}
issue_441_2: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (foo) {
case bar:
// TODO: Fold into the case below
qux();
break;
case fall:
case baz:
qux();
break;
default:
qux();
break;
}
}
expect: {
switch (foo) {
case bar:
qux();
break;
case fall:
case baz:
default:
qux();
}
}
}
issue_1674: {
options = {
dead_code: true,
evaluate: true,
side_effects: true,
switches: true,
}
input: {
switch (0) {
default:
console.log("FAIL");
break;
case 0:
console.log("PASS");
break;
}
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_1679: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
var a = 100, b = 10;
function f() {
switch (--b) {
default:
case !function x() {}:
break;
case b--:
switch (0) {
default:
case a--:
}
break;
case (a++):
break;
}
}
f();
console.log(a, b);
}
expect: {
var a = 100, b = 10;
function f() {
switch (--b) {
default:
case !function x() {}:
break;
case b--:
switch (0) {
default:
case a--:
}
break;
case (a++):
}
}
f();
console.log(a, b);
}
expect_stdout: true
}
issue_1680_1: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
function f(x) {
console.log(x);
return x + 1;
}
switch (2) {
case f(0):
case f(1):
f(2);
case 2:
case f(3):
case f(4):
f(5);
}
}
expect: {
function f(x) {
console.log(x);
return x + 1;
}
switch (2) {
case f(0):
case f(1):
f(2);
case 2:
f(5);
}
}
expect_stdout: [
"0",
"1",
"2",
"5",
]
}
issue_1680_2: {
options = {
dead_code: true,
switches: true,
}
input: {
var a = 100, b = 10;
switch (b) {
case a--:
break;
case b:
var c;
break;
case a:
break;
case a--:
break;
}
console.log(a, b);
}
expect: {
var a = 100, b = 10;
switch (b) {
case a--:
break;
case b:
var c;
break;
case a:
case a--:
}
console.log(a, b);
}
expect_stdout: true
}
issue_1690_1: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (console.log("PASS")) {}
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_1690_2: {
options = {
dead_code: false,
switches: true,
}
input: {
switch (console.log("PASS")) {}
}
expect: {
switch (console.log("PASS")) {}
}
expect_stdout: "PASS"
}
if_switch_typeof: {
options = {
conditionals: true,
dead_code: true,
side_effects: true,
switches: true,
}
input: {
if (a) switch(typeof b) {}
}
expect: {
a;
}
}
issue_1698: {
options = {
side_effects: true,
switches: true,
}
input: {
var a = 1;
!function() {
switch (a++) {}
}();
console.log(a);
}
expect: {
var a = 1;
!function() {
switch (a++) {}
}();
console.log(a);
}
expect_stdout: "2"
}
issue_1705_1: {
options = {
dead_code: true,
switches: true,
}
input: {
var a = 0;
switch (a) {
default:
console.log("FAIL");
case 0:
break;
}
}
expect: {
var a = 0;
switch (a) {
default:
console.log("FAIL");
case 0:
}
}
expect_stdout: true
}
issue_1705_2: {
options = {
dead_code: true,
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
switches: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
switch (a) {
default:
console.log("FAIL");
case 0:
break;
}
}
expect: {
}
expect_stdout: true
}
issue_1705_3: {
options = {
dead_code: true,
switches: true,
}
input: {
switch (a) {
case 0:
break;
default:
break;
}
}
expect: {
a;
}
expect_stdout: true
}
beautify: {
beautify = {
beautify: true,
}
input: {
switch (a) {
case 0:
case 1:
break;
case 2:
default:
}
switch (b) {
case 3:
foo();
bar();
default:
break;
}
}
expect_exact: [
"switch (a) {",
" case 0:",
" case 1:",
" break;",
"",
" case 2:",
" default:",
"}",
"",
"switch (b) {",
" case 3:",
" foo();",
" bar();",
"",
" default:",
" break;",
"}",
]
}
issue_1758: {
options = {
dead_code: true,
switches: true,
}
input: {
var a = 1, b = 2;
switch (a--) {
default:
b++;
}
console.log(a, b);
}
expect: {
var a = 1, b = 2;
a--;
b++;
console.log(a, b);
}
expect_stdout: "0 3"
}

View File

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

View File

@@ -1,130 +0,0 @@
booleans_evaluate: {
options = {
booleans: true,
evaluate: true,
}
input: {
console.log(typeof void 0 != "undefined");
console.log(1 == 1, 1 === 1)
console.log(1 != 1, 1 !== 1)
}
expect: {
console.log(!1);
console.log(!0, !0);
console.log(!1, !1);
}
expect_stdout: true
}
booleans_global_defs: {
options = {
booleans: true,
evaluate: true,
global_defs: {
A: true,
},
}
input: {
console.log(A == 1);
}
expect: {
console.log(!0);
}
}
condition_evaluate: {
options = {
booleans: true,
dead_code: false,
evaluate: true,
loops: false,
}
input: {
while (1 === 2);
for (; 1 == true;);
if (void 0 == null);
}
expect: {
while (!1);
for (; !0;);
if (!0);
}
}
if_else_empty: {
options = {
conditionals: true,
}
input: {
if ({} ? a : b); else {}
}
expect: {
!{} ? b : a;
}
}
label_if_break: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
L: if (true) {
a;
break L;
}
}
expect: {
a;
}
}
while_if_break: {
options = {
conditionals: true,
loops: true,
sequences: true,
}
input: {
while (a) {
if (b) if(c) d;
if (e) break;
}
}
expect: {
for(; a && (b && c && d, !e););
}
}
if_return: {
options = {
booleans: true,
conditionals: true,
if_return: true,
sequences: true,
}
input: {
function f(w, x, y, z) {
if (x) return;
if (w) {
if (y) return;
} else if (z) return;
if (x == y) return true;
if (x) w();
if (y) z();
return true;
}
}
expect: {
function f(w, x, y, z) {
if (!x) {
if (w) {
if (y) return;
} else if (z) return;
return x == y || (x && w(), y && z(), !0);
}
}
}
}

View File

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

View File

@@ -48,15 +48,3 @@ typeof_in_boolean_context: {
foo();
}
}
issue_1668: {
options = {
booleans: true,
}
input: {
if (typeof bar);
}
expect: {
if (!0);
}
}

View File

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

192
test/compress/yield.js Normal file
View File

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

View File

@@ -1,13 +0,0 @@
exports["Compressor"] = Compressor;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["OutputStream"] = OutputStream;
exports["SourceMap"] = SourceMap;
exports["TreeWalker"] = TreeWalker;
exports["base54"] = base54;
exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify;
exports["parse"] = parse;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;

View File

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

View File

@@ -1 +0,0 @@
console.log(2 || (Math.random() /= 2));

View File

@@ -1 +0,0 @@
console.log(3 || ++this);

View File

@@ -1 +0,0 @@
++null

View File

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

View File

@@ -1,14 +0,0 @@
function f(x) {
delete 42;
delete (0, x);
delete null;
delete x;
}
function g(x) {
"use strict";
delete 42;
delete (0, x);
delete null;
delete x;
}

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