Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83e0939088 | ||
|
|
9798d96e37 | ||
|
|
ac2caf1088 | ||
|
|
8511e80f48 | ||
|
|
91bc3f1f92 | ||
|
|
8463b48f90 | ||
|
|
e3342a3cf6 | ||
|
|
7bf59b5bcd | ||
|
|
025f3e9596 | ||
|
|
8258edd8a5 | ||
|
|
8669ca219b | ||
|
|
71652690b6 | ||
|
|
37693d2812 | ||
|
|
8fbe200012 | ||
|
|
1a34a13e33 | ||
|
|
ef772b0049 | ||
|
|
6fcabbde08 | ||
|
|
14f290f8ab | ||
|
|
e2e09d5754 | ||
|
|
448a8d3845 | ||
|
|
514936beb8 | ||
|
|
f5c09d0bbf | ||
|
|
014f655c5f | ||
|
|
bf30dc3038 | ||
|
|
ef2ef07cbd | ||
|
|
1a4440080d | ||
|
|
ac0086a745 | ||
|
|
2494daaf68 | ||
|
|
9b404f9de6 | ||
|
|
5344b7dab8 | ||
|
|
0007a53b9c | ||
|
|
1dd05f44eb | ||
|
|
bf7b122ab2 | ||
|
|
e29048b54a | ||
|
|
2eeb640eca | ||
|
|
ceb200fe81 | ||
|
|
f5f8239057 | ||
|
|
6f9d051784 | ||
|
|
931862e97f | ||
|
|
1d0127de21 | ||
|
|
2d8fc61677 | ||
|
|
1e31011874 | ||
|
|
75cdbf19aa | ||
|
|
4339bd5cfa | ||
|
|
1ab2fdaa10 | ||
|
|
eda540f6ec | ||
|
|
90a330da16 | ||
|
|
cad1f9cbd1 | ||
|
|
f6203bd5a8 | ||
|
|
03cf94ebe8 | ||
|
|
c3087dd179 | ||
|
|
2c305af478 | ||
|
|
72e6f64ca8 | ||
|
|
b9fac687ff | ||
|
|
2c88eb6fbe | ||
|
|
a67e3bfdd3 | ||
|
|
27142df4f5 | ||
|
|
5e4c7f4245 | ||
|
|
b521b4b926 | ||
|
|
aa9de76370 | ||
|
|
5a083a938d | ||
|
|
7a30d826b8 | ||
|
|
be55a09edf | ||
|
|
15a148ff6d | ||
|
|
428e19fed2 | ||
|
|
f65e55dff4 | ||
|
|
b634018618 | ||
|
|
fa3300f314 | ||
|
|
bd0886a2c0 | ||
|
|
248f304f02 | ||
|
|
dc5f70eab5 | ||
|
|
df8c5623af | ||
|
|
a790c09c91 | ||
|
|
8f35a363d9 | ||
|
|
d2190c2bf3 | ||
|
|
ea10642572 | ||
|
|
547561a568 | ||
|
|
c16d538ce7 | ||
|
|
73d082df2e | ||
|
|
50b8d7272c | ||
|
|
7d11b96f48 | ||
|
|
dfa395f6ff |
@@ -1,7 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.4"
|
||||
- "0.6"
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
|
||||
11
README.md
11
README.md
@@ -54,6 +54,10 @@ The available options are:
|
||||
--source-map-url The path to the source map to be added in //#
|
||||
sourceMappingURL. Defaults to the value passed with
|
||||
--source-map. [string]
|
||||
--source-map-include-sources
|
||||
Pass this flag if you want to include the content of
|
||||
source files in the source map as sourcesContent
|
||||
property. [boolean]
|
||||
--in-source-map Input source map, useful if you're compressing JS that was
|
||||
generated from some other original code.
|
||||
--screw-ie8 Pass this flag if you don't care about full compliance
|
||||
@@ -169,7 +173,7 @@ To enable the mangler you need to pass `--mangle` (`-m`). The following
|
||||
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||
default).
|
||||
|
||||
- `eval` — mangle names visible in scopes where `eval` or `when` are used
|
||||
- `eval` — mangle names visible in scopes where `eval` or `with` are used
|
||||
(disabled by default).
|
||||
|
||||
When mangling is enabled but you want to prevent certain names from being
|
||||
@@ -249,6 +253,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
||||
statement would get discarded. The current implementation adds some
|
||||
overhead (compression will be slower).
|
||||
|
||||
- `drop_console` -- default `false`. Pass `true` to discard calls to
|
||||
`console.*` functions.
|
||||
|
||||
### The `unsafe` option
|
||||
|
||||
It enables some transformations that *might* break code logic in certain
|
||||
@@ -502,7 +509,7 @@ something like this:
|
||||
```javascript
|
||||
var toplevel = null;
|
||||
files.forEach(function(file){
|
||||
var code = fs.readFileSync(file);
|
||||
var code = fs.readFileSync(file, "utf8");
|
||||
toplevel = UglifyJS.parse(code, {
|
||||
filename: file,
|
||||
toplevel: toplevel
|
||||
|
||||
22
bin/uglifyjs
22
bin/uglifyjs
@@ -22,6 +22,7 @@ mangling you need to use `-c` and `-m`.\
|
||||
.describe("source-map", "Specify an output file where to generate source map.")
|
||||
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
||||
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
|
||||
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
|
||||
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
||||
.describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).")
|
||||
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
|
||||
@@ -62,6 +63,7 @@ You need to pass an argument to this option to specify the name that your module
|
||||
.describe("lint", "Display some scope warnings")
|
||||
.describe("v", "Verbose")
|
||||
.describe("V", "Print version number and exit.")
|
||||
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
|
||||
|
||||
.alias("p", "prefix")
|
||||
.alias("o", "output")
|
||||
@@ -87,6 +89,7 @@ You need to pass an argument to this option to specify the name that your module
|
||||
.string("p")
|
||||
|
||||
.boolean("expr")
|
||||
.boolean("source-map-include-sources")
|
||||
.boolean("screw-ie8")
|
||||
.boolean("export-all")
|
||||
.boolean("self")
|
||||
@@ -96,6 +99,7 @@ You need to pass an argument to this option to specify the name that your module
|
||||
.boolean("spidermonkey")
|
||||
.boolean("lint")
|
||||
.boolean("V")
|
||||
.boolean("noerr")
|
||||
|
||||
.wrap(80)
|
||||
|
||||
@@ -104,6 +108,12 @@ You need to pass an argument to this option to specify the name that your module
|
||||
|
||||
normalize(ARGS);
|
||||
|
||||
if (ARGS.noerr) {
|
||||
UglifyJS.DefaultsError.croak = function(msg, defs) {
|
||||
sys.error("WARN: " + msg);
|
||||
};
|
||||
}
|
||||
|
||||
if (ARGS.version || ARGS.V) {
|
||||
var json = require("../package.json");
|
||||
sys.puts(json.name + ' ' + json.version);
|
||||
@@ -210,6 +220,7 @@ var STATS = {};
|
||||
var OUTPUT_FILE = ARGS.o;
|
||||
var TOPLEVEL = null;
|
||||
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
|
||||
var SOURCES_CONTENT = {};
|
||||
|
||||
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
||||
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
|
||||
@@ -247,6 +258,7 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
}
|
||||
}
|
||||
}
|
||||
SOURCES_CONTENT[file] = code;
|
||||
time_it("parse", function(){
|
||||
if (ARGS.spidermonkey) {
|
||||
var program = JSON.parse(code);
|
||||
@@ -329,6 +341,15 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
if (MANGLE) time_it("mangle", function(){
|
||||
TOPLEVEL.mangle_names(MANGLE);
|
||||
});
|
||||
|
||||
if (ARGS.source_map_include_sources) {
|
||||
for (var file in SOURCES_CONTENT) {
|
||||
if (SOURCES_CONTENT.hasOwnProperty(file)) {
|
||||
SOURCE_MAP.get().setSourceContent(file, SOURCES_CONTENT[file]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_it("generate", function(){
|
||||
TOPLEVEL.print(output);
|
||||
});
|
||||
@@ -349,7 +370,6 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
||||
} else {
|
||||
sys.print(output);
|
||||
sys.error("\n");
|
||||
}
|
||||
|
||||
if (ARGS.stats) {
|
||||
|
||||
12
lib/ast.js
12
lib/ast.js
@@ -295,10 +295,10 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
var parameters = [];
|
||||
|
||||
arg_parameter_pairs.forEach(function(pair) {
|
||||
var split = pair.split(":");
|
||||
var splitAt = pair.lastIndexOf(":");
|
||||
|
||||
args.push(split[0]);
|
||||
parameters.push(split[1]);
|
||||
args.push(pair.substr(0, splitAt));
|
||||
parameters.push(pair.substr(splitAt + 1));
|
||||
});
|
||||
|
||||
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
|
||||
@@ -498,12 +498,6 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
|
||||
}
|
||||
}, AST_Block);
|
||||
|
||||
// XXX: this is wrong according to ECMA-262 (12.4). the catch block
|
||||
// should introduce another scope, as the argname should be visible
|
||||
// only inside the catch block. However, doing it this way because of
|
||||
// IE which simply introduces the name in the surrounding scope. If
|
||||
// we ever want to fix this then AST_Catch should inherit from
|
||||
// AST_Scope.
|
||||
var AST_Catch = DEFNODE("Catch", "argname", {
|
||||
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
||||
$propdoc: {
|
||||
|
||||
179
lib/compress.js
179
lib/compress.js
@@ -61,6 +61,7 @@ function Compressor(options, false_by_default) {
|
||||
loops : !false_by_default,
|
||||
unused : !false_by_default,
|
||||
hoist_funs : !false_by_default,
|
||||
keep_fargs : false,
|
||||
hoist_vars : false,
|
||||
if_return : !false_by_default,
|
||||
join_vars : !false_by_default,
|
||||
@@ -70,6 +71,8 @@ function Compressor(options, false_by_default) {
|
||||
pure_funcs : null,
|
||||
negate_iife : !false_by_default,
|
||||
screw_ie8 : false,
|
||||
drop_console : false,
|
||||
angular : false,
|
||||
|
||||
warnings : true,
|
||||
global_defs : {}
|
||||
@@ -85,13 +88,14 @@ merge(Compressor.prototype, {
|
||||
},
|
||||
before: function(node, descend, in_list) {
|
||||
if (node._squeezed) return node;
|
||||
var was_scope = false;
|
||||
if (node instanceof AST_Scope) {
|
||||
//node.drop_unused(this);
|
||||
node = node.hoist_declarations(this);
|
||||
was_scope = true;
|
||||
}
|
||||
descend(node, this);
|
||||
node = node.optimize(this);
|
||||
if (node instanceof AST_Scope) {
|
||||
if (was_scope && node instanceof AST_Scope) {
|
||||
node.drop_unused(this);
|
||||
descend(node, this);
|
||||
}
|
||||
@@ -196,6 +200,9 @@ merge(Compressor.prototype, {
|
||||
var CHANGED;
|
||||
do {
|
||||
CHANGED = false;
|
||||
if (compressor.option("angular")) {
|
||||
statements = process_for_angular(statements);
|
||||
}
|
||||
statements = eliminate_spurious_blocks(statements);
|
||||
if (compressor.option("dead_code")) {
|
||||
statements = eliminate_dead_code(statements, compressor);
|
||||
@@ -217,6 +224,50 @@ merge(Compressor.prototype, {
|
||||
|
||||
return statements;
|
||||
|
||||
function process_for_angular(statements) {
|
||||
function make_injector(func, name) {
|
||||
return make_node(AST_SimpleStatement, func, {
|
||||
body: make_node(AST_Assign, func, {
|
||||
operator: "=",
|
||||
left: make_node(AST_Dot, name, {
|
||||
expression: make_node(AST_SymbolRef, name, name),
|
||||
property: "$inject"
|
||||
}),
|
||||
right: make_node(AST_Array, func, {
|
||||
elements: func.argnames.map(function(sym){
|
||||
return make_node(AST_String, sym, { value: sym.name });
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
return statements.reduce(function(a, stat){
|
||||
a.push(stat);
|
||||
var token = stat.start;
|
||||
var comments = token.comments_before;
|
||||
if (comments && comments.length > 0) {
|
||||
var last = comments.pop();
|
||||
if (/@ngInject/.test(last.value)) {
|
||||
// case 1: defun
|
||||
if (stat instanceof AST_Defun) {
|
||||
a.push(make_injector(stat, stat.name));
|
||||
}
|
||||
else if (stat instanceof AST_Definitions) {
|
||||
stat.definitions.forEach(function(def){
|
||||
if (def.value && def.value instanceof AST_Lambda) {
|
||||
a.push(make_injector(def.value, def.name));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token);
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function eliminate_spurious_blocks(statements) {
|
||||
var seen_dirs = [];
|
||||
return statements.reduce(function(a, stat){
|
||||
@@ -326,7 +377,7 @@ merge(Compressor.prototype, {
|
||||
stat = stat.clone();
|
||||
stat.condition = stat.condition.negate(compressor);
|
||||
stat.body = make_node(AST_BlockStatement, stat, {
|
||||
body: ret
|
||||
body: as_statement_array(stat.alternative).concat(ret)
|
||||
});
|
||||
stat.alternative = make_node(AST_BlockStatement, stat, {
|
||||
body: body
|
||||
@@ -724,6 +775,14 @@ merge(Compressor.prototype, {
|
||||
if (d && d.constant && d.init) return ev(d.init, compressor);
|
||||
throw def;
|
||||
});
|
||||
def(AST_Dot, function(compressor){
|
||||
if (compressor.option("unsafe") && this.property == "length") {
|
||||
var str = ev(this.expression, compressor);
|
||||
if (typeof str == "string")
|
||||
return str.length;
|
||||
}
|
||||
throw def;
|
||||
});
|
||||
})(function(node, func){
|
||||
node.DEFMETHOD("_eval", func);
|
||||
});
|
||||
@@ -994,18 +1053,20 @@ merge(Compressor.prototype, {
|
||||
var tt = new TreeTransformer(
|
||||
function before(node, descend, in_list) {
|
||||
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||
var sym = a[i];
|
||||
if (sym.unreferenced()) {
|
||||
a.pop();
|
||||
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
|
||||
name : sym.name,
|
||||
file : sym.start.file,
|
||||
line : sym.start.line,
|
||||
col : sym.start.col
|
||||
});
|
||||
if (!compressor.option("keep_fargs")) {
|
||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||
var sym = a[i];
|
||||
if (sym.unreferenced()) {
|
||||
a.pop();
|
||||
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
|
||||
name : sym.name,
|
||||
file : sym.start.file,
|
||||
line : sym.start.line,
|
||||
col : sym.start.col
|
||||
});
|
||||
}
|
||||
else break;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_Defun && node !== self) {
|
||||
@@ -1668,10 +1729,10 @@ merge(Compressor.prototype, {
|
||||
return arg.value;
|
||||
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
|
||||
var ast = parse(code);
|
||||
ast.figure_out_scope();
|
||||
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
|
||||
var comp = new Compressor(compressor.options);
|
||||
ast = ast.transform(comp);
|
||||
ast.figure_out_scope();
|
||||
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
|
||||
ast.mangle_names();
|
||||
var fun;
|
||||
try {
|
||||
@@ -1772,6 +1833,14 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_Undefined, self).transform(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("drop_console")) {
|
||||
if (self.expression instanceof AST_PropAccess &&
|
||||
self.expression.expression instanceof AST_SymbolRef &&
|
||||
self.expression.expression.name == "console" &&
|
||||
self.expression.expression.undeclared()) {
|
||||
return make_node(AST_Undefined, self).transform(compressor);
|
||||
}
|
||||
}
|
||||
return self.evaluate(compressor)[0];
|
||||
});
|
||||
|
||||
@@ -1810,9 +1879,15 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (compressor.option("cascade")) {
|
||||
if (self.car instanceof AST_Assign
|
||||
&& !self.car.left.has_side_effects(compressor)
|
||||
&& self.car.left.equivalent_to(self.cdr)) {
|
||||
return self.car;
|
||||
&& !self.car.left.has_side_effects(compressor)) {
|
||||
if (self.car.left.equivalent_to(self.cdr)) {
|
||||
return self.car;
|
||||
}
|
||||
if (self.cdr instanceof AST_Call
|
||||
&& self.cdr.expression.equivalent_to(self.car.left)) {
|
||||
self.cdr.expression = self.car;
|
||||
return self.cdr;
|
||||
}
|
||||
}
|
||||
if (!self.car.has_side_effects(compressor)
|
||||
&& !self.cdr.has_side_effects(compressor)
|
||||
@@ -1820,6 +1895,18 @@ merge(Compressor.prototype, {
|
||||
return self.car;
|
||||
}
|
||||
}
|
||||
if (self.cdr instanceof AST_UnaryPrefix
|
||||
&& self.cdr.operator == "void"
|
||||
&& !self.cdr.expression.has_side_effects(compressor)) {
|
||||
self.cdr.operator = self.car;
|
||||
return self.cdr;
|
||||
}
|
||||
if (self.cdr instanceof AST_Undefined) {
|
||||
return make_node(AST_UnaryPrefix, self, {
|
||||
operator : "void",
|
||||
expression : self.car
|
||||
});
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
@@ -2030,16 +2117,6 @@ merge(Compressor.prototype, {
|
||||
&& self.right.getValue() === "" && self.left instanceof AST_Binary
|
||||
&& self.left.operator == "+" && self.left.is_string(compressor)) {
|
||||
return self.left;
|
||||
} else if (self.operator == "+" && self.right instanceof AST_String
|
||||
&& self.right.getValue() === "" && self.left instanceof AST_Binary
|
||||
&& self.left.operator == "+" && self.left.right instanceof AST_Number) {
|
||||
return make_node(AST_Binary, self, {
|
||||
left: self.left.left,
|
||||
operator: "+",
|
||||
right: make_node(AST_String, self.right, {
|
||||
value: String(self.left.right.value)
|
||||
})
|
||||
});
|
||||
}
|
||||
if (compressor.option("evaluate")) {
|
||||
if (self.operator == "+") {
|
||||
@@ -2201,7 +2278,7 @@ merge(Compressor.prototype, {
|
||||
* ==>
|
||||
* exp = foo ? something : something_else;
|
||||
*/
|
||||
self = make_node(AST_Assign, self, {
|
||||
return make_node(AST_Assign, self, {
|
||||
operator: consequent.operator,
|
||||
left: consequent.left,
|
||||
right: make_node(AST_Conditional, self, {
|
||||
@@ -2211,6 +2288,38 @@ merge(Compressor.prototype, {
|
||||
})
|
||||
});
|
||||
}
|
||||
if (consequent instanceof AST_Call
|
||||
&& alternative.TYPE === consequent.TYPE
|
||||
&& consequent.args.length == alternative.args.length
|
||||
&& consequent.expression.equivalent_to(alternative.expression)) {
|
||||
if (consequent.args.length == 0) {
|
||||
return make_node(AST_Seq, self, {
|
||||
car: self.condition,
|
||||
cdr: consequent
|
||||
});
|
||||
}
|
||||
if (consequent.args.length == 1) {
|
||||
consequent.args[0] = make_node(AST_Conditional, self, {
|
||||
condition: self.condition,
|
||||
consequent: consequent.args[0],
|
||||
alternative: alternative.args[0]
|
||||
});
|
||||
return consequent;
|
||||
}
|
||||
}
|
||||
// x?y?z:a:a --> x&&y?z:a
|
||||
if (consequent instanceof AST_Conditional
|
||||
&& consequent.alternative.equivalent_to(alternative)) {
|
||||
return make_node(AST_Conditional, self, {
|
||||
condition: make_node(AST_Binary, self, {
|
||||
left: self.condition,
|
||||
operator: "&&",
|
||||
right: consequent.condition
|
||||
}),
|
||||
consequent: consequent.consequent,
|
||||
alternative: alternative
|
||||
});
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
@@ -2248,12 +2357,22 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_Dot, self, {
|
||||
expression : self.expression,
|
||||
property : prop
|
||||
}).optimize(compressor);
|
||||
}
|
||||
var v = parseFloat(prop);
|
||||
if (!isNaN(v) && v.toString() == prop) {
|
||||
self.property = make_node(AST_Number, self.property, {
|
||||
value: v
|
||||
});
|
||||
}
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Dot, function(self, compressor){
|
||||
return self.evaluate(compressor)[0];
|
||||
});
|
||||
|
||||
function literals_in_boolean_context(self, compressor) {
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
return make_node(AST_True, self);
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M),
|
||||
body : from_moz(M.block).body,
|
||||
bcatch : from_moz(M.handlers[0]),
|
||||
bcatch : from_moz(M.handlers ? M.handlers[0] : M.handler),
|
||||
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
|
||||
});
|
||||
},
|
||||
|
||||
103
lib/output.js
103
lib/output.js
@@ -46,22 +46,23 @@
|
||||
function OutputStream(options) {
|
||||
|
||||
options = defaults(options, {
|
||||
indent_start : 0,
|
||||
indent_level : 4,
|
||||
quote_keys : false,
|
||||
space_colon : true,
|
||||
ascii_only : false,
|
||||
inline_script : false,
|
||||
width : 80,
|
||||
max_line_len : 32000,
|
||||
beautify : false,
|
||||
source_map : null,
|
||||
bracketize : false,
|
||||
semicolons : true,
|
||||
comments : false,
|
||||
preserve_line : false,
|
||||
screw_ie8 : false,
|
||||
preamble : null,
|
||||
indent_start : 0,
|
||||
indent_level : 4,
|
||||
quote_keys : false,
|
||||
space_colon : true,
|
||||
ascii_only : false,
|
||||
unescape_regexps : false,
|
||||
inline_script : false,
|
||||
width : 80,
|
||||
max_line_len : 32000,
|
||||
beautify : false,
|
||||
source_map : null,
|
||||
bracketize : false,
|
||||
semicolons : true,
|
||||
comments : false,
|
||||
preserve_line : false,
|
||||
screw_ie8 : false,
|
||||
preamble : null,
|
||||
}, true);
|
||||
|
||||
var indentation = 0;
|
||||
@@ -386,13 +387,20 @@ function OutputStream(options) {
|
||||
var comments = start.comments_before || [];
|
||||
|
||||
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
|
||||
// if this node is `return` or `throw`, we cannot allow comments before
|
||||
// the returned or thrown value.
|
||||
if (self instanceof AST_Exit && self.value
|
||||
&& self.value.start.comments_before
|
||||
&& self.value.start.comments_before.length > 0) {
|
||||
comments = comments.concat(self.value.start.comments_before);
|
||||
self.value.start.comments_before = [];
|
||||
// and https://github.com/mishoo/UglifyJS2/issues/372
|
||||
if (self instanceof AST_Exit && self.value) {
|
||||
self.value.walk(new TreeWalker(function(node){
|
||||
if (node.start && node.start.comments_before) {
|
||||
comments = comments.concat(node.start.comments_before);
|
||||
node.start.comments_before = [];
|
||||
}
|
||||
if (node instanceof AST_Function ||
|
||||
node instanceof AST_Array ||
|
||||
node instanceof AST_Object)
|
||||
{
|
||||
return true; // don't go inside.
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (c.test) {
|
||||
@@ -456,7 +464,7 @@ function OutputStream(options) {
|
||||
|| p instanceof AST_Unary // !(foo, bar, baz)
|
||||
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
|
||||
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|
||||
|| p instanceof AST_Dot // (1, {foo:2}).foo ==> 2
|
||||
|| p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|
||||
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||
@@ -648,7 +656,7 @@ function OutputStream(options) {
|
||||
output.print("for");
|
||||
output.space();
|
||||
output.with_parens(function(){
|
||||
if (self.init) {
|
||||
if (self.init && !(self.init instanceof AST_EmptyStatement)) {
|
||||
if (self.init instanceof AST_Definitions) {
|
||||
self.init.print(output);
|
||||
} else {
|
||||
@@ -988,8 +996,12 @@ function OutputStream(options) {
|
||||
DEFPRINT(AST_UnaryPrefix, function(self, output){
|
||||
var op = self.operator;
|
||||
output.print(op);
|
||||
if (/^[a-z]/i.test(op))
|
||||
if (/^[a-z]/i.test(op)
|
||||
|| (/[+-]$/.test(op)
|
||||
&& self.expression instanceof AST_UnaryPrefix
|
||||
&& /^[+-]/.test(self.expression.operator))) {
|
||||
output.space();
|
||||
}
|
||||
self.expression.print(output);
|
||||
});
|
||||
DEFPRINT(AST_UnaryPostfix, function(self, output){
|
||||
@@ -1111,10 +1123,47 @@ function OutputStream(options) {
|
||||
DEFPRINT(AST_Number, function(self, output){
|
||||
output.print(make_num(self.getValue()));
|
||||
});
|
||||
|
||||
function regexp_safe_literal(code) {
|
||||
return [
|
||||
0x5c , // \
|
||||
0x2f , // /
|
||||
0x2e , // .
|
||||
0x2b , // +
|
||||
0x2a , // *
|
||||
0x3f , // ?
|
||||
0x28 , // (
|
||||
0x29 , // )
|
||||
0x5b , // [
|
||||
0x5d , // ]
|
||||
0x7b , // {
|
||||
0x7d , // }
|
||||
0x24 , // $
|
||||
0x5e , // ^
|
||||
0x3a , // :
|
||||
0x7c , // |
|
||||
0x21 , // !
|
||||
0x0a , // \n
|
||||
0x0d , // \r
|
||||
0x00 , // \0
|
||||
0xfeff , // Unicode BOM
|
||||
0x2028 , // unicode "line separator"
|
||||
0x2029 , // unicode "paragraph separator"
|
||||
].indexOf(code) < 0;
|
||||
};
|
||||
|
||||
DEFPRINT(AST_RegExp, function(self, output){
|
||||
var str = self.getValue().toString();
|
||||
if (output.option("ascii_only"))
|
||||
if (output.option("ascii_only")) {
|
||||
str = output.to_ascii(str);
|
||||
} else if (output.option("unescape_regexps")) {
|
||||
str = str.split("\\\\").map(function(str){
|
||||
return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
|
||||
var code = parseInt(s.substr(2), 16);
|
||||
return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
|
||||
});
|
||||
}).join("\\\\");
|
||||
}
|
||||
output.print(str);
|
||||
var p = output.parent();
|
||||
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
|
||||
|
||||
13
lib/parse.js
13
lib/parse.js
@@ -46,7 +46,7 @@
|
||||
|
||||
var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
|
||||
var KEYWORDS_ATOM = 'false null true';
|
||||
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile'
|
||||
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield'
|
||||
+ " " + KEYWORDS_ATOM + " " + KEYWORDS;
|
||||
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
|
||||
|
||||
@@ -168,14 +168,7 @@ function is_identifier_char(ch) {
|
||||
};
|
||||
|
||||
function is_identifier_string(str){
|
||||
var i = str.length;
|
||||
if (i == 0) return false;
|
||||
if (!is_identifier_start(str.charCodeAt(0))) return false;
|
||||
while (--i >= 0) {
|
||||
if (!is_identifier_char(str.charAt(i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return /^[a-z_$][a-z0-9_$]*$/i.test(str);
|
||||
};
|
||||
|
||||
function parse_js_number(num) {
|
||||
@@ -1381,7 +1374,7 @@ function parse($TEXT, options) {
|
||||
condition : expr,
|
||||
consequent : yes,
|
||||
alternative : expression(false, no_in),
|
||||
end : peek()
|
||||
end : prev()
|
||||
});
|
||||
}
|
||||
return expr;
|
||||
|
||||
53
lib/scope.js
53
lib/scope.js
@@ -71,27 +71,34 @@ SymbolDef.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
||||
// This does what ast_add_scope did in UglifyJS v1.
|
||||
//
|
||||
// Part of it could be done at parse time, but it would complicate
|
||||
// the parser (and it's already kinda complex). It's also worth
|
||||
// having it separated because we might need to call it multiple
|
||||
// times on the same tree.
|
||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
options = defaults(options, {
|
||||
screw_ie8: false
|
||||
});
|
||||
|
||||
// pass 1: setup scope chaining and handle definitions
|
||||
var self = this;
|
||||
var scope = self.parent_scope = null;
|
||||
var defun = null;
|
||||
var nesting = 0;
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (options.screw_ie8 && node instanceof AST_Catch) {
|
||||
var save_scope = scope;
|
||||
scope = new AST_Scope(node);
|
||||
scope.init_scope_vars(nesting);
|
||||
scope.parent_scope = save_scope;
|
||||
descend();
|
||||
scope = save_scope;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope) {
|
||||
node.init_scope_vars(nesting);
|
||||
var save_scope = node.parent_scope = scope;
|
||||
++nesting;
|
||||
scope = node;
|
||||
descend();
|
||||
var save_defun = defun;
|
||||
defun = scope = node;
|
||||
++nesting; descend(); --nesting;
|
||||
scope = save_scope;
|
||||
--nesting;
|
||||
defun = save_defun;
|
||||
return true; // don't descend again in TreeWalker
|
||||
}
|
||||
if (node instanceof AST_Directive) {
|
||||
@@ -108,7 +115,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
||||
node.scope = scope;
|
||||
}
|
||||
if (node instanceof AST_SymbolLambda) {
|
||||
scope.def_function(node);
|
||||
defun.def_function(node);
|
||||
}
|
||||
else if (node instanceof AST_SymbolDefun) {
|
||||
// Careful here, the scope where this should be defined is
|
||||
@@ -116,22 +123,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
||||
// scope when we encounter the AST_Defun node (which is
|
||||
// instanceof AST_Scope) but we get to the symbol a bit
|
||||
// later.
|
||||
(node.scope = scope.parent_scope).def_function(node);
|
||||
(node.scope = defun.parent_scope).def_function(node);
|
||||
}
|
||||
else if (node instanceof AST_SymbolVar
|
||||
|| node instanceof AST_SymbolConst) {
|
||||
var def = scope.def_variable(node);
|
||||
var def = defun.def_variable(node);
|
||||
def.constant = node instanceof AST_SymbolConst;
|
||||
def.init = tw.parent().value;
|
||||
}
|
||||
else if (node instanceof AST_SymbolCatch) {
|
||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
||||
// `catch` argument name should be visible only inside the
|
||||
// catch block. For a quick fix AST_Catch should inherit
|
||||
// from AST_Scope. Keeping it this way because of IE,
|
||||
// which doesn't obey the standard. (it introduces the
|
||||
// identifier in the enclosing scope)
|
||||
scope.def_variable(node);
|
||||
(options.screw_ie8 ? scope : defun)
|
||||
.def_variable(node);
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
@@ -244,6 +246,11 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
|
||||
out: while (true) {
|
||||
var m = base54(++this.cname);
|
||||
if (!is_identifier(m)) continue; // skip over "do"
|
||||
|
||||
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
|
||||
// shadow a name excepted from mangling.
|
||||
if (options.except.indexOf(m) >= 0) continue;
|
||||
|
||||
// we must ensure that the mangled name does not shadow a name
|
||||
// from some parent scope that is referenced in this or in
|
||||
// inner scopes.
|
||||
@@ -358,6 +365,10 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
node.mangled_name = name;
|
||||
return true;
|
||||
}
|
||||
if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
|
||||
to_mangle.push(node.definition());
|
||||
return;
|
||||
}
|
||||
});
|
||||
this.walk(tw);
|
||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
||||
|
||||
@@ -49,6 +49,9 @@ function SourceMap(options) {
|
||||
file : null,
|
||||
root : null,
|
||||
orig : null,
|
||||
|
||||
orig_line_diff : 0,
|
||||
dest_line_diff : 0,
|
||||
});
|
||||
var generator = new MOZ_SourceMap.SourceMapGenerator({
|
||||
file : options.file,
|
||||
@@ -61,14 +64,17 @@ function SourceMap(options) {
|
||||
line: orig_line,
|
||||
column: orig_col
|
||||
});
|
||||
if (info.source === null) {
|
||||
return;
|
||||
}
|
||||
source = info.source;
|
||||
orig_line = info.line;
|
||||
orig_col = info.column;
|
||||
name = info.name;
|
||||
}
|
||||
generator.addMapping({
|
||||
generated : { line: gen_line, column: gen_col },
|
||||
original : { line: orig_line, column: orig_col },
|
||||
generated : { line: gen_line + options.dest_line_diff, column: gen_col },
|
||||
original : { line: orig_line + options.orig_line_diff, column: orig_col },
|
||||
source : source,
|
||||
name : name
|
||||
});
|
||||
|
||||
@@ -82,16 +82,23 @@ function repeat_string(str, i) {
|
||||
};
|
||||
|
||||
function DefaultsError(msg, defs) {
|
||||
Error.call(this, msg);
|
||||
this.msg = msg;
|
||||
this.defs = defs;
|
||||
};
|
||||
DefaultsError.prototype = Object.create(Error.prototype);
|
||||
DefaultsError.prototype.constructor = DefaultsError;
|
||||
|
||||
DefaultsError.croak = function(msg, defs) {
|
||||
throw new DefaultsError(msg, defs);
|
||||
};
|
||||
|
||||
function defaults(args, defs, croak) {
|
||||
if (args === true)
|
||||
args = {};
|
||||
var ret = args || {};
|
||||
if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
|
||||
throw new DefaultsError("`" + i + "` is not a supported option", defs);
|
||||
DefaultsError.croak("`" + i + "` is not a supported option", defs);
|
||||
for (var i in defs) if (defs.hasOwnProperty(i)) {
|
||||
ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"homepage": "http://lisperator.net/uglifyjs",
|
||||
"main": "tools/node.js",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.15",
|
||||
"engines": { "node" : ">=0.4.0" },
|
||||
"maintainers": [{
|
||||
"name": "Mihai Bazon",
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async" : "~0.2.6",
|
||||
"source-map" : "~0.1.7",
|
||||
"source-map" : "0.1.34",
|
||||
"optimist" : "~0.3.5",
|
||||
"uglify-to-browserify": "~1.0.0"
|
||||
},
|
||||
|
||||
@@ -141,3 +141,94 @@ ifs_6: {
|
||||
x = foo || bar || baz || boo ? 20 : 10;
|
||||
}
|
||||
}
|
||||
|
||||
cond_1: {
|
||||
options = {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
if (some_condition()) {
|
||||
do_something(x);
|
||||
} else {
|
||||
do_something(y);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
do_something(some_condition() ? x : y);
|
||||
}
|
||||
}
|
||||
|
||||
cond_2: {
|
||||
options = {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
if (some_condition()) {
|
||||
x = new FooBar(1);
|
||||
} else {
|
||||
x = new FooBar(2);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
x = new FooBar(some_condition() ? 1 : 2);
|
||||
}
|
||||
}
|
||||
|
||||
cond_3: {
|
||||
options = {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
if (some_condition()) {
|
||||
new FooBar(1);
|
||||
} else {
|
||||
FooBar(2);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
some_condition() ? new FooBar(1) : FooBar(2);
|
||||
}
|
||||
}
|
||||
|
||||
cond_4: {
|
||||
options = {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
if (some_condition()) {
|
||||
do_something();
|
||||
} else {
|
||||
do_something();
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
some_condition(), do_something();
|
||||
}
|
||||
}
|
||||
|
||||
cond_5: {
|
||||
options = {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
if (some_condition()) {
|
||||
if (some_other_condition()) {
|
||||
do_something();
|
||||
} else {
|
||||
alternate();
|
||||
}
|
||||
} else {
|
||||
alternate();
|
||||
}
|
||||
|
||||
if (some_condition()) {
|
||||
if (some_other_condition()) {
|
||||
do_something();
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
some_condition() && some_other_condition() ? do_something() : alternate();
|
||||
some_condition() && some_other_condition() && do_something();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,3 +119,47 @@ unused_keep_setter_arg: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unused_var_in_catch: {
|
||||
options = { unused: true };
|
||||
input: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {
|
||||
var x = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
used_var_in_catch: {
|
||||
options = { unused: true };
|
||||
input: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {
|
||||
var x = 10;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {
|
||||
var x = 10;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,15 +54,13 @@ strings_concat: {
|
||||
input: {
|
||||
f(
|
||||
String(x + 'str'),
|
||||
String('str' + x),
|
||||
String(x + 5)
|
||||
String('str' + x)
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
f(
|
||||
x + 'str',
|
||||
'str' + x,
|
||||
x + '5'
|
||||
'str' + x
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,16 +60,16 @@ negate_iife_4: {
|
||||
};
|
||||
input: {
|
||||
if ((function(){ return true })()) {
|
||||
console.log(true);
|
||||
foo(true);
|
||||
} else {
|
||||
console.log(false);
|
||||
bar(false);
|
||||
}
|
||||
(function(){
|
||||
console.log("something");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
!function(){ return true }() ? console.log(false) : console.log(true), function(){
|
||||
!function(){ return true }() ? bar(false) : foo(true), function(){
|
||||
console.log("something");
|
||||
}();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ dot_properties: {
|
||||
a.foo = "bar";
|
||||
a["if"] = "if";
|
||||
a["*"] = "asterisk";
|
||||
a.\u0EB3 = "unicode";
|
||||
a["\u0EB3"] = "unicode";
|
||||
a[""] = "whitespace";
|
||||
a["1_1"] = "foo";
|
||||
}
|
||||
@@ -48,7 +48,27 @@ dot_properties_es5: {
|
||||
a.foo = "bar";
|
||||
a.if = "if";
|
||||
a["*"] = "asterisk";
|
||||
a.\u0EB3 = "unicode";
|
||||
a["\u0EB3"] = "unicode";
|
||||
a[""] = "whitespace";
|
||||
}
|
||||
}
|
||||
|
||||
evaluate_length: {
|
||||
options = {
|
||||
properties: true,
|
||||
unsafe: true,
|
||||
evaluate: true
|
||||
};
|
||||
input: {
|
||||
a = "foo".length;
|
||||
a = ("foo" + "bar")["len" + "gth"];
|
||||
a = b.length;
|
||||
a = ("foo" + b).length;
|
||||
}
|
||||
expect: {
|
||||
a = 3;
|
||||
a = 6;
|
||||
a = b.length;
|
||||
a = ("foo" + b).length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ for (var i in UglifyJS) {
|
||||
|
||||
exports.minify = function(files, options) {
|
||||
options = UglifyJS.defaults(options, {
|
||||
spidermonkey : false,
|
||||
outSourceMap : null,
|
||||
sourceRoot : null,
|
||||
inSourceMap : null,
|
||||
@@ -60,22 +61,28 @@ exports.minify = function(files, options) {
|
||||
output : null,
|
||||
compress : {}
|
||||
});
|
||||
if (typeof files == "string")
|
||||
files = [ files ];
|
||||
|
||||
UglifyJS.base54.reset();
|
||||
|
||||
// 1. parse
|
||||
var toplevel = null;
|
||||
files.forEach(function(file){
|
||||
var code = options.fromString
|
||||
? file
|
||||
: fs.readFileSync(file, "utf8");
|
||||
toplevel = UglifyJS.parse(code, {
|
||||
filename: options.fromString ? "?" : file,
|
||||
toplevel: toplevel
|
||||
var toplevel = null,
|
||||
sourcesContent = {};
|
||||
|
||||
if (options.spidermonkey) {
|
||||
toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
|
||||
} else {
|
||||
if (typeof files == "string")
|
||||
files = [ files ];
|
||||
files.forEach(function(file){
|
||||
var code = options.fromString
|
||||
? file
|
||||
: fs.readFileSync(file, "utf8");
|
||||
sourcesContent[file] = code;
|
||||
toplevel = UglifyJS.parse(code, {
|
||||
filename: options.fromString ? "?" : file,
|
||||
toplevel: toplevel
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 2. compress
|
||||
if (options.compress) {
|
||||
@@ -105,12 +112,25 @@ exports.minify = function(files, options) {
|
||||
orig: inMap,
|
||||
root: options.sourceRoot
|
||||
});
|
||||
if (options.sourceMapIncludeSources) {
|
||||
for (var file in sourcesContent) {
|
||||
if (sourcesContent.hasOwnProperty(file)) {
|
||||
output.source_map.get().setSourceContent(file, sourcesContent[file]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (options.output) {
|
||||
UglifyJS.merge(output, options.output);
|
||||
}
|
||||
var stream = UglifyJS.OutputStream(output);
|
||||
toplevel.print(stream);
|
||||
|
||||
if(options.outSourceMap){
|
||||
stream += "\n//# sourceMappingURL=" + options.outSourceMap;
|
||||
}
|
||||
|
||||
return {
|
||||
code : stream + "",
|
||||
map : output.source_map + ""
|
||||
|
||||
Reference in New Issue
Block a user