Handlebars: Fix invalid output for <style> containing mustaches (#20030)

Co-authored-by: fisker <lionkay@gmail.com>
This commit is contained in:
Konv Suuandfisker authored and GitHub committed 2026-09-11 18:09:38 +08:00
1 parent 3014654149
commit c150b07c07
8 files changed
+105 -16

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
#### Fix invalid output for `<style>` containing mustaches (#20030 by @kovsu)
<!-- prettier-ignore -->
```hbs
{{! Input }}
<style>
a {
color: {{foo}}
}
</style>
{{! Prettier stable }}
<style>
a {
color:{{foo}}}
</style>
{{! Prettier main }}
<style>
a { color:
{{foo}}
}
</style>
```
+3 -6
View File
@@ -1,3 +1,5 @@
import { isPlainTextStyleElement } from "./utilities.js";
function embed(path /* , options*/) { function embed(path /* , options*/) {
const { node } = path; const { node } = path;
@@ -7,12 +9,7 @@ function embed(path /* , options*/) {
const { parent } = path; const { parent } = path;
if (!( if (!(isPlainTextStyleElement(parent) && parent.children[0] === node)) {
parent.type === "ElementNode" &&
parent.tag === "style" &&
parent.children.length === 1 &&
parent.children[0] === node
)) {
return; return;
} }
+2 -5
View File
@@ -1,4 +1,5 @@
import htmlWhitespace from "../../utilities/html-whitespace.js"; import htmlWhitespace from "../../utilities/html-whitespace.js";
import { isPlainTextStyleElement } from "../utilities.js";
function massageAstNode(original, cloned, parent) { function massageAstNode(original, cloned, parent) {
// (Glimmer/HTML) ignore TextNode // (Glimmer/HTML) ignore TextNode
@@ -9,11 +10,7 @@ function massageAstNode(original, cloned, parent) {
} }
// CSS will be formatted // CSS will be formatted
if ( if (isPlainTextStyleElement(parent) && parent.children[0] === original) {
parent.tag === "style" &&
parent.children.length === 1 &&
parent.children[0] === original
) {
cloned.chars = ""; cloned.chars = "";
} else { } else {
cloned.chars = htmlWhitespace.split(trimmed).join(" "); cloned.chars = htmlWhitespace.split(trimmed).join(" ");
+5 -4
View File
@@ -20,6 +20,7 @@ import { locEnd, locStart } from "./loc.js";
import { massageAstNode } from "./massage-ast/index.js"; import { massageAstNode } from "./massage-ast/index.js";
import { import {
hasPrettierIgnore, hasPrettierIgnore,
isPlainTextStyleElement,
isVoidElement, isVoidElement,
isWhitespaceNode, isWhitespaceNode,
} from "./utilities.js"; } from "./utilities.js";
@@ -59,11 +60,11 @@ function print(path, options, print) {
} }
const endingTag = ["</", node.tag, ">"]; const endingTag = ["</", node.tag, ">"];
const isStyle = node.tag === "style"; const isPlainStyle = isPlainTextStyleElement(node);
if ( if (
node.children.length === 0 || node.children.length === 0 ||
((!isWhitespaceSensitive || isStyle) && ((!isWhitespaceSensitive || isPlainStyle) &&
node.children.every((node) => isWhitespaceNode(node))) node.children.every((node) => isWhitespaceNode(node)))
) { ) {
return [startingTag, endingTag]; return [startingTag, endingTag];
@@ -71,7 +72,7 @@ function print(path, options, print) {
const parts = path.map(print, "children"); const parts = path.map(print, "children");
if (isStyle || !isWhitespaceSensitive) { if (isPlainStyle || !isWhitespaceSensitive) {
return [startingTag, indent([softline, ...parts]), softline, endingTag]; return [startingTag, indent([softline, ...parts]), softline, endingTag];
} }
@@ -172,7 +173,7 @@ function print(path, options, print) {
return replaceEndOfLine(text); return replaceEndOfLine(text);
} }
if (parent.tag === "style") { if (isPlainTextStyleElement(parent)) {
text = text.replaceAll(/^\n+/g, ""); text = text.replaceAll(/^\n+/g, "");
text = htmlWhitespace.trimEnd(text); text = htmlWhitespace.trimEnd(text);
text = htmlWhitespace.dedentString(text); text = htmlWhitespace.dedentString(text);
+18 -1
View File
@@ -34,6 +34,18 @@ function isWhitespaceNode(node) {
return node.type === "TextNode" && !/\S/.test(node.chars); return node.type === "TextNode" && !/\S/.test(node.chars);
} }
function isPlainTextElement(node) {
return (
node.type === "ElementNode" &&
node.children.length === 1 &&
node.children[0].type === "TextNode"
);
}
function isPlainTextStyleElement(node) {
return isPlainTextElement(node) && node.tag === "style";
}
function isPrettierIgnoreNode(node) { function isPrettierIgnoreNode(node) {
return ( return (
node?.type === "MustacheCommentStatement" && node?.type === "MustacheCommentStatement" &&
@@ -53,4 +65,9 @@ function hasPrettierIgnore(path) {
); );
} }
export { hasPrettierIgnore, isVoidElement, isWhitespaceNode }; export {
hasPrettierIgnore,
isPlainTextStyleElement,
isVoidElement,
isWhitespaceNode,
};
@@ -13,10 +13,14 @@ parsers: ["glimmer"]
</style> </style>
</div> </div>
<style></style>
=====================================output===================================== =====================================output=====================================
<div> <div>
<style></style> <style></style>
</div> </div>
<style></style>
================================================================================ ================================================================================
`; `;
@@ -195,3 +199,38 @@ parsers: ["glimmer"]
</style> </style>
================================================================================ ================================================================================
`; `;
exports[`with-mustache.hbs format 1`] = `
====================================options=====================================
parsers: ["glimmer"]
printWidth: 80 (default) |
=====================================input======================================
<style>
a {
color: {{foo}}
}
</style>
<style>
.a { color: {{color}}; }
{{#if debug}}.b { color: red; }{{/if}}
</style>
<style>{{foo}}</style>
=====================================output=====================================
<style>
a { color:
{{foo}}
}
</style>
<style>
.a { color:
{{color}}; }
{{#if debug}}.b { color: red; }{{/if}}
</style>
<style>{{foo}}</style>
================================================================================
`;
@@ -5,3 +5,5 @@
</style> </style>
</div> </div>
<style></style>
@@ -0,0 +1,12 @@
<style>
a {
color: {{foo}}
}
</style>
<style>
.a { color: {{color}}; }
{{#if debug}}.b { color: red; }{{/if}}
</style>
<style>{{foo}}</style>