Compare commits

...

347 Commits

Author SHA1 Message Date
Richard van Velzen
3ee46e91e8 Merge branch 'master' into harmony 2016-11-29 22:32:49 +01:00
Richard van Velzen
f6372483a0 v2.7.5 2016-11-29 22:29:59 +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
Martijn Swaagman
98f330658f Generate source map data from normalized files
If using `inSourceMap` this fix will ensure the copying of `sourcesContent` is based on potentially normalized `sources` values (https://github.com/mozilla/source-map/blob/master/lib/source-map-consumer.js#L304-L309).

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

Based on: PR #1316

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Reason for revert: introduce issue #882

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

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

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

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

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

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

instead of:

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

it's now possible:

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

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

1
.gitattributes vendored Normal file
View File

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

View File

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

209
README.md
View File

@@ -62,12 +62,15 @@ The available options are:
--source-map-include-sources Pass this flag if you want to include the
content of source files in the source map as
sourcesContent property.
--source-map-inline Write base64-encoded source map to the end of js output.
--in-source-map Input source map, useful if you're compressing
JS that was generated from some other original
code.
--screw-ie8 Pass this flag if you don't care about full
compliance with Internet Explorer 6-8 quirks
(by default UglifyJS will try to be IE-proof).
--screw-ie8 Use this flag if you don't wish to support
Internet Explorer 6-8 quirks.
By default UglifyJS will not try to be IE-proof.
--support-ie8 Use this flag to support Internet Explorer 6-8 quirks.
Note: may break standards compliant `catch` identifiers.
--expr Parse a single expression, rather than a
program (for parsing JSON)
-p, --prefix Skip prefix for original filenames that appear
@@ -95,8 +98,8 @@ The available options are:
"@preserve". You can optionally pass one of the
following arguments to this flag:
- "all" to keep all comments
- a valid JS regexp (needs to start with a
slash) to keep only comments that match.
- a valid JS RegExp like `/foo/` or `/^!/` to
keep only matching comments.
Note that currently not *all* comments can be
kept when compression is on, because of dead
code removal or cascading statements into
@@ -125,15 +128,28 @@ The available options are:
--noerr Don't throw an error for unknown options in -c,
-b or -m.
--bare-returns Allow return outside of functions. Useful when
minifying CommonJS modules.
minifying CommonJS modules and Userscripts that
may be anonymous function wrapped (IIFE) by the
.user.js engine `caller`.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--reserved-file File containing reserved names
--reserve-domprops Make (most?) DOM properties reserved for
--mangle-props
--mangle-props Mangle property names
--mangle-props Mangle property names (default `0`). Set to
`true` or `1` to mangle all property names. Set
to `unquoted` or `2` to only mangle unquoted
property names. Mode `2` also enables the
`keep_quoted_props` beautifier option to
preserve the quotes around property names and
disables the `properties` compressor option to
prevent rewriting quoted properties with dot
notation. You can override these by setting
them explicitly on the command line.
--mangle-regex Only mangle property names matching the regex
--name-cache File to hold mangled names mappings
--pure-funcs List of functions that can be safely removed if
their return value is not used [array]
```
Specify `--output` (`-o`) to declare the output file. Otherwise the output
@@ -188,11 +204,6 @@ input files from the command line.
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `sort` — to assign shorter names to most frequently used variables. This
saves a few hundred bytes on jQuery before gzip, but the output is
_bigger_ after gzip (and seems to happen for other libraries I tried it
on) therefore it's not enabled by default.
- `toplevel` — mangle names declared in the toplevel scope (disabled by
default).
@@ -274,6 +285,21 @@ of mangled property names.
Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS.
#### Debugging property name mangling
You can also pass `--mangle-props-debug` in order to mangle property names
without completely obscuring them. For example the property `o.foo`
would mangle to `o._$foo$_` with this option. This allows property mangling
of a large codebase while still being able to debug the code and identify
where mangling is breaking things.
You can also pass a custom suffix using `--mangle-props-debug=XYZ`. This would then
mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a
script to identify how a property got mangled. One technique is to pass a
random number on every compile to simulate mangling changing with different
inputs (e.g. as you update the input script with new properties), and to help
identify mistakes like writing mangled keys to storage.
## Compressor options
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
@@ -281,7 +307,14 @@ you can pass a comma-separated list of options. Options are in the form
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
to set `true`; it's effectively a shortcut for `foo=true`).
- `sequences` -- join consecutive simple statements using the comma operator
- `sequences` (default: true) -- join consecutive simple statements using the
comma operator. May be set to a positive integer to specify the maximum number
of consecutive comma sequences that will be generated. If this option is set to
`true` then the default `sequences` limit is `200`. Set option to `false` or `0`
to disable. The smallest `sequences` length is `2`. A `sequences` value of `1`
is grandfathered to be equivalent to `true` and as such means `200`. On rare
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `properties` -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
@@ -292,12 +325,19 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: false) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`,
or `valueOf`. This could cause change in execution order after operands in the
comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true.
- `conditionals` -- apply optimizations for `if`-s and conditional
expressions
- `comparisons` -- apply certain optimizations to binary nodes, for example:
`!(a <= b) → a > b` (only when `unsafe`), attempts to negate binary nodes,
e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
`!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `evaluate` -- attempt to evaluate constant expressions
@@ -321,6 +361,12 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `collapse_vars` -- default `false`. Collapse single-use `var` and `const`
definitions when possible.
- `reduce_vars` -- default `false`. Improve optimization on variables assigned
with and used as constant values.
- `warnings` -- display warnings when dropping unreachable code or unused
declarations etc.
@@ -346,14 +392,17 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `drop_console` -- default `false`. Pass `true` to discard calls to
`console.*` functions.
- `keep_fargs` -- default `false`. Pass `true` to prevent the
- `keep_fargs` -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
- `keep_fnames` -- default `false`. Pass `true` to prevent the
compressor from mangling/discarding function names. Useful for code relying on
`Function.prototype.name`.
compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `passes` -- default `1`. Number of times to run compress. Use an
integer argument larger than 1 to further reduce code size in some cases.
Note: raising the number of passes will increase uglify compress time.
### The `unsafe` option
@@ -370,7 +419,6 @@ when this flag is on:
- `void 0` → `undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically
reduced to a single character)
- discards unused function arguments (affects `function.length`)
### Conditional compilation
@@ -394,6 +442,8 @@ separate file and include it into the build. For example you can have a
```javascript
const DEBUG = false;
const PRODUCTION = true;
// Alternative for environments that don't support `const`
/** @const */ var STAGING = false;
// etc.
```
@@ -403,10 +453,26 @@ and build your code like this:
UglifyJS will notice the constants and, since they cannot be altered, it
will evaluate references to them to the value itself and drop unreachable
code as usual. The possible downside of this approach is that the build
will contain the `const` declarations.
code as usual. The build will contain the `const` declarations if you use
them. If you are targeting < ES6 environments, use `/** @const */ var`.
<a name="codegen-options"></a>
#### Conditional compilation, API
You can also use conditional compilation via the programmatic API. With the difference that the
property name is `global_defs` and is a compressor property:
```js
uglifyJS.minify([ "input.js"], {
compress: {
dead_code: true,
global_defs: {
DEBUG: false
}
}
});
```
## Beautifier options
The code generator tries to output shortest code possible by default. In
@@ -423,7 +489,7 @@ can pass additional arguments that control the code output:
objects
- `space-colon` (default `true`) -- insert a space after the colon signs
- `ascii-only` (default `false`) -- escape Unicode characters in strings and
regexps
regexps (affects directives with non-ascii characters becoming invalid)
- `inline-script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `width` (default 80) -- only takes effect when beautification is on, this
@@ -450,6 +516,11 @@ can pass additional arguments that control the code output:
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
- `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
@@ -579,9 +650,26 @@ console.log(result.code); // minified output
console.log(result.map);
```
To generate a source map with the fromString option, you can also use an object:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function () {};"}, {
outSourceMap: "out.js.map",
outFileName: "out.js",
fromString: true
});
```
Note that the source map is not saved in a file, it's just returned in
`result.map`. The value passed for `outSourceMap` is only used to set the
`file` attribute in the source map (see [the spec][sm-spec]).
`result.map`. The value passed for `outSourceMap` is only used to set
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
`outFileName` is only used to set `file` attribute in source map file.
The `file` attribute in the source map (see [the spec][sm-spec]) will
use `outFileName` firstly, if it's falsy, then will be deduced from
`outSourceMap` (by removing `'.map'`).
You can set option `sourceMapInline` to be `true` and source map will
be appended to code.
You can also specify sourceRoot property to be included in source map:
```javascript
@@ -614,6 +702,17 @@ var result = UglifyJS.minify("compiled.js", {
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
no sense otherwise).
To set the source map url, use the `sourceMapUrl` option.
If you're using the X-SourceMap header instead, you can just set the `sourceMapUrl` option to false.
Defaults to outSourceMap:
```javascript
var result = UglifyJS.minify([ "file1.js" ], {
outSourceMap: "out.js.map",
sourceMapUrl: "localhost/out.js.map"
});
```
Other options:
- `warnings` (default `false`) — pass `true` to display compressor warnings.
@@ -621,7 +720,11 @@ Other options:
- `fromString` (default `false`) — if you pass `true` then you can pass
JavaScript source code, rather than file names.
- `mangle` — pass `false` to skip mangling names.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
an object to specify mangling options (see below).
- `mangleProperties` (default `false`) — pass an object to specify custom
mangle property options.
- `output` (default `null`) — pass an object if you wish to specify
additional [output options][codegen]. The defaults are optimized
@@ -630,6 +733,49 @@ Other options:
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compressor options][compressor].
- `parse` (default {}) — pass an object if you wish to specify some
additional [parser options][parser]. (not all options available... see below)
##### mangle
- `except` - pass an array of identifiers that should be excluded from mangling
- `toplevel` — mangle names declared in the toplevel scope (disabled by
default).
- `eval` — mangle names visible in scopes where eval or with are used
(disabled by default).
- `keep_fnames` -- default `false`. Pass `true` to not mangle
function names. Useful for code relying on `Function.prototype.name`.
See also: the `keep_fnames` [compress option](#compressor-options).
Examples:
```javascript
//tst.js
var globalVar;
function funcName(firstLongName, anotherLongName)
{
var myVariable = firstLongName + anotherLongName;
}
UglifyJS.minify("tst.js").code;
// 'function funcName(a,n){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { except: ['firstLongName'] } }).code;
// 'function funcName(firstLongName,a){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { toplevel: true } }).code;
// 'function n(n,a){}var a;'
```
##### mangleProperties options
- `regex` — Pass a RegExp to only mangle certain names (maps to the `--mangle-regex` CLI arguments option)
- `ignore_quoted` Only mangle unquoted property names (maps to the `--mangle-props 2` CLI arguments option)
- `debug` Mangle names with the original name still present (maps to the `--mangle-props-debug` CLI arguments option). Defaults to `false`. Pass an empty string to enable, or a non-empty string to set the suffix.
We could add more options to `UglifyJS.minify` — if you need additional
functionality please suggest!
@@ -648,6 +794,9 @@ properties are available:
- `strict` — disable automatic semicolon insertion and support for trailing
comma in arrays and objects
- `bare_returns` — Allow return outside of functions. (maps to the
`--bare-returns` CLI arguments option and available to `minify` `parse`
other options object)
- `filename` — the name of the file where this code is coming from
- `toplevel` — a `toplevel` node (as returned by a previous invocation of
`parse`)
@@ -732,8 +881,11 @@ which we care about here are `source_map` and `comments`.
#### Keeping comments in the output
In order to keep certain comments in the output you need to pass the
`comments` option. Pass a RegExp or a function. If you pass a RegExp, only
those comments whose body matches the regexp will be kept. Note that body
`comments` option. Pass a RegExp (as string starting and closing with `/`
or pass a RegExp object), a boolean or a function. Stringified options
`all` and `some` can be passed too, where `some` behaves like it's cli
equivalent `--comments` without passing a value. If you pass a RegExp,
only those comments whose body matches the RegExp will be kept. Note that body
means without the initial `//` or `/*`. If you pass a function, it will be
called for every comment in the tree and will receive two arguments: the
node that the comment is attached to, and the comment token itself.
@@ -782,8 +934,9 @@ The `source_map_options` (optional) can contain the following properties:
came from. It can be simply a string in JSON, or a JSON object containing
the original source map.
[acorn]: https://github.com/marijnh/acorn
[acorn]: https://github.com/ternjs/acorn
[source-map]: https://github.com/mozilla/source-map
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
[codegen]: http://lisperator.net/uglifyjs/codegen
[compressor]: http://lisperator.net/uglifyjs/compress
[parser]: http://lisperator.net/uglifyjs/parser

24
appveyor.yml Normal file
View File

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

View File

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

View File

@@ -71,7 +71,7 @@ function DEFNODE(type, props, methods, base) {
if (type) {
ctor.prototype.TYPE = ctor.TYPE = type;
}
if (methods) for (i in methods) if (methods.hasOwnProperty(i)) {
if (methods) for (i in methods) if (HOP(methods, i)) {
if (/^\$/.test(i)) {
ctor[i.substr(1)] = methods[i];
} else {
@@ -85,7 +85,7 @@ function DEFNODE(type, props, methods, base) {
return ctor;
};
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file", {
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
}, null);
var AST_Node = DEFNODE("Node", "start end", {
@@ -143,7 +143,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
}, AST_Statement);
function walk_body(node, visitor) {
if (node.body instanceof AST_Statement) {
if (node.body instanceof AST_Node) {
node.body._walk(visitor);
}
else node.body.forEach(function(stat){
@@ -263,6 +263,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: {
@@ -290,6 +294,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", {
@@ -359,13 +370,97 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
}
}, 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) {
if (ex instanceof AST_Object) {
return new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: false,
default: default_seen_above,
names: ex.properties.map(to_fun_args)
});
} else if (ex instanceof AST_ObjectKeyVal) {
if (ex.key instanceof AST_SymbolRef) {
ex.key = to_fun_args(ex.key, 0, [ex.key], ex.default);
}
ex.value = to_fun_args(ex.value, 0, [ex.key], ex.default);
return ex;
} 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 ex;
} else if (ex instanceof AST_SymbolRef) {
return new AST_SymbolFunarg({
name: ex.name,
default: default_seen_above,
start: ex.start,
end: ex.end
});
} else if (ex instanceof AST_Expansion) {
return ex;
} else if (ex instanceof AST_Array) {
return new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: true,
default: default_seen_above,
names: ex.elements.map(to_fun_args)
});
} else if (ex instanceof AST_Assign) {
return to_fun_args(ex.left, undefined, undefined, ex.right);
} 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*] 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);
@@ -385,10 +480,79 @@ 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 default", {
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
$propdoc: {
"names": "[AST_Destructuring|AST_Expansion|AST_Hole|AST_ObjectKeyVal|AST_Symbol] Array of properties or elements",
"is_array": "[Boolean] Whether the destructuring represents an object or array",
"default": "[AST_Node?] Default assign value"
},
_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, {
@@ -542,16 +706,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);
@@ -633,6 +859,13 @@ var AST_Seq = DEFNODE("Seq", "car cdr", {
p = p.cdr;
}
},
len: function() {
if (this.cdr instanceof AST_Seq) {
return this.cdr.len() + 1;
} else {
return 2;
}
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.car._walk(visitor);
@@ -757,38 +990,99 @@ 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.",
default: "[AST_Expression] The default for this parameter, only used when nested inside a binding pattern"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.key instanceof AST_Node)
this.key._walk(visitor);
this.value._walk(visitor);
});
}
});
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote default", {
$documentation: "A key: value object property",
$propdoc: {
quote: "[string] the original quote character"
quote: "[string] the original quote character",
default: "[AST_Expression] The default parameter value, only used when nested inside a binding pattern"
}
}, 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_Symbol = DEFNODE("Symbol", "scope name thedef", {
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 default", {
$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"
thedef: "[SymbolDef/S] the definition of this symbol",
default: "[AST_Expression] The default parameter value, only used when nested inside a binding pattern"
},
$documentation: "Base class for all symbols",
_walk: function (visitor) {
return visitor._visit(this, function() {
if (this.default) this.default._walk(visitor);
});
}
});
var AST_NewTarget = DEFNODE("NewTarget", null, {
$documentation: "A reference to new.target"
});
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
@@ -799,16 +1093,24 @@ 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",
@@ -818,13 +1120,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)",
@@ -849,6 +1171,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() {
@@ -922,32 +1248,59 @@ 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) {
this.visit = callback;
this.stack = [];
this.directives = Object.create(null);
};
TreeWalker.prototype = {
_visit: function(node, descend) {
this.stack.push(node);
this.push(node);
var ret = this.visit(node, descend ? function(){
descend.call(node);
} : noop);
if (!ret && descend) {
descend.call(node);
}
this.stack.pop();
this.pop(node);
return ret;
},
parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)];
},
push: function (node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive) {
this.directives[node.value] = this.directives[node.value] ? "up" : true;
} 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() {
return this.stack.pop();
pop: function(node) {
this.stack.pop();
if (node instanceof AST_Lambda || node instanceof AST_Class) {
this.directives = Object.getPrototypeOf(this.directives);
}
},
self: function() {
return this.stack[this.stack.length - 1];
@@ -960,7 +1313,16 @@ TreeWalker.prototype = {
}
},
has_directive: function(type) {
return this.find_parent(AST_Scope).has_directive(type);
var dir = this.directives[type];
if (dir) return dir;
var node = this.stack[this.stack.length - 1];
if (node instanceof AST_Scope && 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 true;
}
}
},
in_boolean_context: function() {
var stack = this.stack;

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -43,6 +43,22 @@
"use strict";
var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
function is_some_comments(comment) {
var text = comment.value;
var type = comment.type;
if (type == "comment2") {
// multiline comment
return /@preserve|@license|@cc_on/i.test(text);
}
return type == "comment5";
}
function is_comment5(comment) {
return comment.type == "comment5";
}
function OutputStream(options) {
options = defaults(options, {
@@ -51,6 +67,7 @@ function OutputStream(options) {
quote_keys : false,
space_colon : true,
ascii_only : false,
ascii_identifiers: undefined,
unescape_regexps : false,
inline_script : false,
width : 80,
@@ -62,11 +79,49 @@ function OutputStream(options) {
comments : false,
shebang : true,
preserve_line : false,
screw_ie8 : false,
screw_ie8 : true,
preamble : null,
quote_style : 0
quote_style : 0,
keep_quoted_props: false,
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 = options.shebang ? is_comment5 : return_false; // Default case, throw all comments away except shebangs
if (options.comments) {
var comments = options.comments;
if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) {
var regex_pos = options.comments.lastIndexOf("/");
comments = new RegExp(
options.comments.substr(1, regex_pos - 1),
options.comments.substr(regex_pos + 1)
);
}
if (comments instanceof RegExp) {
comment_filter = function(comment) {
return comment.type == "comment5" || comments.test(comment.value);
};
}
else if (typeof comments === "function") {
comment_filter = function(comment) {
return comment.type == "comment5" || comments(this, comment);
};
}
else if (comments === "some") {
comment_filter = is_some_comments;
} else { // NOTE includes "all" option
comment_filter = return_true;
}
}
var indentation = 0;
var current_col = 0;
var current_line = 1;
@@ -74,9 +129,19 @@ function OutputStream(options) {
var OUTPUT = "";
function to_ascii(str, identifier) {
return str.replace(/[\u0080-\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 {
@@ -88,19 +153,23 @@ function OutputStream(options) {
function make_string(str, quote) {
var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g,
function(s, i){
switch (s) {
case "\\": return "\\\\";
case "\b": return "\\b";
case "\f": return "\\f";
case "\n": return "\\n";
case "\r": return "\\r";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
case '"': ++dq; return '"';
case "'": ++sq; return "'";
case "\0": return "\\x00";
case "\\": return "\\\\";
case "\n": return "\\n";
case "\r": return "\\r";
case "\t": return "\\t";
case "\b": return "\\b";
case "\f": return "\\f";
case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff";
case "\0":
return /[0-7]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
}
return s;
});
@@ -110,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();
@@ -125,14 +209,17 @@ function OutputStream(options) {
function encode_string(str, quote) {
var ret = make_string(str, quote);
if (options.inline_script)
if (options.inline_script) {
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
ret = ret.replace(/--\x3e/g, "--\\x3e");
}
return ret;
};
function make_name(name) {
name = name.toString();
if (options.ascii_only)
if (options.ascii_identifiers)
name = to_ascii(name, true);
return name;
};
@@ -148,7 +235,13 @@ function OutputStream(options) {
var last = null;
function last_char() {
return last.charAt(last.length - 1);
var char = last.charAt(last.length - 1);
if (is_surrogate_pair_tail(char)) {
return last.charAt(last.length - 2) + char;
}
return char;
};
function maybe_newline() {
@@ -160,7 +253,7 @@ function OutputStream(options) {
function print(str) {
str = String(str);
var ch = str.charAt(0);
var ch = get_full_char(str, 0);
if (might_need_semicolon) {
might_need_semicolon = false;
@@ -222,6 +315,10 @@ function OutputStream(options) {
OUTPUT += str;
};
var star = function(){
print("*");
}
var space = options.beautify ? function() {
print(" ");
} : function() {
@@ -339,6 +436,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,
@@ -347,7 +445,22 @@ function OutputStream(options) {
force_semicolon : force_semicolon,
to_ascii : to_ascii,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote) { print(encode_string(str, quote)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
if (escape_directive === true && encoded.indexOf("\\") === -1) {
// Insert semicolons to break directive prologue
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
force_semicolon();
}
force_semicolon();
}
print(encoded);
},
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,
with_block : with_block,
@@ -355,6 +468,7 @@ function OutputStream(options) {
with_square : with_square,
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
comment_filter : comment_filter,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
@@ -378,8 +492,14 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
};
var use_asm = false;
var in_directive = false;
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen;
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) {
use_asm = true;
}
function doit() {
self.add_comments(stream);
self.add_source_map(stream);
@@ -392,10 +512,14 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
}
});
AST_Node.DEFMETHOD("print_to_string", function(options){
var s = OutputStream(options);
if (!options) s._readonly = true;
this.print(s);
return s.get();
});
@@ -403,7 +527,8 @@ function OutputStream(options) {
/* -----[ comments ]----- */
AST_Node.DEFMETHOD("add_comments", function(output){
var c = output.option("comments"), self = this;
if (output._readonly) return;
var self = this;
var start = self.start;
if (start && !start._comments_dumped) {
start._comments_dumped = true;
@@ -426,19 +551,7 @@ function OutputStream(options) {
}));
}
if (!c) {
comments = comments.filter(function(comment) {
return comment.type == "comment5";
});
} else if (c.test) {
comments = comments.filter(function(comment){
return c.test(comment.value) || comment.type == "comment5";
});
} else if (typeof c == "function") {
comments = comments.filter(function(comment){
return c(self, comment) || comment.type == "comment5";
});
}
comments = comments.filter(output.comment_filter, self);
// Keep single line comments after nlb, after nlb
if (!output.option("beautify") && comments.length > 0 &&
@@ -489,7 +602,16 @@ function OutputStream(options) {
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
PARENS(AST_Function, function(output){
return first_in_statement(output);
if (first_in_statement(output)) {
return true;
}
if (output.option('wrap_iife')) {
var p = output.parent();
return p instanceof AST_Call && p.expression === this;
}
return false;
});
// same goes for an object literal, because otherwise it would be
@@ -500,7 +622,14 @@ function OutputStream(options) {
PARENS([ AST_Unary, AST_Undefined ], function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this;
return p instanceof AST_PropAccess && p.expression === this
|| p instanceof AST_Call && p.expression === this
|| p instanceof AST_Binary
&& p.operator === "**"
&& this instanceof AST_UnaryPrefix
&& p.left === this
&& this.operator !== "++"
&& this.operator !== "--";
});
PARENS(AST_Seq, function(output){
@@ -512,8 +641,13 @@ function OutputStream(options) {
|| p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|| (p instanceof AST_SymbolConst && p.default === this) // const { xCover = (0, function() {}) }
|| (p instanceof AST_SymbolLet && p.default === this) // let { xCover = (0, function() {}) }
|| (p instanceof AST_SymbolVar && p.default === this) // var { xCover = (0, function() {}) }
|| (p instanceof AST_SymbolCatch && p.default === this) // } catch (xCover = (0, function() {}) ) {
|| 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)
;
});
@@ -540,6 +674,20 @@ 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;
});
PARENS(AST_PropAccess, function(output){
var p = output.parent();
if (p instanceof AST_New && p.expression === this) {
@@ -576,7 +724,7 @@ function OutputStream(options) {
PARENS(AST_New, function(output){
var p = output.parent();
if (no_constructor_parens(this, output)
if (!need_constructor_parens(this, output)
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
return true;
@@ -584,8 +732,12 @@ function OutputStream(options) {
PARENS(AST_Number, function(output){
var p = output.parent();
if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
return true;
if (p instanceof AST_PropAccess && p.expression === this) {
var value = this.getValue();
if (value < 0 || /^0/.test(make_num(value))) {
return true;
}
}
});
PARENS([ AST_Assign, AST_Conditional ], function (output){
@@ -613,6 +765,34 @@ 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 ? "]" : "}");
if (self.default) {
output.space();
output.print('=');
output.space();
self.default.print(output)
}
});
DEFPRINT(AST_Debugger, function(self, output){
output.print("debugger");
output.semicolon();
@@ -620,9 +800,16 @@ function OutputStream(options) {
/* -----[ statements ]----- */
function display_body(body, is_toplevel, output) {
function display_body(body, is_toplevel, output, allow_directives) {
var last = body.length - 1;
in_directive = allow_directives;
body.forEach(function(stmt, i){
if (in_directive === true && !(stmt instanceof AST_Directive ||
stmt instanceof AST_EmptyStatement ||
(stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String)
)) {
in_directive = false;
}
if (!(stmt instanceof AST_EmptyStatement)) {
output.indent();
stmt.print(output);
@@ -631,7 +818,14 @@ function OutputStream(options) {
if (is_toplevel) output.newline();
}
}
if (in_directive === true &&
stmt instanceof AST_SimpleStatement &&
stmt.body instanceof AST_String
) {
in_directive = false;
}
});
in_directive = false;
};
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){
@@ -643,7 +837,7 @@ function OutputStream(options) {
output.semicolon();
});
DEFPRINT(AST_Toplevel, function(self, output){
display_body(self.body, true, output);
display_body(self.body, true, output, true);
output.print("");
});
DEFPRINT(AST_LabeledStatement, function(self, output){
@@ -655,9 +849,9 @@ function OutputStream(options) {
self.body.print(output);
output.semicolon();
});
function print_bracketed(body, output) {
function print_bracketed(body, output, allow_directives) {
if (body.length > 0) output.with_block(function(){
display_body(body, false, output);
display_body(body, false, output, allow_directives);
});
else output.print("{}");
};
@@ -723,7 +917,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);
});
@@ -745,10 +943,23 @@ 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();
self.name.print(output);
if (self.name instanceof AST_Symbol) {
if (typeof self.name.name === "string" && !is_identifier_string(self.name.name)) {
output.print_string(self.name.name);
} else {
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){
@@ -757,12 +968,62 @@ function OutputStream(options) {
});
});
output.space();
print_bracketed(self.body, output);
print_bracketed(self.body, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
});
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].default) {
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);
@@ -779,6 +1040,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);
@@ -810,8 +1082,8 @@ function OutputStream(options) {
// adds the block brackets if needed.
if (!self.body)
return output.force_semicolon();
if (self.body instanceof AST_Do
&& !output.option("screw_ie8")) {
if (self.body instanceof AST_Do) {
// Unconditionally use the if/do-while workaround for all browsers.
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
// croaks with "syntax error" on code like this: if (foo)
// do ... while(cond); else ... we need block brackets
@@ -935,12 +1207,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);
@@ -973,7 +1310,7 @@ function OutputStream(options) {
/* -----[ other expressions ]----- */
DEFPRINT(AST_Call, function(self, output){
self.expression.print(output);
if (self instanceof AST_New && no_constructor_parens(self, output))
if (self instanceof AST_New && !need_constructor_parens(self, output))
return;
output.with_parens(function(){
self.args.forEach(function(expr, i){
@@ -1014,7 +1351,7 @@ function OutputStream(options) {
var expr = self.expression;
expr.print(output);
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.]/i.test(output.last())) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
}
@@ -1045,16 +1382,24 @@ function OutputStream(options) {
output.print(self.operator);
});
DEFPRINT(AST_Binary, function(self, output){
var op = self.operator;
self.left.print(output);
output.space();
output.print(self.operator);
if (self.operator == "<"
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
&& self.left instanceof AST_UnaryPostfix
&& self.left.operator == "--") {
// space is mandatory to avoid outputting -->
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space();
}
output.print(op);
if ((op == "<" || op == "<<")
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "!"
&& self.right.expression instanceof AST_UnaryPrefix
&& self.right.expression.operator == "--") {
// space is mandatory to avoid outputting <!--
// http://javascript.spec.whatwg.org/#comment-syntax
output.print(" ");
} else {
// the space is optional depending on "beautify"
@@ -1104,9 +1449,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"
@@ -1115,28 +1486,99 @@ function OutputStream(options) {
&& parseFloat(key) >= 0) {
output.print(make_num(key));
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
output.print_name(key);
if (quote && output.option("keep_quoted_props")) {
output.print_string(key, quote);
} else {
output.print_name(key);
}
} else {
output.print_string(key, quote);
}
output.colon();
self.value.print(output);
});
DEFPRINT(AST_ObjectKeyVal, function(self, output){
function get_name(self) {
var def = self.value.definition();
return def ? def.mangled_name || def.name : self.value.name;
}
if (output.option("shorthand") &&
self.value instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self) === self.key
) {
self.print_property_name(self.key, self.quote, 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);
}
if (self.default) {
output.space();
output.print('=');
output.space();
self.default.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);
if (self.default) {
output.space();
output.print('=');
output.space();
self.default.print(output)
}
});
DEFPRINT(AST_Undefined, function(self, output){
output.print("void 0");
@@ -1151,17 +1593,18 @@ function OutputStream(options) {
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());
});
DEFPRINT(AST_String, function(self, output){
output.print_string(self.getValue(), self.quote);
output.print_string(self.getValue(), self.quote, in_directive);
});
DEFPRINT(AST_Number, function(self, output){
if (self.literal !== undefined
&& +self.literal === self.value /* paranoid check */
&& self.scope && self.scope.has_directive('use asm')) {
output.print(self.literal);
if (use_asm && self.start && self.start.raw != null) {
output.print(self.start.raw);
} else {
output.print(make_num(self.getValue()));
}
@@ -1257,8 +1700,11 @@ function OutputStream(options) {
};
// self should be AST_New. decide if we want to show parens or not.
function no_constructor_parens(self, output) {
return self.args.length == 0 && !output.option("beautify");
function need_constructor_parens(self, output) {
// Always print parentheses with arguments
if (self.args.length > 0) return true;
return output.option("beautify");
};
function best_of(a) {
@@ -1340,6 +1786,12 @@ function OutputStream(options) {
DEFMAP(AST_Finally, basic_sourcemap_gen);
DEFMAP(AST_Definitions, basic_sourcemap_gen);
DEFMAP(AST_Constant, basic_sourcemap_gen);
DEFMAP(AST_ObjectSetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectGetter, function(self, output){
output.add_mapping(self.start, self.key.name);
});
DEFMAP(AST_ObjectProperty, function(self, output){
output.add_mapping(self.start, self.key);
});

File diff suppressed because one or more lines are too long

View File

@@ -44,10 +44,27 @@
"use strict";
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) {
@@ -65,7 +82,9 @@ function mangle_properties(ast, options) {
reserved : null,
cache : null,
only_cache : false,
regex : null
regex : null,
ignore_quoted : false,
debug : false
});
var reserved = options.reserved;
@@ -81,35 +100,46 @@ function mangle_properties(ast, options) {
}
var regex = options.regex;
var ignore_quoted = options.ignore_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
// the same as passing an empty string.
var debug = (options.debug !== false);
var debug_name_suffix;
if (debug) {
debug_name_suffix = (options.debug === true ? "" : options.debug);
}
var names_to_mangle = [];
var unmangleable = [];
var ignored = {};
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
add(node.key);
add(node.key, ignore_quoted && node.quote);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
add(node.key.name);
}
else if (node instanceof AST_Dot) {
if (this.parent() instanceof AST_Assign) {
add(node.property);
}
add(node.property);
}
else if (node instanceof AST_Sub) {
if (this.parent() instanceof AST_Assign) {
addStrings(node.property);
}
addStrings(node.property, ignore_quoted);
}
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) {
node.key = mangle(node.key);
if (!(ignore_quoted && node.quote))
node.key = mangle(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter
@@ -119,7 +149,13 @@ function mangle_properties(ast, options) {
node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
if (!ignore_quoted)
node.property = mangleStrings(node.property);
}
else if (node instanceof AST_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)) {
@@ -148,13 +184,19 @@ function mangle_properties(ast, options) {
}
function should_mangle(name) {
if (ignore_quoted && name in ignored) return false;
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0;
}
function add(name) {
function add(name, ignore) {
if (ignore) {
ignored[name] = true;
return;
}
if (can_mangle(name))
push_uniq(names_to_mangle, name);
@@ -170,15 +212,31 @@ function mangle_properties(ast, options) {
var mangled = cache.props.get(name);
if (!mangled) {
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled));
if (debug) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(ignore_quoted && debug_mangled in ignored)) {
mangled = debug_mangled;
}
}
// either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) {
// note can_mangle() does not check if the name collides with the 'ignored' set
// (filled with quoted properties when ignore_quoted set). Make sure we add this
// check so we don't collide with a quoted name.
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored));
}
cache.props.set(name, mangled);
}
return mangled;
}
function addStrings(node) {
function addStrings(node, ignore) {
var out = {};
try {
(function walk(node){
@@ -188,7 +246,7 @@ function mangle_properties(ast, options) {
return true;
}
if (node instanceof AST_String) {
add(node.value);
add(node.value, ignore);
return true;
}
if (node instanceof AST_Conditional) {

View File

@@ -49,22 +49,33 @@ 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.constant = false;
this.index = index;
this.id = SymbolDef.next_id++;
};
SymbolDef.next_id = 1;
SymbolDef.prototype = {
unmangleable: function(options) {
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;
@@ -85,43 +96,67 @@ SymbolDef.prototype = {
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, {
screw_ie8: false,
screw_ie8: true,
cache: null
});
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var last_var_had_const_pragma = false;
var nesting = 0;
var in_destructuring = null;
var in_export;
var tw = new TreeWalker(function(node, descend){
if (options.screw_ie8 && node instanceof AST_Catch) {
if (node.is_block_scope()) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(nesting);
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(nesting);
var save_scope = node.parent_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
labels = new Dictionary();
++nesting; descend(); --nesting;
scope = save_scope;
defun = save_defun;
labels = save_labels;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Directive) {
node.scope = scope;
push_uniq(scope.directives, node.value);
return true;
if (node instanceof AST_Export) {
in_export = true;
descend();
in_export = false;
}
if (node instanceof AST_Number) {
node.scope = scope;
return true;
if (node instanceof AST_LabeledStatement) {
var l = node.label;
if (labels.has(l.name)) {
throw new Error(string_template("Label {name} defined twice", l));
}
labels.set(l.name, l);
descend();
labels.del(l.name);
return true; // no descend again
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope)
@@ -131,8 +166,16 @@ 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;
defun.def_variable(node, in_export);
}
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);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
@@ -140,23 +183,53 @@ 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);
}
else if (node instanceof AST_SymbolClass) {
defun.def_variable(node, in_export);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node, in_export);
}
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);
}
else if (node instanceof AST_Var) {
last_var_had_const_pragma = node.has_const_pragma();
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
var def = defun.def_variable(node);
def.constant = node instanceof AST_SymbolConst;
|| node instanceof AST_SymbolConst
|| node instanceof AST_SymbolLet) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export);
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
def.destructuring = in_destructuring;
def.init = tw.parent().value;
}
else if (node instanceof AST_SymbolCatch) {
(options.screw_ie8 ? scope : defun)
.def_variable(node);
}
else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name);
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name,
line: node.start.line,
col: node.start.col
}));
node.thedef = sym;
}
});
self.walk(tw);
// 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) {
@@ -166,9 +239,29 @@ 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;
}
if (node instanceof AST_SymbolRef) {
var name = node.name;
var parent = tw.parent();
if (name == "eval" && parent instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
s.uses_eval = true;
}
}
var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) {
var g;
if (globals.has(name)) {
@@ -179,16 +272,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
g.global = true;
globals.set(name, g);
}
node.thedef = g;
if (name == "eval" && tw.parent() instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
s.uses_eval = true;
}
if (func && name == "arguments") {
func.uses_arguments = true;
}
} else {
node.thedef = sym;
sym = g;
}
node.thedef = sym;
if (parent instanceof AST_Unary && (parent.operator === '++' || parent.operator === '--')
|| parent instanceof AST_Assign && parent.left === node) {
sym.modified = true;
}
node.reference();
return true;
@@ -202,7 +291,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
});
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
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
@@ -213,13 +301,26 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
});
AST_Scope.DEFMETHOD("strict", function(){
return this.has_directive("use strict");
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;
var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end });
var def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def);
});
AST_SymbolRef.DEFMETHOD("reference", function() {
@@ -240,21 +341,20 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("has_directive", function(value){
return this.parent_scope && this.parent_scope.has_directive(value)
|| (this.directives.indexOf(value) >= 0 ? this : null);
AST_Scope.DEFMETHOD("def_function", function(symbol, in_export){
this.functions.set(symbol.name, this.def_variable(symbol, in_export));
});
AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol));
});
AST_Scope.DEFMETHOD("def_variable", function(symbol){
AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export){
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;
}
def.global = !this.parent_scope && !(symbol instanceof AST_SymbolBlockDeclaration);
} else {
def = this.variables.get(symbol.name);
def.orig.push(symbol);
@@ -290,9 +390,13 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
// a function expression's argument cannot shadow the function expression's name
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
// the function's mangled_name is null when keep_fnames is true
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
while (true) {
var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
if (!(tricky_def && tricky_def.mangled_name == name))
if (!tricky_name || tricky_name != name)
return name;
}
});
@@ -303,7 +407,8 @@ AST_Scope.DEFMETHOD("references", function(sym){
});
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
@@ -341,19 +446,30 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
});
AST_Var.DEFMETHOD("has_const_pragma", function() {
var comments_before = this.start && this.start.comments_before;
var lastComment = comments_before && comments_before[comments_before.length - 1];
return lastComment && /@const\b/.test(lastComment.value);
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
except : [],
eval : false,
sort : false,
sort : false, // Ignored. Flag retained for backwards compatibility.
toplevel : false,
screw_ie8 : false,
keep_fnames : false
screw_ie8 : true,
keep_fnames : false,
keep_classnames : false
});
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options);
// Never mangle arguments
options.except.push('arguments');
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of
@@ -384,9 +500,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
a.push(symbol);
}
});
if (options.sort) a.sort(function(a, b){
return b.references.length - a.references.length;
});
to_mangle.push.apply(to_mangle, a);
return;
}
@@ -396,13 +509,19 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.mangled_name = name;
return true;
}
if (options.screw_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;
@@ -453,21 +572,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)

View File

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

View File

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

@@ -59,10 +59,7 @@ function characters(str) {
};
function member(name, array) {
for (var i = array.length; --i >= 0;)
if (array[i] == name)
return true;
return false;
return array.indexOf(name) >= 0;
};
function find_if(func, array) {
@@ -97,17 +94,17 @@ function defaults(args, defs, croak) {
if (args === true)
args = {};
var ret = args || {};
if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i))
DefaultsError.croak("`" + i + "` is not a supported option", defs);
for (var i in defs) if (defs.hasOwnProperty(i)) {
ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
}
return ret;
};
function merge(obj, ext) {
var count = 0;
for (var i in ext) if (ext.hasOwnProperty(i)) {
for (var i in ext) if (HOP(ext, i)) {
obj[i] = ext[i];
count++;
}
@@ -115,6 +112,8 @@ function merge(obj, ext) {
};
function noop() {};
function return_false() { return false; }
function return_true() { return true; }
var MAP = (function(){
function MAP(a, f, backwards) {
@@ -150,7 +149,7 @@ var MAP = (function(){
}
}
else {
for (i in a) if (a.hasOwnProperty(i)) if (doit()) break;
for (i in a) if (HOP(a, i)) if (doit()) break;
}
return top.concat(ret);
};
@@ -230,10 +229,19 @@ function makePredicate(words) {
}
cats.push([words[i]]);
}
function quote(word) {
return JSON.stringify(word).replace(/[\u2028\u2029]/g, function(s) {
switch (s) {
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
}
return s;
});
}
function compareTo(arr) {
if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
if (arr.length == 1) return f += "return str === " + quote(arr[0]) + ";";
f += "switch(str){";
for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
for (var i = 0; i < arr.length; ++i) f += "case " + quote(arr[i]) + ":";
f += "return true}return false;";
}
// When there are more than three length categories, an outer
@@ -308,3 +316,7 @@ Dictionary.fromObject = function(obj) {
dict._size = merge(dict._values, obj);
return dict;
};
function HOP(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}

6
npm-shrinkwrap.json generated
View File

@@ -115,9 +115,9 @@
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
},
"yargs": {
"version": "3.5.4",
"from": "yargs@>=3.5.4 <3.6.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz"
"version": "3.10.0",
"from": "yargs@>=3.10.0 <3.11.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz"
},
"zeparser": {
"version": "0.0.7",

View File

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

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,,];
@@ -72,3 +74,76 @@ constant_join_2: {
var f = "strstr" + variable + "foobarmoo" + foo;
}
}
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]
}
}

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

@@ -0,0 +1,128 @@
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";'
}

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

@@ -0,0 +1,36 @@
ascii_only_true: {
options = {}
beautify = {
ascii_only : true,
screw_ie8 : true,
beautify : false,
}
input: {
function f() {
return "\x000\x001\x007\x008\x00" +
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
"\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff";
}
}
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0e\\x0f"+"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f"+\' !"# ... }~\\x7f\\x80\\x81 ... \\xfe\\xff\\u0fff\\uffff\'}'
}
ascii_only_false: {
options = {}
beautify = {
ascii_only : false,
screw_ie8 : true,
beautify : false,
}
input: {
function f() {
return "\x000\x001\x007\x008\x00" +
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
"\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff";
}
}
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}'
}

View File

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

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

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

View File

@@ -0,0 +1,174 @@
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;
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

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

View File

@@ -11,6 +11,9 @@ concat_1: {
var d = 1 + x() + 2 + 3 + "boo";
var e = 1 + x() + 2 + "X" + 3 + "boo";
// be careful with concatentation with "\0" with octal-looking strings.
var f = "\0" + 360 + "\0" + 8 + "\0";
}
expect: {
var a = "foobar" + x() + "moofoo" + y() + "xyz" + q();
@@ -18,5 +21,6 @@ concat_1: {
var c = 1 + x() + 2 + "boo";
var d = 1 + x() + 2 + 3 + "boo";
var e = 1 + x() + 2 + "X3boo";
var f = "\x00360\08\0";
}
}

View File

@@ -332,59 +332,309 @@ cond_7_1: {
cond_8: {
options = {
conditionals: true,
evaluate : true
evaluate : true,
booleans : false
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
if (condition) {
a = true;
} else {
a = false;
}
if (condition) {
a = !0;
} else {
a = !1;
}
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
// don't compress these
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !!condition;
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = condition ? 1 : false;
a = condition ? 0 : true;
a = !!condition;
a = !condition();
a = !condition;
a = !condition;
a = !!condition && 1;
a = !condition || 0;
a = condition ? 1 : 0;
}
}
cond_8b: {
options = {
conditionals: true,
evaluate : true,
booleans : true
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
if (condition) {
a = true;
} else {
a = false;
}
if (condition) {
a = !0;
} else {
a = !1;
}
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !!condition;
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !condition;
a = !!condition && 1;
a = !condition || 0;
a = condition ? 1 : 0;
}
}
cond_8c: {
options = {
conditionals: true,
evaluate : false,
booleans : false
};
input: {
var a;
// compress these
a = condition ? true : false;
a = !condition ? true : false;
a = condition() ? true : false;
a = condition ? !0 : !1;
a = !condition ? !null : !2;
a = condition() ? !0 : !-3.5;
if (condition) {
a = true;
} else {
a = false;
}
if (condition) {
a = !0;
} else {
a = !1;
}
a = condition ? false : true;
a = !condition ? false : true;
a = condition() ? false : true;
a = condition ? !3 : !0;
a = !condition ? !2 : !0;
a = condition() ? !1 : !0;
if (condition) {
a = false;
} else {
a = true;
}
if (condition) {
a = !1;
} else {
a = !0;
}
a = condition ? 1 : false;
a = !condition ? true : 0;
a = condition ? 1 : 0;
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
a = !!condition;
a = !condition;
a = !!condition() || !-3.5;
a = !!condition;
a = !!condition;
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !!condition;
a = !condition();
a = !condition;
a = !condition;
a = !!condition && 1;
a = !condition || 0;
a = condition ? 1 : 0;
}
}
ternary_boolean_consequent: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f1() { return a == b ? true : x; }
function f2() { return a == b ? false : x; }
function f3() { return a < b ? !0 : x; }
function f4() { return a < b ? !1 : x; }
function f5() { return c ? !0 : x; }
function f6() { return c ? false : x; }
function f7() { return !c ? true : x; }
function f8() { return !c ? !1 : x; }
}
expect: {
function f1() { return a == b || x; }
function f2() { return a != b && x; }
function f3() { return a < b || x; }
function f4() { return !(a < b) && x; }
function f5() { return !!c || x; }
function f6() { return !c && x; }
function f7() { return !c || x; }
function f8() { return !!c && x; }
}
}
ternary_boolean_alternative: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
}
input: {
function f1() { return a == b ? x : true; }
function f2() { return a == b ? x : false; }
function f3() { return a < b ? x : !0; }
function f4() { return a < b ? x : !1; }
function f5() { return c ? x : true; }
function f6() { return c ? x : !1; }
function f7() { return !c ? x : !0; }
function f8() { return !c ? x : false; }
}
expect: {
function f1() { return a != b || x; }
function f2() { return a == b && x; }
function f3() { return !(a < b) || x; }
function f4() { return a < b && x; }
function f5() { return !c || x; }
function f6() { return !!c && x; }
function f7() { return !!c || x; }
function f8() { return !c && x; }
}
}
conditional_and: {
options = {
conditionals: true,
@@ -544,3 +794,115 @@ conditional_or: {
a = condition + 3 || null;
}
}
trivial_boolean_ternary_expressions : {
options = {
conditionals: true,
evaluate : true,
booleans : true
};
input: {
f('foo' in m ? true : false);
f('foo' in m ? false : true);
f(g ? true : false);
f(foo() ? true : false);
f("bar" ? true : false);
f(5 ? true : false);
f(5.7 ? true : false);
f(x - y ? true : false);
f(x == y ? true : false);
f(x === y ? !0 : !1);
f(x < y ? !0 : false);
f(x <= y ? true : false);
f(x > y ? true : !1);
f(x >= y ? !0 : !1);
f(g ? false : true);
f(foo() ? false : true);
f("bar" ? false : true);
f(5 ? false : true);
f(5.7 ? false : true);
f(x - y ? false : true);
f(x == y ? !1 : !0);
f(x === y ? false : true);
f(x < y ? false : true);
f(x <= y ? false : !0);
f(x > y ? !1 : true);
f(x >= y ? !1 : !0);
}
expect: {
f('foo' in m);
f(!('foo' in m));
f(!!g);
f(!!foo());
f(!0);
f(!0);
f(!0);
f(!!(x - y));
f(x == y);
f(x === y);
f(x < y);
f(x <= y);
f(x > y);
f(x >= y);
f(!g);
f(!foo());
f(!1);
f(!1);
f(!1);
f(!(x - y));
f(x != y);
f(x !== y);
f(!(x < y));
f(!(x <= y));
f(!(x > y));
f(!(x >= y));
}
}
issue_1154: {
options = {
conditionals: true,
evaluate : true,
booleans : true,
};
input: {
function f1(x) { return x ? -1 : -1; }
function f2(x) { return x ? +2 : +2; }
function f3(x) { return x ? ~3 : ~3; }
function f4(x) { return x ? !4 : !4; }
function f5(x) { return x ? void 5 : void 5; }
function f6(x) { return x ? typeof 6 : typeof 6; }
function g1() { return g() ? -1 : -1; }
function g2() { return g() ? +2 : +2; }
function g3() { return g() ? ~3 : ~3; }
function g4() { return g() ? !4 : !4; }
function g5() { return g() ? void 5 : void 5; }
function g6() { return g() ? typeof 6 : typeof 6; }
}
expect: {
function f1(x) { return -1; }
function f2(x) { return 2; }
function f3(x) { return -4; }
function f4(x) { return !1; }
function f5(x) { return; }
function f6(x) { return "number"; }
function g1() { return g(), -1; }
function g2() { return g(), 2; }
function g3() { return g(), -4; }
function g4() { return g(), !1; }
function g5() { return g(), void 0; }
function g6() { return g(), "number"; }
}
}

View File

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

View File

@@ -0,0 +1,106 @@
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: {
input: {
var {cc,} = foo;
}
expect_exact: "var{cc}=foo;"
}
nested_destructuring_objects: {
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: {
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: {
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: {
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});"
}

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: {

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

@@ -0,0 +1,127 @@
negative_zero: {
options = { evaluate: true }
input: {
console.log(
-"",
- -"",
1 / (-0),
1 / (-"")
);
}
expect: {
console.log(
-0,
0,
1 / (-0),
1 / (-0)
);
}
}
positive_zero: {
options = { evaluate: true }
input: {
console.log(
+"",
+ -"",
1 / (+0),
1 / (+"")
);
}
expect: {
console.log(
0,
-0,
1 / (0),
1 / (0)
);
}
}
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;
}
}

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;
}
}

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 = { };
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

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

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

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

View File

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

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

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

View File

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

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

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

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

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

View File

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

View File

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

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

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

View File

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

View File

@@ -0,0 +1,30 @@
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
}
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,23 +1,27 @@
remove_redundant_sequence_items: {
options = { side_effects: true };
input: {
(0, 1, eval)();
(0, 1, logThis)();
(0, 1, _decorators.logThis)();
}
expect: {
(0, logThis)();
(0, eval)();
logThis();
(0, _decorators.logThis)();
}
}
dont_remove_lexical_binding_sequence: {
dont_remove_this_binding_sequence: {
options = { side_effects: true };
input: {
(0, eval)();
(0, logThis)();
(0, _decorators.logThis)();
}
expect: {
(0, logThis)();
(0, eval)();
logThis();
(0, _decorators.logThis)();
}
}

View File

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

View File

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

View File

@@ -0,0 +1,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

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

View File

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

View File

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

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

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

View File

@@ -74,3 +74,101 @@ negate_iife_4: {
}();
}
}
negate_iife_nested: {
options = {
negate_iife: true,
sequences: true,
conditionals: true,
};
input: {
function Foo(f) {
this.f = f;
}
new Foo(function() {
(function(x) {
(function(y) {
console.log(y);
})(x);
})(7);
}).f();
}
expect: {
function Foo(f) {
this.f = f;
}
new Foo(function() {
!function(x) {
!function(y) {
console.log(y);
}(x);
}(7);
}).f();
}
}
negate_iife_issue_1073: {
options = {
negate_iife: true,
sequences: true,
conditionals: true,
};
input: {
new (function(a) {
return function Foo() {
this.x = a;
console.log(this);
};
}(7))();
}
expect: {
new (function(a) {
return function Foo() {
this.x = a,
console.log(this);
};
}(7))();
}
}
issue_1254_negate_iife_false: {
options = {
negate_iife: false,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: '(function(){return function(){console.log("test")}})()();'
}
issue_1254_negate_iife_true: {
options = {
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: '!function(){return function(){console.log("test")}}()();'
}
issue_1254_negate_iife_nested: {
options = {
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '!function(){return function(){console.log("test")}}()()()()();'
}

View File

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

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

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

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

@@ -0,0 +1,504 @@
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: {
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: {
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]:{}}}"
}

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

@@ -0,0 +1,159 @@
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: {
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: {
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: {
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

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

View File

@@ -0,0 +1,171 @@
reduce_vars: {
options = {
conditionals : true,
evaluate : true,
global_defs : {
C : 0
},
reduce_vars : true,
unused : true
}
input: {
var A = 1;
(function f0() {
var a = 2;
console.log(a - 5);
console.log(A - 5);
})();
(function f1() {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})();
(function f2(eval) {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})(eval);
(function f3() {
var b = typeof C !== "undefined";
var c = 4;
if (b) {
return 'yes';
} else {
return 'no';
}
})();
console.log(A + 1);
}
expect: {
var A = 1;
(function() {
console.log(-3);
console.log(-4);
})();
(function f1() {
var a = 2;
console.log(-3);
eval("console.log(a);");
})();
(function f2(eval) {
var a = 2;
console.log(-3);
eval("console.log(a);");
})(eval);
(function() {
return "yes";
})();
console.log(2);
}
}
modified: {
options = {
conditionals : true,
evaluate : true,
reduce_vars : true,
unused : true
}
input: {
function f0() {
var a = 1, b = 2;
b++;
console.log(a + 1);
console.log(b + 1);
}
function f1() {
var a = 1, b = 2;
--b;
console.log(a + 1);
console.log(b + 1);
}
function f2() {
var a = 1, b = 2, c = 3;
b = c;
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f3() {
var a = 1, b = 2, c = 3;
b *= c;
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f4() {
var a = 1, b = 2, c = 3;
if (a) {
b = c;
} else {
c = b;
}
console.log(a + b);
console.log(b + c);
// TODO: as "modified" is determined in "figure_out_scope",
// even "passes" wouldn't improve this any further
console.log(a + c);
console.log(a + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
}
}
expect: {
function f0() {
var b = 2;
b++;
console.log(2);
console.log(b + 1);
}
function f1() {
var b = 2;
--b;
console.log(2);
console.log(b + 1);
}
function f2() {
var a = 1, b = 2, c = 3;
b = c;
console.log(a + b);
console.log(b + c);
console.log(4);
console.log(a + b + c);
}
function f3() {
var a = 1, b = 2, c = 3;
b *= c;
console.log(a + b);
console.log(b + c);
console.log(4);
console.log(a + b + c);
}
function f4() {
var a = 1, b = 2, c = 3;
b = c;
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
}
}
}

View File

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

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

@@ -0,0 +1,130 @@
do_screw: {
options = { screw_ie8: true };
beautify = {
screw_ie8: true,
ascii_only: true
};
input: f("\v");
expect_exact: 'f("\\v");';
}
dont_screw: {
options = { screw_ie8: false };
beautify = { screw_ie8: false, ascii_only: true };
input: f("\v");
expect_exact: 'f("\\x0B");';
}
do_screw_try_catch: {
options = { screw_ie8: true };
mangle = { screw_ie8: true };
beautify = { screw_ie8: true };
input: {
good = function(e){
return function(error){
try{
e()
} catch(e) {
error(e)
}
}
};
}
expect: {
good = function(n){
return function(t){
try{
n()
} catch(n) {
t(n)
}
}
};
}
}
dont_screw_try_catch: {
options = { screw_ie8: false };
mangle = { screw_ie8: false };
beautify = { screw_ie8: false };
input: {
bad = function(e){
return function(error){
try{
e()
} catch(e) {
error(e)
}
}
};
}
expect: {
bad = function(n){
return function(n){
try{
t()
} catch(t) {
n(t)
}
}
};
}
}
do_screw_try_catch_undefined: {
options = { screw_ie8: true };
mangle = { screw_ie8: true };
beautify = { screw_ie8: true };
input: {
function a(b){
try {
throw 'Stuff';
} catch (undefined) {
console.log('caught: ' + undefined);
}
console.log('undefined is ' + undefined);
return b === undefined;
};
}
expect: {
function a(o){
try{
throw "Stuff"
} catch (o) {
console.log("caught: "+o)
}
console.log("undefined is " + void 0);
return void 0===o
}
}
}
dont_screw_try_catch_undefined: {
options = { screw_ie8: false };
mangle = { screw_ie8: false };
beautify = { screw_ie8: false };
input: {
function a(b){
try {
throw 'Stuff';
} catch (undefined) {
console.log('caught: ' + undefined);
}
console.log('undefined is ' + undefined);
return b === undefined;
};
}
expect: {
function a(o){
try{
throw "Stuff"
} catch (n) {
console.log("caught: "+n)
}
console.log("undefined is " + void 0);
return void 0===o
}
}
}

View File

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

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

@@ -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

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

View File

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

View File

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

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

@@ -0,0 +1,166 @@
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);"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

29
test/mocha.js Normal file
View File

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

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

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

83
test/mocha/arrow.js Normal file
View File

@@ -0,0 +1,83 @@
var assert = require("assert");
var uglify = require("../../");
describe("Arrow functions", function() {
it("Should not accept spread tokens on non-last parameters or without arguments parentheses", function() {
var tests = [
"var a = ...a => {return a.join()}",
"var b = (a, ...b, c) => { return a + b.join() + c}",
"var c = (...a, b) => a.join()"
];
var test = function(code) {
return function() {
uglify.parse(code, {fromString: true});
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: expand (...)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept holes in object binding patterns, while still allowing a trailing elision", function() {
var tests = [
"f = ({, , ...x} = [1, 2]) => {};"
];
var test = function(code) {
return function() {
uglify.parse(code, {fromString: true});
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: punc (,)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept newlines before arrow token", function() {
var tests = [
"f = foo\n=> 'foo';",
"f = (foo, bar)\n=> 'foo';",
"f = ()\n=> 'foo';",
"foo((bar)\n=>'baz';);"
];
var test = function(code) {
return function() {
uglify.parse(code, {fromString: true});
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected newline before arrow (=>)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept arrow functions in the middle or end of an expression", function() {
var tests = [
"typeof x => 0",
"0 + x => 0"
];
var test = function(code) {
return function() {
uglify.parse(code, {fromString: true});
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: arrow (=>)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
});

34
test/mocha/builtins.js Normal file
View File

@@ -0,0 +1,34 @@
var UglifyJS = require("../../");
var assert = require("assert");
describe("builtins", function() {
it ("Should not mangle builtins", function() {
var test = "function foo(something){\n" +
" return [Object,Array,Function,Number,String,Boolean,Error,Math,Date,RegExp,Symbol,Map,Promise,Proxy,Reflect,Set,WeakMap,WeakSet,Float32Array,something];\n" +
"};";
var result = UglifyJS.minify(test, {fromString: true, parse: {bare_returns: true}}).code;
assert.strictEqual(result.indexOf("something"), -1);
assert.notEqual(result.indexOf("Object"), -1);
assert.notEqual(result.indexOf("Array"), -1);
assert.notEqual(result.indexOf("Function"), -1);
assert.notEqual(result.indexOf("Number"), -1);
assert.notEqual(result.indexOf("String"), -1);
assert.notEqual(result.indexOf("Boolean"), -1);
assert.notEqual(result.indexOf("Error"), -1);
assert.notEqual(result.indexOf("Math"), -1);
assert.notEqual(result.indexOf("Date"), -1);
assert.notEqual(result.indexOf("RegExp"), -1);
assert.notEqual(result.indexOf("Symbol"), -1);
assert.notEqual(result.indexOf("Promise"), -1);
assert.notEqual(result.indexOf("Proxy"), -1);
assert.notEqual(result.indexOf("Reflect"), -1);
assert.notEqual(result.indexOf("Set"), -1);
assert.notEqual(result.indexOf("WeakMap"), -1);
assert.notEqual(result.indexOf("WeakSet"), -1);
assert.notEqual(result.indexOf("Map"), -1);
assert.notEqual(result.indexOf("Float32Array"), -1);
});
});

57
test/mocha/class.js Normal file
View File

@@ -0,0 +1,57 @@
var assert = require("assert");
var uglify = require("../../");
describe("Class", function() {
it("Should not accept spread on non-last parameters in methods", function() {
var tests = [
"class foo { bar(...a, b) { return a.join(b) } }",
"class foo { bar(a, b, ...c, d) { return c.join(a + b) + d } }",
"class foo { *bar(...a, b) { return a.join(b) } }",
"class foo { *bar(a, b, ...c, d) { return c.join(a + b) + d } }"
];
var test = function(code) {
return function() {
uglify.parse(code, {fromString: true});
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message.substr(0, 31) === "SyntaxError: Unexpected token: ";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should return the correct token for class methods", function() {
var tests = [
{
code: "class foo{static test(){}}",
token_value_start: "static",
token_value_end: "}"
},
{
code: "class bar{*procedural(){}}",
token_value_start: "*",
token_value_end: "}"
},
{
code: "class foobar{aMethod(){}}",
token_value_start: "aMethod",
token_value_end: "}"
},
{
code: "class foobaz{get something(){}}",
token_value_start: "get",
token_value_end: "}"
}
];
for (var i = 0; i < tests.length; i++) {
var ast = uglify.parse(tests[i].code);
assert.strictEqual(ast.body[0].properties[0].start.value, tests[i].token_value_start);
assert.strictEqual(ast.body[0].properties[0].end.value, tests[i].token_value_end);
}
});
});

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

@@ -0,0 +1,73 @@
var assert = require("assert");
var exec = require("child_process").exec;
describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) {
this.timeout(15000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
exec(command, function (err, stdout) {
if (err) throw err;
eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object');
assert.strictEqual(true, WrappedUglifyJS.parse('foo;') instanceof WrappedUglifyJS.AST_Node);
done();
});
});
it("Should be able to filter comments correctly with `--comment all`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments all';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "// foo\n/*@preserve*/\n// bar\n\n");
done();
});
});
it("Should be able to filter comments correctly with `--comment <RegExp>`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments /r/';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n// bar\n\n");
done();
});
});
it("Should be able to filter comments correctly with just `--comment`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n\n");
done();
});
});
it("Should append source map to output when using --source-map-inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js --source-map-inline';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFPLFdBQ1AsUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n");
done();
});
});
it("should not append source map to output when not using --source-map-inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n");
done();
});
});
});

View File

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

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

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

View File

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

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

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

36
test/mocha/eof.js Normal file
View File

@@ -0,0 +1,36 @@
var assert = require("assert");
var uglify = require("../../");
describe("EOF", function() {
it("Should test code for at least throwing syntax error when incomplete", function() {
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
/^SyntaxError: /.test(e.message);
}
var parse = function(test) {
return function() {
uglify.parse(test);
}
}
// Chops off 1 char at a time until limit or start of string is reached
// The passed code must still be valid when unchopped
var test_eol = function(input, chopLimit) {
if (chopLimit === undefined) {
chopLimit = input.length - 1;
}
assert.doesNotThrow(parse(input), "Expected valid code for \n" + input);
for (var i = input.length - 1; chopLimit > 0; chopLimit--, i--) {
var code = input.substr(0, i);
assert.throws(parse(code), error, code);
}
}
test_eol("var \\u1234", 7); // Incomplete identifier
test_eol("'Incomplete string'");
test_eol("/Unterminated regex/");
test_eol("` Unterminated template string`");
test_eol("/* Unfinishing multiline comment */");
});
});

32
test/mocha/expression.js Normal file
View File

@@ -0,0 +1,32 @@
var assert = require("assert");
var uglify = require("../../");
describe("Expression", function() {
it("Should not allow the first exponentiation operator to be prefixed with an unary operator", function() {
var tests = [
"+5 ** 3",
"-5 ** 3",
"~5 ** 3",
"!5 ** 3",
"void 5 ** 3",
"typeof 5 ** 3",
"delete 5 ** 3",
"var a = -(5) ** 3;"
];
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error &&
/^SyntaxError: Unexpected token: operator \((?:[!+~-]|void|typeof|delete)\)/.test(e.message);
}
var exec = function(test) {
return function() {
uglify.parse(test);
}
}
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail, tests[i]);
}
});
});

233
test/mocha/function.js Normal file
View File

@@ -0,0 +1,233 @@
var assert = require("assert");
var uglify = require("../../");
describe("Function", function() {
it ("Should parse binding patterns correctly", function() {
// Function argument nodes are correct
function get_args(args) {
return args.map(function (arg) {
return [arg.TYPE, arg.name];
});
}
// Destructurings as arguments
var destr_fun1 = uglify.parse('(function ({a, b}) {})').body[0].body;
var destr_fun2 = uglify.parse('(function ([a, [b]]) {})').body[0].body;
var destr_fun3 = uglify.parse('({a, b}) => null').body[0].body;
var destr_fun4 = uglify.parse('([a, [b]]) => null').body[0].body;
assert.equal(destr_fun1.argnames.length, 1);
assert.equal(destr_fun2.argnames.length, 1);
assert.equal(destr_fun3.argnames.length, 1);
assert.equal(destr_fun4.argnames.length, 1);
var destruct1 = destr_fun1.argnames[0];
var destruct2 = destr_fun2.argnames[0];
var destruct3 = destr_fun3.argnames[0];
var destruct4 = destr_fun4.argnames[0];
assert(destruct1 instanceof uglify.AST_Destructuring);
assert(destruct2 instanceof uglify.AST_Destructuring);
assert(destruct3 instanceof uglify.AST_Destructuring);
assert(destruct4 instanceof uglify.AST_Destructuring);
assert(destruct2.names[1] instanceof uglify.AST_Destructuring);
assert(destruct4.names[1] instanceof uglify.AST_Destructuring);
assert.equal(destruct1.start.value, '{');
assert.equal(destruct1.end.value, '}');
assert.equal(destruct2.start.value, '[');
assert.equal(destruct2.end.value, ']');
assert.equal(destruct3.start.value, '{');
assert.equal(destruct3.end.value, '}');
assert.equal(destruct4.start.value, '[');
assert.equal(destruct4.end.value, ']');
assert.equal(destruct1.is_array, false);
assert.equal(destruct2.is_array, true);
assert.equal(destruct3.is_array, false);
assert.equal(destruct4.is_array, true);
var aAndB = [
['SymbolFunarg', 'a'],
['SymbolFunarg', 'b']
];
assert.deepEqual(
[
destruct1.names[0].TYPE,
destruct1.names[0].name
],
aAndB[0]);
assert.deepEqual(
[
destruct1.names[1].TYPE,
destruct1.names[1].name
],
aAndB[1]);
assert.deepEqual(
[
destruct2.names[0].TYPE,
destruct2.names[0].name
],
aAndB[0]);
assert.deepEqual(
[
destruct2.names[1].names[0].TYPE,
destruct2.names[1].names[0].name
],
aAndB[1]);
assert.strictEqual(typeof destruct3.names[0].key, "string");
assert.strictEqual(destruct3.names[0].key, "a");
assert.strictEqual(typeof destruct3.names[1].key, "string");
assert.strictEqual(destruct3.names[1].key, "b");
assert.deepEqual(
[
destruct4.names[0].TYPE,
destruct4.names[0].name
],
aAndB[0]);
assert.deepEqual(
[
destruct4.names[1].names[0].TYPE,
destruct4.names[1].names[0].name
],
aAndB[1]);
assert.deepEqual(
get_args(destr_fun1.args_as_names()),
aAndB);
assert.deepEqual(
get_args(destr_fun2.args_as_names()),
aAndB);
assert.deepEqual(
get_args(destr_fun3.args_as_names()),
aAndB);
assert.deepEqual(
get_args(destr_fun4.args_as_names()),
aAndB);
// Making sure we don't accidentally accept things which
// Aren't argument destructurings
assert.throws(function () {
uglify.parse('(function ( { a, [ b ] } ) { })')
});
assert.throws(function () {
uglify.parse('(function (1) { })');
}, /Invalid function parameter/);
assert.throws(function () {
uglify.parse('(function (this) { })');
});
assert.throws(function () {
uglify.parse('(function ([1]) { })');
}, /Invalid function parameter/);
assert.throws(function () {
uglify.parse('(function [a] { })');
});
// generators
var generators_def = uglify.parse('function* fn() {}').body[0];
assert.equal(generators_def.is_generator, true);
assert.throws(function () {
uglify.parse('function* (){ }');
});
var generators_yield_def = uglify.parse('function* fn() {\nyield remote();\}').body[0].body[0];
assert.strictEqual(generators_yield_def.body.is_star, false);
});
it("Should not accept spread on non-last parameters", function() {
var tests = [
"var a = function(...a, b) { return a.join(b) }",
"var b = function(a, b, ...c, d) { return c.join(a + b) + d }",
"function foo(...a, b) { return a.join(b) }",
"function bar(a, b, ...c, d) { return c.join(a + b) + d }",
"var a = function*(...a, b) { return a.join(b) }",
"var b = function*(a, b, ...c, d) { return c.join(a + b) + d }",
"function* foo(...a, b) { return a.join(b) }",
"function* bar(a, b, ...c, d) { return c.join(a + b) + d }"
];
var test = function(code) {
return function() {
uglify.parse(code, {fromString: true});
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message.substr(0, 31) === "SyntaxError: Unexpected token: ";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept empty parameters after elision", function() {
var tests = [
"(function(,){})()",
"(function(a,){})()",
];
var test = function(code) {
return function() {
uglify.parse(code, {fromString: true});
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Invalid function parameter";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept an initializer when parameter is a rest parameter", function() {
var tests = [
"(function(...a = b){})()",
"(function(a, ...b = [c, d]))"
];
var test = function(code) {
return function () {
uglify.parse(code, {fromString: true});
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
it("Shoult not accept duplicated identifiers inside parameters in strict mode or when using default assigment or spread", function() {
// From: ES2016 9.2.12 FunctionDeclarationInstantiation (func, argumentsList)
// NOTE Early errors ensure that duplicate parameter names can only occur
// in non-strict functions that do not have parameter default values or
// rest parameters.
var tests = [
"(function(a = 1, a){})()",
"(function(a, [a = 3]){})()",
"(function(a, b, c, d, [{e: [...a]}]){})()",
"'use strict'; (function(a, a){})",
"(function({a, a = b}))",
"(function(a, [...a]){})",
"(function(a, ...a){})",
"(function(a, [a, ...b]){})",
"(function(a, {b: a, c: [...d]}){})",
"(function(a, a, {b: [...c]}){})"
];
var test = function(code) {
return function () {
uglify.parse(code, {fromString: true});
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error &&
/^SyntaxError: Parameter [a-zA-Z]+ was used already$/.test(e.message);
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
});

28
test/mocha/glob.js Normal file
View File

@@ -0,0 +1,28 @@
var Uglify = require('../../');
var assert = require("assert");
describe("minify() with input file globs", function() {
it("minify() with one input file glob string.", function() {
var result = Uglify.minify("test/input/issue-1242/foo.*", {
compress: { collapse_vars: true }
});
assert.strictEqual(result.code, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);');
});
it("minify() with an array of one input file glob.", function() {
var result = Uglify.minify([
"test/input/issue-1242/b*.es5",
], {
compress: { collapse_vars: true }
});
assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}');
});
it("minify() with an array of multiple input file globs.", function() {
var result = Uglify.minify([
"test/input/issue-1242/???.es5",
"test/input/issue-1242/*.js",
], {
compress: { collapse_vars: true }
});
assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}function foo(n){print("Foo:",2*n)}var print=console.log.bind(console);print("qux",bar(3),baz(12)),foo(11);');
});
});

View File

@@ -0,0 +1,19 @@
var Uglify = require('../../');
var assert = require("assert");
describe("Huge number of comments.", function() {
it("Should parse and compress code with thousands of consecutive comments", function() {
var js = 'function lots_of_comments(x) { return 7 -';
var i;
for (i = 1; i <= 5000; ++i) { js += "// " + i + "\n"; }
for (; i <= 10000; ++i) { js += "/* " + i + " */ /**/"; }
js += "x; }";
var result = Uglify.minify(js, {
fromString: true,
mangle: false,
compress: {}
});
assert.strictEqual(result.code, "function lots_of_comments(x){return 7-x}");
});
});

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