From f62f271d5a1e599c5a6ce9aad33a2516882c1f1c Mon Sep 17 00:00:00 2001 From: Denis Bardadym Date: Sat, 12 Jul 2014 19:55:35 +0400 Subject: [PATCH] Added support of respecting quotes --- bin/uglifyjs | 3 +++ lib/ast.js | 5 ++++- lib/output.js | 15 ++++++++++----- lib/parse.js | 19 ++++++++++++------- test/compress/issue-495.js | 0 5 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 test/compress/issue-495.js 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