Add option to preserve/enforce string quote style
`-q 0` (default) use single or double quotes such as to minimize the number of bytes (prefers double quotes when both will do); this is the previous behavior. `-q 1` -- always use single quotes `-q 2` -- always use double quotes `-q 3` or just `-q` -- always use the original quotes. Related codegen option: `quote_style`. Close #495 Close #460 Some `yargs` guru please tell me why `uglifyjs --help` doesn't display the help string for `-q` / `--quotes`, and why it doesn't output the expected argument types anymore, like good old `optimist` did.
This commit is contained in:
@@ -352,6 +352,13 @@ can pass additional arguments that control the code output:
|
|||||||
it will be prepended to the output literally. The source map will
|
it will be prepended to the output literally. The source map will
|
||||||
adjust for this text. Can be used to insert a comment containing
|
adjust for this text. Can be used to insert a comment containing
|
||||||
licensing information, for example.
|
licensing information, for example.
|
||||||
|
- `quote_style` (default `0`) -- preferred quote style for strings (affects
|
||||||
|
quoted property names and directives as well):
|
||||||
|
- `0` -- prefers double quotes, switches to single quotes when there are
|
||||||
|
more double quotes in the string itself.
|
||||||
|
- `1` -- always use single quotes
|
||||||
|
- `2` -- always use double quotes
|
||||||
|
- `3` -- always use the original quotes
|
||||||
|
|
||||||
### Keeping copyright notices or other comments
|
### Keeping copyright notices or other comments
|
||||||
|
|
||||||
|
|||||||
11
bin/uglifyjs
11
bin/uglifyjs
@@ -66,6 +66,7 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
|
.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("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("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)")
|
||||||
|
|
||||||
.alias("p", "prefix")
|
.alias("p", "prefix")
|
||||||
.alias("o", "output")
|
.alias("o", "output")
|
||||||
@@ -77,6 +78,7 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.alias("r", "reserved")
|
.alias("r", "reserved")
|
||||||
.alias("V", "version")
|
.alias("V", "version")
|
||||||
.alias("e", "enclose")
|
.alias("e", "enclose")
|
||||||
|
.alias("q", "quotes")
|
||||||
|
|
||||||
.string("source-map")
|
.string("source-map")
|
||||||
.string("source-map-root")
|
.string("source-map-root")
|
||||||
@@ -151,9 +153,14 @@ if (ARGS.r) {
|
|||||||
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ARGS.quotes === true) {
|
||||||
|
ARGS.quotes = 3;
|
||||||
|
}
|
||||||
|
|
||||||
var OUTPUT_OPTIONS = {
|
var OUTPUT_OPTIONS = {
|
||||||
beautify: BEAUTIFY ? true : false,
|
beautify : BEAUTIFY ? true : false,
|
||||||
preamble: ARGS.preamble || null,
|
preamble : ARGS.preamble || null,
|
||||||
|
quote_style : ARGS.quotes != null ? ARGS.quotes : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ARGS.screw_ie8) {
|
if (ARGS.screw_ie8) {
|
||||||
|
|||||||
15
lib/ast.js
15
lib/ast.js
@@ -120,11 +120,12 @@ var AST_Debugger = DEFNODE("Debugger", null, {
|
|||||||
$documentation: "Represents a debugger statement",
|
$documentation: "Represents a debugger statement",
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_Directive = DEFNODE("Directive", "value scope", {
|
var AST_Directive = DEFNODE("Directive", "value scope quote", {
|
||||||
$documentation: "Represents a directive, like \"use strict\";",
|
$documentation: "Represents a directive, like \"use strict\";",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
|
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
|
||||||
scope: "[AST_Scope/S] The scope that this directive affects"
|
scope: "[AST_Scope/S] The scope that this directive affects",
|
||||||
|
quote: "[string] the original quote character"
|
||||||
},
|
},
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
@@ -766,8 +767,11 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
|
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
||||||
$documentation: "A key: value object property",
|
$documentation: "A key: value object property",
|
||||||
|
$propdoc: {
|
||||||
|
quote: "[string] the original quote character"
|
||||||
|
}
|
||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
|
|
||||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
||||||
@@ -852,10 +856,11 @@ var AST_Constant = DEFNODE("Constant", null, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_String = DEFNODE("String", "value", {
|
var AST_String = DEFNODE("String", "value quote", {
|
||||||
$documentation: "A string literal",
|
$documentation: "A string literal",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
value: "[string] the contents of this string"
|
value: "[string] the contents of this string",
|
||||||
|
quote: "[string] the original quote character"
|
||||||
}
|
}
|
||||||
}, AST_Constant);
|
}, AST_Constant);
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ function OutputStream(options) {
|
|||||||
preserve_line : false,
|
preserve_line : false,
|
||||||
screw_ie8 : false,
|
screw_ie8 : false,
|
||||||
preamble : null,
|
preamble : null,
|
||||||
|
quote_style : 0
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
var indentation = 0;
|
var indentation = 0;
|
||||||
@@ -84,7 +85,7 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function make_string(str) {
|
function make_string(str, quote) {
|
||||||
var dq = 0, sq = 0;
|
var dq = 0, sq = 0;
|
||||||
str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
|
str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
|
||||||
switch (s) {
|
switch (s) {
|
||||||
@@ -102,13 +103,27 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
|
function quote_single() {
|
||||||
|
return "'" + str.replace(/\x27/g, "\\'") + "'";
|
||||||
|
}
|
||||||
|
function quote_double() {
|
||||||
|
return '"' + str.replace(/\x22/g, '\\"') + '"';
|
||||||
|
}
|
||||||
if (options.ascii_only) str = to_ascii(str);
|
if (options.ascii_only) str = to_ascii(str);
|
||||||
if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
|
switch (options.quote_style) {
|
||||||
else return '"' + str.replace(/\x22/g, '\\"') + '"';
|
case 1:
|
||||||
|
return quote_single();
|
||||||
|
case 2:
|
||||||
|
return quote_double();
|
||||||
|
case 3:
|
||||||
|
return quote == "'" ? quote_single() : quote_double();
|
||||||
|
default:
|
||||||
|
return dq > sq ? quote_single() : quote_double();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function encode_string(str) {
|
function encode_string(str, quote) {
|
||||||
var ret = make_string(str);
|
var ret = make_string(str, quote);
|
||||||
if (options.inline_script)
|
if (options.inline_script)
|
||||||
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
|
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
|
||||||
return ret;
|
return ret;
|
||||||
@@ -324,7 +339,7 @@ function OutputStream(options) {
|
|||||||
force_semicolon : force_semicolon,
|
force_semicolon : force_semicolon,
|
||||||
to_ascii : to_ascii,
|
to_ascii : to_ascii,
|
||||||
print_name : function(name) { print(make_name(name)) },
|
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,
|
next_indent : next_indent,
|
||||||
with_indent : with_indent,
|
with_indent : with_indent,
|
||||||
with_block : with_block,
|
with_block : with_block,
|
||||||
@@ -581,7 +596,7 @@ function OutputStream(options) {
|
|||||||
/* -----[ PRINTERS ]----- */
|
/* -----[ PRINTERS ]----- */
|
||||||
|
|
||||||
DEFPRINT(AST_Directive, function(self, output){
|
DEFPRINT(AST_Directive, function(self, output){
|
||||||
output.print_string(self.value);
|
output.print_string(self.value, self.quote);
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Debugger, function(self, output){
|
DEFPRINT(AST_Debugger, function(self, output){
|
||||||
@@ -1077,6 +1092,7 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||||
var key = self.key;
|
var key = self.key;
|
||||||
|
var quote = self.quote;
|
||||||
if (output.option("quote_keys")) {
|
if (output.option("quote_keys")) {
|
||||||
output.print_string(key + "");
|
output.print_string(key + "");
|
||||||
} else if ((typeof key == "number"
|
} else if ((typeof key == "number"
|
||||||
@@ -1087,7 +1103,7 @@ function OutputStream(options) {
|
|||||||
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
|
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
|
||||||
output.print_name(key);
|
output.print_name(key);
|
||||||
} else {
|
} else {
|
||||||
output.print_string(key);
|
output.print_string(key, quote);
|
||||||
}
|
}
|
||||||
output.colon();
|
output.colon();
|
||||||
self.value.print(output);
|
self.value.print(output);
|
||||||
@@ -1125,7 +1141,7 @@ function OutputStream(options) {
|
|||||||
output.print(self.getValue());
|
output.print(self.getValue());
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_String, function(self, output){
|
DEFPRINT(AST_String, function(self, output){
|
||||||
output.print_string(self.getValue());
|
output.print_string(self.getValue(), self.quote);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Number, function(self, output){
|
DEFPRINT(AST_Number, function(self, output){
|
||||||
output.print(make_num(self.getValue()));
|
output.print(make_num(self.getValue()));
|
||||||
|
|||||||
26
lib/parse.js
26
lib/parse.js
@@ -367,7 +367,7 @@ function tokenizer($TEXT, filename, html5_comments) {
|
|||||||
return num;
|
return num;
|
||||||
};
|
};
|
||||||
|
|
||||||
var read_string = with_eof_error("Unterminated string constant", function(){
|
var read_string = with_eof_error("Unterminated string constant", function(quote_char){
|
||||||
var quote = next(), ret = "";
|
var quote = next(), ret = "";
|
||||||
for (;;) {
|
for (;;) {
|
||||||
var ch = next(true);
|
var ch = next(true);
|
||||||
@@ -392,7 +392,9 @@ function tokenizer($TEXT, filename, html5_comments) {
|
|||||||
else if (ch == quote) break;
|
else if (ch == quote) break;
|
||||||
ret += ch;
|
ret += ch;
|
||||||
}
|
}
|
||||||
return token("string", ret);
|
var tok = token("string", ret);
|
||||||
|
tok.quote = quote_char;
|
||||||
|
return tok;
|
||||||
});
|
});
|
||||||
|
|
||||||
function skip_line_comment(type) {
|
function skip_line_comment(type) {
|
||||||
@@ -547,7 +549,7 @@ function tokenizer($TEXT, filename, html5_comments) {
|
|||||||
if (!ch) return token("eof");
|
if (!ch) return token("eof");
|
||||||
var code = ch.charCodeAt(0);
|
var code = ch.charCodeAt(0);
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 34: case 39: return read_string();
|
case 34: case 39: return read_string(ch);
|
||||||
case 46: return handle_dot();
|
case 46: return handle_dot();
|
||||||
case 47: return handle_slash();
|
case 47: return handle_slash();
|
||||||
}
|
}
|
||||||
@@ -737,8 +739,14 @@ function parse($TEXT, options) {
|
|||||||
case "string":
|
case "string":
|
||||||
var dir = S.in_directives, stat = simple_statement();
|
var dir = S.in_directives, stat = simple_statement();
|
||||||
// XXXv2: decide how to fix directives
|
// XXXv2: decide how to fix directives
|
||||||
if (dir && stat.body instanceof AST_String && !is("punc", ","))
|
if (dir && stat.body instanceof AST_String && !is("punc", ",")) {
|
||||||
return new AST_Directive({ value: stat.body.value });
|
return new AST_Directive({
|
||||||
|
start : stat.body.start,
|
||||||
|
end : stat.body.end,
|
||||||
|
quote : stat.body.quote,
|
||||||
|
value : stat.body.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
return stat;
|
return stat;
|
||||||
case "num":
|
case "num":
|
||||||
case "regexp":
|
case "regexp":
|
||||||
@@ -1124,7 +1132,12 @@ function parse($TEXT, options) {
|
|||||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||||
break;
|
break;
|
||||||
case "string":
|
case "string":
|
||||||
ret = new AST_String({ start: tok, end: tok, value: tok.value });
|
ret = new AST_String({
|
||||||
|
start : tok,
|
||||||
|
end : tok,
|
||||||
|
value : tok.value,
|
||||||
|
quote : tok.quote
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case "regexp":
|
case "regexp":
|
||||||
ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
|
ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
|
||||||
@@ -1237,6 +1250,7 @@ function parse($TEXT, options) {
|
|||||||
expect(":");
|
expect(":");
|
||||||
a.push(new AST_ObjectKeyVal({
|
a.push(new AST_ObjectKeyVal({
|
||||||
start : start,
|
start : start,
|
||||||
|
quote : start.quote,
|
||||||
key : name,
|
key : name,
|
||||||
value : expression(false),
|
value : expression(false),
|
||||||
end : prev()
|
end : prev()
|
||||||
|
|||||||
Reference in New Issue
Block a user