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,
|
isBinaryCastExpression,
|
||||||
isBinaryish,
|
isBinaryish,
|
||||||
isMemberExpression,
|
isMemberExpression,
|
||||||
isSimpleTemplateLiteral,
|
|
||||||
} from "../utils/index.js";
|
} from "../utils/index.js";
|
||||||
|
|
||||||
function printTemplateLiteral(path, print, options) {
|
function printTemplateLiteral(path, print, options) {
|
||||||
@@ -39,18 +38,7 @@ function printTemplateLiteral(path, print, options) {
|
|||||||
}
|
}
|
||||||
const parts = [];
|
const parts = [];
|
||||||
|
|
||||||
let expressionDocs = path.map(print, expressionsKey);
|
const expressionDocs = path.map(print, expressionsKey);
|
||||||
const isSimple = isSimpleTemplateLiteral(node);
|
|
||||||
|
|
||||||
if (isSimple) {
|
|
||||||
expressionDocs = expressionDocs.map(
|
|
||||||
(doc) =>
|
|
||||||
printDocToString(doc, {
|
|
||||||
...options,
|
|
||||||
printWidth: Number.POSITIVE_INFINITY,
|
|
||||||
}).formatted,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
parts.push(lineSuffixBoundary, "`");
|
parts.push(lineSuffixBoundary, "`");
|
||||||
|
|
||||||
@@ -82,44 +70,43 @@ function printTemplateLiteral(path, print, options) {
|
|||||||
|
|
||||||
let expressionDoc = expressionDocs[index];
|
let expressionDoc = expressionDocs[index];
|
||||||
|
|
||||||
if (!isSimple) {
|
const expression = node[expressionsKey][index];
|
||||||
const expression = node[expressionsKey][index];
|
|
||||||
|
|
||||||
let interpolationHasNewline = hasNewlineInRange(
|
let interpolationHasNewline = hasNewlineInRange(
|
||||||
options.originalText,
|
options.originalText,
|
||||||
locEnd(quasi),
|
locEnd(quasi),
|
||||||
locStart(node.quasis[index + 1]),
|
locStart(node.quasis[index + 1]),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!interpolationHasNewline) {
|
if (!interpolationHasNewline) {
|
||||||
// Never add a newline to an interpolation which didn't already have one...
|
// Never add a newline to an interpolation which didn't already have one...
|
||||||
const renderedExpression = printDocToString(expressionDoc, {
|
const renderedExpression = printDocToString(expressionDoc, {
|
||||||
...options,
|
...options,
|
||||||
printWidth: Number.POSITIVE_INFINITY,
|
printWidth: Number.POSITIVE_INFINITY,
|
||||||
}).formatted;
|
}).formatted;
|
||||||
|
|
||||||
// ... unless one will be introduced anyway, e.g. by a nested function.
|
// ... 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.
|
// This case is rare, so we can pay the cost of re-rendering.
|
||||||
if (renderedExpression.includes("\n")) {
|
if (renderedExpression.includes("\n")) {
|
||||||
interpolationHasNewline = true;
|
interpolationHasNewline = true;
|
||||||
} else {
|
} else {
|
||||||
expressionDoc = renderedExpression;
|
expressionDoc = renderedExpression;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Breaks at the template element boundaries (${ and }) are preferred to breaking
|
// Breaks at the template element boundaries (${ and }) are preferred to breaking
|
||||||
// in the middle of a MemberExpression
|
// in the middle of a MemberExpression
|
||||||
if (
|
if (
|
||||||
interpolationHasNewline &&
|
interpolationHasNewline &&
|
||||||
(hasComment(expression) ||
|
(hasComment(expression) ||
|
||||||
isMemberExpression(expression) ||
|
expression.type === "Identifier" ||
|
||||||
expression.type === "ConditionalExpression" ||
|
isMemberExpression(expression) ||
|
||||||
expression.type === "SequenceExpression" ||
|
expression.type === "ConditionalExpression" ||
|
||||||
isBinaryCastExpression(expression) ||
|
expression.type === "SequenceExpression" ||
|
||||||
isBinaryish(expression))
|
isBinaryCastExpression(expression) ||
|
||||||
) {
|
isBinaryish(expression))
|
||||||
expressionDoc = [indent([softline, expressionDoc]), softline];
|
) {
|
||||||
}
|
expressionDoc = [indent([softline, expressionDoc]), softline];
|
||||||
}
|
}
|
||||||
|
|
||||||
const aligned =
|
const aligned =
|
||||||
|
|||||||
@@ -420,68 +420,6 @@ const isMemberExpression = skipChainExpression(
|
|||||||
createTypeCheckFunction(["MemberExpression", "OptionalMemberExpression"]),
|
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
|
* Attempts to gauge the rough complexity of a node, for example
|
||||||
* to detect deeply-nested booleans, call expressions with lots of arguments, etc.
|
* to detect deeply-nested booleans, call expressions with lots of arguments, etc.
|
||||||
@@ -1209,11 +1147,8 @@ export {
|
|||||||
isPrettierIgnoreComment,
|
isPrettierIgnoreComment,
|
||||||
isRegExpLiteral,
|
isRegExpLiteral,
|
||||||
isSignedNumericLiteral,
|
isSignedNumericLiteral,
|
||||||
isSimpleAtomicExpression,
|
|
||||||
isSimpleCallArgument,
|
isSimpleCallArgument,
|
||||||
isSimpleExpressionByNodeCount,
|
isSimpleExpressionByNodeCount,
|
||||||
isSimpleMemberExpression,
|
|
||||||
isSimpleTemplateLiteral,
|
|
||||||
isSimpleType,
|
isSimpleType,
|
||||||
isStringLiteral,
|
isStringLiteral,
|
||||||
isTemplateOnItsOwnLine,
|
isTemplateOnItsOwnLine,
|
||||||
|
|||||||
@@ -415,6 +415,12 @@ and this other one: \${
|
|||||||
which already had a linebreak so can be broken up
|
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=====================================
|
=====================================output=====================================
|
||||||
foo(
|
foo(
|
||||||
\`a long string \${1 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3} with expr\`,
|
\`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
|
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
|
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=====================================
|
=====================================output=====================================
|
||||||
foo(
|
foo(
|
||||||
\`a long string \${1 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3 + 2 + 3} with expr\`
|
\`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
|
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
|
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