diff --git a/src/language-handlebars/embed.js b/src/language-handlebars/embed.js index 4d46e2cab..84ff0a185 100644 --- a/src/language-handlebars/embed.js +++ b/src/language-handlebars/embed.js @@ -1,4 +1,8 @@ -import { isPlainTextStyleOrScriptElement } from "./utilities.js"; +import { indent, replaceEndOfLine, softline } from "../document/index.js"; +import { replacePlaceholders } from "../language-js/embed/css.js"; +import { locEnd, locStart } from "./loc.js"; +import { printStartingTag } from "./print/tag.js"; +import { isPlainTextElement } from "./utilities.js"; function getAttribute(node, name) { return node.attributes.find( @@ -29,16 +33,61 @@ function getScriptTextToDocOptions(node) { } } -function getStyleTextToDocOptions(node) { - const langAttribute = getAttribute(node, "lang"); - if (!langAttribute || getTextValue(langAttribute) === "css") { - return { parser: "css" }; +function isEmbedCss(node) { + if (node.type !== "ElementNode" || node.tag !== "style") { + return false; } + + const langAttribute = getAttribute(node, "lang"); + return !langAttribute || getTextValue(langAttribute) === "css"; +} + +async function printEmbedCss(textToDoc, print, path, options) { + const { node } = path; + + let text = ""; + const mustacheDocs = []; + for (const child of node.children) { + if (child.type === "TextNode") { + text += child.chars; + continue; + } + + text += `@prettier-placeholder-${mustacheDocs.length}-id`; + mustacheDocs.push( + replaceEndOfLine( + options.originalText.slice(locStart(child), locEnd(child)), + ), + ); + } + + if (!text.trim()) { + return [printStartingTag(path, options, print), ""]; + } + + const doc = await textToDoc(text, { parser: "css" }); + const contentDoc = replacePlaceholders(doc, mustacheDocs); + + /* c8 ignore next 3 */ + if (!contentDoc) { + throw new Error("Couldn't insert all the mustaches"); + } + + return [ + printStartingTag(path, options, print), + indent([softline, contentDoc]), + softline, + "", + ]; } function embed(path /* , options*/) { const { node } = path; + if (isEmbedCss(node)) { + return printEmbedCss; + } + if (node.type !== "TextNode") { return; } @@ -46,15 +95,14 @@ function embed(path /* , options*/) { const { parent } = path; if (!( - isPlainTextStyleOrScriptElement(parent) && parent.children[0] === node + isPlainTextElement(parent) && + parent.tag === "script" && + parent.children[0] === node )) { return; } - const textToDocOptions = - parent.tag === "style" - ? getStyleTextToDocOptions(parent) - : getScriptTextToDocOptions(parent); + const textToDocOptions = getScriptTextToDocOptions(parent); if (!textToDocOptions) { return; diff --git a/src/language-handlebars/massage-ast/index.js b/src/language-handlebars/massage-ast/index.js index 226439819..232ba46d1 100644 --- a/src/language-handlebars/massage-ast/index.js +++ b/src/language-handlebars/massage-ast/index.js @@ -1,5 +1,5 @@ import htmlWhitespace from "../../utilities/html-whitespace.js"; -import { isPlainTextStyleOrScriptElement } from "../utilities.js"; +import { isPlainTextElement } from "../utilities.js"; function massageAstNode(original, cloned, parent) { // (Glimmer/HTML) ignore TextNode @@ -11,8 +11,10 @@ function massageAstNode(original, cloned, parent) { // CSS/JS will be formatted if ( - isPlainTextStyleOrScriptElement(parent) && - parent.children[0] === original + (parent.type === "ElementNode" && parent.tag === "style") || + (isPlainTextElement(parent) && + parent.tag === "script" && + parent.children[0] === original) ) { cloned.chars = ""; } else { diff --git a/src/language-handlebars/print/tag.js b/src/language-handlebars/print/tag.js new file mode 100644 index 000000000..63d86929f --- /dev/null +++ b/src/language-handlebars/print/tag.js @@ -0,0 +1,58 @@ +import { + group, + ifBreak, + indent, + line, + softline, +} from "../../document/index.js"; +import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; +import { locStart } from "../loc.js"; +import { isVoidElement } from "../utilities.js"; + +/* ElementNode print helpers */ + +function sortByLoc(a, b) { + return locStart(a) - locStart(b); +} + +function printStartingTag(path, options, print) { + const { node } = path; + + const types = ["attributes", "modifiers", "comments"].filter((property) => + isNonEmptyArray(node[property]), + ); + const attributes = types.flatMap((type) => node[type]).sort(sortByLoc); + + for (const attributeType of types) { + path.each(({ node }) => { + const index = attributes.indexOf(node); + attributes[index] = [line, print()]; + }, attributeType); + } + + if (isNonEmptyArray(node.blockParams)) { + attributes.push(line, printBlockParams(node)); + } + + return [ + options.htmlWhitespaceSensitivity === "ignore" && + path.previous?.type === "ElementNode" + ? softline + : "", + group(["<", node.tag, indent(attributes), printStartingTagEndMarker(node)]), + ]; +} + +function printStartingTagEndMarker(node) { + if (isVoidElement(node)) { + return ifBreak([softline, "/>"], [" />", softline]); + } + + return ifBreak([softline, ">"], ">"); +} + +function printBlockParams(node) { + return ["as |", node.blockParams.join(" "), "|"]; +} + +export { printBlockParams, printStartingTag }; diff --git a/src/language-handlebars/printer-glimmer.js b/src/language-handlebars/printer-glimmer.js index f771f50ab..10f76f202 100644 --- a/src/language-handlebars/printer-glimmer.js +++ b/src/language-handlebars/printer-glimmer.js @@ -3,7 +3,6 @@ import { fill, group, hardline, - ifBreak, indent, join, line, @@ -18,6 +17,7 @@ import embed from "./embed.js"; import getVisitorKeys from "./get-visitor-keys.js"; import { locEnd, locStart } from "./loc.js"; import { massageAstNode } from "./massage-ast/index.js"; +import { printBlockParams, printStartingTag } from "./print/tag.js"; import { hasPrettierIgnore, isPlainTextStyleOrScriptElement, @@ -48,12 +48,7 @@ function print(path, options, print) { const isWhitespaceSensitive = options.htmlWhitespaceSensitivity !== "ignore"; - const startingTag = [ - !isWhitespaceSensitive && path.previous?.type === "ElementNode" - ? softline - : "", - group([printStartingTag(path, print)]), - ]; + const startingTag = printStartingTag(path, options, print); if (isVoidElement(node)) { return [startingTag]; @@ -424,42 +419,6 @@ function print(path, options, print) { } } -/* ElementNode print helpers */ - -function sortByLoc(a, b) { - return locStart(a) - locStart(b); -} - -function printStartingTag(path, print) { - const { node } = path; - - const types = ["attributes", "modifiers", "comments"].filter((property) => - isNonEmptyArray(node[property]), - ); - const attributes = types.flatMap((type) => node[type]).sort(sortByLoc); - - for (const attributeType of types) { - path.each(({ node }) => { - const index = attributes.indexOf(node); - attributes[index] = [line, print()]; - }, attributeType); - } - - if (isNonEmptyArray(node.blockParams)) { - attributes.push(line, printBlockParams(node)); - } - - return ["<", node.tag, indent(attributes), printStartingTagEndMarker(node)]; -} - -function printStartingTagEndMarker(node) { - if (isVoidElement(node)) { - return ifBreak([softline, "/>"], [" />", softline]); - } - - return ifBreak([softline, ">"], ">"); -} - /* MustacheStatement print helpers */ function printOpeningMustache(node) { @@ -801,10 +760,6 @@ function printParams(path, print) { return join(line, parts); } -function printBlockParams(node) { - return ["as |", node.blockParams.join(" "), "|"]; -} - // https://handlebarsjs.com/guide/expressions.html#literal-segments const PATH_EXPRESSION_FORBIDDEN_CHARACTERS = new Set( "!\"#%&'()*+,./;<=>@[\\]^`{|}~", diff --git a/src/language-handlebars/utilities.js b/src/language-handlebars/utilities.js index c1eea8eba..67dd098fa 100644 --- a/src/language-handlebars/utilities.js +++ b/src/language-handlebars/utilities.js @@ -69,6 +69,7 @@ function hasPrettierIgnore(path) { export { hasPrettierIgnore, + isPlainTextElement, isPlainTextStyleOrScriptElement, isVoidElement, isWhitespaceNode, diff --git a/src/language-js/embed/css.js b/src/language-js/embed/css.js index baaa60b51..ad81fa55d 100644 --- a/src/language-js/embed/css.js +++ b/src/language-js/embed/css.js @@ -171,4 +171,4 @@ const isEmbedCss = (path /* , options*/) => isCssProp(path) || isAngularComponentStyles(path); -export { isEmbedCss, printEmbedCss }; +export { isEmbedCss, printEmbedCss, replacePlaceholders }; diff --git a/tests/format/handlebars/style-tag/__snapshots__/format.test.js.snap b/tests/format/handlebars/style-tag/__snapshots__/format.test.js.snap index c9ec2a8cb..35a33b5d2 100644 --- a/tests/format/handlebars/style-tag/__snapshots__/format.test.js.snap +++ b/tests/format/handlebars/style-tag/__snapshots__/format.test.js.snap @@ -218,19 +218,113 @@ parsers: ["glimmer"] -=====================================output===================================== + + + + + + + + + + + +=====================================output===================================== + + + - + + + + + + + + + + + + + + + ================================================================================ `; diff --git a/tests/format/handlebars/style-tag/with-mustache.hbs b/tests/format/handlebars/style-tag/with-mustache.hbs index c8b9b8506..0972ba21b 100644 --- a/tests/format/handlebars/style-tag/with-mustache.hbs +++ b/tests/format/handlebars/style-tag/with-mustache.hbs @@ -10,3 +10,42 @@ + + + + + + + + + + + + + +