diff --git a/src/language-html/embed.js b/src/language-html/embed.js
index a9660ee4e..4566d76c1 100644
--- a/src/language-html/embed.js
+++ b/src/language-html/embed.js
@@ -28,17 +28,18 @@ import {
inferElementParser,
htmlTrimPreserveIndentation,
dedentString,
- unescapeQuoteEntities,
isVueSlotAttribute,
isVueSfcBindingsAttribute,
getTextValueParts,
+ getUnescapedAttributeValue,
} from "./utils/index.js";
+import isVueSfcWithTypescriptScript from "./utils/is-vue-sfc-with-typescript-script.js";
import getNodeContent from "./get-node-content.js";
-async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {
+async function printEmbeddedAttributeValue(path, htmlTextToDoc, options) {
+ const { node } = path;
const isKeyMatched = (patterns) =>
new RegExp(patterns.join("|")).test(node.fullName);
- const getValue = () => unescapeQuoteEntities(node.value);
let shouldHug = false;
@@ -77,40 +78,43 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {
__embeddedInHtml: true,
...opts,
});
+ const value = getUnescapedAttributeValue(node);
if (
node.fullName === "srcset" &&
(node.parent.fullName === "img" || node.parent.fullName === "source")
) {
- return printExpand(printImgSrcset(getValue()));
+ return printExpand(printImgSrcset(value));
}
- if (node.fullName === "class" && !options.parentParser) {
- const value = getValue();
- if (!value.includes("{{")) {
- return printClassNames(value);
- }
+ if (
+ node.fullName === "class" &&
+ !options.parentParser &&
+ !value.includes("{{")
+ ) {
+ return printClassNames(value);
}
- if (node.fullName === "style" && !options.parentParser) {
- const value = getValue();
- if (!value.includes("{{")) {
- return printExpand(
- await attributeTextToDoc(value, {
- parser: "css",
- __isHTMLStyleAttribute: true,
- })
- );
- }
+ if (
+ node.fullName === "style" &&
+ !options.parentParser &&
+ !value.includes("{{")
+ ) {
+ return printExpand(
+ await attributeTextToDoc(value, {
+ parser: "css",
+ __isHTMLStyleAttribute: true,
+ })
+ );
}
if (options.parser === "vue") {
if (node.fullName === "v-for") {
- return printVueFor(getValue(), attributeTextToDoc, options);
+ return printVueFor(path, attributeTextToDoc, options);
}
if (isVueSlotAttribute(node) || isVueSfcBindingsAttribute(node, options)) {
- return printVueBindings(getValue(), attributeTextToDoc, options);
+ return printVueBindings(path, attributeTextToDoc, options);
}
/**
@@ -131,12 +135,11 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {
const jsExpressionBindingPatterns = ["^v-"];
if (isKeyMatched(vueEventBindingPatterns)) {
- const value = getValue();
const parser = isVueEventBindingExpression(value)
- ? options.__should_parse_vue_template_with_ts
+ ? isVueSfcWithTypescriptScript(path, options)
? "__ts_expression"
: "__js_expression"
- : options.__should_parse_vue_template_with_ts
+ : isVueSfcWithTypescriptScript(path, options)
? "__vue_ts_event_binding"
: "__vue_event_binding";
return printMaybeHug(await attributeTextToDoc(value, { parser }));
@@ -144,8 +147,8 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {
if (isKeyMatched(vueExpressionBindingPatterns)) {
return printMaybeHug(
- await attributeTextToDoc(getValue(), {
- parser: options.__should_parse_vue_template_with_ts
+ await attributeTextToDoc(value, {
+ parser: isVueSfcWithTypescriptScript(path, options)
? "__vue_ts_expression"
: "__vue_expression",
})
@@ -154,8 +157,8 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {
if (isKeyMatched(jsExpressionBindingPatterns)) {
return printMaybeHug(
- await attributeTextToDoc(getValue(), {
- parser: options.__should_parse_vue_template_with_ts
+ await attributeTextToDoc(value, {
+ parser: isVueSfcWithTypescriptScript(path, options)
? "__ts_expression"
: "__js_expression",
})
@@ -196,33 +199,29 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {
const ngI18nPatterns = ["^i18n(-.+)?$"];
if (isKeyMatched(ngStatementBindingPatterns)) {
- return printMaybeHug(
- await ngTextToDoc(getValue(), { parser: "__ng_action" })
- );
+ return printMaybeHug(await ngTextToDoc(value, { parser: "__ng_action" }));
}
if (isKeyMatched(ngExpressionBindingPatterns)) {
return printMaybeHug(
- await ngTextToDoc(getValue(), { parser: "__ng_binding" })
+ await ngTextToDoc(value, { parser: "__ng_binding" })
);
}
if (isKeyMatched(ngI18nPatterns)) {
- const value = getValue().trim();
return printExpand(
- fill(getTextValueParts(node, value)),
+ fill(getTextValueParts(node, value.trim())),
!value.includes("@@")
);
}
if (isKeyMatched(ngDirectiveBindingPatterns)) {
return printMaybeHug(
- await ngTextToDoc(getValue(), { parser: "__ng_directive" })
+ await ngTextToDoc(value, { parser: "__ng_directive" })
);
}
const interpolationRegex = /{{(.+?)}}/s;
- const value = getValue();
if (interpolationRegex.test(value)) {
const parts = [];
for (const [index, part] of value.split(interpolationRegex).entries()) {
@@ -341,10 +340,12 @@ function embed(path, options) {
textToDocOptions.parser = "__ng_interpolation";
textToDocOptions.trailingComma = "none";
} else if (options.parser === "vue") {
- textToDocOptions.parser =
- options.__should_parse_vue_template_with_ts
- ? "__vue_ts_expression"
- : "__vue_expression";
+ textToDocOptions.parser = isVueSfcWithTypescriptScript(
+ path,
+ options
+ )
+ ? "__vue_ts_expression"
+ : "__vue_expression";
} else {
textToDocOptions.parser = "__js_expression";
}
@@ -394,7 +395,7 @@ function embed(path, options) {
return async (textToDoc) => {
const embeddedAttributeValueDoc = await printEmbeddedAttributeValue(
- node,
+ path,
(code, opts) =>
// strictly prefer single quote to avoid unnecessary html entity escape
textToDoc(code, {
diff --git a/src/language-html/print-preprocess.js b/src/language-html/print-preprocess.js
index efc418876..b16f869a5 100644
--- a/src/language-html/print-preprocess.js
+++ b/src/language-html/print-preprocess.js
@@ -9,7 +9,6 @@ import {
isLeadingSpaceSensitiveNode,
isTrailingSpaceSensitiveNode,
isWhitespaceSensitiveNode,
- isVueScriptTag,
} from "./utils/index.js";
const PREPROCESS_PIPELINE = [
@@ -23,7 +22,6 @@ const PREPROCESS_PIPELINE = [
addHasHtmComponentClosingTag,
addIsSpaceSensitive,
mergeSimpleElementIntoText,
- markTsScript,
];
function preprocess(ast, options) {
@@ -403,14 +401,4 @@ function addIsSpaceSensitive(ast, options) {
});
}
-function markTsScript(ast, options) {
- options.__should_parse_vue_template_with_ts =
- options.parser === "vue" &&
- ast.children.some(
- (child) =>
- isVueScriptTag(child, options) &&
- ["ts", "typescript"].includes(child.attrMap.lang)
- );
-}
-
export default preprocess;
diff --git a/src/language-html/syntax-vue.js b/src/language-html/syntax-vue.js
index b95118311..d56dc19b0 100644
--- a/src/language-html/syntax-vue.js
+++ b/src/language-html/syntax-vue.js
@@ -1,4 +1,7 @@
import { group } from "../document/builders.js";
+import { getUnescapedAttributeValue } from "./utils/index.js";
+import isVueSfcWithTypescriptScript from "./utils/is-vue-sfc-with-typescript-script.js";
+
/**
* @typedef {import("../document/builders.js").Doc} Doc
*/
@@ -9,14 +12,14 @@ import { group } from "../document/builders.js";
* v-for="(..., ...) in ..."
* v-for="(..., ...) of ..."
*
- * @param {*} value
* @param {(code: string, opts: *) => Doc} attributeTextToDoc
* @param {*} options
* @returns {Promise}
*/
-async function printVueFor(value, attributeTextToDoc, options) {
+async function printVueFor(path, attributeTextToDoc, options) {
+ const value = getUnescapedAttributeValue(path.node);
const { left, operator, right } = parseVueFor(value);
- const parseWithTs = options.__should_parse_vue_template_with_ts;
+ const parseWithTs = isVueSfcWithTypescriptScript(path, options);
return [
group(
await attributeTextToDoc(`function _(${left}) {}`, {
@@ -80,14 +83,14 @@ function parseVueFor(value) {
}
/**
- * @param {*} value
* @param {(code: string, opts: *) => Doc} attributeTextToDoc
* @param {*} options
* @returns {Doc}
*/
-function printVueBindings(value, attributeTextToDoc, options) {
+function printVueBindings(path, attributeTextToDoc, options) {
+ const value = getUnescapedAttributeValue(path.node);
return attributeTextToDoc(`function _(${value}) {}`, {
- parser: options.__should_parse_vue_template_with_ts ? "babel-ts" : "babel",
+ parser: isVueSfcWithTypescriptScript(path, options) ? "babel-ts" : "babel",
__isVueBindings: true,
});
}
diff --git a/src/language-html/utils/index.js b/src/language-html/utils/index.js
index b8ec26213..943ae6595 100644
--- a/src/language-html/utils/index.js
+++ b/src/language-html/utils/index.js
@@ -551,6 +551,10 @@ function unescapeQuoteEntities(text) {
return text.replaceAll("'", "'").replaceAll(""", '"');
}
+function getUnescapedAttributeValue(node) {
+ return unescapeQuoteEntities(node.value);
+}
+
// top-level elements (excluding ,