This commit is contained in:
2022-07-18 02:50:52 +00:00
parent befd344ab0
commit 06181b34d6
8569 changed files with 818704 additions and 352705 deletions
+428 -145
View File
@@ -4,10 +4,11 @@ UglifyJS 3
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
#### Note:
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS/tree/v2.x)**.
- `uglify-js` only supports JavaScript (ECMAScript 5).
- To minify ECMAScript 2015 or above, transpile using tools like [Babel](https://babeljs.io/).
- `uglify-js` supports JavaScript and most language features in ECMAScript.
- For more exotic parts of ECMAScript, process your source file with transpilers
like [Babel](https://babeljs.io/) before passing onto `uglify-js`.
- `uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage)
that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS/tree/v2.x).
Install
-------
@@ -84,13 +85,16 @@ a double dash to prevent input files being used as option arguments:
1 - single
2 - double
3 - original
`wrap_iife` Wrap IIFEs in parenthesis. Note: you may
`wrap_iife` Wrap IIFEs in parentheses. Note: you may
want to disable `negate_iife` under
compressor options.
-O, --output-opts [options] Specify output options (`beautify` disabled by default).
-o, --output <file> Output file path (default STDOUT). Specify `ast` or
`spidermonkey` to write UglifyJS or SpiderMonkey AST
as JSON to STDOUT respectively.
--annotations Process and preserve comment annotations.
(`/*@__PURE__*/` or `/*#__PURE__*/`)
--no-annotations Ignore and discard comment annotations.
--comments [filter] Preserve copyright comments in the output. By
default this works like Google Closure, keeping
JSDoc-style comments that contain "@license" or
@@ -107,12 +111,14 @@ a double dash to prevent input files being used as option arguments:
-d, --define <expr>[=value] Global definitions.
-e, --enclose [arg[:value]] Embed everything in a big function, with configurable
argument(s) & value(s).
--ie8 Support non-standard Internet Explorer 8.
Equivalent to setting `ie8: true` in `minify()`
--ie Support non-standard Internet Explorer.
Equivalent to setting `ie: true` in `minify()`
for `compress`, `mangle` and `output` options.
By default UglifyJS will not try to be IE-proof.
--keep-fargs Do not mangle/drop function arguments.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--module Process input as ES module (implies --toplevel)
--name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options:
@@ -133,8 +139,16 @@ a double dash to prevent input files being used as option arguments:
`//# sourceMappingURL`.
--timings Display operations run time on STDERR.
--toplevel Compress and/or mangle variables in top level scope.
--v8 Support non-standard Chrome & Node.js
Equivalent to setting `v8: true` in `minify()`
for `mangle` and `output` options.
By default UglifyJS will not try to be v8-proof.
--verbose Print diagnostic messages.
--warn Print warning messages.
--webkit Support non-standard Safari/Webkit.
Equivalent to setting `webkit: true` in `minify()`
for `compress`, `mangle` and `output` options.
By default UglifyJS will not try to be Safari-proof.
--wrap <name> Embed everything in a big function, making the
“exports” and “global” variables available. You
need to pass an argument to this option to
@@ -212,17 +226,16 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `toplevel` (default `false`) -- mangle names declared in the top level scope.
- `eval` (default: `false`) mangle names visible in scopes where `eval` or
`with` are used.
- `eval` (default `false`) -- mangle names visible in scopes where `eval` or `with` are used.
- `reserved` (default: `[]`) — when mangling is enabled but you want to
prevent certain names from being mangled, you can declare those names with
`--mangle reserved` — pass a comma-separated list of names. For example:
When mangling is enabled but you want to prevent certain names from being
mangled, you can declare those names with `--mangle reserved` — pass a
comma-separated list of names. For example:
uglifyjs ... -m reserved=['$','require','exports']
uglifyjs ... -m reserved=['$','require','exports']
to prevent the `require`, `exports` and `$` names from being changed.
to prevent the `require`, `exports` and `$` names from being changed.
### CLI mangling property names (`--mangle-props`)
@@ -483,43 +496,60 @@ if (result.error) throw result.error;
## Minify options
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
- `annotations` — pass `false` to ignore all comment annotations and elide them
from output. Useful when, for instance, external tools incorrectly applied
`/*@__PURE__*/` or `/*#__PURE__*/`. Pass `true` to both compress and retain
comment annotations in output to allow for further processing downstream.
- `compress` (default: `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compress options](#compress-options).
- `ie8` (default `false`) -- set to `true` to support IE8.
- `ie` (default: `false`) — enable workarounds for Internet Explorer bugs.
- `keep_fnames` (default: `false`) -- pass `true` to prevent discarding or mangling
- `keep_fargs` (default: `false`) pass `true` to prevent discarding or mangling
of function arguments.
- `keep_fnames` (default: `false`) — pass `true` to prevent discarding or mangling
of function names. Useful for code relying on `Function.prototype.name`.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass
- `mangle` (default: `true`) — pass `false` to skip mangling names, or pass
an object to specify [mangle options](#mangle-options) (see below).
- `mangle.properties` (default `false`) — a subcategory of the mangle option.
- `mangle.properties` (default: `false`) — a subcategory of the mangle option.
Pass an object to specify custom [mangle property options](#mangle-properties-options).
- `nameCache` (default `null`) -- pass an empty object `{}` or a previously
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` and support for top-level `await`,
alongside with `toplevel` enabled.
- `nameCache` (default: `null`) — pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
a read/write property. `minify()` will read the name cache state of this
object and update it during minification so that it may be
reused or externally persisted by the user.
- `output` (default `null`) — pass an object if you wish to specify
- `output` (default: `null`) — pass an object if you wish to specify
additional [output options](#output-options). The defaults are optimized
for best compression.
- `parse` (default `{}`) — pass an object if you wish to specify some
- `parse` (default: `{}`) — pass an object if you wish to specify some
additional [parse options](#parse-options).
- `sourceMap` (default `false`) -- pass an object if you wish to specify
- `sourceMap` (default: `false`) pass an object if you wish to specify
[source map options](#source-map-options).
- `toplevel` (default `false`) -- set to `true` if you wish to enable top level
- `toplevel` (default: `false`) set to `true` if you wish to enable top level
variable and function name mangling and to drop unused variables and functions.
- `warnings` (default `false`) — pass `true` to return compressor warnings
- `v8` (default: `false`) — enable workarounds for Chrome & Node.js bugs.
- `warnings` (default: `false`) — pass `true` to return compressor warnings
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
- `webkit` (default: `false`) — enable workarounds for Safari/WebKit bugs.
PhantomJS users should set this option to `true`.
## Minify options structure
```javascript
@@ -545,7 +575,6 @@ if (result.error) throw result.error;
},
nameCache: null, // or specify a name cache object
toplevel: false,
ie8: false,
warnings: false,
}
```
@@ -602,107 +631,131 @@ to be `false` and all symbol names will be omitted.
## Parse options
- `bare_returns` (default `false`) -- support top level `return` statements
- `bare_returns` (default: `false`) support top level `return` statements
- `html5_comments` (default `true`)
- `expression` (default: `false`) — parse as a single expression, e.g. JSON
- `shebang` (default `true`) -- support `#!command` as the first line
- `html5_comments` (default: `true`) — process HTML comment as workaround for
browsers which do not recognise `<script>` tags
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` and support for top-level `await`.
- `shebang` (default: `true`) — support `#!command` as the first line
## Compress options
- `arguments` (default: `true`) -- replace `arguments[index]` with function
- `annotations` (default: `true`) — Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `arguments` (default: `true`) — replace `arguments[index]` with function
parameter name whenever possible.
- `assignments` (default: `true`) -- apply optimizations to assignment expressions.
- `arrows` (default: `true`) apply optimizations to arrow functions
- `booleans` (default: `true`) -- various optimizations for boolean context,
- `assignments` (default: `true`) — apply optimizations to assignment expressions
- `awaits` (default: `true`) — apply optimizations to `await` expressions
- `booleans` (default: `true`) — various optimizations for boolean context,
for example `!!a ? b : c → a ? b : c`
- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables,
- `collapse_vars` (default: `true`) Collapse single-use non-constant variables,
side effects permitting.
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
- `comparisons` (default: `true`) apply certain optimizations to binary nodes,
e.g. `!(a <= b) → a > b`, attempts to negate binary nodes, e.g.
`a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
- `conditionals` (default: `true`) apply optimizations for `if`-s and conditional
expressions
- `dead_code` (default: `true`) -- remove unreachable code
- `dead_code` (default: `true`) remove unreachable code
- `directives` (default: `true`) -- remove redundant or non-standard directives
- `default_values` (default: `true`) — drop overshadowed default values
- `drop_console` (default: `false`) -- Pass `true` to discard calls to
- `directives` (default: `true`) — remove redundant or non-standard directives
- `drop_console` (default: `false`) — Pass `true` to discard calls to
`console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead.
- `drop_debugger` (default: `true`) -- remove `debugger;` statements
- `drop_debugger` (default: `true`) remove `debugger;` statements
- `evaluate` (default: `true`) -- Evaluate expression for shorter constant
- `evaluate` (default: `true`) Evaluate expression for shorter constant
representation. Pass `"eager"` to always replace function calls whenever
possible, or a positive integer to specify an upper bound for each individual
evaluation in number of characters.
- `expression` (default: `false`) -- Pass `true` to preserve completion values
- `expression` (default: `false`) Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `functions` (default: `true`) -- convert declarations from `var`to `function`
- `functions` (default: `true`) convert declarations from `var` to `function`
whenever possible.
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `global_defs` (default: `{}`) see [conditional compilation](#conditional-compilation)
- `hoist_funs` (default: `false`) -- hoist function declarations
- `hoist_exports` (default: `true`) hoist `export` statements to facilitate
various `compress` and `mangle` optimizations.
- `hoist_props` (default: `true`) -- hoist properties from constant object and
- `hoist_funs` (default: `false`) hoist function declarations
- `hoist_props` (default: `true`) — hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher,
and the `compress` option `toplevel` enabled.
works best with `toplevel` and `mangle` enabled, alongside with `compress` option
`passes` set to `2` or higher.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
- `hoist_vars` (default: `false`) hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general)
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
- `if_return` (default: `true`) optimizations for if/return and if/continue
- `inline` (default: `true`) -- inline calls to function with simple/`return` statement:
- `false` -- same as `0`
- `0` -- disabled inlining
- `1` -- inline simple functions
- `2` -- inline functions with arguments
- `3` -- inline functions with arguments and variables
- `true` -- same as `3`
- `imports` (default: `true`) — drop unreferenced import symbols when used with `unused`
- `join_vars` (default: `true`) -- join consecutive `var` statements
- `inline` (default: `true`) — inline calls to function with simple/`return` statement:
- `false` — same as `0`
- `0` — disabled inlining
- `1` — inline simple functions
- `2` — inline functions with arguments
- `3` — inline functions with arguments and variables
- `4` — inline functions with arguments, variables and statements
- `true` — same as `4`
- `keep_fargs` (default: `strict`) -- Discard unused function arguments. Code
which relies on `Function.length` will break if this is done indiscriminately,
i.e. when passing `true`. Pass `false` to always retain function arguments.
- `join_vars` (default: `true`) — join consecutive `var` statements
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle-options).
- `keep_fargs` (default: `false`) — discard unused function arguments except
when unsafe to do so, e.g. code which relies on `Function.prototype.length`.
Pass `true` to always retain function arguments.
- `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
- `keep_infinity` (default: `false`) Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
- `loops` (default: `true`) optimizations for `do`, `while` and `for` loops
when we can statically determine the condition.
- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions"
- `merge_vars` (default: `true`) — combine and reuse variables.
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` alongside with `toplevel` enabled.
- `negate_iife` (default: `true`) — negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the
code generator would insert.
- `objects` (default: `true`) -- compact duplicate keys in object literals.
- `objects` (default: `true`) compact duplicate keys in object literals.
- `passes` (default: `1`) -- The maximum number of times to run compress.
- `passes` (default: `1`) The maximum number of times to run compress.
In some cases more than one pass leads to further compressed code. Keep in
mind more passes will take more time.
- `properties` (default: `true`) -- rewrite property access using the dot notation, for
- `properties` (default: `true`) rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `pure_funcs` (default: `null`) -- You can pass an array of names and
- `pure_funcs` (default: `null`) You can pass an array of names and
UglifyJS will assume that those functions do not produce side
effects. DANGER: will not check if the name is redefined in scope.
An example case here, for instance `var q = Math.floor(a/b)`. If
@@ -714,22 +767,24 @@ to be `false` and all symbol names will be omitted.
overhead (compression will be slower). Make sure symbols under `pure_funcs`
are also under `mangle.reserved` to avoid mangling.
- `pure_getters` (default: `"strict"`) -- If you pass `true` for
- `pure_getters` (default: `"strict"`) If you pass `true` for
this, UglifyJS will assume that object property access
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `reduce_funcs` (default: `true`) -- Allows single-use functions to be
- `reduce_funcs` (default: `true`) Allows single-use functions to be
inlined as function expressions when permissible allowing further
optimization. Enabled by default. Option depends on `reduce_vars`
being enabled. Some code runs faster in the Chrome V8 engine if this
option is disabled. Does not negatively impact other major browsers.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
- `reduce_vars` (default: `true`) Improve optimization on variables assigned with and
used as constant values.
- `sequences` (default: `true`) -- join consecutive simple statements using the
- `rests` (default: `true`) — apply optimizations to rest parameters
- `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`
@@ -738,64 +793,68 @@ to be `false` and all symbol names will be omitted.
occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended.
- `side_effects` (default: `true`) -- Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `side_effects` (default: `true`) — drop extraneous code which does not affect
outcome of runtime execution.
- `strings` (default: `true`) -- compact string concatenations.
- `spreads` (default: `true`) — flatten spread expressions.
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `strings` (default: `true`) — compact string concatenations.
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
both unreferenced functions and variables)
- `switches` (default: `true`) — de-duplicate and remove unreachable `switch` branches
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
- `templates` (default: `true`) — compact template literals by embedding expressions
and/or converting to string literals, e.g. `` `foo ${42}` → "foo 42"``
- `top_retain` (default: `null`) — prevent specific toplevel functions and
variables from `unused` removal (can be array, comma-separated, RegExp or
function. Implies `toplevel`)
- `typeofs` (default: `true`) -- Transforms `typeof foo == "undefined"` into
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues.
- `toplevel` (default: `false`) — drop unreferenced functions (`"funcs"`) and/or
variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
both unreferenced functions and variables)
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `typeofs` (default: `true`) — compress `typeof` expressions, e.g.
`typeof foo == "undefined" → void 0 === foo`
- `unsafe_comps` (default: `false`) -- compress expressions like `a <= b` assuming
none of the operands can be (coerced to) `NaN`.
- `unsafe` (default: `false`) — apply "unsafe" transformations (discussion below)
- `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
- `unsafe_comps` (default: `false`) — assume operands cannot be (coerced to) `NaN`
in numeric comparisons, e.g. `a <= b`. In addition, expressions involving `in`
or `instanceof` would never throw.
- `unsafe_Function` (default: `false`) — compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: `false`) -- optimize numerical expressions like
- `unsafe_math` (default: `false`) — optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_proto` (default: `false`) -- optimize expressions like
- `unsafe_proto` (default: `false`) — optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
- `unsafe_regexp` (default: `false`) — enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `unsafe_undefined` (default: `false`) -- substitute `void 0` if there is a
- `unsafe_undefined` (default: `false`) — substitute `void 0` if there is a
variable named `undefined` in scope (variable name will be mangled, typically
reduced to a single character)
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple
- `unused` (default: `true`) — drop unreferenced functions and variables (simple
direct variable assignments do not count as references unless set to `"keep_assign"`)
- `varify` (default: `true`) — convert block-scoped declaractions into `var`
whenever safe to do so
- `yields` (default: `true`) — apply optimizations to `yield` expressions
## Mangle options
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
- `eval` (default: `false`) — Pass `true` to mangle names visible in scopes
where `eval` or `with` are used.
- `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](#compress-options).
- `reserved` (default `[]`) -- Pass an array of identifiers that should be
- `reserved` (default: `[]`) — Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` (default `false`) -- Pass `true` to mangle names declared in the
- `toplevel` (default: `false`) — Pass `true` to mangle names declared in the
top level scope.
Examples:
@@ -822,18 +881,21 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options
- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin
- `builtins` (default: `false`) — Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
- `debug` (default: `false`) -— Mangle names with the original name still present.
- `debug` (default: `false`) — Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `keep_quoted` (default: `false`) -Only mangle unquoted property names.
- `keep_fargs` (default: `false`) — Use `true` to prevent mangling of function
arguments.
- `regex` (default: `null`) -Pass a RegExp literal to only mangle property
- `keep_quoted` (default: `false`) — Only mangle unquoted property names.
- `regex` (default: `null`) — Pass a RegExp literal to only mangle property
names matching the regular expression.
- `reserved` (default: `[]`) -- Do not mangle property names listed in the
- `reserved` (default: `[]`) — Do not mangle property names listed in the
`reserved` array.
## Output options
@@ -842,76 +904,81 @@ The code generator tries to output shortest code possible by default. In
case you want beautified output, pass `--beautify` (`-b`). Optionally you
can pass additional arguments that control the code output:
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
- `annotations` (default: `false`) — pass `true` to retain comment annotations
`/*@__PURE__*/` or `/*#__PURE__*/`, otherwise they will be discarded even if
`comments` is set.
- `ascii_only` (default: `false`) — escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `beautify` (default `true`) -- whether to actually beautify the output.
- `beautify` (default: `true`) — whether to actually beautify the output.
Passing `-b` will set this to true, but you might need to pass `-b` even
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `braces` (default `false`) -- always insert braces in `if`, `for`,
- `braces` (default: `false`) — always insert braces in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
- `comments` (default `false`) -- pass `true` or `"all"` to preserve all
- `comments` (default: `false`) — pass `true` or `"all"` to preserve all
comments, `"some"` to preserve multi-line comments that contain `@cc_on`,
`@license`, or `@preserve` (case-insensitive), a regular expression string
(e.g. `/^!/`), or a function which returns `boolean`, e.g.
```js
```javascript
function(node, comment) {
return comment.value.indexOf("@type " + node.TYPE) >= 0;
}
```
- `indent_level` (default `4`)
- `galio` (default: `false`) — enable workarounds for ANT Galio bugs
- `indent_start` (default `0`) -- prefix all lines by that many spaces
- `indent_level` (default: `4`) — indent by specified number of spaces or the
exact whitespace sequence supplied, e.g. `"\t"`.
- `inline_script` (default `true`) -- escape HTML comments and the slash in
- `indent_start` (default: `0`) — prefix all lines by whitespace sequence
specified in the same format as `indent_level`.
- `inline_script` (default: `true`) — escape HTML comments and the slash in
occurrences of `</script>` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
- `keep_quoted_props` (default: `false`) when turned on, prevents stripping
quotes from property names in object literals.
- `max_line_len` (default `false`) -- maximum line length (for uglified code)
- `max_line_len` (default: `false`) maximum line length (for uglified code)
- `preamble` (default `null`) -- when passed it must be a string and
- `preamble` (default: `null`) when passed it must be a string and
it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
licensing information, for example.
- `preserve_line` (default `false`) -- pass `true` to retain line numbering on
- `preserve_line` (default: `false`) pass `true` to retain line numbering on
a best effort basis.
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
- `quote_keys` (default: `false`) pass `true` to quote all keys in literal
objects
- `quote_style` (default `0`) -- preferred quote style for strings (affects
- `quote_style` (default: `0`) preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
- `0` prefers double quotes, switches to single quotes when there are
more double quotes in the string itself. `0` is best for gzip size.
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `1` always use single quotes
- `2` always use double quotes
- `3` always use the original quotes
- `semicolons` (default `true`) -- separate statements with semicolons. If
- `semicolons` (default: `true`) separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
- `shebang` (default: `true`) preserve shebang `#!` in preamble (bash scripts)
- `webkit` (default `false`) -- enable workarounds for WebKit bugs.
PhantomJS users should set this option to `true`.
- `width` (default `80`) -- only takes effect when beautification is on, this
- `width` (default: `80`) — only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
- `wrap_iife` (default: `false`) pass `true` to wrap immediately invoked
function expressions. See
[#640](https://github.com/mishoo/UglifyJS/issues/640) for more details.
@@ -1112,29 +1179,25 @@ in total it's a bit more than just using UglifyJS's own parser.
It's not well known, but whitespace removal and symbol mangling accounts
for 95% of the size reduction in minified code for most JavaScript - not
elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to
[`butternut`](https://www.npmjs.com/package/butternut):
Uglify builds by 3 to 5 times.
| d3.js | minify size | gzip size | minify time (seconds) |
| --- | ---: | ---: | ---: |
| original | 451,131 | 108,733 | - |
| uglify-js@3.0.24 mangle=false, compress=false | 316,600 | 85,245 | 0.70 |
| uglify-js@3.0.24 mangle=true, compress=false | 220,216 | 72,730 | 1.13 |
| butternut@0.4.6 | 217,568 | 72,738 | 1.41 |
| uglify-js@3.0.24 mangle=true, compress=true | 212,511 | 71,560 | 3.36 |
| babili@0.1.4 | 210,713 | 72,140 | 12.64 |
| original | 511,371 | 119,932 | - |
| uglify-js@3.13.0 mangle=false, compress=false | 363,988 | 95,695 | 0.56 |
| uglify-js@3.13.0 mangle=true, compress=false | 253,305 | 81,281 | 0.99 |
| uglify-js@3.13.0 mangle=true, compress=true | 244,436 | 79,854 | 5.30 |
To enable fast minify mode from the CLI use:
```
uglifyjs file.js -m
```
To enable fast minify mode with the API use:
```js
```javascript
UglifyJS.minify(code, { compress: false, mangle: true });
```
#### Source maps and debugging
### Source maps and debugging
Various `compress` transforms that simplify, rearrange, inline and remove code
are known to have an adverse effect on debugging with source maps. This is
@@ -1146,6 +1209,10 @@ disable the Uglify `compress` option and just use `mangle`.
To allow for better optimizations, the compiler makes various assumptions:
- The code does not rely on preserving its runtime performance characteristics.
Typically uglified code will run faster due to less instructions and easier
inlining, but may be slower on rare occasions for a specific platform, e.g.
see [`reduce_funcs`](#compress-options).
- `.toString()` and `.valueOf()` don't have side effects, and for built-in
objects they have not been overridden.
- `undefined`, `NaN` and `Infinity` have not been externally redefined.
@@ -1157,3 +1224,219 @@ To allow for better optimizations, the compiler makes various assumptions:
- Object properties can be added, removed and modified (not prevented with
`Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`,
`Object.preventExtensions()` or `Object.seal()`).
- If array destructuring is present, index-like properties in `Array.prototype`
have not been overridden:
```javascript
Object.prototype[0] = 42;
var [ a ] = [];
var { 0: b } = {};
// 42 undefined
console.log([][0], a);
// 42 42
console.log({}[0], b);
```
- Earlier versions of JavaScript will throw `SyntaxError` with the following:
```javascript
({
p: 42,
get p() {},
});
// SyntaxError: Object literal may not have data and accessor property with
// the same name
```
UglifyJS may modify the input which in turn may suppress those errors.
- Iteration order of keys over an object which contains spread syntax in later
versions of Chrome and Node.js may be altered.
- When `toplevel` is enabled, UglifyJS effectively assumes input code is wrapped
within `function(){ ... }`, thus forbids aliasing of declared global variables:
```javascript
A = "FAIL";
var B = "FAIL";
// can be `global`, `self`, `window` etc.
var top = function() {
return this;
}();
// "PASS"
top.A = "PASS";
console.log(A);
// "FAIL" after compress and/or mangle
top.B = "PASS";
console.log(B);
```
- Use of `arguments` alongside destructuring as function parameters, e.g.
`function({}, arguments) {}` will result in `SyntaxError` in earlier versions
of Chrome and Node.js - UglifyJS may modify the input which in turn may
suppress those errors.
- Earlier versions of Chrome and Node.js will throw `ReferenceError` with the
following:
```javascript
var a;
try {
throw 42;
} catch ({
[a]: b,
// ReferenceError: a is not defined
}) {
let a;
}
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
a => {
let a;
};
// SyntaxError: Identifier 'a' has already been declared
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
try {
// ...
} catch ({ message: a }) {
var a;
}
// SyntaxError: Identifier 'a' has already been declared
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of Chrome and Node.js will throw `ReferenceError` with the
following:
```javascript
console.log(((a, b = function() {
return a;
// ReferenceError: a is not defined
}()) => b)());
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some arithmetic operations with `BigInt` may throw `TypeError`:
```javascript
1n + 1;
// TypeError: can't convert BigInt to number
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of JavaScript will throw `SyntaxError` with the
following:
```javascript
console.log(String.raw`\uFo`);
// SyntaxError: Invalid Unicode escape sequence
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of JavaScript will throw `SyntaxError` with the
following:
```javascript
try {} catch (e) {
for (var e of []);
}
// SyntaxError: Identifier 'e' has already been declared
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
console.log({
...{
set 42(v) {},
42: "PASS",
},
});
// Expected: { '42': 'PASS' }
// Actual: { '42': undefined }
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
var await;
class A {
static p = await;
}
// SyntaxError: Unexpected reserved word
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
var async;
for (async of []);
// SyntaxError: The left-hand side of a for-of loop may not be 'async'.
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
console.log({
...console,
get 42() {
return "FAIL";
},
[42]: "PASS",
}[42]);
// Expected: "PASS"
// Actual: "FAIL"
```
UglifyJS may modify the input which in turn may suppress those errors.
- Earlier versions of JavaScript will throw `TypeError` with the following:
```javascript
(function() {
{
const a = "foo";
}
{
const a = "bar";
}
})();
// TypeError: const 'a' has already been declared
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
try {
class A {
static 42;
static get 42() {}
}
console.log("PASS");
} catch (e) {
console.log("FAIL");
}
// Expected: "PASS"
// Actual: "FAIL"
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
(async function(a) {
(function() {
var b = await => console.log("PASS");
b();
})();
})().catch(console.error);
// Expected: "PASS"
// Actual: SyntaxError: Unexpected reserved word
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
try {
f();
function f() {
throw 42;
}
} catch (e) {
console.log(typeof f, e);
}
// Expected: "function 42"
// Actual: "undefined 42"
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
"use strict";
console.log(function f() {
return f = "PASS";
}());
// Expected: "PASS"
// Actual: TypeError: invalid assignment to const 'f'
```
UglifyJS may modify the input which in turn may suppress those errors.
+120 -30
View File
@@ -3,14 +3,16 @@
"use strict";
require("../tools/exit");
require("../tools/tty");
var fs = require("fs");
var info = require("../package.json");
var path = require("path");
var UglifyJS = require("../tools/node");
var skip_keys = [ "cname", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
var skip_keys = [ "cname", "fixed", "in_arg", "inlined", "length_read", "parent_scope", "redef", "scope", "unused" ];
var truthy_keys = [ "optional", "pure", "terminal", "uses_arguments", "uses_eval", "uses_with" ];
var files = {};
var options = {};
var short_forms = {
@@ -70,6 +72,7 @@ function process_option(name, no_value) {
} else {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
toplevels.push([ {
keep_fargs: "keep-fargs",
keep_fnames: "keep-fnames",
nameCache: "name-cache",
}[name] || name, option ]);
@@ -95,12 +98,16 @@ function process_option(name, no_value) {
" -b, --beautify [options] Beautify output/specify output options.",
" -O, --output-opts <options> Output options (beautify disabled).",
" -o, --output <file> Output file (default STDOUT).",
" --annotations Process and preserve comment annotations.",
" --no-annotations Ignore and discard comment annotations.",
" --comments [filter] Preserve copyright comments in the output.",
" --config-file <file> Read minify() options from JSON file.",
" -d, --define <expr>[=value] Global definitions.",
" -e, --enclose [arg[,...][:value[,...]]] Embed everything in a big function, with configurable argument(s) & value(s).",
" --ie8 Support non-standard Internet Explorer 8.",
" --ie Support non-standard Internet Explorer.",
" --keep-fargs Do not mangle/drop function arguments.",
" --keep-fnames Do not mangle/drop function names. Useful for code relying on Function.prototype.name.",
" --module Process input as ES module (implies --toplevel)",
" --name-cache <file> File to hold mangled name mappings.",
" --rename Force symbol expansion.",
" --no-rename Disable symbol expansion.",
@@ -108,10 +115,15 @@ function process_option(name, no_value) {
" --source-map [options] Enable source map/specify source map options.",
" --timings Display operations run time on STDERR.",
" --toplevel Compress and/or mangle variables in toplevel scope.",
" --v8 Support non-standard Chrome & Node.js.",
" --validate Perform validation during AST manipulations.",
" --verbose Print diagnostic messages.",
" --warn Print warning messages.",
" --webkit Support non-standard Safari/Webkit.",
" --wrap <name> Embed everything as a function with “exports” corresponding to “name” globally.",
"",
"(internal debug use only)",
" --in-situ Warning: replaces original source files with minified output.",
" --reduce-test Reduce a standalone test case (assumes cloned repository).",
].join("\n"));
}
@@ -138,12 +150,23 @@ function process_option(name, no_value) {
case "enclose":
options[name] = read_value();
break;
case "annotations":
case "ie":
case "ie8":
case "module":
case "timings":
case "toplevel":
case "v8":
case "validate":
case "webkit":
options[name] = true;
break;
case "no-annotations":
options.annotations = false;
break;
case "keep-fargs":
options.keep_fargs = true;
break;
case "keep-fnames":
options.keep_fnames = true;
break;
@@ -192,6 +215,7 @@ function process_option(name, no_value) {
case "no-rename":
options.rename = false;
break;
case "in-situ":
case "reduce-test":
case "self":
break;
@@ -223,10 +247,11 @@ if (options.mangle && options.mangle.properties) {
});
}
}
if (output == "ast") options.output = {
ast: true,
code: false,
};
if (/^ast|spidermonkey$/.test(output)) {
if (typeof options.output != "object") options.output = {};
options.output.ast = true;
options.output.code = false;
}
if (options.parse && (options.parse.acorn || options.parse.spidermonkey)
&& options.sourceMap && options.sourceMap.content == "inline") {
fatal("inline source map only works with built-in parser");
@@ -252,18 +277,35 @@ if (specified["self"]) {
if (!options.wrap) options.wrap = "UglifyJS";
paths = UglifyJS.FILES;
}
if (paths.length) {
if (specified["in-situ"]) {
if (output && output != "spidermonkey" || specified["reduce-test"] || specified["self"]) {
fatal("incompatible options specified");
}
paths.forEach(function(name) {
print(name);
if (/^ast|spidermonkey$/.test(name)) fatal("invalid file name specified");
files = {};
files[convert_path(name)] = read_file(name);
output = name;
run();
});
} else if (paths.length) {
simple_glob(paths).forEach(function(name) {
files[convert_path(name)] = read_file(name);
});
run();
} else {
var timerId = process.stdin.isTTY && process.argv.length < 3 && setTimeout(function() {
print_error("Waiting for input... (use `--help` to print usage information)");
}, 1500);
var chunks = [];
process.stdin.setEncoding("utf8");
process.stdin.on("data", function(chunk) {
process.stdin.once("data", function() {
clearTimeout(timerId);
}).on("data", function(chunk) {
chunks.push(chunk);
}).on("end", function() {
files = [ chunks.join("") ];
files = { STDIN: chunks.join("") };
run();
});
process.stdin.resume();
@@ -276,19 +318,51 @@ function convert_ast(fn) {
function run() {
var content = options.sourceMap && options.sourceMap.content;
if (content && content != "inline") {
UglifyJS.AST_Node.info("Using input source map: " + content);
UglifyJS.AST_Node.info("Using input source map: {content}", {
content : content,
});
options.sourceMap.content = read_file(content, content);
}
try {
if (options.parse) {
if (options.parse.acorn) {
var annotations = Object.create(null);
files = convert_ast(function(toplevel, name) {
return require("acorn").parse(files[name], {
var content = files[name];
var list = annotations[name] = [];
var prev = -1;
return require("acorn").parse(content, {
allowHashBang: true,
ecmaVersion: "latest",
locations: true,
onComment: function(block, text, start, end) {
var match = /[@#]__PURE__/.exec(text);
if (!match) {
if (start != prev) return;
match = [ list[prev] ];
}
while (/\s/.test(content[end])) end++;
list[end] = match[0];
prev = end;
},
preserveParens: true,
program: toplevel,
sourceFile: name
sourceFile: name,
sourceType: "module",
});
});
files.walk(new UglifyJS.TreeWalker(function(node) {
if (!(node instanceof UglifyJS.AST_Call)) return;
var list = annotations[node.start.file];
var pure = list[node.start.pos];
if (!pure) {
var tokens = node.start.parens;
if (tokens) for (var i = 0; !pure && i < tokens.length; i++) {
pure = list[tokens[i].pos];
}
}
if (pure) node.pure = pure;
}));
} else if (options.parse.spidermonkey) {
files = convert_ast(function(toplevel, name) {
var obj = JSON.parse(files[name]);
@@ -342,19 +416,30 @@ function run() {
}
fatal(ex);
} else if (output == "ast") {
if (!options.compress && !options.mangle) result.ast.figure_out_scope({});
if (!options.compress && !options.mangle) {
var toplevel = result.ast;
if (!(toplevel instanceof UglifyJS.AST_Toplevel)) {
if (!(toplevel instanceof UglifyJS.AST_Statement)) toplevel = new UglifyJS.AST_SimpleStatement({
body: toplevel,
});
toplevel = new UglifyJS.AST_Toplevel({
body: [ toplevel ],
});
}
toplevel.figure_out_scope({});
}
print(JSON.stringify(result.ast, function(key, value) {
if (value) switch (key) {
case "thedef":
return symdef(value);
case "enclosed":
return value.length ? value.map(symdef) : undefined;
case "variables":
case "functions":
case "globals":
case "variables":
return value.size() ? value.map(symdef) : undefined;
case "thedef":
return symdef(value);
}
if (skip_key(key)) return;
if (skip_property(key, value)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
if (value instanceof UglifyJS.AST_Node) {
@@ -369,16 +454,19 @@ function run() {
return value;
}, 2));
} else if (output == "spidermonkey") {
print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
ast: true,
code: false
},
}).ast.to_mozilla_ast(), null, 2));
print(JSON.stringify(result.ast.to_mozilla_ast(), null, 2));
} else if (output) {
fs.writeFileSync(output, result.code);
var code;
if (result.ast) {
var opts = {};
for (var name in options.output) {
if (!/^ast|code$/.test(name)) opts[name] = options.output[name];
}
code = UglifyJS.AST_Node.from_mozilla_ast(result.ast.to_mozilla_ast()).print_to_string(opts);
} else {
code = result.code;
}
fs.writeFileSync(output, code);
if (result.map) fs.writeFileSync(output + ".map", result.map);
} else {
print(result.code);
@@ -440,7 +528,7 @@ function read_file(path, default_value) {
}
function parse_js(value, options, flag) {
if (!options || typeof options != "object") options = {};
if (!options || typeof options != "object") options = Object.create(null);
if (typeof value == "string") try {
UglifyJS.parse(value, {
expression: true
@@ -480,8 +568,10 @@ function parse_js(value, options, flag) {
return options;
}
function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
function skip_property(key, value) {
return skip_keys.indexOf(key) >= 0
// only skip truthy_keys if their value is falsy
|| truthy_keys.indexOf(key) >= 0 && !value;
}
function symdef(def) {
+1104 -194
View File
File diff suppressed because it is too large Load Diff
+8021 -2710
View File
File diff suppressed because it is too large Load Diff
+53 -37
View File
@@ -28,12 +28,14 @@ function read_source_map(name, toplevel) {
var match = /^# ([^\s=]+)=(\S+)\s*$/.exec(comment.value);
if (!match) break;
if (match[1] == "sourceMappingURL") {
match = /^data:application\/json(;.*?)?;base64,(\S+)$/.exec(match[2]);
match = /^data:application\/json(;.*?)?;base64,([^,]+)$/.exec(match[2]);
if (!match) break;
return to_ascii(match[2]);
}
}
AST_Node.warn("inline source map not found: " + name);
AST_Node.warn("inline source map not found: {name}", {
name: name,
});
}
function parse_source_map(content) {
@@ -45,14 +47,12 @@ function parse_source_map(content) {
}
function set_shorthand(name, options, keys) {
if (options[name]) {
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
function init_cache(cache) {
@@ -73,42 +73,52 @@ function to_json(cache) {
function minify(files, options) {
try {
options = defaults(options, {
annotations: undefined,
compress: {},
enclose: false,
ie: false,
ie8: false,
keep_fargs: false,
keep_fnames: false,
mangle: {},
module: false,
nameCache: null,
output: {},
parse: {},
rename: undefined,
sourceMap: false,
timings: false,
toplevel: false,
toplevel: !!(options && options["module"]),
v8: false,
validate: false,
warnings: false,
webkit: false,
wrap: false,
}, true);
if (options.validate) AST_Node.enable_validation();
var timings = options.timings && {
start: Date.now()
};
if (options.rename === undefined) {
options.rename = options.compress && options.mangle;
}
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
var timings = options.timings && { start: Date.now() };
if (options.annotations !== undefined) set_shorthand("annotations", options, [ "compress", "output" ]);
if (options.ie8) options.ie = options.ie || options.ie8;
if (options.ie) set_shorthand("ie", options, [ "compress", "mangle", "output", "rename" ]);
if (options.keep_fargs) set_shorthand("keep_fargs", options, [ "compress", "mangle", "rename" ]);
if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle", "rename" ]);
if (options.module) set_shorthand("module", options, [ "compress", "parse" ]);
if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle", "rename" ]);
if (options.v8) set_shorthand("v8", options, [ "mangle", "output", "rename" ]);
if (options.webkit) set_shorthand("webkit", options, [ "compress", "mangle", "output", "rename" ]);
var quoted_props;
if (options.mangle) {
options.mangle = defaults(options.mangle, {
cache: options.nameCache && (options.nameCache.vars || {}),
eval: false,
ie8: false,
ie: false,
keep_fargs: false,
keep_fnames: false,
properties: false,
reserved: [],
toplevel: false,
v8: false,
webkit: false,
}, true);
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") {
@@ -126,6 +136,7 @@ function minify(files, options) {
init_cache(options.mangle.cache);
init_cache(options.mangle.properties.cache);
}
if (options.rename === undefined) options.rename = options.compress && options.mangle;
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
content: null,
@@ -178,13 +189,17 @@ function minify(files, options) {
toplevel = toplevel[action](option);
files[toplevel.start.file] = toplevel.print_to_string().replace(orig, "");
});
if (options.validate) toplevel.validate_ast();
if (timings) timings.rename = Date.now();
if (options.rename) {
toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle);
toplevel.figure_out_scope(options.rename);
toplevel.expand_names(options.rename);
}
if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (options.compress) {
toplevel = new Compressor(options.compress).compress(toplevel);
if (options.validate) toplevel.validate_ast();
}
if (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
@@ -193,32 +208,32 @@ function minify(files, options) {
toplevel.mangle_names(options.mangle);
}
if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties);
if (timings) timings.output = Date.now();
var result = {};
if (options.output.ast) {
result.ast = toplevel;
}
if (!HOP(options.output, "code") || options.output.code) {
var output = defaults(options.output, {
ast: false,
code: true,
});
if (output.ast) result.ast = toplevel;
if (output.code) {
if (options.sourceMap) {
options.output.source_map = SourceMap(options.sourceMap);
output.source_map = SourceMap(options.sourceMap);
if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) if (HOP(files, name)) {
options.output.source_map.setSourceContent(name, files[name]);
output.source_map.setSourceContent(name, files[name]);
}
}
}
delete options.output.ast;
delete options.output.code;
var stream = OutputStream(options.output);
delete output.ast;
delete output.code;
var stream = OutputStream(output);
toplevel.print(stream);
result.code = stream.get();
if (options.sourceMap) {
result.map = options.output.source_map.toString();
result.map = output.source_map.toString();
var url = options.sourceMap.url;
if (url) {
result.code = result.code.replace(/\n\/\/# sourceMappingURL=\S+\s*$/, "");
@@ -256,6 +271,7 @@ function minify(files, options) {
} catch (ex) {
return { error: ex };
} finally {
AST_Node.log_function();
AST_Node.disable_validation();
}
}
+830 -195
View File
File diff suppressed because it is too large Load Diff
+977 -551
View File
File diff suppressed because it is too large Load Diff
+1335 -370
View File
File diff suppressed because it is too large Load Diff
+92 -67
View File
@@ -43,7 +43,8 @@
"use strict";
function find_builtins(reserved) {
var builtins = function() {
var names = new Dictionary();
// NaN will be included due to Number.NaN
[
"null",
@@ -67,19 +68,23 @@ function find_builtins(reserved) {
].forEach(function(ctor) {
Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) {
Object.getOwnPropertyNames(new ctor()).map(add);
Object.getOwnPropertyNames(ctor.prototype).map(add);
}
});
return names;
function add(name) {
push_uniq(reserved, name);
names.set(name, true);
}
}
}();
function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) {
add(node.key);
if (node instanceof AST_ClassProperty) {
if (node.start && node.start.quote) add(node.key);
} else if (node instanceof AST_ObjectProperty) {
if (node.start && node.start.quote) add(node.key);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
}
@@ -91,17 +96,14 @@ function reserve_quoted_keys(ast, reserved) {
}
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.tail_node(), add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
}
return true;
}));
if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
} else if (node instanceof AST_Sequence) {
addStrings(node.tail_node(), add);
} else if (node instanceof AST_String) {
add(node.value);
}
}
function mangle_properties(ast, options) {
@@ -110,21 +112,21 @@ function mangle_properties(ast, options) {
cache: null,
debug: false,
keep_quoted: false,
only_cache: false,
regex: null,
reserved: null,
}, true);
var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved);
var reserved = options.builtins ? new Dictionary() : builtins.clone();
if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) {
reserved.set(name, true);
});
var cname = -1;
var cache;
if (options.cache) {
cache = options.cache.props;
cache.each(function(mangled_name) {
push_uniq(reserved, mangled_name);
cache.each(function(name) {
reserved.set(name, true);
});
} else {
cache = new Dictionary();
@@ -139,72 +141,98 @@ function mangle_properties(ast, options) {
var debug_suffix;
if (debug) debug_suffix = options.debug === true ? "" : options.debug;
var names_to_mangle = [];
var unmangleable = [];
var names_to_mangle = new Dictionary();
var unmangleable = reserved.clone();
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal) {
add(node.key);
} else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
add(node.key.name);
if (node instanceof AST_Binary) {
if (node.operator == "in") addStrings(node.left, add);
} else if (node.TYPE == "Call") {
var exp = node.expression;
if (exp instanceof AST_Dot) switch (exp.property) {
case "defineProperty":
case "getOwnPropertyDescriptor":
if (node.args.length < 2) break;
exp = exp.expression;
if (!(exp instanceof AST_SymbolRef)) break;
if (exp.name != "Object") break;
if (!exp.definition().undeclared) break;
addStrings(node.args[1], add);
break;
case "hasOwnProperty":
if (node.args.length < 1) break;
addStrings(node.args[0], add);
break;
}
} else if (node instanceof AST_ClassProperty) {
if (typeof node.key == "string") add(node.key);
} else if (node instanceof AST_Dot) {
add(node.property);
} else if (node instanceof AST_ObjectProperty) {
if (typeof node.key == "string") add(node.key);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
} else if (node instanceof AST_Call
&& node.expression.print_to_string() == "Object.defineProperty") {
addStrings(node.args[1], add);
}
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node) {
if (node instanceof AST_ObjectKeyVal) {
node.key = mangle(node.key);
} else if (node instanceof AST_ObjectProperty) {
// setter or getter
node.key.name = mangle(node.key.name);
// step 2: renaming properties
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_Binary) {
if (node.operator == "in") mangleStrings(node.left);
} else if (node.TYPE == "Call") {
var exp = node.expression;
if (exp instanceof AST_Dot) switch (exp.property) {
case "defineProperty":
case "getOwnPropertyDescriptor":
if (node.args.length < 2) break;
exp = exp.expression;
if (!(exp instanceof AST_SymbolRef)) break;
if (exp.name != "Object") break;
if (!exp.definition().undeclared) break;
mangleStrings(node.args[1]);
break;
case "hasOwnProperty":
if (node.args.length < 1) break;
mangleStrings(node.args[0]);
break;
}
} else if (node instanceof AST_ClassProperty) {
if (typeof node.key == "string") node.key = mangle(node.key);
} else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
} else if (!options.keep_quoted && node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
} else if (node instanceof AST_Call
&& node.expression.print_to_string() == "Object.defineProperty") {
node.args[1] = mangleStrings(node.args[1]);
} else if (node instanceof AST_ObjectProperty) {
if (typeof node.key == "string") node.key = mangle(node.key);
} else if (node instanceof AST_Sub) {
if (!options.keep_quoted) mangleStrings(node.property);
}
}));
// only function declarations after this line
function can_mangle(name) {
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) return cache.has(name);
if (unmangleable.has(name)) return false;
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
return true;
}
function should_mangle(name) {
if (reserved.has(name)) return false;
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.has(name) || names_to_mangle.indexOf(name) >= 0;
return cache.has(name) || names_to_mangle.has(name);
}
function add(name) {
if (can_mangle(name)) push_uniq(names_to_mangle, name);
if (!should_mangle(name)) push_uniq(unmangleable, name);
if (can_mangle(name)) names_to_mangle.set(name, true);
if (!should_mangle(name)) unmangleable.set(name, true);
}
function mangle(name) {
if (!should_mangle(name)) {
return name;
}
if (!should_mangle(name)) return name;
var mangled = cache.get(name);
if (!mangled) {
if (debug) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo ---> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_suffix + "_";
if (can_mangle(debug_mangled)) mangled = debug_mangled;
}
@@ -212,23 +240,20 @@ function mangle_properties(ast, options) {
if (!mangled) do {
mangled = base54(++cname);
} while (!can_mangle(mangled));
if (/^#/.test(name)) mangled = "#" + mangled;
cache.set(name, mangled);
}
return mangled;
}
function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node) {
if (node instanceof AST_Sequence) {
var last = node.expressions.length - 1;
node.expressions[last] = mangleStrings(node.expressions[last]);
} else if (node instanceof AST_String) {
node.value = mangle(node.value);
} else if (node instanceof AST_Conditional) {
node.consequent = mangleStrings(node.consequent);
node.alternative = mangleStrings(node.alternative);
}
return node;
}));
if (node instanceof AST_Sequence) {
mangleStrings(node.expressions.tail_node());
} else if (node instanceof AST_String) {
node.value = mangle(node.value);
} else if (node instanceof AST_Conditional) {
mangleStrings(node.consequent);
mangleStrings(node.alternative);
}
}
}
+422 -195
View File
@@ -44,82 +44,168 @@
"use strict";
function SymbolDef(id, scope, orig, init) {
this._bits = 0;
this.defun = undefined;
this.eliminated = 0;
this.global = false;
this.id = id;
this.init = init;
this.lambda = orig instanceof AST_SymbolLambda;
this.mangled_name = null;
this.name = orig.name;
this.orig = [ orig ];
this.references = [];
this.replaced = 0;
this.safe_ids = undefined;
this.scope = scope;
this.undeclared = false;
}
SymbolDef.prototype = {
unmangleable: function(options) {
return this.global && !options.toplevel
|| this.undeclared
|| !options.eval && this.scope.pinned()
|| options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun);
forEach: function(fn) {
this.orig.forEach(fn);
this.references.forEach(fn);
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
if (this.mangled_name) return;
var cache = this.global && options.cache && options.cache.props;
if (cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
} else if (!this.mangled_name && !this.unmangleable(options)) {
var def;
if (def = this.redefined()) {
} else if (this.unmangleable(options)) {
names_in_use(this.scope, options).set(this.name, true);
} else {
var def = this.redefined();
if (def) {
this.mangled_name = def.mangled_name || def.name;
} else {
this.mangled_name = next_mangled_name(this.scope, options, this);
}
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
this.mangled_name = next_mangled_name(this, options);
}
if (cache) cache.set(this.name, this.mangled_name);
}
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
}
var self = this;
var scope = self.defun;
if (!scope) return;
var name = self.name;
var def = scope.variables.get(name)
|| scope instanceof AST_Toplevel && scope.globals.get(name)
|| self.orig[0] instanceof AST_SymbolConst && find_if(function(def) {
return def.name == name;
}, scope.enclosed);
if (def && def !== self) return def.redefined() || def;
},
unmangleable: function(options) {
if (this.exported) return true;
if (this.undeclared) return true;
if (!options.eval && this.scope.pinned()) return true;
if (options.keep_fargs && is_funarg(this)) return true;
if (options.keep_fnames) {
var sym = this.orig[0];
if (sym instanceof AST_SymbolClass) return true;
if (sym instanceof AST_SymbolDefClass) return true;
if (sym instanceof AST_SymbolDefun) return true;
if (sym instanceof AST_SymbolLambda) return true;
}
if (!options.toplevel && this.global) return true;
return false;
},
};
DEF_BITPROPS(SymbolDef, [
"const_redefs",
"cross_loop",
"direct_access",
"exported",
"global",
"undeclared",
]);
function is_funarg(def) {
return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg;
}
var unary_side_effects = makePredicate("delete ++ --");
function is_lhs(node, parent) {
if (parent instanceof AST_Assign) return parent.left === node && node;
if (parent instanceof AST_DefaultValue) return parent.name === node && node;
if (parent instanceof AST_Destructured) return node;
if (parent instanceof AST_DestructuredKeyVal) return node;
if (parent instanceof AST_ForEnumeration) return parent.init === node && node;
if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression;
}
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
options = defaults(options, {
cache: null,
ie8: false,
ie: false,
});
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var defun = null;
var exported = false;
var next_def_id = 0;
var scope = self.parent_scope = null;
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Catch) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(save_scope);
descend();
scope = save_scope;
if (node instanceof AST_DefClass) {
var save_exported = exported;
exported = tw.parent() instanceof AST_ExportDeclaration;
node.name.walk(tw);
exported = save_exported;
walk_scope(function() {
if (node.extends) node.extends.walk(tw);
node.properties.forEach(function(prop) {
prop.walk(tw);
});
});
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(scope);
var save_scope = scope;
var save_defun = defun;
defun = scope = node;
if (node instanceof AST_Definitions) {
var save_exported = exported;
exported = tw.parent() instanceof AST_ExportDeclaration;
descend();
scope = save_scope;
defun = save_defun;
exported = save_exported;
return true;
}
if (node instanceof AST_LambdaDefinition) {
var save_exported = exported;
exported = tw.parent() instanceof AST_ExportDeclaration;
node.name.walk(tw);
exported = save_exported;
walk_scope(function() {
node.argnames.forEach(function(argname) {
argname.walk(tw);
});
if (node.rest) node.rest.walk(tw);
walk_body(node, tw);
});
return true;
}
if (node instanceof AST_SwitchBranch) {
node.init_vars(scope);
descend();
return true;
}
if (node instanceof AST_Try) {
walk_scope(function() {
walk_body(node, tw);
});
if (node.bcatch) node.bcatch.walk(tw);
if (node.bfinally) node.bfinally.walk(tw);
return true;
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope) s.uses_with = true;
return;
var s = scope;
do {
s = s.resolve();
if (s.uses_with) break;
s.uses_with = true;
} while (s = s.parent_scope);
walk_scope(descend);
return true;
}
if (node instanceof AST_BlockScope) {
walk_scope(descend);
return true;
}
if (node instanceof AST_Symbol) {
node.scope = scope;
@@ -128,73 +214,160 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolDefun) {
// This should be defined in the parent scope, as we encounter the
// AST_Defun node before getting to its AST_Symbol.
(node.scope = defun.parent_scope.resolve()).def_function(node, defun);
if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
} else if (node instanceof AST_SymbolConst) {
var def = scope.def_variable(node);
def.defun = defun;
if (exported) def.exported = true;
} else if (node instanceof AST_SymbolDefun) {
var def = defun.def_function(node, tw.parent());
if (exported) def.exported = true;
} else if (node instanceof AST_SymbolFunarg) {
defun.def_variable(node);
} else if (node instanceof AST_SymbolLambda) {
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
if (options.ie8) def.defun = defun.parent_scope.resolve();
if (options.ie && node.name != "arguments") def.defun = defun.parent_scope.resolve();
} else if (node instanceof AST_SymbolLet) {
var def = scope.def_variable(node);
if (exported) def.exported = true;
} else if (node instanceof AST_SymbolVar) {
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
if (defun !== scope) {
node.mark_enclosed(options);
var def = scope.find_variable(node);
if (node.thedef !== def) {
node.thedef = def;
}
node.reference(options);
}
} else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
var def = defun.def_variable(node, node instanceof AST_SymbolImport ? undefined : null);
if (exported) def.exported = true;
}
function walk_scope(descend) {
node.init_vars(scope);
var save_defun = defun;
var save_scope = scope;
if (node instanceof AST_Scope) defun = node;
scope = node;
descend();
scope = save_scope;
defun = save_defun;
}
});
self.next_def_id = 0;
self.make_def = function(orig, init) {
return new SymbolDef(++next_def_id, this, orig, init);
};
self.walk(tw);
// pass 2: find back references and eval
self.globals = new Dictionary();
var in_arg = [];
var tw = new TreeWalker(function(node) {
if (node instanceof AST_Catch) {
if (!(node.argname instanceof AST_Destructured)) return;
in_arg.push(node);
node.argname.walk(tw);
in_arg.pop();
walk_body(node, tw);
return true;
}
if (node instanceof AST_Lambda) {
in_arg.push(node);
if (node.name) node.name.walk(tw);
node.argnames.forEach(function(argname) {
argname.walk(tw);
});
if (node.rest) node.rest.walk(tw);
in_arg.pop();
walk_lambda(node, tw);
return true;
}
if (node instanceof AST_LoopControl) {
if (node.label) node.label.thedef.references.push(node);
return true;
}
if (node instanceof AST_SymbolDeclaration) {
var def = node.definition();
def.preinit = def.references.length;
if (node instanceof AST_SymbolCatch) {
// ensure mangling works if `catch` reuses a scope variable
var redef = def.redefined();
if (redef) for (var s = node.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, redef);
if (s === redef.scope) break;
}
} else if (node instanceof AST_SymbolConst) {
// ensure compression works if `const` reuses a scope variable
var redef = def.redefined();
if (redef) redef.const_redefs = true;
} else if (def.scope !== node.scope && (node instanceof AST_SymbolDefun
|| node instanceof AST_SymbolFunarg
|| node instanceof AST_SymbolVar)) {
node.mark_enclosed(options);
var redef = node.scope.find_variable(node.name);
if (node.thedef !== redef) {
node.thedef = redef;
redef.orig.push(node);
node.mark_enclosed(options);
}
}
if (node.name != "arguments") return true;
var parent = node instanceof AST_SymbolVar && tw.parent();
if (parent instanceof AST_VarDef && !parent.value) return true;
var sym = node.scope.resolve().find_variable("arguments");
if (sym && is_arguments(sym)) sym.scope.uses_arguments = 3;
return true;
}
if (node instanceof AST_SymbolRef) {
var name = node.name;
var sym = node.scope.find_variable(name);
for (var i = in_arg.length; i > 0 && sym;) {
i = in_arg.lastIndexOf(sym.scope, i - 1);
if (i < 0) break;
var decl = sym.orig[0];
if (decl instanceof AST_SymbolCatch
|| decl instanceof AST_SymbolFunarg
|| decl instanceof AST_SymbolLambda) {
node.in_arg = true;
break;
}
sym = sym.scope.parent_scope.find_variable(name);
}
if (!sym) {
sym = self.def_global(node);
} else if (sym.scope instanceof AST_Lambda && name == "arguments") {
sym.scope.uses_arguments = true;
} else if (name == "arguments" && is_arguments(sym)) {
var parent = tw.parent();
if (is_lhs(node, parent)) {
sym.scope.uses_arguments = 3;
} else if (sym.scope.uses_arguments < 2
&& !(parent instanceof AST_PropAccess && parent.expression === node)) {
sym.scope.uses_arguments = 2;
} else if (!sym.scope.uses_arguments) {
sym.scope.uses_arguments = true;
}
}
if (name == "eval") {
var parent = tw.parent();
if (parent.TYPE == "Call" && parent.expression === node) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
var s = node.scope;
do {
s = s.resolve();
if (s.uses_eval) break;
s.uses_eval = true;
}
} while (s = s.parent_scope);
} else if (sym.undeclared) {
self.uses_eval = true;
}
}
if (sym.init instanceof AST_LambdaDefinition && sym.scope !== sym.init.name.scope) {
var scope = node.scope;
do {
if (scope === sym.init.name.scope) break;
} while (scope = scope.parent_scope);
if (!scope) sym.init = undefined;
}
node.thedef = sym;
node.reference(options);
return true;
}
// ensure mangling works if catch reuses a scope variable
if (node instanceof AST_SymbolCatch) {
var def = node.definition().redefined();
if (def) for (var s = node.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
}
return true;
}
});
self.walk(tw);
// pass 3: fix up any scoping issue with IE8
if (options.ie8) self.walk(new TreeWalker(function(node) {
if (options.ie) self.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolCatch) {
var scope = node.thedef.defun;
if (scope.name instanceof AST_SymbolLambda && scope.name.name == node.name) {
@@ -205,8 +378,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}
if (node instanceof AST_SymbolLambda) {
var def = node.thedef;
redefine(node, node.scope.parent_scope.resolve());
if (typeof node.thedef.init !== "undefined") {
if (!redefine(node, node.scope.parent_scope.resolve())) {
def.defun = undefined;
} else if (typeof node.thedef.init !== "undefined") {
node.thedef.init = false;
} else if (def.init) {
node.thedef.init = def.init;
@@ -215,13 +389,22 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}
}));
function is_arguments(sym) {
return sym.orig[0] instanceof AST_SymbolFunarg
&& !(sym.orig[1] instanceof AST_SymbolFunarg || sym.orig[2] instanceof AST_SymbolFunarg)
&& !is_arrow(sym.scope);
}
function redefine(node, scope) {
var name = node.name;
var old_def = node.thedef;
if (!all(old_def.orig, function(sym) {
return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
})) return false;
var new_def = scope.find_variable(name);
if (new_def) {
var redef;
while (redef = new_def.redefined()) new_def = redef;
var redef = new_def.redefined();
if (redef) new_def = redef;
} else {
new_def = self.globals.get(name);
}
@@ -230,21 +413,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
} else {
new_def = scope.def_variable(node);
}
old_def.orig.concat(old_def.references).forEach(function(node) {
if (new_def.undeclared) self.variables.set(name, new_def);
if (name == "arguments" && is_arguments(old_def) && node instanceof AST_SymbolLambda) return true;
old_def.defun = new_def.scope;
old_def.forEach(function(node) {
node.redef = old_def;
node.thedef = new_def;
node.reference(options);
});
if (old_def.lambda) new_def.lambda = true;
if (new_def.undeclared) self.variables.set(name, new_def);
return true;
}
});
AST_Scope.DEFMETHOD("make_def", function(orig, init) {
var top = this;
while (top.parent_scope) top = top.parent_scope;
return new SymbolDef(++top.next_def_id, this, orig, init);
});
AST_Toplevel.DEFMETHOD("def_global", function(node) {
var globals = this.globals, name = node.name;
if (globals.has(name)) {
@@ -258,32 +438,56 @@ AST_Toplevel.DEFMETHOD("def_global", function(node) {
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
this.parent_scope = parent_scope; // the parent scope
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
this.cname = -1; // the current index for mangling functions/variables
});
function init_block_vars(scope, parent) {
scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
scope.parent_scope = parent; // the parent scope (null if this is the top level)
scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
}
AST_Lambda.DEFMETHOD("init_scope_vars", function() {
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
function init_scope_vars(scope, parent) {
init_block_vars(scope, parent);
scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
}
AST_BlockScope.DEFMETHOD("init_vars", function(parent_scope) {
init_block_vars(this, parent_scope);
});
AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
});
AST_Arrow.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
return this;
});
AST_AsyncArrow.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
});
AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
this.uses_arguments = false;
this.def_variable(new AST_SymbolFunarg({
name: "arguments",
scope: this,
start: this.start,
end: this.end
end: this.end,
}));
return this;
});
AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
var def = this.definition();
for (var s = this.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, def);
if (options.keep_fnames) {
s.functions.each(function(d) {
if (!options) {
s._var_names = undefined;
} else {
if (options.keep_fargs && s instanceof AST_Lambda) s.each_argname(function(arg) {
push_uniq(def.scope.enclosed, arg.definition());
});
if (options.keep_fnames) s.functions.each(function(d) {
push_uniq(def.scope.enclosed, d);
});
}
@@ -296,24 +500,23 @@ AST_Symbol.DEFMETHOD("reference", function(options) {
this.mark_enclosed(options);
});
AST_Scope.DEFMETHOD("find_variable", function(name) {
if (name instanceof AST_Symbol) name = name.name;
AST_BlockScope.DEFMETHOD("find_variable", function(name) {
return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name));
|| this.parent_scope && this.parent_scope.find_variable(name);
});
AST_Scope.DEFMETHOD("def_function", function(symbol, init) {
AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) {
var def = this.def_variable(symbol, init);
if (!def.init || def.init instanceof AST_Defun) def.init = init;
if (!def.init || def.init instanceof AST_LambdaDefinition) def.init = init;
this.functions.set(symbol.name, def);
return def;
});
AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) {
var def = this.variables.get(symbol.name);
if (def) {
def.orig.push(symbol);
if (def.init instanceof AST_Function) def.init = init;
if (def.init instanceof AST_LambdaExpression) def.init = init;
} else {
def = this.make_def(symbol, init);
this.variables.set(symbol.name, def);
@@ -322,59 +525,54 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
return symbol.thedef = def;
});
AST_Lambda.DEFMETHOD("resolve", return_this);
AST_Scope.DEFMETHOD("resolve", function() {
return this.parent_scope.resolve();
});
AST_Toplevel.DEFMETHOD("resolve", return_this);
function names_in_use(scope, options) {
var names = scope.names_in_use;
if (!names) {
scope.names_in_use = names = Object.create(scope.mangled_names || null);
scope.cname = -1;
scope.cname_holes = [];
scope.names_in_use = names = new Dictionary();
var cache = options.cache && options.cache.props;
scope.enclosed.forEach(function(def) {
if (def.unmangleable(options)) names[def.name] = true;
if (def.unmangleable(options)) names.set(def.name, true);
if (def.global && cache && cache.has(def.name)) {
names[cache.get(def.name)] = true;
names.set(cache.get(def.name), true);
}
});
}
return names;
}
function next_mangled_name(scope, options, def) {
function next_mangled_name(def, options) {
var scope = def.scope;
var in_use = names_in_use(scope, options);
var holes = scope.cname_holes;
var names = Object.create(null);
var names = new Dictionary();
var scopes = [ scope ];
def.references.forEach(function(sym) {
def.forEach(function(sym) {
var scope = sym.scope;
do {
if (scopes.indexOf(scope) < 0) {
for (var name in names_in_use(scope, options)) {
names[name] = true;
}
scopes.push(scope);
} else break;
if (member(scope, scopes)) break;
names_in_use(scope, options).each(function(marker, name) {
names.set(name, marker);
});
scopes.push(scope);
} while (scope = scope.parent_scope);
});
var name;
for (var i = 0; i < holes.length; i++) {
name = base54(holes[i]);
if (names[name]) continue;
if (names.has(name)) continue;
holes.splice(i, 1);
scope.names_in_use[name] = true;
in_use.set(name, true);
return name;
}
while (true) {
name = base54(++scope.cname);
if (in_use[name] || RESERVED_WORDS[name] || options.reserved.has[name]) continue;
if (!names[name]) break;
if (in_use.has(name) || RESERVED_WORDS[name] || options.reserved.has[name]) continue;
if (!names.has(name)) break;
holes.push(scope.cname);
}
scope.names_in_use[name] = true;
in_use.set(name, true);
return name;
}
@@ -386,59 +584,67 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options) {
// labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", return_false);
AST_Symbol.DEFMETHOD("unreferenced", function() {
return !this.definition().references.length && !this.scope.pinned();
});
AST_Symbol.DEFMETHOD("definition", function() {
return this.thedef;
});
AST_Symbol.DEFMETHOD("global", function() {
return this.definition().global;
});
function _default_mangler_options(options) {
options = defaults(options, {
eval : false,
ie8 : false,
ie : false,
keep_fargs : false,
keep_fnames : false,
reserved : [],
toplevel : false,
v8 : false,
webkit : false,
});
if (!Array.isArray(options.reserved)) options.reserved = [];
// Never mangle arguments
// Never mangle `arguments`
push_uniq(options.reserved, "arguments");
options.reserved.has = makePredicate(options.reserved);
return options;
}
// We only need to mangle declaration nodes. Special logic wired into the code
// generator will display the mangled name if it is present (and for
// `AST_SymbolRef`s it will use the mangled name of the `AST_SymbolDeclaration`
// that it points to).
AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
options = _default_mangler_options(options);
// 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
// the AST_SymbolDeclaration that it points to).
var lname = -1;
if (options.cache && options.cache.props) {
var mangled_names = this.mangled_names = Object.create(null);
var mangled_names = names_in_use(this, options);
options.cache.props.each(function(mangled_name) {
mangled_names[mangled_name] = true;
mangled_names.set(mangled_name, true);
});
}
var cutoff = 36;
var lname = -1;
var redefined = [];
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_LabeledStatement) {
// lname is incremented when we get to the AST_Label
var save_nesting = lname;
descend();
lname = save_nesting;
return true;
}
if (node instanceof AST_Scope) {
var save_nesting;
if (node instanceof AST_BlockScope) {
// `lname` is incremented when we get to the `AST_Label`
if (node instanceof AST_LabeledStatement) save_nesting = lname;
if (options.webkit && node instanceof AST_IterationStatement && node.init instanceof AST_Let) {
node.init.definitions.forEach(function(defn) {
defn.name.match_symbol(function(sym) {
if (!(sym instanceof AST_SymbolLet)) return;
var def = sym.definition();
var scope = sym.scope.parent_scope;
var redef = scope.def_variable(sym);
sym.thedef = def;
scope.to_mangle.push(redef);
def.redefined = function() {
return redef;
};
});
}, true);
}
var to_mangle = node.to_mangle = [];
node.variables.each(function(def) {
if (!defer_redef(def)) to_mangle.push(def);
});
descend();
if (options.cache && node instanceof AST_Toplevel) {
node.globals.each(mangle);
@@ -448,9 +654,24 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
sym.scope = node;
sym.reference(options);
}
node.variables.each(function(def) {
if (!defer_redef(def)) mangle(def);
});
if (to_mangle.length > cutoff) {
var indices = to_mangle.map(function(def, index) {
return index;
}).sort(function(i, j) {
return to_mangle[j].references.length - to_mangle[i].references.length || i - j;
});
to_mangle = indices.slice(0, cutoff).sort(function(i, j) {
return i - j;
}).map(function(index) {
return to_mangle[index];
}).concat(indices.slice(cutoff).sort(function(i, j) {
return i - j;
}).map(function(index) {
return to_mangle[index];
}));
}
to_mangle.forEach(mangle);
if (node instanceof AST_LabeledStatement && !(options.v8 && in_label(tw))) lname = save_nesting;
return true;
}
if (node instanceof AST_Label) {
@@ -461,13 +682,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
node.mangled_name = name;
return true;
}
if (!options.ie8 && node instanceof AST_Catch) {
var def = node.argname.definition();
var redef = defer_redef(def, node.argname);
descend();
if (!redef) mangle(def);
return true;
}
});
this.walk(tw);
redefined.forEach(mangle);
@@ -477,12 +691,20 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
def.mangle(options);
}
function defer_redef(def, node) {
function defer_redef(def) {
var sym = def.orig[0];
var redef = def.redefined();
if (!redef) return false;
if (!redef) {
if (!(sym instanceof AST_SymbolConst)) return false;
var scope = def.scope.resolve();
if (def.scope === scope) return false;
if (def.scope.parent_scope.find_variable(sym.name)) return false;
redef = scope.def_variable(sym);
scope.to_mangle.push(redef);
}
redefined.push(def);
def.references.forEach(reference);
if (node) reference(node);
if (sym instanceof AST_SymbolCatch || sym instanceof AST_SymbolConst) reference(sym);
return true;
function reference(sym) {
@@ -491,16 +713,23 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
sym.thedef = def;
}
}
function in_label(tw) {
var level = 0, parent;
while (parent = tw.parent(level++)) {
if (parent instanceof AST_Block) return parent instanceof AST_Toplevel && !options.toplevel;
if (parent instanceof AST_LabeledStatement) return true;
}
}
});
AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
var cache = options.cache && options.cache.props;
var avoid = Object.create(null);
var avoid = Object.create(RESERVED_WORDS);
options.reserved.forEach(to_avoid);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
if (node instanceof AST_BlockScope) node.variables.each(add_def);
}));
return avoid;
@@ -524,15 +753,14 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var cname = 0;
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
if (node instanceof AST_BlockScope) node.variables.each(rename);
}));
function next_name() {
var name;
do {
name = base54(cname++);
} while (avoid[name] || RESERVED_WORDS[name]);
} while (avoid[name]);
return name;
}
@@ -543,7 +771,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var redef = def.redefined();
var name = redef ? redef.rename || redef.name : next_name();
def.rename = name;
def.orig.concat(def.references).forEach(function(sym) {
def.forEach(function(sym) {
if (sym.definition() === def) sym.name = name;
});
}
@@ -557,22 +785,24 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
options = _default_mangler_options(options);
base54.reset();
var fn = AST_Symbol.prototype.add_source_map;
try {
AST_Node.prototype.print = function(stream, force_parens) {
this._print(stream, force_parens);
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
base54.consider(this.name, -1);
} else if (options.properties) {
if (this instanceof AST_Dot) {
base54.consider(this.property, -1);
} else if (this instanceof AST_Sub) {
skip_string(this.property);
}
}
AST_Symbol.prototype.add_source_map = function() {
if (!this.unmangleable(options)) base54.consider(this.name, -1);
};
if (options.properties) {
AST_Dot.prototype.add_source_map = function() {
base54.consider(this.property, -1);
};
AST_Sub.prototype.add_source_map = function() {
skip_string(this.property);
};
}
base54.consider(this.print_to_string(), 1);
} finally {
AST_Node.prototype.print = AST_Node.prototype._print;
AST_Symbol.prototype.add_source_map = fn;
delete AST_Dot.prototype.add_source_map;
delete AST_Sub.prototype.add_source_map;
}
base54.sort();
@@ -603,6 +833,7 @@ var base54 = (function() {
var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_");
var chars, frequency;
function reset() {
chars = null;
frequency = Object.create(freq);
}
base54.consider = function(str, delta) {
@@ -614,19 +845,15 @@ var base54 = (function() {
return frequency[b] - frequency[a];
}
base54.sort = function() {
chars = leading.sort(compare).concat(digits.sort(compare));
chars = leading.sort(compare).concat(digits).sort(compare);
};
base54.reset = reset;
reset();
function base54(num) {
var ret = "", base = 54;
num++;
do {
num--;
ret += chars[num % base];
num = Math.floor(num / base);
base = 64;
} while (num > 0);
var ret = leading[num % 54];
for (num = Math.floor(num / 54); --num >= 0; num >>= 6) {
ret += chars[num & 0x3F];
}
return ret;
}
return base54;
+12 -8
View File
@@ -77,21 +77,23 @@ function vlq_encode(num) {
}
function create_array_map() {
var map = Object.create(null);
var map = new Dictionary();
var array = [];
array.index = function(name) {
if (!HOP(map, name)) {
map[name] = array.length;
var index = map.get(name);
if (!(index >= 0)) {
index = array.length;
array.push(name);
map.set(name, index);
}
return map[name];
return index;
};
return array;
}
function SourceMap(options) {
var sources = create_array_map();
var sources_content = options.includeSources && Object.create(null);
var sources_content = options.includeSources && new Dictionary();
var names = create_array_map();
var mappings = "";
if (options.orig) Object.keys(options.orig).forEach(function(name) {
@@ -110,7 +112,7 @@ function SourceMap(options) {
if (!sources_content || !map.sourcesContent) return;
for (var i = 0; i < map.sources.length; i++) {
var content = map.sourcesContent[i];
if (content) sources_content[map.sources[i]] = content;
if (content) sources_content.set(map.sources[i], content);
}
});
var prev_source;
@@ -144,7 +146,9 @@ function SourceMap(options) {
add(source, gen_line, gen_col, orig_line, orig_col, name);
} : add,
setSourceContent: sources_content ? function(source, content) {
sources_content[source] = content;
if (!sources_content.has(source)) {
sources_content.set(source, content);
}
} : noop,
toString: function() {
return JSON.stringify({
@@ -153,7 +157,7 @@ function SourceMap(options) {
sourceRoot: options.root || undefined,
sources: sources,
sourcesContent: sources_content ? sources.map(function(source) {
return sources_content[source] || null;
return sources_content.get(source) || null;
}) : undefined,
names: names,
mappings: mappings,
+67 -2
View File
@@ -82,7 +82,7 @@ TreeTransformer.prototype = new TreeWalker;
if (self.step) self.step = self.step.transform(tw);
self.body = self.body.transform(tw);
});
DEF(AST_ForIn, function(self, tw) {
DEF(AST_ForEnumeration, function(self, tw) {
self.init = self.init.transform(tw);
self.object = self.object.transform(tw);
self.body = self.body.transform(tw);
@@ -116,7 +116,7 @@ TreeTransformer.prototype = new TreeWalker;
if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
});
DEF(AST_Catch, function(self, tw) {
self.argname = self.argname.transform(tw);
if (self.argname) self.argname = self.argname.transform(tw);
self.body = do_list(self.body, tw);
});
DEF(AST_Definitions, function(self, tw) {
@@ -126,11 +126,36 @@ TreeTransformer.prototype = new TreeWalker;
self.name = self.name.transform(tw);
if (self.value) self.value = self.value.transform(tw);
});
DEF(AST_DefaultValue, function(self, tw) {
self.name = self.name.transform(tw);
self.value = self.value.transform(tw);
});
DEF(AST_Lambda, function(self, tw) {
if (self.name) self.name = self.name.transform(tw);
self.argnames = do_list(self.argnames, tw);
if (self.rest) self.rest = self.rest.transform(tw);
self.body = do_list(self.body, tw);
});
function transform_arrow(self, tw) {
self.argnames = do_list(self.argnames, tw);
if (self.rest) self.rest = self.rest.transform(tw);
if (self.value) {
self.value = self.value.transform(tw);
} else {
self.body = do_list(self.body, tw);
}
}
DEF(AST_Arrow, transform_arrow);
DEF(AST_AsyncArrow, transform_arrow);
DEF(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);
});
DEF(AST_ClassProperty, function(self, tw) {
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
if (self.value) self.value = self.value.transform(tw);
});
DEF(AST_Call, function(self, tw) {
self.expression = self.expression.transform(tw);
self.args = do_list(self.args, tw);
@@ -138,6 +163,12 @@ TreeTransformer.prototype = new TreeWalker;
DEF(AST_Sequence, function(self, tw) {
self.expressions = do_list(self.expressions, tw);
});
DEF(AST_Await, function(self, tw) {
self.expression = self.expression.transform(tw);
});
DEF(AST_Yield, function(self, tw) {
if (self.expression) self.expression = self.expression.transform(tw);
});
DEF(AST_Dot, function(self, tw) {
self.expression = self.expression.transform(tw);
});
@@ -145,6 +176,9 @@ TreeTransformer.prototype = new TreeWalker;
self.expression = self.expression.transform(tw);
self.property = self.property.transform(tw);
});
DEF(AST_Spread, function(self, tw) {
self.expression = self.expression.transform(tw);
});
DEF(AST_Unary, function(self, tw) {
self.expression = self.expression.transform(tw);
});
@@ -160,12 +194,43 @@ TreeTransformer.prototype = new TreeWalker;
DEF(AST_Array, function(self, tw) {
self.elements = do_list(self.elements, tw);
});
DEF(AST_DestructuredArray, function(self, tw) {
self.elements = do_list(self.elements, tw);
if (self.rest) self.rest = self.rest.transform(tw);
});
DEF(AST_DestructuredKeyVal, function(self, tw) {
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
self.value = self.value.transform(tw);
});
DEF(AST_DestructuredObject, function(self, tw) {
self.properties = do_list(self.properties, tw);
if (self.rest) self.rest = self.rest.transform(tw);
});
DEF(AST_Object, function(self, tw) {
self.properties = do_list(self.properties, tw);
});
DEF(AST_ObjectProperty, function(self, tw) {
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
self.value = self.value.transform(tw);
});
DEF(AST_ExportDeclaration, function(self, tw) {
self.body = self.body.transform(tw);
});
DEF(AST_ExportDefault, function(self, tw) {
self.body = self.body.transform(tw);
});
DEF(AST_ExportReferences, function(self, tw) {
self.properties = do_list(self.properties, tw);
});
DEF(AST_Import, function(self, tw) {
if (self.all) self.all = self.all.transform(tw);
if (self.default) self.default = self.default.transform(tw);
if (self.properties) self.properties = do_list(self.properties, tw);
});
DEF(AST_Template, function(self, tw) {
if (self.tag) self.tag = self.tag.transform(tw);
self.expressions = do_list(self.expressions, tw);
});
})(function(node, descend) {
node.DEFMETHOD("transform", function(tw, in_list) {
var x, y;
+88 -85
View File
@@ -55,14 +55,6 @@ function find_if(func, array) {
for (var i = array.length; --i >= 0;) if (func(array[i])) return array[i];
}
function repeat_string(str, i) {
if (i <= 0) return "";
if (i == 1) return str;
var d = repeat_string(str, i >> 1);
d += d;
return i & 1 ? d + str : d;
}
function configure_error_stack(fn) {
Object.defineProperty(fn.prototype, "stack", {
get: function() {
@@ -96,15 +88,6 @@ function defaults(args, defs, croak) {
return defs;
}
function merge(obj, ext) {
var count = 0;
for (var i in ext) if (HOP(ext, i)) {
obj[i] = ext[i];
count++;
}
return count;
}
function noop() {}
function return_false() { return false; }
function return_true() { return true; }
@@ -112,51 +95,29 @@ function return_this() { return this; }
function return_null() { return null; }
var List = (function() {
function List(a, f, backwards) {
var ret = [], top = [], i;
function doit() {
function List(a, f) {
var ret = [];
for (var i = 0; i < a.length; i++) {
var val = f(a[i], i);
var is_last = val instanceof Last;
if (is_last) val = val.v;
if (val instanceof AtTop) {
val = val.v;
if (val instanceof Splice) {
top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
} else {
top.push(val);
}
} else if (val !== skip) {
if (val instanceof Splice) {
ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
} else {
ret.push(val);
}
}
return is_last;
}
if (Array.isArray(a)) {
if (backwards) {
for (i = a.length; --i >= 0;) if (doit()) break;
ret.reverse();
top.reverse();
if (val === skip) continue;
if (val instanceof Splice) {
ret.push.apply(ret, val.v);
} else {
for (i = 0; i < a.length; ++i) if (doit()) break;
ret.push(val);
}
} else {
for (i in a) if (HOP(a, i)) if (doit()) break;
}
return top.concat(ret);
return ret;
}
List.is_op = function(val) {
return val === skip || val instanceof AtTop || val instanceof Last || val instanceof Splice;
return val === skip || val instanceof Splice;
};
List.splice = function(val) {
return new Splice(val);
};
List.at_top = function(val) { return new AtTop(val); };
List.splice = function(val) { return new Splice(val); };
List.last = function(val) { return new Last(val); };
var skip = List.skip = {};
function AtTop(val) { this.v = val; }
function Splice(val) { this.v = val; }
function Last(val) { this.v = val; }
function Splice(val) {
this.v = val;
}
return List;
})();
@@ -165,8 +126,9 @@ function push_uniq(array, el) {
}
function string_template(text, props) {
return text.replace(/\{(.+?)\}/g, function(str, p) {
return props && props[p];
return text.replace(/\{([^{}]+)\}/g, function(str, p) {
var value = props[p];
return value instanceof AST_Node ? value.print_to_string() : value;
});
}
@@ -192,63 +154,80 @@ function all(array, predicate) {
}
function Dictionary() {
this._values = Object.create(null);
this._size = 0;
this.values = Object.create(null);
}
Dictionary.prototype = {
set: function(key, val) {
if (!this.has(key)) ++this._size;
this._values["$" + key] = val;
if (key == "__proto__") {
this.proto_value = val;
} else {
this.values[key] = val;
}
return this;
},
add: function(key, val) {
if (this.has(key)) {
this.get(key).push(val);
var list = this.get(key);
if (list) {
list.push(val);
} else {
this.set(key, [ val ]);
}
return this;
},
get: function(key) { return this._values["$" + key] },
get: function(key) {
return key == "__proto__" ? this.proto_value : this.values[key];
},
del: function(key) {
if (this.has(key)) {
--this._size;
delete this._values["$" + key];
if (key == "__proto__") {
delete this.proto_value;
} else {
delete this.values[key];
}
return this;
},
has: function(key) { return ("$" + key) in this._values },
has: function(key) {
return key == "__proto__" ? "proto_value" in this : key in this.values;
},
all: function(predicate) {
for (var i in this._values)
if (!predicate(this._values[i], i.substr(1)))
return false;
for (var i in this.values)
if (!predicate(this.values[i], i)) return false;
if ("proto_value" in this && !predicate(this.proto_value, "__proto__")) return false;
return true;
},
each: function(f) {
for (var i in this._values)
f(this._values[i], i.substr(1));
for (var i in this.values)
f(this.values[i], i);
if ("proto_value" in this) f(this.proto_value, "__proto__");
},
size: function() {
return this._size;
return Object.keys(this.values).length + ("proto_value" in this);
},
map: function(f) {
var ret = [];
for (var i in this._values)
ret.push(f(this._values[i], i.substr(1)));
for (var i in this.values)
ret.push(f(this.values[i], i));
if ("proto_value" in this) ret.push(f(this.proto_value, "__proto__"));
return ret;
},
clone: function() {
var ret = new Dictionary();
for (var i in this._values)
ret._values[i] = this._values[i];
ret._size = this._size;
this.each(function(value, i) {
ret.set(i, value);
});
return ret;
},
toObject: function() { return this._values }
toObject: function() {
var obj = {};
this.each(function(value, i) {
obj["$" + i] = value;
});
return obj;
},
};
Dictionary.fromObject = function(obj) {
var dict = new Dictionary();
dict._size = merge(dict._values, obj);
for (var i in obj)
if (HOP(obj, i)) dict.set(i.slice(1), obj[i]);
return dict;
};
@@ -259,24 +238,48 @@ function HOP(obj, prop) {
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(stack) {
function first_in_statement(stack, arrow, export_default) {
var node = stack.parent(-1);
for (var i = 0, p; p = stack.parent(i++); node = p) {
if (p.TYPE == "Call") {
if (p.expression === node) continue;
if (is_arrow(p)) {
return arrow && p.value === node;
} else if (p instanceof AST_Binary) {
if (p.left === node) continue;
} else if (p.TYPE == "Call") {
if (p.expression === node) continue;
} else if (p instanceof AST_Conditional) {
if (p.condition === node) continue;
} else if (p instanceof AST_ExportDefault) {
return export_default;
} else if (p instanceof AST_PropAccess) {
if (p.expression === node) continue;
} else if (p instanceof AST_Sequence) {
if (p.expressions[0] === node) continue;
} else if (p instanceof AST_Statement) {
return p.body === node;
} else if (p instanceof AST_SimpleStatement) {
return true;
} else if (p instanceof AST_Template) {
if (p.tag === node) continue;
} else if (p instanceof AST_UnaryPostfix) {
if (p.expression === node) continue;
}
return false;
}
}
function DEF_BITPROPS(ctor, props) {
if (props.length > 31) throw new Error("Too many properties: " + props.length + "\n" + props.join(", "));
props.forEach(function(name, pos) {
var mask = 1 << pos;
Object.defineProperty(ctor.prototype, name, {
get: function() {
return !!(this._bits & mask);
},
set: function(val) {
if (val)
this._bits |= mask;
else
this._bits &= ~mask;
},
});
});
}
+13 -13
View File
@@ -1,32 +1,32 @@
{
"_args": [
[
"uglify-js@3.10.0",
"/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series"
"uglify-js@3.16.2",
"/home/node/nuxt"
]
],
"_from": "uglify-js@3.10.0",
"_id": "uglify-js@3.10.0",
"_from": "uglify-js@3.16.2",
"_id": "uglify-js@3.16.2",
"_inBundle": false,
"_integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==",
"_integrity": "sha512-AaQNokTNgExWrkEYA24BTNMSjyqEXPSfhqoS0AxmHkCJ4U+Dyy5AvbGV/sqxuxficEfGGoX3zWw9R7QpLFfEsg==",
"_location": "/uglify-js",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "uglify-js@3.10.0",
"raw": "uglify-js@3.16.2",
"name": "uglify-js",
"escapedName": "uglify-js",
"rawSpec": "3.10.0",
"rawSpec": "3.16.2",
"saveSpec": null,
"fetchSpec": "3.10.0"
"fetchSpec": "3.16.2"
},
"_requiredBy": [
"/html-minifier"
],
"_resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz",
"_spec": "3.10.0",
"_where": "/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series",
"_resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.2.tgz",
"_spec": "3.16.2",
"_where": "/home/node/nuxt",
"author": {
"name": "Mihai Bazon",
"email": "mihai.bazon@gmail.com",
@@ -40,7 +40,7 @@
},
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"devDependencies": {
"acorn": "~7.1.0",
"acorn": "~8.2.1",
"semver": "~6.3.0"
},
"engines": {
@@ -98,5 +98,5 @@
"scripts": {
"test": "node test/compress.js && node test/mocha.js"
},
"version": "3.10.0"
"version": "3.16.2"
}
+456
View File
@@ -0,0 +1,456 @@
<!doctype html>
<html>
<body>
<script>
!function(G) {
var domprops = [];
var objs = [ G ];
var tagNames = [
"a",
"abbr",
"acronym",
"address",
"applet",
"area",
"article",
"aside",
"audio",
"b",
"base",
"basefont",
"bdi",
"bdo",
"bgsound",
"big",
"blink",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"center",
"checked",
"cite",
"code",
"col",
"colgroup",
"command",
"comment",
"compact",
"content",
"data",
"datalist",
"dd",
"declare",
"defer",
"del",
"details",
"dfn",
"dialog",
"dir",
"disabled",
"div",
"dl",
"dt",
"element",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"font",
"footer",
"form",
"frame",
"frameset",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hgroup",
"hr",
"html",
"i",
"iframe",
"image",
"img",
"input",
"ins",
"isindex",
"ismap",
"kbd",
"keygen",
"label",
"legend",
"li",
"link",
"listing",
"main",
"map",
"mark",
"marquee",
"math",
"menu",
"menuitem",
"meta",
"meter",
"multicol",
"multiple",
"nav",
"nextid",
"nobr",
"noembed",
"noframes",
"nohref",
"noresize",
"noscript",
"noshade",
"nowrap",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"plaintext",
"pre",
"progress",
"q",
"rb",
"readonly",
"rp",
"rt",
"rtc",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"selected",
"shadow",
"slot",
"small",
"source",
"spacer",
"span",
"strike",
"strong",
"style",
"sub",
"summary",
"sup",
"svg",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"tt",
"u",
"ul",
"var",
"video",
"wbr",
"xmp",
"XXX",
];
for (var n = 0; n < tagNames.length; n++) {
add(document.createElement(tagNames[n]));
}
var nsNames = {
"http://www.w3.org/1998/Math/MathML": [
"annotation",
"annotation-xml",
"maction",
"maligngroup",
"malignmark",
"math",
"menclose",
"merror",
"mfenced",
"mfrac",
"mglyph",
"mi",
"mlabeledtr",
"mlongdiv",
"mmultiscripts",
"mn",
"mo",
"mover",
"mpadded",
"mphantom",
"mprescripts",
"mroot",
"mrow",
"ms",
"mscarries",
"mscarry",
"msgroup",
"msline",
"mspace",
"msqrt",
"msrow",
"mstack",
"mstyle",
"msub",
"msubsup",
"msup",
"mtable",
"mtd",
"mtext",
"mtr",
"munder",
"munderover",
"none",
"semantics",
],
"http://www.w3.org/2000/svg": [
"a",
"altGlyph",
"altGlyphDef",
"altGlyphItem",
"animate",
"animateColor",
"animateMotion",
"animateTransform",
"circle",
"clipPath",
"color-profile",
"cursor",
"defs",
"desc",
"discard",
"ellipse",
"feBlend",
"feColorMatrix",
"feComponentTransfer",
"feComposite",
"feConvolveMatrix",
"feDiffuseLighting",
"feDisplacementMap",
"feDistantLight",
"feDropShadow",
"feFlood",
"feFuncA",
"feFuncB",
"feFuncG",
"feFuncR",
"feGaussianBlur",
"feImage",
"feMerge",
"feMergeNode",
"feMorphology",
"feOffset",
"fePointLight",
"feSpecularLighting",
"feSpotLight",
"feTile",
"feTurbulence",
"filter",
"font",
"font-face",
"font-face-format",
"font-face-name",
"font-face-src",
"font-face-uri",
"foreignObject",
"g",
"glyph",
"glyphRef",
"hatch",
"hatchpath",
"hkern",
"image",
"line",
"linearGradient",
"marker",
"mask",
"mesh",
"meshgradient",
"meshpatch",
"meshrow",
"metadata",
"missing-glyph",
"mpath",
"path",
"pattern",
"polygon",
"polyline",
"radialGradient",
"rect",
"script",
"set",
"solidcolor",
"stop",
"style",
"svg",
"switch",
"symbol",
"text",
"textPath",
"title",
"tref",
"tspan",
"unknown",
"use",
"view",
"vkern",
],
};
if (document.createElementNS) for (var ns in nsNames) {
for (var n = 0; n < nsNames[ns].length; n++) {
add(document.createElementNS(ns, nsNames[ns][n]));
}
}
var skips = [
G.alert,
G.back,
G.blur,
G.captureEvents,
G.clearImmediate,
G.clearInterval,
G.clearTimeout,
G.close,
G.confirm,
G.console,
G.dump,
G.fetch,
G.find,
G.focus,
G.forward,
G.getAttention,
G.history,
G.home,
G.location,
G.moveBy,
G.moveTo,
G.navigator,
G.open,
G.openDialog,
G.print,
G.process,
G.prompt,
G.resizeBy,
G.resizeTo,
G.setImmediate,
G.setInterval,
G.setTimeout,
G.showModalDialog,
G.sizeToContent,
G.stop,
];
var types = [];
var interfaces = [
"beforeunloadevent",
"compositionevent",
"customevent",
"devicemotionevent",
"deviceorientationevent",
"dragevent",
"event",
"events",
"focusevent",
"hashchangeevent",
"htmlevents",
"keyboardevent",
"messageevent",
"mouseevent",
"mouseevents",
"storageevent",
"svgevents",
"textevent",
"touchevent",
"uievent",
"uievents",
];
var i = 0, full = false;
var addEvent = document.createEvent ? function(type) {
if (~indexOf(types, type)) return;
types.push(type);
for (var j = 0; j < interfaces.length; j++) try {
var event = document.createEvent(interfaces[j]);
event.initEvent(type, true, true);
add(event);
} catch (e) {}
} : function() {};
var scanProperties = Object.getOwnPropertyNames ? function(o, fn) {
var names = Object.getOwnPropertyNames(o);
names.forEach(fn);
for (var k in o) if (!~indexOf(names, k)) fn(k);
} : function(o, fn) {
for (var k in o) fn(k);
};
setTimeout(function next() {
for (var j = 10; --j >= 0 && i < objs.length; i++) {
var o = objs[i];
var skip = ~indexOf(skips, o);
try {
scanProperties(o, function(k) {
if (!~indexOf(domprops, k)) domprops.push(k);
if (/^on/.test(k)) addEvent(k.slice(2));
if (!full) try {
add(o[k]);
} catch (e) {}
});
} catch (e) {}
if (skip || full) continue;
try {
add(o.__proto__);
} catch (e) {}
try {
add(o.prototype);
} catch (e) {}
try {
add(new o());
} catch (e) {}
try {
add(o());
} catch (e) {}
}
if (!full && objs.length > 20000) {
alert(objs.length);
full = true;
}
if (i < objs.length) {
setTimeout(next, 0);
} else {
document.write('<pre>[\n "' + domprops.sort().join('",\n "').replace(/&/g, "&amp;").replace(/</g, "&lt;") + '"\n]</pre>');
}
}, 0);
function add(o) {
if (o) switch (typeof o) {
case "function":
case "object":
if (!~indexOf(objs, o)) objs.push(o);
}
}
function indexOf(list, value) {
var j = list.length;
while (--j >= 0) {
if (list[j] === value) break;
}
return j;
}
}(function() {
return this;
}());
</script>
</body>
</html>
+1475
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,4 +1,5 @@
exports["Dictionary"] = Dictionary;
exports["is_statement"] = is_statement;
exports["List"] = List;
exports["minify"] = minify;
exports["parse"] = parse;
+37 -5
View File
@@ -3,11 +3,11 @@ var fs = require("fs");
exports.FILES = [
require.resolve("../lib/utils.js"),
require.resolve("../lib/ast.js"),
require.resolve("../lib/parse.js"),
require.resolve("../lib/transform.js"),
require.resolve("../lib/parse.js"),
require.resolve("../lib/scope.js"),
require.resolve("../lib/output.js"),
require.resolve("../lib/compress.js"),
require.resolve("../lib/output.js"),
require.resolve("../lib/sourcemap.js"),
require.resolve("../lib/mozilla-ast.js"),
require.resolve("../lib/propmangle.js"),
@@ -23,8 +23,42 @@ new Function("exports", function() {
return code.join("\n\n");
}())(exports);
function to_comment(value) {
if (typeof value != "string") value = JSON.stringify(value, function(key, value) {
return typeof value == "function" ? "<[ " + value + " ]>" : value;
}, 2);
return "// " + value.replace(/\n/g, "\n// ");
}
if (+process.env["UGLIFY_BUG_REPORT"]) exports.minify = function(files, options) {
if (typeof options == "undefined") options = "<<undefined>>";
var code = [
"// UGLIFY_BUG_REPORT",
to_comment(options),
];
if (typeof files == "string") {
code.push("");
code.push("//-------------------------------------------------------------")
code.push("// INPUT CODE", files);
} else for (var name in files) {
code.push("");
code.push("//-------------------------------------------------------------")
code.push(to_comment(name), files[name]);
}
if (options.sourceMap && options.sourceMap.url) {
code.push("");
code.push("//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9");
}
var result = { code: code.join("\n") };
if (options.sourceMap) result.map = '{"version":3,"sources":[],"names":[],"mappings":""}';
return result;
};
function describe_ast() {
var out = OutputStream({ beautify: true });
doitem(AST_Node);
return out.get() + "\n";
function doitem(ctor) {
out.print("AST_" + ctor.TYPE);
var props = ctor.SELF_PROPS.filter(function(prop) {
@@ -55,9 +89,7 @@ function describe_ast() {
});
});
}
};
doitem(AST_Node);
return out + "\n";
}
}
function infer_options(options) {
+22
View File
@@ -0,0 +1,22 @@
// workaround for tty output truncation on Node.js
try {
// prevent buffer overflow and other asynchronous bugs
process.stdout._handle.setBlocking(true);
process.stderr._handle.setBlocking(true);
} catch (e) {
// ensure output buffers are flushed before process termination
var exit = process.exit;
process.exit = function() {
var args = [].slice.call(arguments);
process.once("uncaughtException", function() {
(function callback() {
if (process.stdout.bufferSize || process.stderr.bufferSize) {
setTimeout(callback, 1);
} else {
exit.apply(process, args);
}
})();
});
throw exit;
};
}