From bae1eb4a14a019c3fe4d13465fdef014d0af648b Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Mon, 27 Mar 2023 14:22:54 +0800 Subject: [PATCH] Move ts script check into `embed` print (#14559) --- src/language-html/embed.js | 83 ++++++++++--------- src/language-html/print-preprocess.js | 12 --- src/language-html/syntax-vue.js | 15 ++-- src/language-html/utils/index.js | 5 ++ .../is-vue-sfc-with-typescript-script.js | 20 +++++ 5 files changed, 76 insertions(+), 59 deletions(-) create mode 100644 src/language-html/utils/is-vue-sfc-with-typescript-script.js 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