diff --git a/bin/uglifyjs b/bin/uglifyjs index 3a3318b2..6ce67a6b 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -64,6 +64,7 @@ You need to pass an argument to this option to specify the name that your module .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("respect-quotes", "When output javascript save original quotes") .alias("p", "prefix") .alias("o", "output") @@ -100,6 +101,7 @@ You need to pass an argument to this option to specify the name that your module .boolean("lint") .boolean("V") .boolean("noerr") + .boolean("respect-quotes") .wrap(80) @@ -150,6 +152,7 @@ if (ARGS.r) { var OUTPUT_OPTIONS = { beautify: BEAUTIFY ? true : false, preamble: ARGS.preamble || null, + respect_quotes: ARGS.respect_quotes }; if (ARGS.screw_ie8) { diff --git a/lib/ast.js b/lib/ast.js index 051cd2fb..7b290e5f 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -846,10 +846,13 @@ var AST_Constant = DEFNODE("Constant", null, { } }); -var AST_String = DEFNODE("String", "value", { +var AST_String = DEFNODE("String", "value quote", { $documentation: "A string literal", $propdoc: { value: "[string] the contents of this string" + }, + getQuote: function() { + return this.quote; } }, AST_Constant); diff --git a/lib/output.js b/lib/output.js index 6c8f15aa..bb1fae32 100644 --- a/lib/output.js +++ b/lib/output.js @@ -63,6 +63,7 @@ function OutputStream(options) { preserve_line : false, screw_ie8 : false, preamble : null, + respect_quotes : false }, true); var indentation = 0; @@ -84,7 +85,7 @@ function OutputStream(options) { }); }; - function make_string(str) { + function make_string(str, quote) { var dq = 0, sq = 0; str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){ switch (s) { @@ -102,12 +103,16 @@ function OutputStream(options) { return s; }); if (options.ascii_only) str = to_ascii(str); + + if (options.respect_quotes && quote) + return quote == "'" ? "'" + str.replace(/\x27/g, "\\'") + "'" : '"' + str.replace(/\x22/g, '\\"') + '"'; + if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'"; else return '"' + str.replace(/\x22/g, '\\"') + '"'; }; - function encode_string(str) { - var ret = make_string(str); + function encode_string(str, quote) { + var ret = make_string(str, quote); if (options.inline_script) ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1"); return ret; @@ -323,7 +328,7 @@ function OutputStream(options) { force_semicolon : force_semicolon, to_ascii : to_ascii, print_name : function(name) { print(make_name(name)) }, - print_string : function(str) { print(encode_string(str)) }, + print_string : function(str, quote) { print(encode_string(str, quote)) }, next_indent : next_indent, with_indent : with_indent, with_block : with_block, @@ -1118,7 +1123,7 @@ function OutputStream(options) { output.print(self.getValue()); }); DEFPRINT(AST_String, function(self, output){ - output.print_string(self.getValue()); + output.print_string(self.getValue(), self.getQuote()); }); DEFPRINT(AST_Number, function(self, output){ output.print(make_num(self.getValue())); diff --git a/lib/parse.js b/lib/parse.js index de982b1e..54a5d5d8 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -379,7 +379,7 @@ function tokenizer($TEXT, filename, html5_comments) { else if (ch == quote) break; ret += ch; } - return token("string", ret); + return quote == '"' ? token("string_double_quote", ret) : token("string_single_quote", ret); }); function skip_line_comment(type) { @@ -597,7 +597,7 @@ var PRECEDENCE = (function(a, ret){ var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); -var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); +var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string_single_quote", "string_double_quote", "regexp", "name" ]); /* -----[ Parser ]----- */ @@ -642,7 +642,7 @@ function parse($TEXT, options) { S.token = S.input(); } S.in_directives = S.in_directives && ( - S.token.type == "string" || is("punc", ";") + S.token.type == "string_single_quote" || "string_double_quote" || is("punc", ";") ); return S.token; }; @@ -719,7 +719,8 @@ function parse($TEXT, options) { var tmp; handle_regexp(); switch (S.token.type) { - case "string": + case "string_single_quote": + case "string_double_quote": var dir = S.in_directives, stat = simple_statement(); // XXXv2: decide how to fix directives if (dir && stat.body instanceof AST_String && !is("punc", ",")) @@ -1108,8 +1109,11 @@ function parse($TEXT, options) { case "num": ret = new AST_Number({ start: tok, end: tok, value: tok.value }); break; - case "string": - ret = new AST_String({ start: tok, end: tok, value: tok.value }); + case "string_double_quote": + ret = new AST_String({ start: tok, end: tok, value: tok.value, quote: "\"" }); + break; + case "string_single_quote": + ret = new AST_String({ start: tok, end: tok, value: tok.value, quote: "'" }); break; case "regexp": ret = new AST_RegExp({ start: tok, end: tok, value: tok.value }); @@ -1236,7 +1240,8 @@ function parse($TEXT, options) { next(); switch (tmp.type) { case "num": - case "string": + case "string_single_quote": + case "string_double_quote": case "name": case "operator": case "keyword": diff --git a/test/compress/issue-495.js b/test/compress/issue-495.js new file mode 100644 index 00000000..e69de29b