allow linebreaks in member expressions in template interpolations (#16116)
* allow linebreaks in member expressions in template interpolations * lint * add changelog
This commit is contained in:
1 parent
41baabd627
commit
d50a4e4639
5 files changed
+84
-111
No files matched your search
@@ -0,0 +1,19 @@
|
||||
#### Allow linebreaks in member expressions in template interpolations (#16116 by @bakkot)
|
||||
|
||||
When there is already a linebreak in a template interpolation, allow it to stay there even if it is a member expression. Note that (as of [#15209](https://github.com/prettier/prettier/pull/15209)) Prettier will not insert a linebreak inside an interpolation when one is not already present.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```jsx
|
||||
// Input
|
||||
`template with ${
|
||||
very.very.very.very.very.very.very.very.very.very.very.long.chain
|
||||
}`;
|
||||
|
||||
// Prettier stable
|
||||
`template with ${very.very.very.very.very.very.very.very.very.very.very.long.chain}`;
|
||||
|
||||
// Prettier main
|
||||
`template with ${
|
||||
very.very.very.very.very.very.very.very.very.very.very.long.chain
|
||||
}`;
|
||||
```
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
isBinaryCastExpression,
|
||||
isBinaryish,
|
||||
isMemberExpression,
|
||||
isSimpleTemplateLiteral,
|
||||
} from "../utils/index.js";
|
||||
|
||||
function printTemplateLiteral(path, print, options) {
|
||||
@@ -39,18 +38,7 @@ function printTemplateLiteral(path, print, options) {
|
||||
}
|
||||
const parts = [];
|
||||
|
||||
let expressionDocs = path.map(print, expressionsKey);
|
||||
const isSimple = isSimpleTemplateLiteral(node);
|
||||
|
||||
if (isSimple) {
|
||||
expressionDocs = expressionDocs.map(
|
||||
(doc) =>
|
||||
printDocToString(doc, {
|
||||
...options,
|
||||
printWidth: Number.POSITIVE_INFINITY,
|
||||
}).formatted,
|
||||
);
|
||||
}
|
||||
const expressionDocs = path.map(print, expressionsKey);
|
||||
|
||||
parts.push(lineSuffixBoundary, "`");
|
||||
|
||||
@@ -82,44 +70,43 @@ function printTemplateLiteral(path, print, options) {
|
||||
|
||||
let expressionDoc = expressionDocs[index];
|
||||
|
||||
if (!isSimple) {
|
||||
const expression = node[expressionsKey][index];
|
||||
const expression = node[expressionsKey][index];
|
||||
|
||||
let interpolationHasNewline = hasNewlineInRange(
|
||||
options.originalText,
|
||||
locEnd(quasi),
|
||||
locStart(node.quasis[index + 1]),
|
||||
);
|
||||
let interpolationHasNewline = hasNewlineInRange(
|
||||
options.originalText,
|
||||
locEnd(quasi),
|
||||
locStart(node.quasis[index + 1]),
|
||||
);
|
||||
|
||||
if (!interpolationHasNewline) {
|
||||
// Never add a newline to an interpolation which didn't already have one...
|
||||
const renderedExpression = printDocToString(expressionDoc, {
|
||||
...options,
|
||||
printWidth: Number.POSITIVE_INFINITY,
|
||||
}).formatted;
|
||||
if (!interpolationHasNewline) {
|
||||
// Never add a newline to an interpolation which didn't already have one...
|
||||
const renderedExpression = printDocToString(expressionDoc, {
|
||||
...options,
|
||||
printWidth: Number.POSITIVE_INFINITY,
|
||||
}).formatted;
|
||||
|
||||
// ... unless one will be introduced anyway, e.g. by a nested function.
|
||||
// This case is rare, so we can pay the cost of re-rendering.
|
||||
if (renderedExpression.includes("\n")) {
|
||||
interpolationHasNewline = true;
|
||||
} else {
|
||||
expressionDoc = renderedExpression;
|
||||
}
|
||||
// ... unless one will be introduced anyway, e.g. by a nested function.
|
||||
// This case is rare, so we can pay the cost of re-rendering.
|
||||
if (renderedExpression.includes("\n")) {
|
||||
interpolationHasNewline = true;
|
||||
} else {
|
||||
expressionDoc = renderedExpression;
|
||||
}
|
||||
}
|
||||
|
||||
// Breaks at the template element boundaries (${ and }) are preferred to breaking
|
||||
// in the middle of a MemberExpression
|
||||
if (
|
||||
interpolationHasNewline &&
|
||||
(hasComment(expression) ||
|
||||
isMemberExpression(expression) ||
|
||||
expression.type === "ConditionalExpression" ||
|
||||
expression.type === "SequenceExpression" ||
|
||||
isBinaryCastExpression(expression) ||
|
||||
isBinaryish(expression))
|
||||
) {
|
||||
expressionDoc = [indent([softline, expressionDoc]), softline];
|
||||
}
|
||||
// Breaks at the template element boundaries (${ and }) are preferred to breaking
|
||||
// in the middle of a MemberExpression
|
||||
if (
|
||||
interpolationHasNewline &&
|
||||
(hasComment(expression) ||
|
||||
expression.type === "Identifier" ||
|
||||
isMemberExpression(expression) ||
|
||||
expression.type === "ConditionalExpression" ||
|
||||
expression.type === "SequenceExpression" ||
|
||||
isBinaryCastExpression(expression) ||
|
||||
isBinaryish(expression))
|
||||
) {
|
||||
expressionDoc = [indent([softline, expressionDoc]), softline];
|
||||
}
|
||||
|
||||
const aligned =
|
||||
|
||||
@@ -420,68 +420,6 @@ const isMemberExpression = skipChainExpression(
|
||||
createTypeCheckFunction(["MemberExpression", "OptionalMemberExpression"]),
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isSimpleTemplateLiteral(node) {
|
||||
let expressionsKey = "expressions";
|
||||
if (node.type === "TSTemplateLiteralType") {
|
||||
expressionsKey = "types";
|
||||
}
|
||||
const expressions = node[expressionsKey];
|
||||
|
||||
if (expressions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return expressions.every((expr) => {
|
||||
if (isSimpleAtomicExpression(expr) || isSimpleMemberExpression(expr)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isSimpleMemberExpression(
|
||||
node,
|
||||
{ maxDepth = Number.POSITIVE_INFINITY } = {},
|
||||
) {
|
||||
if (hasComment(node)) {
|
||||
return false;
|
||||
}
|
||||
if (node.type === "ChainExpression") {
|
||||
return isSimpleMemberExpression(node.expression, { maxDepth });
|
||||
}
|
||||
if (!isMemberExpression(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let head = node;
|
||||
let depth = 0;
|
||||
while (isMemberExpression(head) && depth++ <= maxDepth) {
|
||||
if (!isSimpleAtomicExpression(head.property)) {
|
||||
return false;
|
||||
}
|
||||
head = head.object;
|
||||
if (hasComment(head)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return isSimpleAtomicExpression(head);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is intended to return true for small expressions
|
||||
* which cannot be broken.
|
||||
*/
|
||||
function isSimpleAtomicExpression(node) {
|
||||
if (hasComment(node)) {
|
||||
return false;
|
||||
}
|
||||
return isLiteral(node) || isSingleWordType(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to gauge the rough complexity of a node, for example
|
||||
* to detect deeply-nested booleans, call expressions with lots of arguments, etc.
|
||||
@@ -1209,11 +1147,8 @@ export {
|
||||
isPrettierIgnoreComment,
|
||||
isRegExpLiteral,
|
||||
isSignedNumericLiteral,
|
||||
isSimpleAtomicExpression,
|
||||
isSimpleCallArgument,
|
||||
isSimpleExpressionByNodeCount,
|
||||
isSimpleMemberExpression,
|
||||
isSimpleTemplateLiteral,
|
||||
isSimpleType,
|
||||
isStringLiteral,
|
||||
isTemplateOnItsOwnLine,
|
||||
|
||||
@@ -415,6 +415,12 @@ and this other one: \${
|
||||
which already had a linebreak so can be broken up
|
||||
\`;
|
||||
|
||||
// https://github.com/prettier/prettier/issues/16114
|
||||
message = \`this is a long messsage a simple interpolation without a linebreak \${foo} <- like this\`;
|
||||
|
||||
message = \`whereas this messsage has a linebreak in the interpolation \${
|
||||
foo} <- like this\`;
|
||||
|
||||
=====================================output=====================================
|
||||
foo(
|
||||
\`a long string \${1 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3} with expr\`,
|
||||
@@ -537,6 +543,13 @@ and this other one: \${this(
|
||||
which already had a linebreak so can be broken up
|
||||
\`;
|
||||
|
||||
// https://github.com/prettier/prettier/issues/16114
|
||||
message = \`this is a long messsage a simple interpolation without a linebreak \${foo} <- like this\`;
|
||||
|
||||
message = \`whereas this messsage has a linebreak in the interpolation \${
|
||||
foo
|
||||
} <- like this\`;
|
||||
|
||||
================================================================================
|
||||
`;
|
||||
|
||||
@@ -615,6 +628,12 @@ and this other one: \${
|
||||
which already had a linebreak so can be broken up
|
||||
\`;
|
||||
|
||||
// https://github.com/prettier/prettier/issues/16114
|
||||
message = \`this is a long messsage a simple interpolation without a linebreak \${foo} <- like this\`;
|
||||
|
||||
message = \`whereas this messsage has a linebreak in the interpolation \${
|
||||
foo} <- like this\`;
|
||||
|
||||
=====================================output=====================================
|
||||
foo(
|
||||
\`a long string \${1 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3} with expr\`
|
||||
@@ -737,5 +756,12 @@ and this other one: \${this(
|
||||
which already had a linebreak so can be broken up
|
||||
\`;
|
||||
|
||||
// https://github.com/prettier/prettier/issues/16114
|
||||
message = \`this is a long messsage a simple interpolation without a linebreak \${foo} <- like this\`;
|
||||
|
||||
message = \`whereas this messsage has a linebreak in the interpolation \${
|
||||
foo
|
||||
} <- like this\`;
|
||||
|
||||
================================================================================
|
||||
`;
|
||||
@@ -65,3 +65,9 @@ and this other one: ${
|
||||
}
|
||||
which already had a linebreak so can be broken up
|
||||
`;
|
||||
|
||||
// https://github.com/prettier/prettier/issues/16114
|
||||
message = `this is a long messsage a simple interpolation without a linebreak ${foo} <- like this`;
|
||||
|
||||
message = `whereas this messsage has a linebreak in the interpolation ${
|
||||
foo} <- like this`;
|
||||
Reference in new issue
Block a user