update
This commit is contained in:
+428
-145
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+8021
-2710
File diff suppressed because it is too large
Load Diff
+53
-37
@@ -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
File diff suppressed because it is too large
Load Diff
+977
-551
File diff suppressed because it is too large
Load Diff
+1335
-370
File diff suppressed because it is too large
Load Diff
+92
-67
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, "&").replace(/</g, "<") + '"\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
File diff suppressed because it is too large
Load Diff
+1
@@ -1,4 +1,5 @@
|
||||
exports["Dictionary"] = Dictionary;
|
||||
exports["is_statement"] = is_statement;
|
||||
exports["List"] = List;
|
||||
exports["minify"] = minify;
|
||||
exports["parse"] = parse;
|
||||
|
||||
+37
-5
@@ -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
@@ -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;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user