2021-03-29 08:00:50 -04:00
|
|
|
/**
|
2024-10-01 19:45:37 +08:00
|
|
|
* @import AstPath from "../../common/ast-path.js"
|
2021-03-29 08:00:50 -04:00
|
|
|
*/
|
|
|
|
|
|
2025-11-03 01:52:26 +08:00
|
|
|
import {
|
|
|
|
|
hardline,
|
|
|
|
|
join,
|
|
|
|
|
line,
|
|
|
|
|
replaceEndOfLine,
|
|
|
|
|
} from "../../document/index.js";
|
2025-10-30 22:16:54 +08:00
|
|
|
import { isFrontMatter } from "../../main/front-matter/index.js";
|
2025-12-01 07:25:25 +08:00
|
|
|
import htmlWhitespace from "../../utilities/html-whitespace.js";
|
|
|
|
|
import inferParser from "../../utilities/infer-parser.js";
|
2023-12-17 19:34:12 +00:00
|
|
|
import {
|
|
|
|
|
CSS_DISPLAY_DEFAULT,
|
|
|
|
|
CSS_DISPLAY_TAGS,
|
|
|
|
|
CSS_WHITE_SPACE_DEFAULT,
|
|
|
|
|
CSS_WHITE_SPACE_TAGS,
|
|
|
|
|
} from "../constants.evaluate.js";
|
2022-03-13 17:04:06 +08:00
|
|
|
import isUnknownNamespace from "./is-unknown-namespace.js";
|
2026-09-20 21:52:10 +08:00
|
|
|
import { hasOrInNgNonBindable } from "./ng-non-bindable.js";
|
2022-02-21 16:44:56 +08:00
|
|
|
|
2020-04-25 21:56:28 +08:00
|
|
|
const htmlTrimLeadingBlankLines = (string) =>
|
2025-12-21 23:44:08 +08:00
|
|
|
string.replaceAll(/^[\t\f\r ]*\n/g, "");
|
2020-04-25 21:56:28 +08:00
|
|
|
const htmlTrimPreserveIndentation = (string) =>
|
2025-12-01 07:25:25 +08:00
|
|
|
htmlTrimLeadingBlankLines(htmlWhitespace.trimEnd(string));
|
2020-04-25 21:56:28 +08:00
|
|
|
const getLeadingAndTrailingHtmlWhitespace = (string) => {
|
2023-02-24 15:41:20 +08:00
|
|
|
let text = string;
|
2025-12-01 07:25:25 +08:00
|
|
|
const leadingWhitespace = htmlWhitespace.getLeadingWhitespace(text);
|
2023-02-24 15:41:20 +08:00
|
|
|
if (leadingWhitespace) {
|
|
|
|
|
text = text.slice(leadingWhitespace.length);
|
|
|
|
|
}
|
2025-12-01 07:25:25 +08:00
|
|
|
const trailingWhitespace = htmlWhitespace.getTrailingWhitespace(text);
|
2023-02-24 15:41:20 +08:00
|
|
|
if (trailingWhitespace) {
|
|
|
|
|
text = text.slice(0, -trailingWhitespace.length);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-25 21:56:28 +08:00
|
|
|
return {
|
|
|
|
|
leadingWhitespace,
|
|
|
|
|
trailingWhitespace,
|
|
|
|
|
text,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-23 12:33:48 +08:00
|
|
|
function shouldPreserveContent(node, options) {
|
2018-11-09 23:26:59 +08:00
|
|
|
// unterminated node in ie conditional comment
|
|
|
|
|
// e.g. <!--[if lt IE 9]><html><![endif]-->
|
|
|
|
|
if (
|
2025-10-11 23:23:34 +08:00
|
|
|
node.kind === "ieConditionalComment" &&
|
2018-11-09 23:26:59 +08:00
|
|
|
node.lastChild &&
|
|
|
|
|
!node.lastChild.isSelfClosing &&
|
|
|
|
|
!node.lastChild.endSourceSpan
|
|
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 02:09:45 +08:00
|
|
|
// incomplete HTML in ie conditional comment
|
2018-11-10 16:44:44 +08:00
|
|
|
// e.g. <!--[if lt IE 9]></div><![endif]-->
|
2025-10-11 23:23:34 +08:00
|
|
|
if (node.kind === "ieConditionalComment" && !node.complete) {
|
2018-11-10 16:44:44 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-04 23:03:07 +08:00
|
|
|
// TODO: handle non-text children in <pre>
|
|
|
|
|
if (
|
|
|
|
|
isPreLikeNode(node) &&
|
2018-11-09 09:50:57 +08:00
|
|
|
node.children.some(
|
2025-10-11 23:23:34 +08:00
|
|
|
(child) => child.kind !== "text" && child.kind !== "interpolation",
|
2018-11-09 09:50:57 +08:00
|
|
|
)
|
2018-11-04 23:03:07 +08:00
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 07:54:24 +09:00
|
|
|
if (
|
2020-06-17 19:18:29 +08:00
|
|
|
isVueNonHtmlBlock(node, options) &&
|
2025-05-06 03:46:15 +02:00
|
|
|
!isScriptLikeTag(node, options) &&
|
2025-10-11 23:23:34 +08:00
|
|
|
node.kind !== "interpolation"
|
2020-04-17 07:54:24 +09:00
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-09 19:19:03 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-09 23:26:59 +08:00
|
|
|
function hasPrettierIgnore(node) {
|
2022-10-22 23:33:08 +08:00
|
|
|
/* c8 ignore next 3 */
|
2025-10-11 23:23:34 +08:00
|
|
|
if (node.kind === "attribute") {
|
2018-11-09 19:19:03 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-22 23:33:08 +08:00
|
|
|
/* c8 ignore next 3 */
|
2018-11-09 23:26:59 +08:00
|
|
|
if (!node.parent) {
|
2018-09-19 23:07:27 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-11 22:06:46 +09:00
|
|
|
if (!node.prev) {
|
2018-09-19 23:07:27 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-11 22:06:46 +09:00
|
|
|
return isPrettierIgnore(node.prev);
|
2018-09-19 23:07:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isPrettierIgnore(node) {
|
2025-10-11 23:23:34 +08:00
|
|
|
return node.kind === "comment" && node.value.trim() === "prettier-ignore";
|
2018-09-19 23:07:27 +08:00
|
|
|
}
|
|
|
|
|
|
2018-11-23 21:25:58 +08:00
|
|
|
/** there's no opening/closing tag or it's considered not breakable */
|
|
|
|
|
function isTextLikeNode(node) {
|
2025-10-11 23:23:34 +08:00
|
|
|
return node.kind === "text" || node.kind === "comment";
|
2018-11-23 21:25:58 +08:00
|
|
|
}
|
|
|
|
|
|
2025-05-06 03:46:15 +02:00
|
|
|
function isScriptLikeTag(node, options) {
|
2018-11-04 23:03:07 +08:00
|
|
|
return (
|
2025-10-11 23:23:34 +08:00
|
|
|
node.kind === "element" &&
|
2018-11-04 23:03:07 +08:00
|
|
|
(node.fullName === "script" ||
|
|
|
|
|
node.fullName === "style" ||
|
2020-04-02 01:35:58 +08:00
|
|
|
node.fullName === "svg:style" ||
|
2023-04-10 10:43:48 +08:00
|
|
|
node.fullName === "svg:script" ||
|
2025-05-06 03:46:15 +02:00
|
|
|
(node.fullName === "mj-style" && options.parser === "mjml") ||
|
2020-04-02 01:35:58 +08:00
|
|
|
(isUnknownNamespace(node) &&
|
|
|
|
|
(node.name === "script" || node.name === "style")))
|
2018-11-04 23:03:07 +08:00
|
|
|
);
|
2018-09-19 23:07:27 +08:00
|
|
|
}
|
|
|
|
|
|
2025-05-06 03:46:15 +02:00
|
|
|
function canHaveInterpolation(node, options) {
|
2026-09-20 21:52:10 +08:00
|
|
|
if (!node.children || isScriptLikeTag(node, options)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasOrInNgNonBindable(node, options)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2018-11-04 23:03:07 +08:00
|
|
|
}
|
|
|
|
|
|
2025-05-06 03:46:15 +02:00
|
|
|
function isWhitespaceSensitiveNode(node, options) {
|
2018-11-04 23:03:07 +08:00
|
|
|
return (
|
2025-05-06 03:46:15 +02:00
|
|
|
isScriptLikeTag(node, options) ||
|
2025-10-11 23:23:34 +08:00
|
|
|
node.kind === "interpolation" ||
|
2018-11-04 23:03:07 +08:00
|
|
|
isIndentationSensitiveNode(node)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isIndentationSensitiveNode(node) {
|
|
|
|
|
return getNodeCssStyleWhiteSpace(node).startsWith("pre");
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 07:54:24 +09:00
|
|
|
function isLeadingSpaceSensitiveNode(node, options) {
|
2018-11-15 10:34:42 +08:00
|
|
|
const isLeadingSpaceSensitive = _isLeadingSpaceSensitiveNode();
|
2018-10-13 13:55:38 +08:00
|
|
|
|
|
|
|
|
if (
|
2018-11-15 10:34:42 +08:00
|
|
|
isLeadingSpaceSensitive &&
|
2018-11-04 23:03:07 +08:00
|
|
|
!node.prev &&
|
2022-11-03 14:23:07 +08:00
|
|
|
node.parent?.tagDefinition?.ignoreFirstLf
|
2018-10-13 13:55:38 +08:00
|
|
|
) {
|
2025-10-11 23:23:34 +08:00
|
|
|
return node.kind === "interpolation";
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
2018-11-15 10:34:42 +08:00
|
|
|
return isLeadingSpaceSensitive;
|
|
|
|
|
|
|
|
|
|
function _isLeadingSpaceSensitiveNode() {
|
2025-10-11 23:23:34 +08:00
|
|
|
if (isFrontMatter(node) || node.kind === "angularControlFlowBlock") {
|
2018-11-15 10:34:42 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
2025-10-11 23:23:34 +08:00
|
|
|
(node.kind === "text" || node.kind === "interpolation") &&
|
2018-11-15 10:34:42 +08:00
|
|
|
node.prev &&
|
2025-10-11 23:23:34 +08:00
|
|
|
(node.prev.kind === "text" || node.prev.kind === "interpolation")
|
2018-11-15 10:34:42 +08:00
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!node.parent || node.parent.cssDisplay === "none") {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isPreLikeNode(node.parent)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!node.prev &&
|
2025-10-11 23:23:34 +08:00
|
|
|
(node.parent.kind === "root" ||
|
2020-01-06 11:00:09 -08:00
|
|
|
(isPreLikeNode(node) && node.parent) ||
|
2025-05-06 03:46:15 +02:00
|
|
|
isScriptLikeTag(node.parent, options) ||
|
2020-04-17 07:54:24 +09:00
|
|
|
isVueCustomBlock(node.parent, options) ||
|
2018-11-15 10:34:42 +08:00
|
|
|
!isFirstChildLeadingSpaceSensitiveCssDisplay(node.parent.cssDisplay))
|
|
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
node.prev &&
|
|
|
|
|
!isNextLeadingSpaceSensitiveCssDisplay(node.prev.cssDisplay)
|
|
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-04 23:03:07 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
2020-04-17 07:54:24 +09:00
|
|
|
function isTrailingSpaceSensitiveNode(node, options) {
|
2025-10-11 23:23:34 +08:00
|
|
|
if (isFrontMatter(node) || node.kind === "angularControlFlowBlock") {
|
2018-10-13 13:55:38 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-10 01:00:26 +08:00
|
|
|
if (
|
2025-10-11 23:23:34 +08:00
|
|
|
(node.kind === "text" || node.kind === "interpolation") &&
|
2018-11-10 01:00:26 +08:00
|
|
|
node.next &&
|
2025-10-11 23:23:34 +08:00
|
|
|
(node.next.kind === "text" || node.next.kind === "interpolation")
|
2018-11-10 01:00:26 +08:00
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-04 23:03:07 +08:00
|
|
|
if (!node.parent || node.parent.cssDisplay === "none") {
|
2018-10-13 13:55:38 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-04 23:03:07 +08:00
|
|
|
if (isPreLikeNode(node.parent)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-13 13:55:38 +08:00
|
|
|
if (
|
2018-11-04 23:03:07 +08:00
|
|
|
!node.next &&
|
2025-10-11 23:23:34 +08:00
|
|
|
(node.parent.kind === "root" ||
|
2020-01-06 11:00:09 -08:00
|
|
|
(isPreLikeNode(node) && node.parent) ||
|
2025-05-06 03:46:15 +02:00
|
|
|
isScriptLikeTag(node.parent, options) ||
|
2020-04-17 07:54:24 +09:00
|
|
|
isVueCustomBlock(node.parent, options) ||
|
2018-11-04 23:03:07 +08:00
|
|
|
!isLastChildTrailingSpaceSensitiveCssDisplay(node.parent.cssDisplay))
|
2018-10-13 13:55:38 +08:00
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-04 23:03:07 +08:00
|
|
|
if (
|
|
|
|
|
node.next &&
|
|
|
|
|
!isPrevTrailingSpaceSensitiveCssDisplay(node.next.cssDisplay)
|
|
|
|
|
) {
|
2018-10-13 13:55:38 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-06 03:46:15 +02:00
|
|
|
function isDanglingSpaceSensitiveNode(node, options) {
|
2018-11-04 23:03:07 +08:00
|
|
|
return (
|
|
|
|
|
isDanglingSpaceSensitiveCssDisplay(node.cssDisplay) &&
|
2025-05-06 03:46:15 +02:00
|
|
|
!isScriptLikeTag(node, options)
|
2018-11-04 23:03:07 +08:00
|
|
|
);
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function forceNextEmptyLine(node) {
|
2018-11-04 23:03:07 +08:00
|
|
|
return (
|
2023-02-13 10:17:39 +08:00
|
|
|
isFrontMatter(node) ||
|
2018-11-04 23:03:07 +08:00
|
|
|
(node.next &&
|
2020-07-14 22:02:32 +08:00
|
|
|
node.sourceSpan.end &&
|
2018-11-04 23:03:07 +08:00
|
|
|
node.sourceSpan.end.line + 1 < node.next.sourceSpan.start.line)
|
|
|
|
|
);
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** firstChild leadingSpaces and lastChild trailingSpaces */
|
|
|
|
|
function forceBreakContent(node) {
|
|
|
|
|
return (
|
|
|
|
|
forceBreakChildren(node) ||
|
2025-10-11 23:23:34 +08:00
|
|
|
(node.kind === "element" &&
|
2021-01-04 10:11:57 +08:00
|
|
|
node.children.length > 0 &&
|
2019-11-17 18:06:14 +08:00
|
|
|
(["body", "script", "style"].includes(node.name) ||
|
2018-12-11 12:43:53 +08:00
|
|
|
node.children.some((child) => hasNonTextChild(child)))) ||
|
|
|
|
|
(node.firstChild &&
|
|
|
|
|
node.firstChild === node.lastChild &&
|
2025-10-11 23:23:34 +08:00
|
|
|
node.firstChild.kind !== "text" &&
|
2019-11-09 01:14:31 +01:00
|
|
|
hasLeadingLineBreak(node.firstChild) &&
|
|
|
|
|
(!node.lastChild.isTrailingSpaceSensitive ||
|
|
|
|
|
hasTrailingLineBreak(node.lastChild)))
|
2018-10-13 13:55:38 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** spaces between children */
|
|
|
|
|
function forceBreakChildren(node) {
|
|
|
|
|
return (
|
2025-10-11 23:23:34 +08:00
|
|
|
node.kind === "element" &&
|
2021-01-04 10:11:57 +08:00
|
|
|
node.children.length > 0 &&
|
2019-11-17 18:06:14 +08:00
|
|
|
(["html", "head", "ul", "ol", "select"].includes(node.name) ||
|
2018-10-13 13:55:38 +08:00
|
|
|
(node.cssDisplay.startsWith("table") && node.cssDisplay !== "table-cell"))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function preferHardlineAsLeadingSpaces(node) {
|
|
|
|
|
return (
|
|
|
|
|
preferHardlineAsSurroundingSpaces(node) ||
|
2018-11-04 23:03:07 +08:00
|
|
|
(node.prev && preferHardlineAsTrailingSpaces(node.prev)) ||
|
2018-12-11 12:43:53 +08:00
|
|
|
hasSurroundingLineBreak(node)
|
2018-10-13 13:55:38 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function preferHardlineAsTrailingSpaces(node) {
|
|
|
|
|
return (
|
|
|
|
|
preferHardlineAsSurroundingSpaces(node) ||
|
2025-10-11 23:23:34 +08:00
|
|
|
(node.kind === "element" && node.fullName === "br") ||
|
2018-12-11 12:43:53 +08:00
|
|
|
hasSurroundingLineBreak(node)
|
2018-11-09 09:51:31 +08:00
|
|
|
);
|
2018-11-04 23:03:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hasSurroundingLineBreak(node) {
|
|
|
|
|
return hasLeadingLineBreak(node) && hasTrailingLineBreak(node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hasLeadingLineBreak(node) {
|
|
|
|
|
return (
|
|
|
|
|
node.hasLeadingSpaces &&
|
|
|
|
|
(node.prev
|
|
|
|
|
? node.prev.sourceSpan.end.line < node.sourceSpan.start.line
|
2025-10-11 23:23:34 +08:00
|
|
|
: node.parent.kind === "root" ||
|
2018-11-04 23:03:07 +08:00
|
|
|
node.parent.startSourceSpan.end.line < node.sourceSpan.start.line)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hasTrailingLineBreak(node) {
|
|
|
|
|
return (
|
|
|
|
|
node.hasTrailingSpaces &&
|
|
|
|
|
(node.next
|
|
|
|
|
? node.next.sourceSpan.start.line > node.sourceSpan.end.line
|
2025-10-11 23:23:34 +08:00
|
|
|
: node.parent.kind === "root" ||
|
2020-01-13 22:31:03 +08:00
|
|
|
(node.parent.endSourceSpan &&
|
|
|
|
|
node.parent.endSourceSpan.start.line > node.sourceSpan.end.line))
|
2018-10-13 13:55:38 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function preferHardlineAsSurroundingSpaces(node) {
|
2025-10-11 23:23:34 +08:00
|
|
|
switch (node.kind) {
|
2018-10-13 13:55:38 +08:00
|
|
|
case "ieConditionalComment":
|
|
|
|
|
case "comment":
|
|
|
|
|
case "directive":
|
|
|
|
|
return true;
|
2018-11-04 23:03:07 +08:00
|
|
|
case "element":
|
2019-11-17 18:06:14 +08:00
|
|
|
return ["script", "select"].includes(node.name);
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getLastDescendant(node) {
|
|
|
|
|
return node.lastChild ? getLastDescendant(node.lastChild) : node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hasNonTextChild(node) {
|
2025-10-11 23:23:34 +08:00
|
|
|
return node.children?.some((child) => child.kind !== "text");
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
function inferParserByTypeAttribute(type) {
|
|
|
|
|
if (!type) {
|
|
|
|
|
return;
|
2020-04-17 07:54:24 +09:00
|
|
|
}
|
|
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
switch (type) {
|
|
|
|
|
case "module":
|
|
|
|
|
case "text/javascript":
|
|
|
|
|
case "text/babel":
|
2025-05-27 09:09:14 +08:00
|
|
|
case "text/jsx":
|
2023-03-06 09:24:08 +08:00
|
|
|
case "application/javascript":
|
|
|
|
|
return "babel";
|
2020-04-17 07:54:24 +09:00
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
case "application/x-typescript":
|
|
|
|
|
return "typescript";
|
2020-04-17 07:54:24 +09:00
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
case "text/markdown":
|
|
|
|
|
return "markdown";
|
2020-05-21 20:29:10 +09:00
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
case "text/html":
|
|
|
|
|
return "html";
|
2020-04-17 07:54:24 +09:00
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
case "text/x-handlebars-template":
|
|
|
|
|
return "glimmer";
|
2020-04-17 07:54:24 +09:00
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
default:
|
|
|
|
|
if (
|
|
|
|
|
type.endsWith("json") ||
|
|
|
|
|
type.endsWith("importmap") ||
|
|
|
|
|
type === "speculationrules"
|
|
|
|
|
) {
|
|
|
|
|
return "json";
|
|
|
|
|
}
|
2022-05-01 23:01:24 +08:00
|
|
|
}
|
2020-04-17 07:54:24 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function inferScriptParser(node, options) {
|
2023-03-06 09:24:08 +08:00
|
|
|
const { name, attrMap } = node;
|
|
|
|
|
|
|
|
|
|
if (name !== "script" || Object.hasOwn(attrMap, "src")) {
|
|
|
|
|
return;
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
const { type, lang } = node.attrMap;
|
|
|
|
|
|
|
|
|
|
if (!lang && !type) {
|
|
|
|
|
return "babel";
|
2020-04-17 07:54:24 +09:00
|
|
|
}
|
2018-11-04 23:03:07 +08:00
|
|
|
|
2023-03-06 09:24:08 +08:00
|
|
|
return (
|
|
|
|
|
inferParser(options, { language: lang }) ?? inferParserByTypeAttribute(type)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function inferVueSfcBlockParser(node, options) {
|
|
|
|
|
if (!isVueNonHtmlBlock(node, options)) {
|
|
|
|
|
return;
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
2023-03-06 09:24:08 +08:00
|
|
|
const { attrMap } = node;
|
|
|
|
|
|
|
|
|
|
if (Object.hasOwn(attrMap, "src")) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { type, lang } = attrMap;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
inferParser(options, { language: lang }) ?? inferParserByTypeAttribute(type)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function inferStyleParser(node, options) {
|
2025-05-06 03:46:15 +02:00
|
|
|
if (node.name === "style") {
|
|
|
|
|
const { lang } = node.attrMap;
|
|
|
|
|
return lang ? inferParser(options, { language: lang }) : "css";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.name === "mj-style" && options.parser === "mjml") {
|
|
|
|
|
return "css";
|
2023-03-06 09:24:08 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function inferElementParser(node, options) {
|
|
|
|
|
return (
|
|
|
|
|
inferScriptParser(node, options) ??
|
|
|
|
|
inferStyleParser(node, options) ??
|
|
|
|
|
inferVueSfcBlockParser(node, options)
|
|
|
|
|
);
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isBlockLikeCssDisplay(cssDisplay) {
|
2018-11-04 23:03:07 +08:00
|
|
|
return (
|
|
|
|
|
cssDisplay === "block" ||
|
|
|
|
|
cssDisplay === "list-item" ||
|
|
|
|
|
cssDisplay.startsWith("table")
|
|
|
|
|
);
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
2018-11-04 23:03:07 +08:00
|
|
|
function isFirstChildLeadingSpaceSensitiveCssDisplay(cssDisplay) {
|
|
|
|
|
return !isBlockLikeCssDisplay(cssDisplay) && cssDisplay !== "inline-block";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isLastChildTrailingSpaceSensitiveCssDisplay(cssDisplay) {
|
|
|
|
|
return !isBlockLikeCssDisplay(cssDisplay) && cssDisplay !== "inline-block";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isPrevTrailingSpaceSensitiveCssDisplay(cssDisplay) {
|
|
|
|
|
return !isBlockLikeCssDisplay(cssDisplay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isNextLeadingSpaceSensitiveCssDisplay(cssDisplay) {
|
|
|
|
|
return !isBlockLikeCssDisplay(cssDisplay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isDanglingSpaceSensitiveCssDisplay(cssDisplay) {
|
|
|
|
|
return !isBlockLikeCssDisplay(cssDisplay) && cssDisplay !== "inline-block";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isPreLikeNode(node) {
|
|
|
|
|
return getNodeCssStyleWhiteSpace(node).startsWith("pre");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hasParent(node, fn) {
|
|
|
|
|
let current = node;
|
|
|
|
|
|
|
|
|
|
while (current) {
|
|
|
|
|
if (fn(current)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current = current.parent;
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
2018-11-04 23:03:07 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getNodeCssStyleDisplay(node, options) {
|
2026-06-16 02:09:45 +08:00
|
|
|
// Every root block in Vue.js SFC is a block
|
2023-03-13 09:12:11 +08:00
|
|
|
if (isVueSfcBlock(node, options)) {
|
|
|
|
|
return "block";
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-11 23:23:34 +08:00
|
|
|
if (node.prev?.kind === "comment") {
|
2018-10-13 13:55:38 +08:00
|
|
|
// <!-- display: block -->
|
2025-12-21 23:44:08 +08:00
|
|
|
const match = node.prev.value.match(/^\s*display:\s*([a-z]+)\s*$/);
|
2018-10-13 13:55:38 +08:00
|
|
|
if (match) {
|
|
|
|
|
return match[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-04 23:03:07 +08:00
|
|
|
let isInSvgForeignObject = false;
|
2025-10-11 23:23:34 +08:00
|
|
|
if (node.kind === "element" && node.namespace === "svg") {
|
2018-11-04 23:03:07 +08:00
|
|
|
if (hasParent(node, (parent) => parent.fullName === "svg:foreignObject")) {
|
|
|
|
|
isInSvgForeignObject = true;
|
|
|
|
|
} else {
|
|
|
|
|
return node.name === "svg" ? "inline-block" : "block";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-13 13:55:38 +08:00
|
|
|
switch (options.htmlWhitespaceSensitivity) {
|
|
|
|
|
case "strict":
|
|
|
|
|
return "inline";
|
|
|
|
|
case "ignore":
|
|
|
|
|
return "block";
|
2022-10-08 10:44:30 +08:00
|
|
|
default:
|
2025-06-02 22:56:07 +08:00
|
|
|
if (
|
2025-10-11 23:23:34 +08:00
|
|
|
node.kind === "element" &&
|
2025-06-02 22:56:07 +08:00
|
|
|
(!node.namespace || isInSvgForeignObject || isUnknownNamespace(node)) &&
|
|
|
|
|
Object.hasOwn(CSS_DISPLAY_TAGS, node.name)
|
|
|
|
|
) {
|
|
|
|
|
return CSS_DISPLAY_TAGS[node.name];
|
|
|
|
|
}
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
2025-06-02 22:56:07 +08:00
|
|
|
|
|
|
|
|
return CSS_DISPLAY_DEFAULT;
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getNodeCssStyleWhiteSpace(node) {
|
2025-06-02 22:56:07 +08:00
|
|
|
if (
|
2025-10-11 23:23:34 +08:00
|
|
|
node.kind === "element" &&
|
2025-06-02 22:56:07 +08:00
|
|
|
(!node.namespace || isUnknownNamespace(node)) &&
|
|
|
|
|
Object.hasOwn(CSS_WHITE_SPACE_TAGS, node.name)
|
|
|
|
|
) {
|
|
|
|
|
return CSS_WHITE_SPACE_TAGS[node.name];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CSS_WHITE_SPACE_DEFAULT;
|
2018-10-13 13:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
2018-12-05 11:15:09 +08:00
|
|
|
function unescapeQuoteEntities(text) {
|
2022-11-06 19:55:24 +08:00
|
|
|
return text.replaceAll("'", "'").replaceAll(""", '"');
|
2018-12-05 11:15:09 +08:00
|
|
|
}
|
|
|
|
|
|
2023-03-27 14:22:54 +08:00
|
|
|
function getUnescapedAttributeValue(node) {
|
|
|
|
|
return unescapeQuoteEntities(node.value);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 02:09:45 +08:00
|
|
|
// top-level elements (excluding <template>, <style> and <script>) in Vue.js SFC are considered custom block
|
2020-05-14 23:08:40 +09:00
|
|
|
// See https://vue-loader.vuejs.org/spec.html for detail
|
2020-05-19 10:30:03 +09:00
|
|
|
const vueRootElementsSet = new Set(["template", "style", "script"]);
|
2020-04-17 07:54:24 +09:00
|
|
|
function isVueCustomBlock(node, options) {
|
2020-06-17 19:18:29 +08:00
|
|
|
return isVueSfcBlock(node, options) && !vueRootElementsSet.has(node.fullName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isVueSfcBlock(node, options) {
|
2020-04-17 07:54:24 +09:00
|
|
|
return (
|
|
|
|
|
options.parser === "vue" &&
|
2025-10-11 23:23:34 +08:00
|
|
|
node.kind === "element" &&
|
|
|
|
|
node.parent.kind === "root" &&
|
2020-05-19 10:30:03 +09:00
|
|
|
node.fullName.toLowerCase() !== "html"
|
2020-04-17 07:54:24 +09:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-17 19:18:29 +08:00
|
|
|
function isVueNonHtmlBlock(node, options) {
|
|
|
|
|
return (
|
|
|
|
|
isVueSfcBlock(node, options) &&
|
|
|
|
|
(isVueCustomBlock(node, options) ||
|
|
|
|
|
(node.attrMap.lang && node.attrMap.lang !== "html"))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-13 09:05:30 +08:00
|
|
|
function isVueSlotAttribute(attribute) {
|
|
|
|
|
const attributeName = attribute.fullName;
|
|
|
|
|
return (
|
|
|
|
|
attributeName.charAt(0) === "#" ||
|
|
|
|
|
attributeName === "slot-scope" ||
|
|
|
|
|
attributeName === "v-slot" ||
|
|
|
|
|
attributeName.startsWith("v-slot:")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isVueSfcBindingsAttribute(attribute, options) {
|
|
|
|
|
const element = attribute.parent;
|
|
|
|
|
if (!isVueSfcBlock(element, options)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const tagName = element.fullName;
|
|
|
|
|
const attributeName = attribute.fullName;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
// https://github.com/vuejs/rfcs/blob/sfc-improvements/active-rfcs/0000-sfc-script-setup.md
|
|
|
|
|
(tagName === "script" && attributeName === "setup") ||
|
|
|
|
|
// https://github.com/vuejs/rfcs/blob/sfc-improvements/active-rfcs/0000-sfc-style-variables.md
|
|
|
|
|
(tagName === "style" && attributeName === "vars")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-08 16:32:20 +09:00
|
|
|
function getTextValueParts(node, value = node.value) {
|
|
|
|
|
return node.parent.isWhitespaceSensitive
|
|
|
|
|
? node.parent.isIndentationSensitive
|
2022-08-27 19:14:07 +08:00
|
|
|
? replaceEndOfLine(value)
|
|
|
|
|
: replaceEndOfLine(
|
2025-12-01 07:25:25 +08:00
|
|
|
htmlWhitespace.dedentString(htmlTrimPreserveIndentation(value)),
|
2021-09-08 16:32:20 +09:00
|
|
|
hardline,
|
|
|
|
|
)
|
2025-12-01 07:25:25 +08:00
|
|
|
: join(line, htmlWhitespace.split(value));
|
2021-09-08 16:32:20 +09:00
|
|
|
}
|
|
|
|
|
|
2022-04-17 13:12:19 +09:00
|
|
|
function isVueScriptTag(node, options) {
|
|
|
|
|
return isVueSfcBlock(node, options) && node.name === "script";
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-01 11:25:11 +08:00
|
|
|
function isAttributeValueQuoted(node) {
|
|
|
|
|
const { valueSpan, value } = node;
|
|
|
|
|
return valueSpan.end.offset - valueSpan.start.offset === value.length + 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function shouldUnquoteAttributeValue(node, options) {
|
|
|
|
|
if (isAttributeValueQuoted(node)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { value } = node;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
// Embedded HTML in JS: `` /* HTML */ `<my-element data-for={value}></my-element>` ``
|
2025-12-21 23:44:08 +08:00
|
|
|
/^PRETTIER_HTML_PLACEHOLDER_\d+_\d+_IN_JS$/.test(value) ||
|
2025-12-01 11:25:11 +08:00
|
|
|
// LWC `<div foo={value}></div>`
|
|
|
|
|
(options.parser === "lwc" && value.startsWith("{") && value.endsWith("}"))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-21 16:44:56 +08:00
|
|
|
export {
|
2018-11-04 23:03:07 +08:00
|
|
|
canHaveInterpolation,
|
2018-10-13 13:55:38 +08:00
|
|
|
forceBreakChildren,
|
|
|
|
|
forceBreakContent,
|
|
|
|
|
forceNextEmptyLine,
|
|
|
|
|
getLastDescendant,
|
2023-12-17 19:34:12 +00:00
|
|
|
getLeadingAndTrailingHtmlWhitespace,
|
2018-10-13 13:55:38 +08:00
|
|
|
getNodeCssStyleDisplay,
|
2023-12-17 19:34:12 +00:00
|
|
|
getTextValueParts,
|
|
|
|
|
getUnescapedAttributeValue,
|
2018-09-19 23:07:27 +08:00
|
|
|
hasPrettierIgnore,
|
2023-12-17 19:34:12 +00:00
|
|
|
htmlTrimPreserveIndentation,
|
2023-03-06 09:24:08 +08:00
|
|
|
inferElementParser,
|
2018-10-13 13:55:38 +08:00
|
|
|
isDanglingSpaceSensitiveNode,
|
2018-11-04 23:03:07 +08:00
|
|
|
isIndentationSensitiveNode,
|
2018-10-13 13:55:38 +08:00
|
|
|
isLeadingSpaceSensitiveNode,
|
2018-11-04 23:03:07 +08:00
|
|
|
isPreLikeNode,
|
2018-10-13 13:55:38 +08:00
|
|
|
isScriptLikeTag,
|
2018-11-23 21:25:58 +08:00
|
|
|
isTextLikeNode,
|
2018-10-13 13:55:38 +08:00
|
|
|
isTrailingSpaceSensitiveNode,
|
2023-12-17 19:34:12 +00:00
|
|
|
isVueCustomBlock,
|
|
|
|
|
isVueNonHtmlBlock,
|
|
|
|
|
isVueScriptTag,
|
|
|
|
|
isVueSfcBindingsAttribute,
|
|
|
|
|
isVueSfcBlock,
|
|
|
|
|
isVueSlotAttribute,
|
|
|
|
|
isWhitespaceSensitiveNode,
|
2018-10-13 13:55:38 +08:00
|
|
|
preferHardlineAsLeadingSpaces,
|
2018-12-05 11:15:09 +08:00
|
|
|
shouldPreserveContent,
|
2025-12-01 11:25:11 +08:00
|
|
|
shouldUnquoteAttributeValue,
|
2018-12-05 11:15:09 +08:00
|
|
|
unescapeQuoteEntities,
|
2018-09-19 23:07:27 +08:00
|
|
|
};
|