Spec compatibility for template literals. (#5791)
* Spec compatibility for template literals. * Update preset-es2015 `spec` expected case. * Prevent array mutability by replacing `shift`. * Fix condition for single item. * Group concats to ensure toPrimitive sequence. * Update function test case. * Add semi for function test case. * Simplify concat call expressions creating. * Fix some cases with multiple idengifiers. * Add test case with different literals. * Add test case for `Symbol()` and toPrimitive order * Add actual literal case. * Add minNodeVersion to template literals order. * Flip the logical expression. * Update README for template literals spec option. * docs [skip ci]
This commit is contained in:
parent
1e55653ac1
commit
c4fd05c0c2
@ -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);
|
||||
```
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1 +1 @@
|
||||
var t = "'" + String(foo) + "' \"" + String(bar) + "\"";
|
||||
var t = "'".concat(foo, "' \"").concat(bar, "\"");
|
||||
@ -1 +1 @@
|
||||
var foo = "test " + String(_.test(foo)) + " " + String(bar);
|
||||
var foo = "test ".concat(_.test(foo), " ").concat(bar);
|
||||
@ -0,0 +1 @@
|
||||
var foo = `${1}${f}oo${true}${b}ar${0}${baz}`;
|
||||
@ -0,0 +1 @@
|
||||
var foo = "".concat(1, f, "oo", true).concat(b, "ar", 0).concat(baz);
|
||||
@ -1 +1 @@
|
||||
var foo = "test " + String(foo) + " " + String(bar);
|
||||
var foo = "test ".concat(foo, " ").concat(bar);
|
||||
@ -1 +1 @@
|
||||
var foo = "" + String(test);
|
||||
var foo = "".concat(test);
|
||||
24
packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/order/exec.js
vendored
Normal file
24
packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/order/exec.js
vendored
Normal file
@ -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]);
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"minNodeVersion": "6.0.0"
|
||||
}
|
||||
@ -1 +1 @@
|
||||
var foo = "test " + String(foo);
|
||||
var foo = "test ".concat(foo);
|
||||
@ -1 +1 @@
|
||||
var foo = "test " + String(foo + bar);
|
||||
var foo = "test ".concat(foo + bar);
|
||||
3
packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/symbol/exec.js
vendored
Normal file
3
packages/babel-plugin-transform-es2015-template-literals/test/fixtures/spec/symbol/exec.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
const fn = () => `${Symbol()}`;
|
||||
|
||||
assert.throws(fn, TypeError);
|
||||
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
var _this = undefined;
|
||||
"1" + String(a);
|
||||
"1".concat(a);
|
||||
|
||||
(function () {
|
||||
babelHelpers.newArrowCheck(this, _this);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user