diff --git a/packages/babel-plugin-transform-es2015-template-literals/README.md b/packages/babel-plugin-transform-es2015-template-literals/README.md index 477b11cce60b..cdd6654c1e57 100644 --- a/packages/babel-plugin-transform-es2015-template-literals/README.md +++ b/packages/babel-plugin-transform-es2015-template-literals/README.md @@ -75,16 +75,16 @@ In loose mode, tagged template literal objects aren't frozen. `boolean`, defaults to `false`. -This option wraps all template literal expressions with `String`. See [babel/babel#1065](https://github.com/babel/babel/issues/1065) for more info. +This option combines all template literal expressions and quasis with `String.prototype.concat`. It will handle cases with `Symbol.toPrimitive` correctly and throw correctly if template literal expression is a `Symbol()`. See [babel/babel#5791](https://github.com/babel/babel/pull/5791). **In** ```javascript -`foo${bar}`; +`foo${bar}baz${quux}${1}`; ``` **Out** ```javascript -"foo" + String(bar); +"foo".concat(bar, "baz").concat(quux, 1); ``` diff --git a/packages/babel-plugin-transform-es2015-template-literals/src/index.js b/packages/babel-plugin-transform-es2015-template-literals/src/index.js index 12554e9027aa..ab45375eff45 100644 --- a/packages/babel-plugin-transform-es2015-template-literals/src/index.js +++ b/packages/babel-plugin-transform-es2015-template-literals/src/index.js @@ -1,7 +1,29 @@ export default function ({ types: t }) { + + function buildConcatCallExressions(items) { + let avail = true; + return items.reduce(function(left, right) { + let canBeInserted = t.isLiteral(right); + + if (!canBeInserted && avail) { + canBeInserted = true; + avail = false; + } + if (canBeInserted && t.isCallExpression(left)) { + left.arguments.push(right); + return left; + } + return t.callExpression( + t.memberExpression(left, t.identifier("concat")), + [right] + ); + }); + } + return { visitor: { TaggedTemplateExpression(path, state) { + const { node } = path; const { quasi } = node; @@ -45,9 +67,7 @@ export default function ({ types: t }) { if (index < expressions.length) { const expr = expressions[index++]; const node = expr.node; - if (state.opts.spec && !expr.isBaseType("string") && !expr.isBaseType("number")) { - nodes.push(t.callExpression(t.identifier("String"), [node])); - } else if (!t.isStringLiteral(node, { value: "" })) { + if (!t.isStringLiteral(node, { value: "" })) { nodes.push(node); } } @@ -55,13 +75,20 @@ export default function ({ types: t }) { // since `+` is left-to-right associative // ensure the first node is a string if first/second isn't - if (!t.isStringLiteral(nodes[0]) && !t.isStringLiteral(nodes[1])) { + const considerSecondNode = state.opts.spec || !t.isStringLiteral(nodes[1]); + if (!t.isStringLiteral(nodes[0]) && considerSecondNode) { nodes.unshift(t.stringLiteral("")); } - let root = nodes[0]; - for (let i = 1; i < nodes.length; i++) { - root = t.binaryExpression("+", root, nodes[i]); + + if (state.opts.spec) { + if (nodes.length > 1) { + root = buildConcatCallExressions(nodes); + } + } else { + for (let i = 1; i < nodes.length; i++) { + root = t.binaryExpression("+", root, nodes[i]); + } } path.replaceWith(root); diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/escape-quotes/expected.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/escape-quotes/expected.js index 287788efd92c..bf559e274bc6 100644 --- a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/escape-quotes/expected.js +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/escape-quotes/expected.js @@ -1 +1 @@ -var t = "'" + String(foo) + "' \"" + String(bar) + "\""; +var t = "'".concat(foo, "' \"").concat(bar, "\""); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/functions/expected.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/functions/expected.js index 9268305f36ff..4f675ef40e73 100644 --- a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/functions/expected.js +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/functions/expected.js @@ -1 +1 @@ -var foo = "test " + String(_.test(foo)) + " " + String(bar); +var foo = "test ".concat(_.test(foo), " ").concat(bar); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/literals/actual.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/literals/actual.js new file mode 100644 index 000000000000..a6e9c4a8362b --- /dev/null +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/literals/actual.js @@ -0,0 +1 @@ +var foo = `${1}${f}oo${true}${b}ar${0}${baz}`; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/literals/expected.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/literals/expected.js new file mode 100644 index 000000000000..e2fb8e51c675 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/literals/expected.js @@ -0,0 +1 @@ +var foo = "".concat(1, f, "oo", true).concat(b, "ar", 0).concat(baz); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/multiple/expected.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/multiple/expected.js index 29a6093e5ccd..a3acee1c67bf 100644 --- a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/multiple/expected.js +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/multiple/expected.js @@ -1 +1 @@ -var foo = "test " + String(foo) + " " + String(bar); +var foo = "test ".concat(foo, " ").concat(bar); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/only/expected.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/only/expected.js index eadb924d1dc4..84a771cd4de0 100644 --- a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/only/expected.js +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/only/expected.js @@ -1 +1 @@ -var foo = "" + String(test); +var foo = "".concat(test); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/order/exec.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/order/exec.js new file mode 100644 index 000000000000..895677e3a27a --- /dev/null +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/order/exec.js @@ -0,0 +1,24 @@ +const calls = []; + +` + ${ + calls.push(1), + { + [Symbol.toPrimitive](){ + calls.push(2); + return 'foo'; + } + } + } + ${ + calls.push(3), + { + [Symbol.toPrimitive](){ + calls.push(4); + return 'bar'; + } + } + } +`; + +assert.deepEqual(calls, [1, 2, 3, 4]); diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/order/options.json b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/order/options.json new file mode 100644 index 000000000000..7d8c3c204cc8 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/order/options.json @@ -0,0 +1,3 @@ +{ + "minNodeVersion": "6.0.0" +} diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/single/expected.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/single/expected.js index 265332f7f2d2..f32ccd500f3c 100644 --- a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/single/expected.js +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/single/expected.js @@ -1 +1 @@ -var foo = "test " + String(foo); +var foo = "test ".concat(foo); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/statement/expected.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/statement/expected.js index 7625842cc80b..d0367942ae13 100644 --- a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/statement/expected.js +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/statement/expected.js @@ -1 +1 @@ -var foo = "test " + String(foo + bar); +var foo = "test ".concat(foo + bar); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/symbol/exec.js b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/symbol/exec.js new file mode 100644 index 000000000000..39316c78efa6 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/symbol/exec.js @@ -0,0 +1,3 @@ +const fn = () => `${Symbol()}`; + +assert.throws(fn, TypeError); diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/spec/expected.js b/packages/babel-preset-es2015/test/fixtures/preset-options/spec/expected.js index 1ff90ab1ed8c..6295df920a0f 100644 --- a/packages/babel-preset-es2015/test/fixtures/preset-options/spec/expected.js +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/spec/expected.js @@ -1,7 +1,7 @@ "use strict"; var _this = undefined; -"1" + String(a); +"1".concat(a); (function () { babelHelpers.newArrowCheck(this, _this);