Format <style> containing mustaches
This commit is contained in:
1 parent
891accaaab
commit
05b8f7a0a8
8 files changed
+264
-67
No files matched your search
@@ -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), "</style>"];
|
||||
}
|
||||
|
||||
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,
|
||||
"</style>",
|
||||
];
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 };
|
||||
@@ -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(
|
||||
"!\"#%&'()*+,./;<=>@[\\]^`{|}~",
|
||||
|
||||
@@ -69,6 +69,7 @@ function hasPrettierIgnore(path) {
|
||||
|
||||
export {
|
||||
hasPrettierIgnore,
|
||||
isPlainTextElement,
|
||||
isPlainTextStyleOrScriptElement,
|
||||
isVoidElement,
|
||||
isWhitespaceNode,
|
||||
|
||||
@@ -171,4 +171,4 @@ const isEmbedCss = (path /* , options*/) =>
|
||||
isCssProp(path) ||
|
||||
isAngularComponentStyles(path);
|
||||
|
||||
export { isEmbedCss, printEmbedCss };
|
||||
export { isEmbedCss, printEmbedCss, replacePlaceholders };
|
||||
@@ -218,19 +218,113 @@ parsers: ["glimmer"]
|
||||
|
||||
<style>{{foo}}</style>
|
||||
|
||||
=====================================output=====================================
|
||||
<style>
|
||||
a { color:
|
||||
{{foo}}
|
||||
.{{className}} { width:{{width}}px;background:url({{url}}) }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
a::after { content: "{{content}}" }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* {{comment}} */
|
||||
a { color:red }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@media (max-width: {{breakpoint}}px) { a { color: red } }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
a {
|
||||
{{mixin}}
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.a { color:
|
||||
{{color}}; }
|
||||
a {
|
||||
{{mixin}};
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.a { color:red }
|
||||
{{#if debug}}
|
||||
.b {color:red}
|
||||
.c {color:red}
|
||||
{{/if}}
|
||||
</style>
|
||||
|
||||
=====================================output=====================================
|
||||
<style>
|
||||
a {
|
||||
color: {{foo}};
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.a {
|
||||
color: {{color}};
|
||||
}
|
||||
{{#if debug}}.b { color: red; }{{/if}}
|
||||
</style>
|
||||
|
||||
<style>{{foo}}</style>
|
||||
<style>
|
||||
{{foo}}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.{{className}} {
|
||||
width: {{width}}px;
|
||||
background: url({{url}});
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
a::after {
|
||||
content: "{{content}}";
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* {{comment}} */
|
||||
a {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@media (max-width: {{breakpoint}}px) {
|
||||
a {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
a {
|
||||
{{mixin}}
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
a {
|
||||
{{mixin}};
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.a {
|
||||
color: red;
|
||||
}
|
||||
{{#if debug}}
|
||||
.b {color:red}
|
||||
.c {color:red}
|
||||
{{/if}}
|
||||
</style>
|
||||
================================================================================
|
||||
`;
|
||||
@@ -10,3 +10,42 @@
|
||||
</style>
|
||||
|
||||
<style>{{foo}}</style>
|
||||
|
||||
<style>
|
||||
.{{className}} { width:{{width}}px;background:url({{url}}) }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
a::after { content: "{{content}}" }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* {{comment}} */
|
||||
a { color:red }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@media (max-width: {{breakpoint}}px) { a { color: red } }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
a {
|
||||
{{mixin}}
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
a {
|
||||
{{mixin}};
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.a { color:red }
|
||||
{{#if debug}}
|
||||
.b {color:red}
|
||||
.c {color:red}
|
||||
{{/if}}
|
||||
</style>
|
||||
Reference in new issue
Block a user