return this;
function decode(str) {
- return str.replace(/\\(u\{[^}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
- var s = decode_escape_sequence(seq);
- if (typeof s != "string") malformed = true;
- return s;
- });
+ str = decode_template(str);
+ if (typeof str != "string") malformed = true;
+ return str;
}
});
})(function(node, func) {
&& tag.expression.name == "String";
}
+ function decode_template(str) {
+ var malformed = false;
+ str = str.replace(/\\(u\{[^}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
+ var ch = decode_escape_sequence(seq);
+ if (typeof ch == "string") return ch;
+ malformed = true;
+ });
+ if (!malformed) return str;
+ }
+
OPT(AST_Template, function(self, compressor) {
if (!compressor.option("templates")) return self;
var tag = self.tag;
if (!tag || is_raw_tag(compressor, tag)) {
- var exprs = self.expressions.slice();
- var strs = self.strings.slice();
- var CHANGED = false;
- for (var i = exprs.length; --i >= 0;) {
- var node = exprs[i];
- var ev = node.evaluate(compressor);
- if (ev === node) continue;
- if (tag && /\r|\\|`/.test(ev)) continue;
- ev = ("" + ev).replace(/\r|\\|`/g, function(s) {
- return "\\" + (s == "\r" ? "r" : s);
- });
- if (ev.length > node.print_to_string().length + 3) continue;
- var combined = strs[i] + ev + strs[i + 1];
- if (typeof make_node(AST_Template, self, {
- expressions: [],
- strings: [ combined ],
- tag: tag,
- }).evaluate(compressor) != typeof make_node(AST_Template, self, {
- expressions: [ node ],
- strings: strs.slice(i, i + 2),
- tag: tag,
- }).evaluate(compressor)) continue;
- exprs.splice(i, 1);
- strs.splice(i, 2, combined);
- CHANGED = true;
+ var exprs = [];
+ var strs = [];
+ for (var i = 0, status; i < self.strings.length; i++) {
+ var str = self.strings[i];
+ if (!tag) {
+ var trimmed = decode_template(str);
+ if (trimmed) str = escape_literal(trimmed);
+ }
+ if (i > 0) {
+ var node = self.expressions[i - 1];
+ var value = should_join(node);
+ if (value) {
+ var prev = strs[strs.length - 1];
+ var joined = prev + value + str;
+ var decoded;
+ if (tag || typeof (decoded = decode_template(joined)) == status) {
+ strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined;
+ continue;
+ }
+ }
+ exprs.push(node);
+ }
+ strs.push(str);
+ if (!tag) status = typeof trimmed;
}
- if (CHANGED) {
- self.expressions = exprs;
- self.strings = strs;
+ if (!tag && strs.length > 1) {
+ if (strs[0] == "") return make_node(AST_Binary, self, {
+ operator: "+",
+ left: exprs[0],
+ right: make_node(AST_Template, self, {
+ expressions: exprs.slice(1),
+ strings: strs.slice(1),
+ tag: tag,
+ }).transform(compressor),
+ }).optimize(compressor);
+ if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, {
+ operator: "+",
+ left: make_node(AST_Template, self, {
+ expressions: exprs.slice(0, -1),
+ strings: strs.slice(0, -1),
+ tag: tag,
+ }).transform(compressor),
+ right: exprs[exprs.length - 1],
+ }).optimize(compressor);
}
+ self.expressions = exprs;
+ self.strings = strs;
}
return try_evaluate(compressor, self);
+
+ function escape_literal(str) {
+ return str.replace(/\r|\\|`|\${/g, function(s) {
+ return "\\" + (s == "\r" ? "r" : s);
+ });
+ }
+
+ function should_join(node) {
+ var ev = node.evaluate(compressor);
+ if (ev === node) return;
+ if (tag && /\r|\\|`/.test(ev)) return;
+ ev = escape_literal("" + ev);
+ if (ev.length > node.print_to_string().length + "${}".length) return;
+ return ev;
+ }
});
function is_atomic(lhs, self) {
node_version: ">=4"
}
+booleans: {
+ options = {
+ booleans: true,
+ evaluate: true,
+ templates: true,
+ }
+ input: {
+ var a;
+ console.log(`$${a}${a}` ? "PASS" : "FAIL");
+ }
+ expect: {
+ var a;
+ console.log("$" + a + a ? "PASS" : "FAIL");
+ }
+ expect_stdout: "PASS"
+ node_version: ">=4"
+}
+
+escape_placeholder_1: {
+ options = {
+ templates: true,
+ }
+ input: {
+ console.log(`\${\n`);
+ }
+ expect: {
+ console.log(`\${
+`);
+ }
+ expect_stdout: [
+ "${",
+ "",
+ ]
+ node_version: ">=4"
+}
+
+escape_placeholder_2: {
+ options = {
+ evaluate: true,
+ templates: true,
+ }
+ input: {
+ console.log(`\n${"${"}\n`);
+ }
+ expect: {
+ console.log(`
+\${
+`);
+ }
+ expect_stdout: [
+ "",
+ "${",
+ "",
+ ]
+ node_version: ">=4"
+}
+
+escape_placeholder_3: {
+ options = {
+ evaluate: true,
+ templates: true,
+ }
+ input: {
+ console.log(`\n$${"{"}\n`);
+ }
+ expect: {
+ console.log(`
+\${
+`);
+ }
+ expect_stdout: [
+ "",
+ "${",
+ "",
+ ]
+ node_version: ">=4"
+}
+
+escape_placeholder_4: {
+ options = {
+ evaluate: true,
+ templates: true,
+ }
+ input: {
+ console.log(`\n${"$"}${"{"}\n`);
+ }
+ expect: {
+ console.log(`
+\${
+`);
+ }
+ expect_stdout: [
+ "",
+ "${",
+ "",
+ ]
+ node_version: ">=4"
+}
+
evaluate: {
options = {
evaluate: true,
console.log(`${6 * 7} foo ${console ? `PA` + "SS" : `FA` + `IL`}`);
}
expect: {
- console.log(`42 foo ${console ? "PASS" : "FAIL"}`);
+ console.log("42 foo " + (console ? "PASS" : "FAIL"));
}
expect_stdout: "42 foo PASS"
node_version: ">=4"
console.log(`\u0${0}b${5}`);
}
expect: {
- console.log(`\u0${0}b5`);
+ console.log(`\u00b` + 5);
}
expect_stdout: true
node_version: ">=4"
issue_4606: {
options = {
evaluate: true,
+ strings: true,
templates: true,
}
input: {
console.log(`${typeof A} ${"\r"} ${"\\"} ${"`"}`);
}
expect: {
- console.log(`${typeof A} \r \\ \``);
+ console.log(typeof A + " \r \\ `");
}
expect_stdout: "undefined \r \\ `"
node_version: ">=4"
]
node_version: ">=4"
}
+
+issue_5125_1: {
+ options = {
+ evaluate: true,
+ strings: true,
+ templates: true,
+ }
+ input: {
+ console.log(`PASS ${typeof A}`);
+ }
+ expect: {
+ console.log("PASS " + typeof A);
+ }
+ expect_stdout: "PASS undefined"
+ node_version: ">=4"
+}
+
+issue_5125_2: {
+ options = {
+ evaluate: true,
+ strings: true,
+ templates: true,
+ }
+ input: {
+ console.log(`PASS
+${typeof A}`);
+ }
+ expect: {
+ console.log(`PASS
+` + typeof A);
+ }
+ expect_stdout: [
+ "PASS",
+ "undefined",
+ ]
+ node_version: ">=4"
+}
+
+issue_5125_3: {
+ options = {
+ evaluate: true,
+ strings: true,
+ templates: true,
+ }
+ input: {
+ console.log(`PASS\n${typeof A}`);
+ }
+ expect: {
+ console.log(`PASS
+` + typeof A);
+ }
+ expect_stdout: [
+ "PASS",
+ "undefined",
+ ]
+ node_version: ">=4"
+}
+
+issue_5125_4: {
+ options = {
+ evaluate: true,
+ strings: true,
+ templates: true,
+ }
+ input: {
+ console.log(`PASS
+
+${typeof A}`);
+ }
+ expect: {
+ console.log(`PASS
+
+` + typeof A);
+ }
+ expect_stdout: [
+ "PASS",
+ "",
+ "undefined",
+ ]
+ node_version: ">=4"
+}
+
+issue_5125_5: {
+ options = {
+ evaluate: true,
+ strings: true,
+ templates: true,
+ }
+ input: {
+ console.log(`PASS\n\n${typeof A}`);
+ }
+ expect: {
+ console.log(`PASS
+
+` + typeof A);
+ }
+ expect_stdout: [
+ "PASS",
+ "",
+ "undefined",
+ ]
+ node_version: ">=4"
+}
+
+issue_5125_6: {
+ options = {
+ evaluate: true,
+ strings: true,
+ templates: true,
+ }
+ input: {
+ console.log(`${typeof A} ${typeof B} PASS`);
+ }
+ expect: {
+ console.log(typeof A + ` ${typeof B} PASS`);
+ }
+ expect_stdout: "undefined undefined PASS"
+ node_version: ">=4"
+}
+
+issue_5125_7: {
+ options = {
+ evaluate: true,
+ strings: true,
+ templates: true,
+ }
+ input: {
+ console.log(`${typeof A} ${typeof B} ${typeof C} PASS`);
+ }
+ expect: {
+ console.log(typeof A + ` ${typeof B} ${typeof C} PASS`);
+ }
+ expect_stdout: "undefined undefined undefined PASS"
+ node_version: ">=4"
+}
+
+issue_5125_8: {
+ options = {
+ evaluate: true,
+ strings: true,
+ templates: true,
+ }
+ input: {
+ console.log(`${typeof A}${typeof B}${typeof C} PASS`);
+ }
+ expect: {
+ console.log(typeof A + typeof B + typeof C + " PASS");
+ }
+ expect_stdout: "undefinedundefinedundefined PASS"
+ node_version: ">=4"
+}
[
// native line breaks
[ "`foo\nbar`", "`foo\nbar`" ],
- [ "`foo\rbar`", "`foo\rbar`" ],
+ [ "`foo\rbar`", "`foo\nbar`" ],
[ "`foo\r\nbar`", "`foo\nbar`" ],
- [ "`foo\r\n\rbar`", "`foo\n\rbar`" ],
+ [ "`foo\r\n\rbar`", "`foo\n\nbar`" ],
// escaped line breaks
[ "`foo\\nbar`", "`foo\\nbar`" ],
[ "`foo\\rbar`", "`foo\\rbar`" ],
- [ "`foo\r\\nbar`", "`foo\r\\nbar`" ],
+ [ "`foo\r\\nbar`", "`foo\n\\nbar`" ],
[ "`foo\\r\nbar`", "`foo\\r\nbar`" ],
[ "`foo\\r\\nbar`", "`foo\\r\\nbar`" ],
// continuation
[ "`foo\\\nbar`", "`foo\\\nbar`" ],
- [ "`foo\\\rbar`", "`foo\\\rbar`" ],
+ [ "`foo\\\rbar`", "`foo\\\nbar`" ],
[ "`foo\\\r\nbar`", "`foo\\\nbar`" ],
- [ "`foo\\\r\n\rbar`", "`foo\\\n\rbar`" ],
+ [ "`foo\\\r\n\rbar`", "`foo\\\n\nbar`" ],
[ "`foo\\\\nbar`", "`foo\\\\nbar`" ],
[ "`foo\\\\rbar`", "`foo\\\\rbar`" ],
[ "`foo\\\\r\nbar`", "`foo\\\\r\nbar`" ],