From 1a56c2f31d493a17789e79491d5bce5d3dc194ac Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Fri, 14 Apr 2017 07:08:18 +0800 Subject: [PATCH] unify CLI & API under `minify()` - refactor `screw_ie8` to `ie8` - compact `sourceMap` options - more stringent verification on input `options` - toplevel shorthands - `ie8` - `keep_fnames` - `toplevel` - deprecated `fromString` in `minify()` - `minify()` no longer handles any `fs` operations - unify order of operations for `mangle_properties()` on CLI & API - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor` - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()` - both will now do `Compressor`, `mangle_names()` then `mangle_properties()` - `options.parse` / `--parse` for parser options beyond `bare_returns` closes #96 closes #1366 fixes #124 fixes #263 fixes #379 fixes #423 fixes #576 fixes #737 fixes #958 fixes #1036 fixes #1175 fixes #1220 fixes #1223 fixes #1280 --- bin/uglifyjs | 822 +++++++------------------- lib/ast.js | 57 +- lib/compress.js | 15 +- lib/minify.js | 144 +++++ lib/output.js | 8 +- lib/scope.js | 11 +- package.json | 4 +- test/benchmark.js | 8 +- test/compress/ascii.js | 5 +- test/compress/issue-1446.js | 2 +- test/compress/issue-1588.js | 8 +- test/compress/issue-1704.js | 64 +- test/compress/issue-1733.js | 8 +- test/compress/loops.js | 16 +- test/compress/properties.js | 4 +- test/compress/screw-ie8.js | 72 ++- test/mocha/cli.js | 54 +- test/mocha/comment-filter.js | 2 - test/mocha/comment.js | 4 +- test/mocha/comment_before_constant.js | 5 - test/mocha/directives.js | 10 +- test/mocha/glob.js | 112 ++-- test/mocha/huge-number-of-comments.js | 7 +- test/mocha/input-sourcemaps.js | 6 +- test/mocha/let.js | 2 +- test/mocha/line-endings.js | 3 +- test/mocha/minify-file-map.js | 30 +- test/mocha/minify.js | 80 +-- test/mocha/new.js | 2 - test/mocha/screw-ie8.js | 4 +- test/mocha/spidermonkey.js | 4 +- test/ufuzz.js | 8 +- tools/exports.js | 1 + tools/node.js | 255 +------- 34 files changed, 660 insertions(+), 1177 deletions(-) create mode 100644 lib/minify.js diff --git a/bin/uglifyjs b/bin/uglifyjs index ef776492..360461ed 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -3,633 +3,257 @@ "use strict"; -var UglifyJS = require("../tools/node"); -var sys = require("util"); -var yargs = require("yargs"); var fs = require("fs"); +var info = require("../package.json"); var path = require("path"); -var acorn; -var screw_ie8 = true; -var ARGS = yargs - .usage("$0 input1.js [input2.js ...] [options]\n\ -Use a single dash to read input from the standard input.\ -\n\n\ -NOTE: by default there is no mangling/compression.\n\ -Without [options] it will simply parse input files and dump the AST\n\ -with whitespace and comments discarded. To achieve compression and\n\ -mangling you need to use `-c` and `-m`.\ -") - .describe("source-map", "Specify an output file where to generate source map.") - .describe("source-map-root", "The path to the original source to be included in the source map.") - .describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.") - .describe("source-map-inline", "Write base64-encoded source map to the end of js output. Disabled by default") - .describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.") - .describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.") - .describe("screw-ie8", "Do not support Internet Explorer 6/7/8. This flag is enabled by default.") - .describe("support-ie8", "Support non-standard Internet Explorer 6/7/8 javascript.") - .describe("expr", "Parse a single expression, rather than a program (for parsing JSON)") - .describe("p", "Skip prefix for original filenames that appear in source maps. \ -For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \ -You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \ -the source map and the output file.") - .describe("o", "Output file (default STDOUT).") - .describe("b", "Beautify output/specify output options.") - .describe("m", "Mangle names/pass mangler options.") - .describe("r", "Reserved names to exclude from mangling.") - .describe("c", "Enable compressor/pass compressor options. \ -Pass options like -c hoist_vars=false,if_return=false. \ -Use -c with no argument to use the default compression options.") - .describe("d", "Global definitions") - .describe("e", "Embed everything in a big function, with a configurable parameter/argument list.") +var program = require("commander"); +var UglifyJS = require("../tools/node"); - .describe("comments", "Preserve copyright comments in the output. \ -By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \ -You can optionally pass one of the following arguments to this flag:\n\ -- \"all\" to keep all comments\n\ -- a valid JS RegExp like `/foo/`or `/^!/` to keep only matching comments.\n\ -\ -Note that currently not *all* comments can be kept when compression is on, \ -because of dead code removal or cascading statements into sequences.") - - .describe("preamble", "Preamble to prepend to the output. You can use this to insert a \ -comment, for example for licensing information. This will not be \ -parsed, but the source map will adjust for its presence.") - - .describe("stats", "Display operations run time on STDERR.") - .describe("acorn", "Use Acorn for parsing.") - .describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).") - .describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)") - .describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \ -You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.") - .describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.") - .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.") - .describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.") - .describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.") - .describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)") - .describe("reserved-file", "File containing reserved names") - .describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props") - .describe("mangle-props", "Mangle property names (0 - disabled, 1 - mangle all properties, 2 - mangle unquoted properies)") - .describe("mangle-regex", "Only mangle property names matching the regex") - .describe("name-cache", "File to hold mangled names mappings") - .describe("pure-funcs", "List of functions that can be safely removed if their return value is not used") - .describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.") - .describe("wrap-iife", "Wrap IIFEs in parenthesis. Note: this disables the negate_iife compression option") - - .alias("p", "prefix") - .alias("o", "output") - .alias("v", "verbose") - .alias("b", "beautify") - .alias("m", "mangle") - .alias("c", "compress") - .alias("d", "define") - .alias("r", "reserved") - .alias("V", "version") - .alias("e", "enclose") - .alias("q", "quotes") - - .string("source-map") - .string("source-map-root") - .string("source-map-url") - .string("b") - .string("beautify") - .string("m") - .string("mangle") - .string("mangle-props-debug") - .string("c") - .string("compress") - .string("d") - .string("define") - .string("e") - .string("enclose") - .string("comments") - .string("wrap") - .string("p") - .string("prefix") - .string("name-cache") - - .array("reserved-file") - .array("pure-funcs") - - .boolean("expr") - .boolean("source-map-inline") - .boolean("source-map-include-sources") - .boolean("screw-ie8") - .boolean("support-ie8") - .boolean("export-all") - .boolean("self") - .boolean("v") - .boolean("verbose") - .boolean("stats") - .boolean("acorn") - .boolean("spidermonkey") - .boolean("dump-spidermonkey-ast") - .boolean("lint") - .boolean("V") - .boolean("version") - .boolean("noerr") - .boolean("bare-returns") - .boolean("keep-fnames") - .boolean("reserve-domprops") - .boolean("wrap-iife") - - .wrap(80) - - .argv -; - -normalize(ARGS); - -if (ARGS.noerr) { - UglifyJS.DefaultsError.croak = function(msg, defs) { - print_error("WARN: " + msg); - }; -} - -if (ARGS.version || ARGS.V) { - var json = require("../package.json"); - print(json.name + ' ' + json.version); - process.exit(0); -} - -if (ARGS.ast_help) { - var desc = UglifyJS.describe_ast(); - print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2)); - process.exit(0); -} - -if (ARGS.h || ARGS.help) { - print(yargs.help()); - process.exit(0); -} - -if (ARGS.acorn) { - acorn = require("acorn"); -} - -var COMPRESS = getOptions("c", true); -var MANGLE = getOptions("m", true); -var BEAUTIFY = getOptions("b", true); -var RESERVED = null; - -if (ARGS.reserved_file) ARGS.reserved_file.forEach(function(filename){ - RESERVED = UglifyJS.readReservedFile(filename, RESERVED); -}); - -if (ARGS.reserve_domprops) { - RESERVED = UglifyJS.readDefaultReservedFile(RESERVED); -} - -if (ARGS.d) { - if (COMPRESS) COMPRESS.global_defs = getOptions("d"); -} - -if (ARGS.pure_funcs) { - if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs; -} - -if (ARGS.r) { - if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/); -} - -if (RESERVED && MANGLE) { - if (!MANGLE.except) MANGLE.except = RESERVED.vars; - else MANGLE.except = MANGLE.except.concat(RESERVED.vars); -} - -function readNameCache(key) { - return UglifyJS.readNameCache(ARGS.name_cache, key); -} - -function writeNameCache(key, cache) { - return UglifyJS.writeNameCache(ARGS.name_cache, key, cache); -} - -function extractRegex(str) { - if (/^\/.*\/[a-zA-Z]*$/.test(str)) { - var regex_pos = str.lastIndexOf("/"); - return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1)); - } else { - throw new Error("Invalid regular expression: " + str); - } -} - -if (ARGS.quotes === true) { - ARGS.quotes = 3; -} - -if (ARGS.mangle_props === true) { - ARGS.mangle_props = 1; -} else if (ARGS.mangle_props === "unquoted") { - ARGS.mangle_props = 2; -} - -var OUTPUT_OPTIONS = { - beautify : BEAUTIFY ? true : false, - max_line_len : 32000, - preamble : ARGS.preamble || null, - quote_style : ARGS.quotes != null ? ARGS.quotes : 0, +var files = {}; +var options = { + compress: false, + mangle: false }; - -if (ARGS.mangle_props == 2) { - OUTPUT_OPTIONS.keep_quoted_props = true; - if (COMPRESS && !("properties" in COMPRESS)) - COMPRESS.properties = false; +program._name = info.name; +program.version(info.version); +program.parseArgv = program.parse; +program.parse = undefined; +program.option("-p --parse ", "Specify parser options.", parseJS("parse", true)); +program.option("-c --compress [options]", "Enable compressor/specify compressor options.", parseJS("compress", true)); +program.option("-m --mangle [options]", "Mangle names/specify mangler options.", parseJS("mangle", true)); +program.option("-b --beautify [options]", "Beautify output/specify output options.", parseJS("beautify", true)); +program.option("-o --output ", "Output file (default STDOUT)."); +program.option("--comments [filter]", "Preserve copyright comments in the output."); +program.option("--config-file ", "Read minify() options from JSON file."); +program.option("-d --define [=value]", "Global definitions.", parseJS("define")); +program.option("--ie8", "Support non-standard Internet Explorer 8."); +program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name."); +program.option("--self", "Build UglifyJS2 as a library (implies --wrap UglifyJS)"); +program.option("--source-map [options]", "Enable source map/specify source map options.", parseSourceMap()); +program.option("--stats", "Display operations run time on STDERR.") +program.option("--toplevel", "Compress and/or mangle variables in toplevel scope."); +program.option("--wrap ", "Embed everything as a function with “exports” corresponding to “name” globally."); +program.arguments("[files...]").parseArgv(process.argv); +if (program.configFile) { + options = JSON.parse(readFile(program.configFile)); } - -if (ARGS.support_ie8 === true && ARGS.screw_ie8 !== true) { - screw_ie8 = false; +if (!program.output && program.sourceMap && program.sourceMap.url != "inline") { + fatal("ERROR: cannot write source map to STDOUT"); } - -if (COMPRESS) COMPRESS.screw_ie8 = screw_ie8; -if (MANGLE) MANGLE.screw_ie8 = screw_ie8; -OUTPUT_OPTIONS.screw_ie8 = screw_ie8; - -if (ARGS.keep_fnames) { - if (COMPRESS) COMPRESS.keep_fnames = true; - if (MANGLE) MANGLE.keep_fnames = true; +[ + "compress", + "ie8", + "mangle", + "sourceMap", + "toplevel", + "wrap" +].forEach(function(name) { + if (name in program) { + options[name] = program[name]; + } +}); +if (program.beautify) { + options.output = typeof program.beautify == "object" ? program.beautify : {}; + if (!("beautify" in options.output)) { + options.output.beautify = true; + } } - -if (ARGS.wrap_iife) { - if (COMPRESS) COMPRESS.negate_iife = false; - OUTPUT_OPTIONS.wrap_iife = true; +if (program.comments) { + if (typeof options.output != "object") options.output = {}; + options.output.comments = typeof program.comments == "string" ? program.comments : "some"; } - -if (BEAUTIFY) - UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY); - -if (ARGS.comments === "") { - OUTPUT_OPTIONS.comments = "some"; +if (program.define) { + if (typeof options.compress != "object") options.compress = {}; + if (typeof options.compress.global_defs != "object") options.compress.global_defs = {}; + for (var expr in program.define) { + options.compress.global_defs[expr] = program.define[expr]; + } +} +if (program.keepFnames) { + options.keep_fnames = true; +} +if (program.parse) { + if (program.parse.acorn || program.parse.spidermonkey) { + if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser"); + } else { + options.parse = program.parse; + } +} +if (program.self) { + if (program.args.length) { + console.error("WARN: Ignoring input files since --self was passed"); + } + if (!options.wrap) options.wrap = "UglifyJS"; + simple_glob(UglifyJS.FILES).forEach(function(path) { + files[path] = readFile(path); + }); + run(); +} else if (program.args.length) { + simple_glob(program.args).forEach(function(path) { + files[path] = readFile(path); + }); + run(); } else { - OUTPUT_OPTIONS.comments = ARGS.comments; + var chunks = []; + process.stdin.setEncoding('utf-8'); + process.stdin.on('data', function (chunk) { + chunks.push(chunk); + }).on('end', function () { + files = [ chunks.join("") ]; + run(); + }); + process.openStdin(); } -var files = ARGS._.slice(); - -if (process.platform === "win32") - files = UglifyJS.simple_glob(files); - -if (ARGS.self) { - if (files.length > 0) { - print_error("WARN: Ignoring input files since --self was passed"); - } - files = UglifyJS.FILES; - if (!ARGS.wrap) ARGS.wrap = "UglifyJS"; +function convert_ast(fn) { + return UglifyJS.AST_Node.from_mozilla_ast(Object.keys(files).reduce(fn, null)); } -var ORIG_MAP = ARGS.in_source_map; - -if (ORIG_MAP && ORIG_MAP != "inline") { - ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP)); - if (files.length == 0) { - print_error("INFO: Using file from the input source map: " + ORIG_MAP.file); - files = [ ORIG_MAP.file ]; - } -} - -if (files.length == 0) { - files = [ "-" ]; -} - -if (ORIG_MAP == "inline") { - if (files.length > 1) { - print_error("ERROR: Inline source map only works with singular input"); - process.exit(1); - } - if (ARGS.acorn || ARGS.spidermonkey) { - print_error("ERROR: Inline source map only works with built-in parser"); - process.exit(1); - } -} - -if (files.indexOf("-") >= 0 && ARGS.source_map) { - print_error("ERROR: Source map doesn't work with input from STDIN"); - process.exit(1); -} - -if (files.filter(function(el){ return el == "-" }).length > 1) { - print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)"); - process.exit(1); -} - -var STATS = {}; -var TOPLEVEL = null; -var P_RELATIVE = ARGS.p && ARGS.p == "relative"; -var SOURCES_CONTENT = {}; -var index = 0; - -!function cb() { - if (index == files.length) return done(); - var file = files[index++]; - read_whole_file(file, function (err, code) { - if (err) { - print_error("ERROR: can't read file: " + file); - process.exit(1); - } - if (ORIG_MAP == "inline") { - ORIG_MAP = read_source_map(code); - } - if (ARGS.p != null) { - if (P_RELATIVE) { - file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/'); - } else { - var p = parseInt(ARGS.p, 10); - if (!isNaN(p)) { - file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/"); - } - } - } - SOURCES_CONTENT[file] = code; - time_it("parse", function(){ - if (ARGS.spidermonkey) { - var program = JSON.parse(code); - if (!TOPLEVEL) TOPLEVEL = program; - else TOPLEVEL.body = TOPLEVEL.body.concat(program.body); - } - else if (ARGS.acorn) { - TOPLEVEL = acorn.parse(code, { - locations : true, - sourceFile : file, - program : TOPLEVEL +function run() { + UglifyJS.AST_Node.warn_function = function(msg) { + console.error("WARN:", msg); + }; + if (program.stats) program.stats = Date.now(); + try { + if (program.parse) { + if (program.parse.acorn) { + files = convert_ast(function(toplevel, name) { + return require("acorn").parse(files[name], { + locations: true, + program: toplevel, + sourceFile: name + }); + }); + } else if (program.parse.spidermonkey) { + files = convert_ast(function(toplevel, name) { + var obj = JSON.parse(files[name]); + if (!toplevel) return obj; + toplevel.body = toplevel.body.concat(obj.body); + return toplevel; }); } - else { - try { - TOPLEVEL = UglifyJS.parse(code, { - filename : file, - toplevel : TOPLEVEL, - expression : ARGS.expr, - bare_returns : ARGS.bare_returns, - }); - } catch(ex) { - if (ex instanceof UglifyJS.JS_Parse_Error) { - print_error("Parse error at " + file + ":" + ex.line + "," + ex.col); - var col = ex.col; - var line = code.split(/\r?\n/)[ex.line - (col ? 1 : 2)]; - if (line) { - if (col > 40) { - line = line.slice(col - 40); - col = 40; - } - if (col) { - print_error(line.slice(0, 80)); - print_error(line.slice(0, col).replace(/\S/g, " ") + "^"); - } else { - print_error(line.slice(-40)); - print_error(line.slice(-40).replace(/\S/g, " ") + "^"); - } - } - print_error(ex.stack); - process.exit(1); - } - throw ex; + } + var result = UglifyJS.minify(files, options); + } catch (ex) { + if (ex instanceof UglifyJS.JS_Parse_Error) { + console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col); + var col = ex.col; + var line = files[ex.filename].split(/\r?\n/)[ex.line - (col ? 1 : 2)]; + if (line) { + if (col > 40) { + line = line.slice(col - 40); + col = 40; } - }; - }); - cb(); - }); -}(); + if (col) { + console.error(line.slice(0, 80)); + console.error(line.slice(0, col).replace(/\S/g, " ") + "^"); + } else { + console.error(line.slice(-40)); + console.error(line.slice(-40).replace(/\S/g, " ") + "^"); + } + } + } + fatal("ERROR: " + ex.message); + } + if (program.output == "spidermonkey") { + console.log(JSON.stringify(UglifyJS.parse(result.code).to_mozilla_ast(), null, 2)); + } else if (program.output) { + fs.writeFileSync(program.output, result.code); + if (result.map) { + fs.writeFileSync(program.output + ".map", result.map); + } + } else { + console.log(result.code); + } + if (program.stats) console.error("Elapsed:", Date.now() - program.stats); +} -function done() { - var OUTPUT_FILE = ARGS.o; +function fatal(message) { + console.error(message); + process.exit(1); +} - var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({ - file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE, - root: ARGS.source_map_root || ORIG_MAP && ORIG_MAP.sourceRoot, - orig: ORIG_MAP, - }) : null; - - OUTPUT_OPTIONS.source_map = SOURCE_MAP; +// A file glob function that only supports "*" and "?" wildcards in the basename. +// Example: "foo/bar/*baz??.*.js" +// Argument `glob` may be a string or an array of strings. +// Returns an array of strings. Garbage in, garbage out. +function simple_glob(glob) { + if (Array.isArray(glob)) { + return [].concat.apply([], glob.map(simple_glob)); + } + if (glob.match(/\*|\?/)) { + var dir = path.dirname(glob); + try { + var entries = fs.readdirSync(dir); + } catch (ex) {} + if (entries) { + var pattern = "^" + path.basename(glob) + .replace(/[.+^$[\]\\(){}]/g, "\\$&") + .replace(/\*/g, "[^/\\\\]*") + .replace(/\?/g, "[^/\\\\]") + "$"; + var mod = process.platform === "win32" ? "i" : ""; + var rx = new RegExp(pattern, mod); + var results = entries.filter(function(name) { + return rx.test(name); + }).map(function(name) { + return path.join(dir, name); + }); + if (results.length) return results; + } + } + return [ glob ]; +} +function readFile(path) { try { - var output = UglifyJS.OutputStream(OUTPUT_OPTIONS); - var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS); - } catch(ex) { - if (ex instanceof UglifyJS.DefaultsError) { - print_error(ex.message); - print_error("Supported options:"); - print_error(sys.inspect(ex.defs)); - process.exit(1); - } + return fs.readFileSync(path, "utf8"); + } catch (ex) { + fatal("ERROR: " + ex.message); } +} - if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){ - TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL); - }); - - if (ARGS.wrap != null) { - TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all); - } - - if (ARGS.enclose != null) { - var arg_parameter_list = ARGS.enclose; - if (arg_parameter_list === true) { - arg_parameter_list = []; - } - else if (!(arg_parameter_list instanceof Array)) { - arg_parameter_list = [arg_parameter_list]; - } - TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list); - } - - if (ARGS.mangle_props || ARGS.name_cache) (function(){ - var reserved = RESERVED ? RESERVED.props : null; - var cache = readNameCache("props"); - var regex; - +function parseJS(flag, constants) { + return function(value, options) { + options = options || {}; try { - regex = ARGS.mangle_regex ? extractRegex(ARGS.mangle_regex) : null; - } catch (e) { - print_error("ERROR: Invalid --mangle-regex: " + e.message); - process.exit(1); - } - - TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, { - reserved : reserved, - cache : cache, - only_cache : !ARGS.mangle_props, - regex : regex, - ignore_quoted : ARGS.mangle_props == 2, - debug : typeof ARGS.mangle_props_debug === "undefined" ? false : ARGS.mangle_props_debug - }); - writeNameCache("props", cache); - })(); - - var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint - var TL_CACHE = readNameCache("vars"); - if (MANGLE) MANGLE.cache = TL_CACHE; - - if (SCOPE_IS_NEEDED) { - time_it("scope", function(){ - TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE }); - if (ARGS.lint) { - TOPLEVEL.scope_warnings(); - } - }); - } - - if (COMPRESS) { - time_it("squeeze", function(){ - TOPLEVEL = compressor.compress(TOPLEVEL); - }); - } - - if (SCOPE_IS_NEEDED) { - time_it("scope", function(){ - TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE }); - if (MANGLE && !TL_CACHE) { - TOPLEVEL.compute_char_frequency(MANGLE); - } - }); - } - - if (MANGLE) time_it("mangle", function(){ - TOPLEVEL.mangle_names(MANGLE); - }); - - writeNameCache("vars", TL_CACHE); - - 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]); - } - } - } - - if (ARGS.dump_spidermonkey_ast) { - print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2)); - } else { - time_it("generate", function(){ - TOPLEVEL.print(output); - }); - - output = output.get(); - - if (SOURCE_MAP) { - if (ARGS.source_map_inline) { - var base64_string = new Buffer(SOURCE_MAP.toString()).toString('base64'); - output += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + base64_string; - } else { - fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8"); - var source_map_url = ARGS.source_map_url || ( - P_RELATIVE - ? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map) - : ARGS.source_map - ); - output += "\n//# sourceMappingURL=" + source_map_url; - } - } - - if (OUTPUT_FILE) { - fs.writeFileSync(OUTPUT_FILE, output, "utf8"); - } else { - print(output); - } - } - - if (ARGS.stats) { - print_error(UglifyJS.string_template("Timing information (compressed {count} files):", { - count: files.length - })); - for (var i in STATS) if (STATS.hasOwnProperty(i)) { - print_error(UglifyJS.string_template("- {name}: {time}s", { - name: i, - time: (STATS[i] / 1000).toFixed(3) + UglifyJS.parse(value, { + expression: true + }).walk(new UglifyJS.TreeWalker(function(node) { + if (node instanceof UglifyJS.AST_Assign) { + var name = node.left.print_to_string(); + var value = node.right; + if (!constants) { + options[name] = value; + } else if (value instanceof UglifyJS.AST_Constant) { + options[name] = value.getValue(); + } else { + options[name] = value.print_to_string(); + } + return true; + } + if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_PropAccess) { + var name = node.print_to_string(); + options[name] = true; + return true; + } + if (!(node instanceof UglifyJS.AST_Sequence)) throw node; })); - } - } -} - -/* -----[ functions ]----- */ - -function normalize(o) { - for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) { - o[i.replace(/-/g, "_")] = o[i]; - delete o[i]; - } -} - -function getOptions(flag, constants) { - var x = ARGS[flag]; - if (x == null || x === false) return null; - var ret = {}; - if (x !== "") { - if (Array.isArray(x)) x = x.map(function (v) { return "(" + v + ")"; }).join(", "); - - var ast; - try { - ast = UglifyJS.parse(x, { cli: true, expression: true }); } catch(ex) { - if (ex instanceof UglifyJS.JS_Parse_Error) { - print_error("Error parsing arguments for flag `" + flag + "': " + x); - process.exit(1); - } + fatal("Error parsing arguments for '" + flag + "': " + value); } - ast.walk(new UglifyJS.TreeWalker(function(node){ - if (node instanceof UglifyJS.AST_Sequence) return; // descend - if (node instanceof UglifyJS.AST_Assign) { - var name = node.left.print_to_string().replace(/-/g, "_"); - var value = node.right; - if (constants) - value = new Function("return (" + value.print_to_string() + ")")(); - ret[name] = value; - return true; // no descend - } - if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) { - var name = node.print_to_string().replace(/-/g, "_"); - ret[name] = true; - return true; // no descend - } - print_error(node.TYPE); - print_error("Error parsing arguments for flag `" + flag + "': " + x); - process.exit(1); - })); - } - return ret; -} - -function read_whole_file(filename, cb) { - if (filename == "-") { - var chunks = []; - process.stdin.setEncoding('utf-8'); - process.stdin.on('data', function (chunk) { - chunks.push(chunk); - }).on('end', function () { - cb(null, chunks.join("")); - }); - process.openStdin(); - } else { - fs.readFile(filename, "utf-8", cb); + return options; } } -function read_source_map(code) { - var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code); - if (!match) { - print_error("WARN: inline source map not found"); - return null; +function parseSourceMap() { + var parse = parseJS("sourceMap", true); + return function(value, options) { + var hasContent = options && options.sourceMap && "content" in options.sourceMap; + var settings = parse(value, options); + if (!hasContent && settings.content && settings.content != "inline") { + console.error("INFO: Using input source map:", settings.content); + settings.content = readFile(settings.content); + } + return settings; } - return JSON.parse(new Buffer(match[2], "base64")); -} - -function time_it(name, cont) { - var t1 = new Date().getTime(); - var ret = cont(); - if (ARGS.stats) { - var spent = new Date().getTime() - t1; - if (STATS[name]) STATS[name] += spent; - else STATS[name] = spent; - } - return ret; -} - -function print_error(msg) { - console.error("%s", msg); -} - -function print(txt) { - console.log("%s", txt); } diff --git a/lib/ast.js b/lib/ast.js index f78ac576..a3b32390 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -325,62 +325,13 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", { $propdoc: { globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", }, - wrap_enclose: function(arg_parameter_pairs) { - var self = this; - var args = []; - var parameters = []; - - arg_parameter_pairs.forEach(function(pair) { - var splitAt = pair.lastIndexOf(":"); - - args.push(pair.substr(0, splitAt)); - parameters.push(pair.substr(splitAt + 1)); - }); - - var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")"; + wrap_commonjs: function(name) { + var body = this.body; + var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");"; wrapped_tl = parse(wrapped_tl); wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ if (node instanceof AST_Directive && node.value == "$ORIG") { - return MAP.splice(self.body); - } - })); - return wrapped_tl; - }, - wrap_commonjs: function(name, export_all) { - var self = this; - var to_export = []; - if (export_all) { - self.figure_out_scope(); - self.walk(new TreeWalker(function(node){ - if (node instanceof AST_SymbolDeclaration && node.definition().global) { - if (!find_if(function(n){ return n.name == node.name }, to_export)) - to_export.push(node); - } - })); - } - var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))"; - wrapped_tl = parse(wrapped_tl); - wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ - if (node instanceof AST_Directive) { - switch (node.value) { - case "$ORIG": - return MAP.splice(self.body); - case "$EXPORTS": - var body = []; - to_export.forEach(function(sym){ - body.push(new AST_SimpleStatement({ - body: new AST_Assign({ - left: new AST_Sub({ - expression: new AST_SymbolRef({ name: "exports" }), - property: new AST_String({ value: sym.name }), - }), - operator: "=", - right: new AST_SymbolRef(sym), - }), - })); - }); - return MAP.splice(body); - } + return MAP.splice(body); } })); return wrapped_tl; diff --git a/lib/compress.js b/lib/compress.js index 8c254573..26433840 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -62,6 +62,7 @@ function Compressor(options, false_by_default) { global_defs : {}, hoist_funs : !false_by_default, hoist_vars : false, + ie8 : false, if_return : !false_by_default, join_vars : !false_by_default, keep_fargs : true, @@ -74,7 +75,6 @@ function Compressor(options, false_by_default) { pure_getters : !false_by_default && "strict", pure_funcs : null, reduce_vars : !false_by_default, - screw_ie8 : true, sequences : !false_by_default, side_effects : !false_by_default, switches : !false_by_default, @@ -2845,10 +2845,11 @@ merge(Compressor.prototype, { return arg.value; }).join(",") + "){" + self.args[self.args.length - 1].value + "})()"; var ast = parse(code); - ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); + var mangle = { ie8: compressor.option("ie8") }; + ast.figure_out_scope(mangle); var comp = new Compressor(compressor.options); ast = ast.transform(comp); - ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); + ast.figure_out_scope(mangle); ast.mangle_names(); var fun; try { @@ -3294,7 +3295,7 @@ merge(Compressor.prototype, { && self.right.operator == "typeof") { var expr = self.right.expression; if (expr instanceof AST_SymbolRef ? !expr.undeclared() - : !(expr instanceof AST_PropAccess) || compressor.option("screw_ie8")) { + : !(expr instanceof AST_PropAccess && compressor.option("ie8"))) { self.right = expr; self.left = make_node(AST_Undefined, self.left).optimize(compressor); if (self.operator.length == 2) self.operator += "="; @@ -3618,7 +3619,7 @@ merge(Compressor.prototype, { return def.optimize(compressor); } // testing against !self.scope.uses_with first is an optimization - if (compressor.option("screw_ie8") + if (!compressor.option("ie8") && self.undeclared() && (!self.scope.uses_with || !compressor.find_parent(AST_With))) { switch (self.name) { @@ -3938,7 +3939,7 @@ merge(Compressor.prototype, { var prop = self.property; if (prop instanceof AST_String && compressor.option("properties")) { prop = prop.getValue(); - if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) { + if (RESERVED_WORDS(prop) ? !compressor.option("ie8") : is_identifier_string(prop)) { return make_node(AST_Dot, self, { expression : self.expression, property : prop @@ -3965,7 +3966,7 @@ merge(Compressor.prototype, { return def.optimize(compressor); } var prop = self.property; - if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) { + if (RESERVED_WORDS(prop) && compressor.option("ie8")) { return make_node(AST_Sub, self, { expression : self.expression, property : make_node(AST_String, self, { diff --git a/lib/minify.js b/lib/minify.js new file mode 100644 index 00000000..91607017 --- /dev/null +++ b/lib/minify.js @@ -0,0 +1,144 @@ +"use strict"; + +var to_ascii = typeof atob == "undefined" ? function(b64) { + return new Buffer(b64, "base64").toString(); +} : atob; +var to_base64 = typeof btoa == "undefined" ? function(str) { + return new Buffer(str).toString("base64"); +} : btoa; + +function read_source_map(code) { + var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code); + if (!match) { + AST_Node.warn("inline source map not found"); + return null; + } + return to_ascii(match[2]); +} + +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]; + } + }); + } +} + +function minify(files, options) { + var warn_function = AST_Node.warn_function; + try { + if (typeof files == "string") { + files = [ files ]; + } + options = defaults(options, { + compress: {}, + ie8: false, + keep_fnames: false, + mangle: {}, + output: {}, + parse: {}, + sourceMap: false, + toplevel: false, + wrap: false, + }, true); + set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); + set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); + set_shorthand("toplevel", options, [ "compress", "mangle" ]); + if (options.mangle) { + options.mangle = defaults(options.mangle, { + cache: null, + eval: false, + except: [], + ie8: false, + keep_fnames: false, + properties: false, + toplevel: false, + }, true); + } + if (options.sourceMap) { + options.sourceMap = defaults(options.sourceMap, { + content: null, + filename: null, + includeSources: false, + root: null, + url: null, + }, true); + } + var warnings = []; + if (!AST_Node.warn_function) { + AST_Node.warn_function = function(warning) { + warnings.push(warning); + }; + } + var toplevel; + if (files instanceof AST_Toplevel) { + toplevel = files; + } else { + options.parse = options.parse || {}; + options.parse.toplevel = null; + for (var name in files) { + options.parse.filename = name; + options.parse.toplevel = parse(files[name], options.parse); + if (options.sourceMap && options.sourceMap.content == "inline") { + if (Object.keys(files).length > 1) + throw new Error("inline source map only works with singular input"); + options.sourceMap.content = read_source_map(files[name]); + } + } + toplevel = options.parse.toplevel; + } + if (options.wrap) { + toplevel = toplevel.wrap_commonjs(options.wrap); + } + if (options.compress) { + toplevel.figure_out_scope(options.mangle); + toplevel = new Compressor(options.compress).compress(toplevel); + } + if (options.mangle) { + toplevel.figure_out_scope(options.mangle); + base54.reset(); + toplevel.compute_char_frequency(options.mangle); + toplevel.mangle_names(options.mangle); + if (options.mangle.properties) { + toplevel = mangle_properties(toplevel, options.mangle.properties); + } + } + if (options.sourceMap) { + if (typeof options.sourceMap.content == "string") { + options.sourceMap.content = JSON.parse(options.sourceMap.content); + } + options.output.source_map = SourceMap({ + file: options.sourceMap.filename, + orig: options.sourceMap.content, + root: options.sourceMap.root + }); + if (options.sourceMap.includeSources) { + for (var name in files) { + options.output.source_map.get().setSourceContent(name, files[name]); + } + } + } + var stream = OutputStream(options.output); + toplevel.print(stream); + var result = { + code: stream.get() + }; + if (options.sourceMap) { + result.map = options.output.source_map.toString(); + if (options.sourceMap.url == "inline") { + result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map); + } else if (options.sourceMap.url) { + result.code += "\n//# sourceMappingURL=" + options.sourceMap.url; + } + } + if (warnings.length) { + result.warnings = warnings; + } + return result; + } finally { + AST_Node.warn_function = warn_function; + } +} diff --git a/lib/output.js b/lib/output.js index fe982a7b..e2308370 100644 --- a/lib/output.js +++ b/lib/output.js @@ -57,6 +57,7 @@ function OutputStream(options) { beautify : false, bracketize : false, comments : false, + ie8 : false, indent_level : 4, indent_start : 0, inline_script : true, @@ -66,7 +67,6 @@ function OutputStream(options) { preserve_line : false, quote_keys : false, quote_style : 0, - screw_ie8 : true, semicolons : true, shebang : true, source_map : null, @@ -136,7 +136,7 @@ function OutputStream(options) { case "\t": return "\\t"; case "\b": return "\\b"; case "\f": return "\\f"; - case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B"; + case "\x0B": return options.ie8 ? "\\x0B" : "\\v"; case "\u2028": return "\\u2028"; case "\u2029": return "\\u2029"; case "\ufeff": return "\\ufeff"; @@ -906,7 +906,7 @@ function OutputStream(options) { function make_then(self, output) { var b = self.body; if (output.option("bracketize") - || !output.option("screw_ie8") && b instanceof AST_Do) + || output.option("ie8") && b instanceof AST_Do) return make_block(b, output); // The squeezer replaces "block"-s that contain only a single // statement with the statement itself; technically, the AST @@ -1222,7 +1222,7 @@ function OutputStream(options) { && +key + "" == key) && parseFloat(key) >= 0) { output.print(make_num(key)); - } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) { + } else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) { if (quote && output.option("keep_quoted_props")) { output.print_string(key, quote); } else { diff --git a/lib/scope.js b/lib/scope.js index 74760e4f..4f1471ec 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -76,7 +76,7 @@ SymbolDef.prototype = { else if (!this.mangled_name && !this.unmangleable(options)) { var s = this.scope; var sym = this.orig[0]; - if (!options.screw_ie8 && sym instanceof AST_SymbolLambda) + if (options.ie8 && sym instanceof AST_SymbolLambda) s = s.parent_scope; var def; if (this.defun && (def = this.defun.variables.get(this.name))) { @@ -93,7 +93,7 @@ SymbolDef.prototype = { AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ options = defaults(options, { cache: null, - screw_ie8: true, + ie8: false, }); // pass 1: setup scope chaining and handle definitions @@ -220,7 +220,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ self.walk(tw); // pass 3: fix up any scoping issue with IE8 - if (!options.screw_ie8) { + if (options.ie8) { self.walk(new TreeWalker(function(node, descend) { if (node instanceof AST_SymbolCatch) { var name = node.name; @@ -400,9 +400,8 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ return defaults(options, { eval : false, except : [], + ie8 : false, keep_fnames : false, - screw_ie8 : true, - sort : false, // Ignored. Flag retained for backwards compatibility. toplevel : false, }); }); @@ -452,7 +451,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){ node.mangled_name = name; return true; } - if (options.screw_ie8 && node instanceof AST_SymbolCatch) { + if (!options.ie8 && node instanceof AST_SymbolCatch) { to_mangle.push(node.definition()); return; } diff --git a/package.json b/package.json index cfa8eb74..45b3af9f 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,8 @@ "LICENSE" ], "dependencies": { - "source-map": "~0.5.1", - "yargs": "~3.10.0" + "commander": "~2.9.0", + "source-map": "~0.5.1" }, "devDependencies": { "acorn": "~0.6.0", diff --git a/test/benchmark.js b/test/benchmark.js index c150e5cf..4a387b72 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -29,11 +29,11 @@ function done() { var info = results[url]; console.log(); console.log(url); - console.log(info.log); var elapsed = 0; - info.log.replace(/: ([0-9]+\.[0-9]{3})s/g, function(match, time) { - elapsed += parseFloat(time); - }); + console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) { + elapsed += 1e-3 * parseInt(time); + return ""; + })); console.log("Run-time:", elapsed.toFixed(3), "s"); console.log("Original:", info.input, "bytes"); console.log("Uglified:", info.output, "bytes"); diff --git a/test/compress/ascii.js b/test/compress/ascii.js index 2232d263..9662d413 100644 --- a/test/compress/ascii.js +++ b/test/compress/ascii.js @@ -2,7 +2,7 @@ ascii_only_true: { options = {} beautify = { ascii_only : true, - screw_ie8 : true, + ie8 : false, beautify : false, } input: { @@ -20,7 +20,7 @@ ascii_only_false: { options = {} beautify = { ascii_only : false, - screw_ie8 : true, + ie8 : false, beautify : false, } input: { @@ -33,4 +33,3 @@ ascii_only_false: { } expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}' } - diff --git a/test/compress/issue-1446.js b/test/compress/issue-1446.js index 3d69aa09..cad1ae57 100644 --- a/test/compress/issue-1446.js +++ b/test/compress/issue-1446.js @@ -23,7 +23,7 @@ typeof_eq_undefined: { typeof_eq_undefined_ie8: { options = { comparisons: true, - screw_ie8: false + ie8: true, } input: { var a = typeof b != "undefined"; diff --git a/test/compress/issue-1588.js b/test/compress/issue-1588.js index fce9ba54..4e20a21d 100644 --- a/test/compress/issue-1588.js +++ b/test/compress/issue-1588.js @@ -1,9 +1,9 @@ screw_ie8: { options = { - screw_ie8: true, + ie8: false, } mangle = { - screw_ie8: true, + ie8: false, } input: { try { throw "foo"; } catch (x) { console.log(x); } @@ -16,10 +16,10 @@ screw_ie8: { support_ie8: { options = { - screw_ie8: false, + ie8: true, } mangle = { - screw_ie8: false, + ie8: true, } input: { try { throw "foo"; } catch (x) { console.log(x); } diff --git a/test/compress/issue-1704.js b/test/compress/issue-1704.js index a73f7f99..25e49522 100644 --- a/test/compress/issue-1704.js +++ b/test/compress/issue-1704.js @@ -1,10 +1,10 @@ mangle_catch: { options = { - screw_ie8: true, + ie8: false, toplevel: false, } mangle = { - screw_ie8: true, + ie8: false, toplevel: false, } input: { @@ -22,11 +22,11 @@ mangle_catch: { mangle_catch_ie8: { options = { - screw_ie8: false, + ie8: true, toplevel: false, } mangle = { - screw_ie8: false, + ie8: true, toplevel: false, } input: { @@ -44,11 +44,11 @@ mangle_catch_ie8: { mangle_catch_var: { options = { - screw_ie8: true, + ie8: false, toplevel: false, } mangle = { - screw_ie8: true, + ie8: false, toplevel: false, } input: { @@ -66,11 +66,11 @@ mangle_catch_var: { mangle_catch_var_ie8: { options = { - screw_ie8: false, + ie8: true, toplevel: false, } mangle = { - screw_ie8: false, + ie8: true, toplevel: false, } input: { @@ -88,11 +88,11 @@ mangle_catch_var_ie8: { mangle_catch_toplevel: { options = { - screw_ie8: true, + ie8: false, toplevel: true, } mangle = { - screw_ie8: true, + ie8: false, toplevel: true, } input: { @@ -110,11 +110,11 @@ mangle_catch_toplevel: { mangle_catch_ie8_toplevel: { options = { - screw_ie8: false, + ie8: true, toplevel: true, } mangle = { - screw_ie8: false, + ie8: true, toplevel: true, } input: { @@ -132,11 +132,11 @@ mangle_catch_ie8_toplevel: { mangle_catch_var_toplevel: { options = { - screw_ie8: true, + ie8: false, toplevel: true, } mangle = { - screw_ie8: true, + ie8: false, toplevel: true, } input: { @@ -154,11 +154,11 @@ mangle_catch_var_toplevel: { mangle_catch_var_ie8_toplevel: { options = { - screw_ie8: false, + ie8: true, toplevel: true, } mangle = { - screw_ie8: false, + ie8: true, toplevel: true, } input: { @@ -176,11 +176,11 @@ mangle_catch_var_ie8_toplevel: { mangle_catch_redef_1: { options = { - screw_ie8: true, + ie8: false, toplevel: false, } mangle = { - screw_ie8: true, + ie8: false, toplevel: false, } input: { @@ -198,11 +198,11 @@ mangle_catch_redef_1: { mangle_catch_redef_1_ie8: { options = { - screw_ie8: false, + ie8: true, toplevel: false, } mangle = { - screw_ie8: false, + ie8: true, toplevel: false, } input: { @@ -220,11 +220,11 @@ mangle_catch_redef_1_ie8: { mangle_catch_redef_1_toplevel: { options = { - screw_ie8: true, + ie8: false, toplevel: true, } mangle = { - screw_ie8: true, + ie8: false, toplevel: true, } input: { @@ -242,11 +242,11 @@ mangle_catch_redef_1_toplevel: { mangle_catch_redef_1_ie8_toplevel: { options = { - screw_ie8: false, + ie8: true, toplevel: true, } mangle = { - screw_ie8: false, + ie8: true, toplevel: true, } input: { @@ -264,11 +264,11 @@ mangle_catch_redef_1_ie8_toplevel: { mangle_catch_redef_2: { options = { - screw_ie8: true, + ie8: false, toplevel: false, } mangle = { - screw_ie8: true, + ie8: false, toplevel: false, } input: { @@ -285,11 +285,11 @@ mangle_catch_redef_2: { mangle_catch_redef_2_ie8: { options = { - screw_ie8: false, + ie8: true, toplevel: false, } mangle = { - screw_ie8: false, + ie8: true, toplevel: false, } input: { @@ -306,11 +306,11 @@ mangle_catch_redef_2_ie8: { mangle_catch_redef_2_toplevel: { options = { - screw_ie8: true, + ie8: false, toplevel: true, } mangle = { - screw_ie8: true, + ie8: false, toplevel: true, } input: { @@ -327,11 +327,11 @@ mangle_catch_redef_2_toplevel: { mangle_catch_redef_2_ie8_toplevel: { options = { - screw_ie8: false, + ie8: true, toplevel: true, } mangle = { - screw_ie8: false, + ie8: true, toplevel: true, } input: { diff --git a/test/compress/issue-1733.js b/test/compress/issue-1733.js index 3a940c96..f1e576c7 100644 --- a/test/compress/issue-1733.js +++ b/test/compress/issue-1733.js @@ -1,6 +1,6 @@ function_iife_catch: { mangle = { - screw_ie8: true, + ie8: false, } input: { function f(n) { @@ -21,7 +21,7 @@ function_iife_catch: { function_iife_catch_ie8: { mangle = { - screw_ie8: false, + ie8: true, } input: { function f(n) { @@ -42,7 +42,7 @@ function_iife_catch_ie8: { function_catch_catch: { mangle = { - screw_ie8: true, + ie8: false, } input: { var o = 0; @@ -70,7 +70,7 @@ function_catch_catch: { function_catch_catch_ie8: { mangle = { - screw_ie8: false, + ie8: true, } input: { var o = 0; diff --git a/test/compress/loops.js b/test/compress/loops.js index f13f5cc5..4d354bcf 100644 --- a/test/compress/loops.js +++ b/test/compress/loops.js @@ -245,7 +245,7 @@ issue_1532: { issue_186: { beautify = { beautify: false, - screw_ie8: true, + ie8: false, } input: { var x = 3; @@ -264,7 +264,7 @@ issue_186: { issue_186_ie8: { beautify = { beautify: false, - screw_ie8: false, + ie8: true, } input: { var x = 3; @@ -283,7 +283,7 @@ issue_186_ie8: { issue_186_beautify: { beautify = { beautify: true, - screw_ie8: true, + ie8: false, } input: { var x = 3; @@ -310,7 +310,7 @@ issue_186_beautify: { issue_186_beautify_ie8: { beautify = { beautify: true, - screw_ie8: false, + ie8: true, } input: { var x = 3; @@ -340,7 +340,7 @@ issue_186_bracketize: { beautify = { beautify: false, bracketize: true, - screw_ie8: true, + ie8: false, } input: { var x = 3; @@ -360,7 +360,7 @@ issue_186_bracketize_ie8: { beautify = { beautify: false, bracketize: true, - screw_ie8: false, + ie8: true, } input: { var x = 3; @@ -380,7 +380,7 @@ issue_186_beautify_bracketize: { beautify = { beautify: true, bracketize: true, - screw_ie8: true, + ie8: false, } input: { var x = 3; @@ -412,7 +412,7 @@ issue_186_beautify_bracketize_ie8: { beautify = { beautify: true, bracketize: true, - screw_ie8: false, + ie8: true, } input: { var x = 3; diff --git a/test/compress/properties.js b/test/compress/properties.js index 29bdfe2a..b2c201d7 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -13,7 +13,7 @@ keep_properties: { dot_properties: { options = { properties: true, - screw_ie8: false + ie8: true, }; input: { a["foo"] = "bar"; @@ -36,7 +36,7 @@ dot_properties: { dot_properties_es5: { options = { properties: true, - screw_ie8: true + ie8: false, }; input: { a["foo"] = "bar"; diff --git a/test/compress/screw-ie8.js b/test/compress/screw-ie8.js index 68d1a364..a9fbeb51 100644 --- a/test/compress/screw-ie8.js +++ b/test/compress/screw-ie8.js @@ -1,9 +1,9 @@ do_screw: { options = { - screw_ie8: true, + ie8: false, } beautify = { - screw_ie8: true, + ie8: false, ascii_only: true, } input: { @@ -14,10 +14,10 @@ do_screw: { dont_screw: { options = { - screw_ie8: false, + ie8: true, } beautify = { - screw_ie8: false, + ie8: true, ascii_only: true, } input: { @@ -28,7 +28,7 @@ dont_screw: { do_screw_constants: { options = { - screw_ie8: true, + ie8: false, } input: { f(undefined, Infinity); @@ -38,7 +38,7 @@ do_screw_constants: { dont_screw_constants: { options = { - screw_ie8: false, + ie8: true, } input: { f(undefined, Infinity); @@ -47,9 +47,15 @@ dont_screw_constants: { } do_screw_try_catch: { - options = { screw_ie8: true }; - mangle = { screw_ie8: true }; - beautify = { screw_ie8: true }; + options = { + ie8: false, + } + mangle = { + ie8: false, + } + beautify = { + ie8: false, + } input: { good = function(e){ return function(error){ @@ -75,9 +81,15 @@ do_screw_try_catch: { } dont_screw_try_catch: { - options = { screw_ie8: false }; - mangle = { screw_ie8: false }; - beautify = { screw_ie8: false }; + options = { + ie8: true, + } + mangle = { + ie8: true, + } + beautify = { + ie8: true, + } input: { bad = function(e){ return function(error){ @@ -103,9 +115,15 @@ dont_screw_try_catch: { } do_screw_try_catch_undefined: { - options = { screw_ie8: true }; - mangle = { screw_ie8: true }; - beautify = { screw_ie8: true }; + options = { + ie8: false, + } + mangle = { + ie8: false, + } + beautify = { + ie8: false, + } input: { function a(b){ try { @@ -132,9 +150,15 @@ do_screw_try_catch_undefined: { } dont_screw_try_catch_undefined: { - options = { screw_ie8: false }; - mangle = { screw_ie8: false }; - beautify = { screw_ie8: false }; + options = { + ie8: true, + } + mangle = { + ie8: true, + } + beautify = { + ie8: true, + } input: { function a(b){ try { @@ -164,11 +188,11 @@ reduce_vars: { options = { evaluate: true, reduce_vars: true, - screw_ie8: false, + ie8: true, unused: true, } mangle = { - screw_ie8: false, + ie8: true, } input: { function f() { @@ -196,10 +220,10 @@ reduce_vars: { issue_1586_1: { options = { - screw_ie8: false, + ie8: true, } mangle = { - screw_ie8: false, + ie8: true, } input: { function f() { @@ -215,10 +239,10 @@ issue_1586_1: { issue_1586_2: { options = { - screw_ie8: true, + ie8: false, } mangle = { - screw_ie8: true, + ie8: false, } input: { function f() { diff --git a/test/mocha/cli.js b/test/mocha/cli.js index b956309a..2f3fc0fa 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -20,7 +20,7 @@ describe("bin/uglifyjs", function () { done(); }); }); - it("Should be able to filter comments correctly with `--comment all`", function (done) { + it("Should be able to filter comments correctly with `--comments all`", function (done) { var command = uglifyjscmd + ' test/input/comments/filter.js --comments all'; exec(command, function (err, stdout) { @@ -50,8 +50,8 @@ describe("bin/uglifyjs", function () { done(); }); }); - it("Should append source map to output when using --source-map-inline", function (done) { - var command = uglifyjscmd + ' test/input/issue-1323/sample.js --source-map-inline'; + it("Should append source map to output when using --source-map url=inline", function (done) { + var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map url=inline"; exec(command, function (err, stdout) { if (err) throw err; @@ -61,7 +61,7 @@ describe("bin/uglifyjs", function () { done(); }); }); - it("should not append source map to output when not using --source-map-inline", function (done) { + it("should not append source map to output when not using --source-map url=inline", function (done) { var command = uglifyjscmd + ' test/input/issue-1323/sample.js'; exec(command, function (err, stdout) { @@ -152,7 +152,7 @@ describe("bin/uglifyjs", function () { }); }); it("Should process inline source map", function(done) { - var command = uglifyjscmd + ' test/input/issue-520/input.js -mc toplevel --in-source-map inline --source-map-inline'; + var command = uglifyjscmd + " test/input/issue-520/input.js -mc toplevel --source-map content=inline,url=inline"; exec(command, function (err, stdout) { if (err) throw err; @@ -162,40 +162,44 @@ describe("bin/uglifyjs", function () { }); }); it("Should warn for missing inline source map", function(done) { - var command = uglifyjscmd + ' test/input/issue-1323/sample.js --in-source-map inline'; + var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline"; exec(command, function (err, stdout, stderr) { if (err) throw err; - assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n"); + assert.strictEqual(stdout, [ + "var bar=function(){function foo(bar){return bar}return foo}();", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=", + "", + ].join("\n")); assert.strictEqual(stderr, "WARN: inline source map not found\n"); done(); }); }); it("Should fail with multiple input and inline source map", function(done) { - var command = uglifyjscmd + ' test/input/issue-520/input.js test/input/issue-520/output.js --in-source-map inline --source-map-inline'; + var command = uglifyjscmd + " test/input/issue-520/input.js test/input/issue-520/output.js --source-map content=inline,url=inline"; exec(command, function (err, stdout, stderr) { assert.ok(err); - assert.strictEqual(stderr, "ERROR: Inline source map only works with singular input\n"); + assert.strictEqual(stderr, "ERROR: inline source map only works with singular input\n"); done(); }); }); it("Should fail with acorn and inline source map", function(done) { - var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --acorn'; + var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p acorn"; exec(command, function (err, stdout, stderr) { assert.ok(err); - assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n"); + assert.strictEqual(stderr, "ERROR: inline source map only works with built-in parser\n"); done(); }); }); it("Should fail with SpiderMonkey and inline source map", function(done) { - var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --spidermonkey'; + var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p spidermonkey"; exec(command, function (err, stdout, stderr) { assert.ok(err); - assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n"); + assert.strictEqual(stderr, "ERROR: inline source map only works with built-in parser\n"); done(); }); }); @@ -208,7 +212,7 @@ describe("bin/uglifyjs", function () { assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12"); assert.strictEqual(lines[1], "function f(a{}"); assert.strictEqual(lines[2], " ^"); - assert.strictEqual(lines[3], "SyntaxError: Unexpected token punc «{», expected punc «,»"); + assert.strictEqual(lines[3], "ERROR: Unexpected token punc «{», expected punc «,»"); done(); }); }); @@ -221,7 +225,7 @@ describe("bin/uglifyjs", function () { assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12"); assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);"); assert.strictEqual(lines[2], "\t\t \t ^"); - assert.strictEqual(lines[3], "SyntaxError: Invalid syntax: 0abc"); + assert.strictEqual(lines[3], "ERROR: Invalid syntax: 0abc"); done(); }); }); @@ -234,7 +238,7 @@ describe("bin/uglifyjs", function () { assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0"); assert.strictEqual(lines[1], "foo, bar("); assert.strictEqual(lines[2], " ^"); - assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)"); + assert.strictEqual(lines[3], "ERROR: Unexpected token: eof (undefined)"); done(); }); }); @@ -247,20 +251,10 @@ describe("bin/uglifyjs", function () { assert.strictEqual(lines[0], "Parse error at test/input/invalid/loop-no-body.js:2,0"); assert.strictEqual(lines[1], "for (var i = 0; i < 1; i++) "); assert.strictEqual(lines[2], " ^"); - assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)"); + assert.strictEqual(lines[3], "ERROR: Unexpected token: eof (undefined)"); done(); }); }); - it("Should support hyphen as shorthand", function(done) { - var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep-fnames=true'; - - exec(command, function (err, stdout) { - if (err) throw err; - - assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n"); - done(); - }); - }); it("Should throw syntax error (5--)", function(done) { var command = uglifyjscmd + ' test/input/invalid/assign_1.js'; @@ -271,7 +265,7 @@ describe("bin/uglifyjs", function () { "Parse error at test/input/invalid/assign_1.js:1,18", "console.log(1 || 5--);", " ^", - "SyntaxError: Invalid use of -- operator" + "ERROR: Invalid use of -- operator" ].join("\n")); done(); }); @@ -286,7 +280,7 @@ describe("bin/uglifyjs", function () { "Parse error at test/input/invalid/assign_2.js:1,32", "console.log(2 || (Math.random() /= 2));", " ^", - "SyntaxError: Invalid assignment" + "ERROR: Invalid assignment" ].join("\n")); done(); }); @@ -301,7 +295,7 @@ describe("bin/uglifyjs", function () { "Parse error at test/input/invalid/assign_3.js:1,18", "console.log(3 || ++this);", " ^", - "SyntaxError: Invalid use of ++ operator" + "ERROR: Invalid use of ++ operator" ].join("\n")); done(); }); diff --git a/test/mocha/comment-filter.js b/test/mocha/comment-filter.js index ec17aa8c..4b74ebf9 100644 --- a/test/mocha/comment-filter.js +++ b/test/mocha/comment-filter.js @@ -75,7 +75,6 @@ describe("comment filters", function() { it("Should handle shebang and preamble correctly", function() { var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", { - fromString: true, output: { preamble: "/* Build */" } }).code; assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;"); @@ -83,7 +82,6 @@ describe("comment filters", function() { it("Should handle preamble without shebang correctly", function() { var code = UglifyJS.minify("var x = 10;", { - fromString: true, output: { preamble: "/* Build */" } }).code; assert.strictEqual(code, "/* Build */\nvar x=10;"); diff --git a/test/mocha/comment.js b/test/mocha/comment.js index 56470e0f..acad3693 100644 --- a/test/mocha/comment.js +++ b/test/mocha/comment.js @@ -20,7 +20,7 @@ describe("Comment", function() { for (var i = 0; i < tests.length; i++) { assert.throws(function() { - uglify.parse(tests[i], {fromString: true}) + uglify.parse(tests[i]); }, fail, tests[i]); } }); @@ -43,7 +43,7 @@ describe("Comment", function() { for (var i = 0; i < tests.length; i++) { assert.throws(function() { - uglify.parse(tests[i], {fromString: true}) + uglify.parse(tests[i]); }, fail, tests[i]); } }); diff --git a/test/mocha/comment_before_constant.js b/test/mocha/comment_before_constant.js index eaa8691c..9b69e078 100644 --- a/test/mocha/comment_before_constant.js +++ b/test/mocha/comment_before_constant.js @@ -6,9 +6,7 @@ describe("comment before constant", function() { it("Should test comment before constant is retained and output after mangle.", function() { var result = Uglify.minify(js, { - fromString: true, compress: { collapse_vars: false, reduce_vars: false }, - mangle: {}, output: { comments: true }, }); assert.strictEqual(result.code, 'function f(){/*c1*/var/*c2*/n=/*c3*/!1;return n}'); @@ -16,12 +14,9 @@ describe("comment before constant", function() { it("Should test code works when comments disabled.", function() { var result = Uglify.minify(js, { - fromString: true, compress: { collapse_vars: false, reduce_vars: false }, - mangle: {}, output: { comments: false }, }); assert.strictEqual(result.code, 'function f(){var n=!1;return n}'); }); }); - diff --git a/test/mocha/directives.js b/test/mocha/directives.js index 5189f1ad..16279a5d 100644 --- a/test/mocha/directives.js +++ b/test/mocha/directives.js @@ -197,7 +197,7 @@ describe("Directives", function() { assert.strictEqual( uglify.minify( '"use strict";\'use strict\';"use strict";"use strict";;\'use strict\';console.log(\'use strict\');', - {fromString: true, output: {beautify: true, quote_style: 3}, compress: false} + {output: {beautify: true, quote_style: 3}, compress: false} ).code, '"use strict";\n\n\'use strict\';\n\n"use strict";\n\n"use strict";\n\n;\'use strict\';\n\nconsole.log(\'use strict\');' ); @@ -225,7 +225,7 @@ describe("Directives", function() { for (var i = 0; i < tests.length; i++) { assert.strictEqual( - uglify.minify(tests[i][0], {fromString: true, quote_style: 3, compress: false, mangle: false}).code, + uglify.minify(tests[i][0], {compress: false, mangle: false}).code, tests[i][1], tests[i][0] ); @@ -234,7 +234,7 @@ describe("Directives", function() { it("Should add double semicolon when relying on automatic semicolon insertion", function() { var code = uglify.minify('"use strict";"use\\x20strict";', - {fromString: true, output: {semicolons: false}, compress: false} + {output: {semicolons: false}, compress: false} ).code; assert.strictEqual(code, '"use strict";;"use strict"\n'); }); @@ -340,7 +340,7 @@ describe("Directives", function() { ]; for (var i = 0; i < tests.length; i++) { assert.strictEqual( - uglify.minify(tests[i][0], {fromString: true, output:{quote_style: tests[i][1]}, compress: false}).code, + uglify.minify(tests[i][0], {output:{quote_style: tests[i][1]}, compress: false}).code, tests[i][2], tests[i][0] + " using mode " + tests[i][1] ); @@ -362,7 +362,7 @@ describe("Directives", function() { for (var i = 0; i < tests.length; i++) { assert.strictEqual( - uglify.minify(tests[i][0], {fromString: true, compress: {collapse_vars: true, side_effects: true}}).code, + uglify.minify(tests[i][0], {compress: {collapse_vars: true, side_effects: true}}).code, tests[i][1], tests[i][0] ); diff --git a/test/mocha/glob.js b/test/mocha/glob.js index e9555a52..56d3f82a 100644 --- a/test/mocha/glob.js +++ b/test/mocha/glob.js @@ -1,58 +1,80 @@ -var Uglify = require('../../'); var assert = require("assert"); +var exec = require("child_process").exec; var path = require("path"); +var readFileSync = require("fs").readFileSync; -describe("minify() with input file globs", function() { - it("minify() with one input file glob string.", function() { - var result = Uglify.minify("test/input/issue-1242/foo.*"); - assert.strictEqual(result.code, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);'); - }); - it("minify() with an array of one input file glob.", function() { - var result = Uglify.minify([ - "test/input/issue-1242/b*.es5", - ]); - assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}'); - }); - it("minify() with an array of multiple input file globs.", function() { - var result = Uglify.minify([ - "test/input/issue-1242/???.es5", - "test/input/issue-1242/*.js", - ], { - compress: { toplevel: true } +describe("bin/uglifyjs with input file globs", function() { + var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; + it("bin/uglifyjs with one input file extension glob.", function(done) { + var command = uglifyjscmd + ' "test/input/issue-1242/foo.*" -cm'; + + exec(command, function(err, stdout) { + if (err) throw err; + + assert.strictEqual(stdout, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);\n'); + done(); }); - assert.strictEqual(result.code, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);'); }); - it("should throw with non-matching glob string", function() { - var glob = "test/input/issue-1242/blah.*"; - assert.strictEqual(Uglify.simple_glob(glob).length, 1); - assert.strictEqual(Uglify.simple_glob(glob)[0], glob); - assert.throws(function() { - Uglify.minify(glob); - }, "should throw file not found"); + it("bin/uglifyjs with one input file name glob.", function(done) { + var command = uglifyjscmd + ' "test/input/issue-1242/b*.es5" -cm'; + + exec(command, function(err, stdout) { + if (err) throw err; + + assert.strictEqual(stdout, 'function bar(n){return 3*n}function baz(n){return n/2}\n'); + done(); + }); }); - it('"?" in glob string should not match "/"', function() { - var glob = "test/input?issue-1242/foo.*"; - assert.strictEqual(Uglify.simple_glob(glob).length, 1); - assert.strictEqual(Uglify.simple_glob(glob)[0], glob); - assert.throws(function() { - Uglify.minify(glob); - }, "should throw file not found"); + it("bin/uglifyjs with multiple input file globs.", function(done) { + var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel'; + + exec(command, function(err, stdout) { + if (err) throw err; + + assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n'); + done(); + }); }); - it("should handle special characters in glob string", function() { - var result = Uglify.minify("test/input/issue-1632/^{*}[???](*)+$.??"); - assert.strictEqual(result.code, "console.log(x);"); + it("should throw with non-matching glob string", function(done) { + var command = uglifyjscmd + ' "test/input/issue-1242/blah.*"'; + + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.ok(/^ERROR: ENOENT/.test(stderr)); + done(); + }); }); - it("should handle array of glob strings - matching and otherwise", function() { + it('"?" in glob string should not match "/"', function(done) { + var command = uglifyjscmd + ' "test/input?issue-1242/foo.*"'; + + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.ok(/^ERROR: ENOENT/.test(stderr)); + done(); + }); + }); + it("should handle special characters in glob string", function(done) { + var command = uglifyjscmd + ' "test/input/issue-1632/^{*}[???](*)+$.??" -cm'; + + exec(command, function(err, stdout) { + if (err) throw err; + + assert.strictEqual(stdout, "console.log(x);\n"); + done(); + }); + }); + it("should handle array of glob strings - matching and otherwise", function(done) { var dir = "test/input/issue-1242"; - var matches = Uglify.simple_glob([ + var command = uglifyjscmd + ' "' + [ path.join(dir, "b*.es5"), path.join(dir, "z*.es5"), - path.join(dir, "*.js"), - ]); - assert.strictEqual(matches.length, 4); - assert.strictEqual(matches[0], path.join(dir, "bar.es5")); - assert.strictEqual(matches[1], path.join(dir, "baz.es5")); - assert.strictEqual(matches[2], path.join(dir, "z*.es5")); - assert.strictEqual(matches[3], path.join(dir, "qux.js")); + path.join(dir, "*.js") + ].join('" "') + '"'; + + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.ok(/^ERROR: ENOENT.*?z\*\.es5/.test(stderr)); + done(); + }); }); }); diff --git a/test/mocha/huge-number-of-comments.js b/test/mocha/huge-number-of-comments.js index 3b90bc0e..a58f8d0a 100644 --- a/test/mocha/huge-number-of-comments.js +++ b/test/mocha/huge-number-of-comments.js @@ -8,12 +8,7 @@ describe("Huge number of comments.", function() { for (i = 1; i <= 5000; ++i) { js += "// " + i + "\n"; } for (; i <= 10000; ++i) { js += "/* " + i + " */ /**/"; } js += "x; }"; - var result = Uglify.minify(js, { - fromString: true, - mangle: false, - compress: {} - }); + var result = Uglify.minify(js, { mangle: false }); assert.strictEqual(result.code, "function lots_of_comments(x){return 7-x}"); }); }); - diff --git a/test/mocha/input-sourcemaps.js b/test/mocha/input-sourcemaps.js index d5284e3c..bda6e1a2 100644 --- a/test/mocha/input-sourcemaps.js +++ b/test/mocha/input-sourcemaps.js @@ -25,9 +25,9 @@ describe("input sourcemaps", function() { transpilemap = sourceMap || getMap(); var result = Uglify.minify(transpiled, { - fromString: true, - inSourceMap: transpilemap, - outSourceMap: true + sourceMap: { + content: transpilemap + } }); map = new SourceMapConsumer(result.map); diff --git a/test/mocha/let.js b/test/mocha/let.js index 89fd9f1a..f41fd59b 100644 --- a/test/mocha/let.js +++ b/test/mocha/let.js @@ -11,7 +11,7 @@ describe("let", function() { s += "var v" + i + "=0;"; } s += '}'; - var result = Uglify.minify(s, {fromString: true, compress: false}); + var result = Uglify.minify(s, {compress: false}); // Verify that select keywords and reserved keywords not produced assert.strictEqual(result.code.indexOf("var let="), -1); diff --git a/test/mocha/line-endings.js b/test/mocha/line-endings.js index 10e2a1c5..379ee2b9 100644 --- a/test/mocha/line-endings.js +++ b/test/mocha/line-endings.js @@ -3,9 +3,8 @@ var assert = require("assert"); describe("line-endings", function() { var options = { - fromString: true, - mangle: false, compress: false, + mangle: false, output: { beautify: false, comments: /^!/, diff --git a/test/mocha/minify-file-map.js b/test/mocha/minify-file-map.js index 169e730e..cae5ccb7 100644 --- a/test/mocha/minify-file-map.js +++ b/test/mocha/minify-file-map.js @@ -6,43 +6,41 @@ describe("Input file as map", function() { var jsMap = { '/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};' }; - var result = Uglify.minify(jsMap, {fromString: true, outSourceMap: true}); + var result = Uglify.minify(jsMap, {sourceMap: true}); var map = JSON.parse(result.map); assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); assert.deepEqual(map.sources, ['/scripts/foo.js']); assert.strictEqual(map.file, undefined); - result = Uglify.minify(jsMap, {fromString: true, outFileName: 'out.js'}); - assert.strictEqual(result.map, null); + result = Uglify.minify(jsMap); + assert.strictEqual(result.map, undefined); - result = Uglify.minify(jsMap, {fromString: true, outFileName: 'out.js', outSourceMap: true}); + result = Uglify.minify(jsMap, {sourceMap: {filename: 'out.js'}}); map = JSON.parse(result.map); assert.strictEqual(map.file, 'out.js'); }); - it("Should accept array of objects and strings", function() { + it("Should accept array of strings", function() { var jsSeq = [ - {'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'}, + 'var foo = {"x": 1, y: 2, \'z\': 3};', 'var bar = 15;' ]; - var result = Uglify.minify(jsSeq, {fromString: true, outSourceMap: true}); + var result = Uglify.minify(jsSeq, {sourceMap: true}); var map = JSON.parse(result.map); assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;'); - assert.strictEqual(map.sources[0], '/scripts/foo.js'); + assert.deepEqual(map.sources, ['0', '1']); }); it("Should correctly include source", function() { - var jsSeq = [ - {'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'}, - 'var bar = 15;' - ]; - var result = Uglify.minify(jsSeq, {fromString: true, outSourceMap: true, sourceMapIncludeSources: true}); + var jsMap = { + '/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};' + }; + var result = Uglify.minify(jsMap, {sourceMap: {includeSources: true}}); var map = JSON.parse(result.map); - assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;'); - assert.deepEqual(map.sourcesContent, ['var foo = {"x": 1, y: 2, \'z\': 3};', 'var bar = 15;']); + assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); + assert.deepEqual(map.sourcesContent, ['var foo = {"x": 1, y: 2, \'z\': 3};']); }); - }); diff --git a/test/mocha/minify.js b/test/mocha/minify.js index a4587cb7..e332dbef 100644 --- a/test/mocha/minify.js +++ b/test/mocha/minify.js @@ -2,10 +2,14 @@ var Uglify = require('../../'); var assert = require("assert"); var readFileSync = require("fs").readFileSync; +function read(path) { + return readFileSync(path, "utf8"); +} + describe("minify", function() { it("Should test basic sanity of minify with default options", function() { var js = 'function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }'; - var result = Uglify.minify(js, {fromString: true}); + var result = Uglify.minify(js); assert.strictEqual(result.code, 'function foo(n){return n?3:7}'); }); @@ -13,7 +17,7 @@ describe("minify", function() { it("Should preserve quotes in object literals", function() { var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var result = Uglify.minify(js, { - fromString: true, output: { + output: { keep_quoted_props: true }}); assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};'); @@ -22,7 +26,7 @@ describe("minify", function() { it("Should preserve quote styles when quote_style is 3", function() { var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var result = Uglify.minify(js, { - fromString: true, output: { + output: { keep_quoted_props: true, quote_style: 3 }}); @@ -32,7 +36,7 @@ describe("minify", function() { it("Should not preserve quotes in object literals when disabled", function() { var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var result = Uglify.minify(js, { - fromString: true, output: { + output: { keep_quoted_props: false, quote_style: 3 }}); @@ -44,12 +48,13 @@ describe("minify", function() { it("Shouldn't mangle quoted properties", function() { var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};'; var result = Uglify.minify(js, { - fromString: true, compress: { properties: false }, - mangleProperties: { - ignore_quoted: true + mangle: { + properties: { + ignore_quoted: true + } }, output: { keep_quoted_props: true, @@ -63,10 +68,12 @@ describe("minify", function() { describe("inSourceMap", function() { it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() { - var result = Uglify.minify('./test/input/issue-1236/simple.js', { - outSourceMap: "simple.min.js.map", - inSourceMap: "./test/input/issue-1236/simple.js.map", - sourceMapIncludeSources: true + var result = Uglify.minify(read("./test/input/issue-1236/simple.js"), { + sourceMap: { + content: read("./test/input/issue-1236/simple.js.map"), + filename: "simple.min.js", + includeSources: true + } }); var map = JSON.parse(result.map); @@ -77,10 +84,12 @@ describe("minify", function() { 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));'); }); it("Should process inline source map", function() { - var code = Uglify.minify("./test/input/issue-520/input.js", { + var code = Uglify.minify(read("./test/input/issue-520/input.js"), { compress: { toplevel: true }, - inSourceMap: "inline", - sourceMapInline: true + sourceMap: { + content: "inline", + url: "inline" + } }).code + "\n"; assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8")); }); @@ -91,9 +100,11 @@ describe("minify", function() { warnings.push(txt); }; try { - var result = Uglify.minify("./test/input/issue-1323/sample.js", { - inSourceMap: "inline", + var result = Uglify.minify(read("./test/input/issue-1323/sample.js"), { mangle: false, + sourceMap: { + content: "inline" + } }); assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();"); assert.strictEqual(warnings.length, 1); @@ -105,20 +116,13 @@ describe("minify", function() { it("Should fail with multiple input and inline source map", function() { assert.throws(function() { Uglify.minify([ - "./test/input/issue-520/input.js", - "./test/input/issue-520/output.js" + read("./test/input/issue-520/input.js"), + read("./test/input/issue-520/output.js") ], { - inSourceMap: "inline", - sourceMapInline: true - }); - }); - }); - it("Should fail with SpiderMonkey and inline source map", function() { - assert.throws(function() { - Uglify.minify("./test/input/issue-520/input.js", { - inSourceMap: "inline", - sourceMapInline: true, - spidermonkey: true + sourceMap: { + content: "inline", + url: "inline" + } }); }); }); @@ -127,17 +131,16 @@ describe("minify", function() { describe("sourceMapInline", function() { it("should append source map to output js when sourceMapInline is enabled", function() { var result = Uglify.minify('var a = function(foo) { return foo; };', { - fromString: true, - sourceMapInline: true + sourceMap: { + url: "inline" + } }); var code = result.code; assert.strictEqual(code, "var a=function(n){return n};\n" + - "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIj8iXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0="); + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0="); }); it("should not append source map to output js when sourceMapInline is not enabled", function() { - var result = Uglify.minify('var a = function(foo) { return foo; };', { - fromString: true - }); + var result = Uglify.minify('var a = function(foo) { return foo; };'); var code = result.code; assert.strictEqual(code, "var a=function(n){return n};"); }); @@ -146,7 +149,6 @@ describe("minify", function() { describe("#__PURE__", function() { it("should drop #__PURE__ hint after use", function() { var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', { - fromString: true, output: { comments: "all", beautify: false, @@ -157,7 +159,6 @@ describe("minify", function() { }); it("should not drop #__PURE__ hint if function is retained", function() { var result = Uglify.minify("var a = /*#__PURE__*/(function(){ foo(); })();", { - fromString: true, output: { comments: "all", beautify: false, @@ -171,11 +172,11 @@ describe("minify", function() { describe("JS_Parse_Error", function() { it("should throw syntax error", function() { assert.throws(function() { - Uglify.minify("function f(a{}", { fromString: true }); + Uglify.minify("function f(a{}"); }, function(err) { assert.ok(err instanceof Error); assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»"); - assert.strictEqual(err.filename, 0); + assert.strictEqual(err.filename, "0"); assert.strictEqual(err.line, 1); assert.strictEqual(err.col, 12); return true; @@ -191,5 +192,4 @@ describe("minify", function() { assert.strictEqual(ast.print_to_string(), "function f(a){for(var i=0;i 1) { - throw new Error("inline source map only works with singular input"); - } - } - [].concat(files).forEach(function (files, i) { - if (typeof files === 'string') { - addFile(files, options.fromString ? i : files); - } else { - for (var fileUrl in files) { - addFile(files[fileUrl], fileUrl); - } - } - }); - } - if (options.wrap) { - toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll); - } - - // 2. compress - if (options.compress) { - var compress = { warnings: options.warnings }; - UglifyJS.merge(compress, options.compress); - toplevel.figure_out_scope(options.mangle); - var sq = UglifyJS.Compressor(compress); - toplevel = sq.compress(toplevel); - } - - // 3. mangle properties - if (options.mangleProperties || options.nameCache) { - options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props"); - toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties); - UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache); - } - - // 4. mangle - if (options.mangle) { - toplevel.figure_out_scope(options.mangle); - toplevel.compute_char_frequency(options.mangle); - toplevel.mangle_names(options.mangle); - } - - // 5. output - var output = { max_line_len: 32000 }; - if (options.outSourceMap || options.sourceMapInline) { - output.source_map = UglifyJS.SourceMap({ - // prefer outFileName, otherwise use outSourceMap without .map suffix - file: options.outFileName || (typeof options.outSourceMap === 'string' ? options.outSourceMap.replace(/\.map$/i, '') : null), - 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); - - - var source_map = output.source_map; - if (source_map) { - source_map = source_map + ""; - } - - var mappingUrlPrefix = "\n//# sourceMappingURL="; - if (options.sourceMapInline) { - stream += mappingUrlPrefix + "data:application/json;charset=utf-8;base64," + new Buffer(source_map).toString("base64"); - } else if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) { - stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap); - } - - return { - code : stream + "", - map : source_map - }; -}; - -// UglifyJS.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; -// } - UglifyJS.describe_ast = function() { var out = UglifyJS.OutputStream({ beautify: true }); function doitem(ctor) { @@ -227,94 +65,3 @@ UglifyJS.describe_ast = function() { doitem(UglifyJS.AST_Node); return out + ""; }; - -function readReservedFile(filename, reserved) { - if (!reserved) { - reserved = { vars: [], props: [] }; - } - var data = fs.readFileSync(filename, "utf8"); - data = JSON.parse(data); - if (data.vars) { - data.vars.forEach(function(name){ - UglifyJS.push_uniq(reserved.vars, name); - }); - } - if (data.props) { - data.props.forEach(function(name){ - UglifyJS.push_uniq(reserved.props, name); - }); - } - return reserved; -} - -UglifyJS.readReservedFile = readReservedFile; - -UglifyJS.readDefaultReservedFile = function(reserved) { - return readReservedFile(require.resolve("./domprops.json"), reserved); -}; - -UglifyJS.readNameCache = function(filename, key) { - var cache = null; - if (filename) { - try { - var cache = fs.readFileSync(filename, "utf8"); - cache = JSON.parse(cache)[key]; - if (!cache) throw "init"; - cache.props = UglifyJS.Dictionary.fromObject(cache.props); - } catch(ex) { - cache = { - cname: -1, - props: new UglifyJS.Dictionary() - }; - } - } - return cache; -}; - -UglifyJS.writeNameCache = function(filename, key, cache) { - if (filename) { - var data; - try { - data = fs.readFileSync(filename, "utf8"); - data = JSON.parse(data); - } catch(ex) { - data = {}; - } - data[key] = { - cname: cache.cname, - props: cache.props.toObject() - }; - fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8"); - } -}; - -// A file glob function that only supports "*" and "?" wildcards in the basename. -// Example: "foo/bar/*baz??.*.js" -// Argument `glob` may be a string or an array of strings. -// Returns an array of strings. Garbage in, garbage out. -UglifyJS.simple_glob = function simple_glob(glob) { - if (Array.isArray(glob)) { - return [].concat.apply([], glob.map(simple_glob)); - } - if (glob.match(/\*|\?/)) { - var dir = path.dirname(glob); - try { - var entries = fs.readdirSync(dir); - } catch (ex) {} - if (entries) { - var pattern = "^" + path.basename(glob) - .replace(/[.+^$[\]\\(){}]/g, "\\$&") - .replace(/\*/g, "[^/\\\\]*") - .replace(/\?/g, "[^/\\\\]") + "$"; - var mod = process.platform === "win32" ? "i" : ""; - var rx = new RegExp(pattern, mod); - var results = entries.filter(function(name) { - return rx.test(name); - }).map(function(name) { - return path.join(dir, name); - }); - if (results.length) return results; - } - } - return [ glob ]; -};