it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
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
.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)")
.alias("p", "prefix")
.alias("o", "output")
.alias("r", "reserved")
.alias("V", "version")
.alias("e", "enclose")
+ .alias("q", "quotes")
.string("source-map")
.string("source-map-root")
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
}
+if (ARGS.quotes === true) {
+ ARGS.quotes = 3;
+}
+
var OUTPUT_OPTIONS = {
- beautify: BEAUTIFY ? true : false,
- preamble: ARGS.preamble || null,
+ beautify : BEAUTIFY ? true : false,
+ preamble : ARGS.preamble || null,
+ quote_style : ARGS.quotes != null ? ARGS.quotes : 0
};
if (ARGS.screw_ie8) {
$documentation: "Represents a debugger 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\";",
$propdoc: {
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);
}
});
-var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
+var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
$documentation: "A key: value object property",
+ $propdoc: {
+ quote: "[string] the original quote character"
+ }
}, AST_ObjectProperty);
var AST_ObjectSetter = DEFNODE("ObjectSetter", 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"
+ value: "[string] the contents of this string",
+ quote: "[string] the original quote character"
}
}, AST_Constant);
preserve_line : false,
screw_ie8 : false,
preamble : null,
+ quote_style : 0
}, true);
var indentation = 0;
});
};
- 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\ufeff]/g, function(s){
switch (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 (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
- else return '"' + str.replace(/\x22/g, '\\"') + '"';
+ switch (options.quote_style) {
+ 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) {
- 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;
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,
/* -----[ PRINTERS ]----- */
DEFPRINT(AST_Directive, function(self, output){
- output.print_string(self.value);
+ output.print_string(self.value, self.quote);
output.semicolon();
});
DEFPRINT(AST_Debugger, function(self, output){
});
DEFPRINT(AST_ObjectKeyVal, function(self, output){
var key = self.key;
+ var quote = self.quote;
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
output.print_name(key);
} else {
- output.print_string(key);
+ output.print_string(key, quote);
}
output.colon();
self.value.print(output);
output.print(self.getValue());
});
DEFPRINT(AST_String, function(self, output){
- output.print_string(self.getValue());
+ output.print_string(self.getValue(), self.quote);
});
DEFPRINT(AST_Number, function(self, output){
output.print(make_num(self.getValue()));
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 = "";
for (;;) {
var ch = next(true);
else if (ch == quote) break;
ret += ch;
}
- return token("string", ret);
+ var tok = token("string", ret);
+ tok.quote = quote_char;
+ return tok;
});
function skip_line_comment(type) {
if (!ch) return token("eof");
var code = ch.charCodeAt(0);
switch (code) {
- case 34: case 39: return read_string();
+ case 34: case 39: return read_string(ch);
case 46: return handle_dot();
case 47: return handle_slash();
}
case "string":
var dir = S.in_directives, stat = simple_statement();
// XXXv2: decide how to fix directives
- if (dir && stat.body instanceof AST_String && !is("punc", ","))
- return new AST_Directive({ value: stat.body.value });
+ if (dir && stat.body instanceof AST_String && !is("punc", ",")) {
+ return new AST_Directive({
+ start : stat.body.start,
+ end : stat.body.end,
+ quote : stat.body.quote,
+ value : stat.body.value,
+ });
+ }
return stat;
case "num":
case "regexp":
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 });
+ ret = new AST_String({
+ start : tok,
+ end : tok,
+ value : tok.value,
+ quote : tok.quote
+ });
break;
case "regexp":
ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
expect(":");
a.push(new AST_ObjectKeyVal({
start : start,
+ quote : start.quote,
key : name,
value : expression(false),
end : prev()