We only touch properties that are present in an object literal, or which are
assigned to. Example:
x = { foo: 1 };
x.bar = 2;
x["baz"] = 3;
x[cond ? "qwe" : "asd"] = 4;
console.log(x.stuff);
The names "foo", "bar", "baz", "qwe" and "asd" will be mangled, and the
resulting mangled names will be used for the same properties throughout the
code. The "stuff" will not be, since it's just referenced but never
assigned to.
This *will* break most of the code out there, but could work on carefully
written code: do not use eval, do not define methods or properties by
walking an array of names, etc. Also, a comprehensive list of exclusions
needs to be passed, to avoid mangling properties that are standard in
JavaScript, DOM, used in external libraries etc.
196 lines
5.4 KiB
JavaScript
196 lines
5.4 KiB
JavaScript
var path = require("path");
|
|
var fs = require("fs");
|
|
var vm = require("vm");
|
|
var sys = require("util");
|
|
|
|
var UglifyJS = vm.createContext({
|
|
sys : sys,
|
|
console : console,
|
|
process : process,
|
|
Buffer : Buffer,
|
|
MOZ_SourceMap : require("source-map")
|
|
});
|
|
|
|
function load_global(file) {
|
|
file = path.resolve(path.dirname(module.filename), file);
|
|
try {
|
|
var code = fs.readFileSync(file, "utf8");
|
|
return vm.runInContext(code, UglifyJS, file);
|
|
} catch(ex) {
|
|
// XXX: in case of a syntax error, the message is kinda
|
|
// useless. (no location information).
|
|
sys.debug("ERROR in file: " + file + " / " + ex);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
var FILES = exports.FILES = [
|
|
"../lib/utils.js",
|
|
"../lib/ast.js",
|
|
"../lib/parse.js",
|
|
"../lib/transform.js",
|
|
"../lib/scope.js",
|
|
"../lib/output.js",
|
|
"../lib/compress.js",
|
|
"../lib/sourcemap.js",
|
|
"../lib/mozilla-ast.js",
|
|
"../lib/propmangle.js"
|
|
].map(function(file){
|
|
return fs.realpathSync(path.join(path.dirname(__filename), file));
|
|
});
|
|
|
|
FILES.forEach(load_global);
|
|
|
|
UglifyJS.AST_Node.warn_function = function(txt) {
|
|
sys.error("WARN: " + txt);
|
|
};
|
|
|
|
// XXX: perhaps we shouldn't export everything but heck, I'm lazy.
|
|
for (var i in UglifyJS) {
|
|
if (UglifyJS.hasOwnProperty(i)) {
|
|
exports[i] = UglifyJS[i];
|
|
}
|
|
}
|
|
|
|
exports.minify = function(files, options) {
|
|
options = UglifyJS.defaults(options, {
|
|
spidermonkey : false,
|
|
outSourceMap : null,
|
|
sourceRoot : null,
|
|
inSourceMap : null,
|
|
fromString : false,
|
|
warnings : false,
|
|
mangle : {},
|
|
output : null,
|
|
compress : {}
|
|
});
|
|
UglifyJS.base54.reset();
|
|
|
|
// 1. parse
|
|
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) {
|
|
var compress = { warnings: options.warnings };
|
|
UglifyJS.merge(compress, options.compress);
|
|
toplevel.figure_out_scope();
|
|
var sq = UglifyJS.Compressor(compress);
|
|
toplevel = toplevel.transform(sq);
|
|
}
|
|
|
|
// 3. mangle
|
|
if (options.mangle) {
|
|
toplevel.figure_out_scope(options.mangle);
|
|
toplevel.compute_char_frequency(options.mangle);
|
|
toplevel.mangle_names(options.mangle);
|
|
}
|
|
|
|
// 4. output
|
|
var inMap = options.inSourceMap;
|
|
var output = {};
|
|
if (typeof options.inSourceMap == "string") {
|
|
inMap = fs.readFileSync(options.inSourceMap, "utf8");
|
|
}
|
|
if (options.outSourceMap) {
|
|
output.source_map = UglifyJS.SourceMap({
|
|
file: options.outSourceMap,
|
|
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;
|
|
}
|
|
|
|
var source_map = output.source_map;
|
|
if (source_map) {
|
|
source_map = source_map + "";
|
|
}
|
|
|
|
return {
|
|
code : stream + "",
|
|
map : source_map
|
|
};
|
|
};
|
|
|
|
// exports.describe_ast = function() {
|
|
// function doitem(ctor) {
|
|
// var sub = {};
|
|
// ctor.SUBCLASSES.forEach(function(ctor){
|
|
// sub[ctor.TYPE] = doitem(ctor);
|
|
// });
|
|
// var ret = {};
|
|
// if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
|
|
// if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
|
|
// return ret;
|
|
// }
|
|
// return doitem(UglifyJS.AST_Node).sub;
|
|
// }
|
|
|
|
exports.describe_ast = function() {
|
|
var out = UglifyJS.OutputStream({ beautify: true });
|
|
function doitem(ctor) {
|
|
out.print("AST_" + ctor.TYPE);
|
|
var props = ctor.SELF_PROPS.filter(function(prop){
|
|
return !/^\$/.test(prop);
|
|
});
|
|
if (props.length > 0) {
|
|
out.space();
|
|
out.with_parens(function(){
|
|
props.forEach(function(prop, i){
|
|
if (i) out.space();
|
|
out.print(prop);
|
|
});
|
|
});
|
|
}
|
|
if (ctor.documentation) {
|
|
out.space();
|
|
out.print_string(ctor.documentation);
|
|
}
|
|
if (ctor.SUBCLASSES.length > 0) {
|
|
out.space();
|
|
out.with_block(function(){
|
|
ctor.SUBCLASSES.forEach(function(ctor, i){
|
|
out.indent();
|
|
doitem(ctor);
|
|
out.newline();
|
|
});
|
|
});
|
|
}
|
|
};
|
|
doitem(UglifyJS.AST_Node);
|
|
return out + "";
|
|
};
|