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`.
|
`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**
|
**In**
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
`foo${bar}`;
|
`foo${bar}baz${quux}${1}`;
|
||||||
```
|
```
|
||||||
|
|
||||||
**Out**
|
**Out**
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
"foo" + String(bar);
|
"foo".concat(bar, "baz").concat(quux, 1);
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,7 +1,29 @@
|
|||||||
export default function ({ types: t }) {
|
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 {
|
return {
|
||||||
visitor: {
|
visitor: {
|
||||||
TaggedTemplateExpression(path, state) {
|
TaggedTemplateExpression(path, state) {
|
||||||
|
|
||||||
const { node } = path;
|
const { node } = path;
|
||||||
const { quasi } = node;
|
const { quasi } = node;
|
||||||
|
|
||||||
@ -45,9 +67,7 @@ export default function ({ types: t }) {
|
|||||||
if (index < expressions.length) {
|
if (index < expressions.length) {
|
||||||
const expr = expressions[index++];
|
const expr = expressions[index++];
|
||||||
const node = expr.node;
|
const node = expr.node;
|
||||||
if (state.opts.spec && !expr.isBaseType("string") && !expr.isBaseType("number")) {
|
if (!t.isStringLiteral(node, { value: "" })) {
|
||||||
nodes.push(t.callExpression(t.identifier("String"), [node]));
|
|
||||||
} else if (!t.isStringLiteral(node, { value: "" })) {
|
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,13 +75,20 @@ export default function ({ types: t }) {
|
|||||||
|
|
||||||
// since `+` is left-to-right associative
|
// since `+` is left-to-right associative
|
||||||
// ensure the first node is a string if first/second isn't
|
// 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(""));
|
nodes.unshift(t.stringLiteral(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = nodes[0];
|
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);
|
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";
|
"use strict";
|
||||||
|
|
||||||
var _this = undefined;
|
var _this = undefined;
|
||||||
"1" + String(a);
|
"1".concat(a);
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
babelHelpers.newArrowCheck(this, _this);
|
babelHelpers.newArrowCheck(this, _this);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user