Skip to content

Commit

Permalink
Format TypeScript expressions in Vue template attributes (prettier#14506
Browse files Browse the repository at this point in the history
)

Co-authored-by: fisker <lionkay@gmail.com>
  • Loading branch information
2 people authored and medikoo committed Feb 9, 2024
1 parent 5643efd commit 7afeafa
Show file tree
Hide file tree
Showing 23 changed files with 715 additions and 190 deletions.
22 changes: 22 additions & 0 deletions changelog_unreleased/vue/14506.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#### Format TypeScript expression in attribute bindings (#14506 by @seiyab)

<!-- prettier-ignore -->
```vue
<!-- Input -->
<script lang="ts"></script>
<template>
<comp :foo=" (a:string)=>1"/>
</template>
<!-- Prettier stable -->
<script lang="ts"></script>
<template>
<comp :foo=" (a:string)=>1" />
</template>
<!-- Prettier main -->
<script lang="ts"></script>
<template>
<comp :foo="(a: string) => 1" />
</template>
```
23 changes: 17 additions & 6 deletions src/language-html/embed.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {
rootNode &&
(rootNode.type === "ObjectExpression" ||
rootNode.type === "ArrayExpression" ||
(options.parser === "__vue_expression" &&
((options.parser === "__vue_expression" ||
options.parser === "__vue_ts_expression") &&
(rootNode.type === "TemplateLiteral" ||
rootNode.type === "StringLiteral")))
) {
Expand Down Expand Up @@ -105,11 +106,11 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {

if (options.parser === "vue") {
if (node.fullName === "v-for") {
return printVueFor(getValue(), attributeTextToDoc);
return printVueFor(getValue(), attributeTextToDoc, options);
}

if (isVueSlotAttribute(node) || isVueSfcBindingsAttribute(node, options)) {
return printVueBindings(getValue(), attributeTextToDoc);
return printVueBindings(getValue(), attributeTextToDoc, options);
}

/**
Expand All @@ -132,7 +133,9 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {
if (isKeyMatched(vueEventBindingPatterns)) {
const value = getValue();
const parser = isVueEventBindingExpression(value)
? "__js_expression"
? options.__should_parse_vue_template_with_ts
? "__ts_expression"
: "__js_expression"
: options.__should_parse_vue_template_with_ts
? "__vue_ts_event_binding"
: "__vue_event_binding";
Expand All @@ -141,13 +144,21 @@ async function printEmbeddedAttributeValue(node, htmlTextToDoc, options) {

if (isKeyMatched(vueExpressionBindingPatterns)) {
return printMaybeHug(
await attributeTextToDoc(getValue(), { parser: "__vue_expression" })
await attributeTextToDoc(getValue(), {
parser: options.__should_parse_vue_template_with_ts
? "__vue_ts_expression"
: "__vue_expression",
})
);
}

if (isKeyMatched(jsExpressionBindingPatterns)) {
return printMaybeHug(
await attributeTextToDoc(getValue(), { parser: "__js_expression" })
await attributeTextToDoc(getValue(), {
parser: options.__should_parse_vue_template_with_ts
? "__ts_expression"
: "__js_expression",
})
);
}
}
Expand Down
27 changes: 22 additions & 5 deletions src/language-html/syntax-vue.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import { group } from "../document/builders.js";
/**
* @typedef {import("../document/builders.js").Doc} Doc
*/

/**
* v-for="... in ..."
* v-for="... of ..."
* v-for="(..., ...) in ..."
* v-for="(..., ...) of ..."
*
* @param {*} value
* @param {(code: string, opts: *) => Doc} attributeTextToDoc
* @param {*} options
* @returns {Promise<Doc>}
*/
async function printVueFor(value, attributeTextToDoc) {
async function printVueFor(value, attributeTextToDoc, options) {
const { left, operator, right } = parseVueFor(value);
const parseWithTs = options.__should_parse_vue_template_with_ts;
return [
group(
await attributeTextToDoc(`function _(${left}) {}`, {
parser: "babel",
parser: parseWithTs ? "babel-ts" : "babel",
__isVueForBindingLeft: true,
})
),
" ",
operator,
" ",
await attributeTextToDoc(right, { parser: "__js_expression" }),
await attributeTextToDoc(right, {
parser: parseWithTs ? "__ts_expression" : "__js_expression",
}),
];
}

Expand Down Expand Up @@ -68,9 +79,15 @@ function parseVueFor(value) {
};
}

function printVueBindings(value, attributeTextToDoc) {
/**
* @param {*} value
* @param {(code: string, opts: *) => Doc} attributeTextToDoc
* @param {*} options
* @returns {Doc}
*/
function printVueBindings(value, attributeTextToDoc, options) {
return attributeTextToDoc(`function _(${value}) {}`, {
parser: "babel",
parser: options.__should_parse_vue_template_with_ts ? "babel-ts" : "babel",
__isVueBindings: true,
});
}
Expand Down
1 change: 1 addition & 0 deletions src/language-js/parse/babel.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ export default {
"babel-ts": babelTs,
/** @internal */
__js_expression: babelExpression,
__ts_expression: babelTSExpression,
/** for vue filter */
__vue_expression: babelExpression,
/** for vue filter written in TS */
Expand Down
3 changes: 2 additions & 1 deletion src/language-js/print/binaryish.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ const isBitwiseOrExpression = (node) =>

function isVueFilterSequenceExpression(path, options) {
return (
options.parser === "__vue_expression" &&
(options.parser === "__vue_expression" ||
options.parser === "__vue_ts_expression") &&
isBitwiseOrExpression(path.node) &&
!path.hasAncestor(
(node) => !isBitwiseOrExpression(node) && node.type !== "JsExpressionRoot"
Expand Down
1 change: 1 addition & 0 deletions src/main/comments/attach.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ function attachComments(ast, options) {
options.parser === "json" ||
options.parser === "json5" ||
options.parser === "__js_expression" ||
options.parser === "__ts_expression" ||
options.parser === "__vue_expression" ||
options.parser === "__vue_ts_expression"
) {
Expand Down
1 change: 1 addition & 0 deletions src/plugins/babel.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const parsers = createParsers([
"babel-flow",
"babel-ts",
"__js_expression",
"__ts_expression",
"__vue_expression",
"__vue_ts_expression",
"__vue_event_binding",
Expand Down
36 changes: 36 additions & 0 deletions tests/format/vue/ts-event-binding/__snapshots__/jsfmt.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,39 @@ function log(...args) { console.log(...args); }
================================================================================
`;
exports[`function-expression.vue format 1`] = `
====================================options=====================================
parsers: ["vue"]
printWidth: 80
| printWidth
=====================================input======================================
<script setup lang="ts"></script>
<template>
<div @click=" ( x : never) => null">arrow</div>
<div @click=" function( a : unknown[]) {
console.log( 'abcdefg');
return;
}">anonymous function</div>
</template>
=====================================output=====================================
<script setup lang="ts"></script>
<template>
<div @click="(x: never) => null">arrow</div>
<div
@click="
function (a: unknown[]) {
console.log('abcdefg');
return;
}
"
>
anonymous function
</div>
</template>
================================================================================
`;
9 changes: 9 additions & 0 deletions tests/format/vue/ts-event-binding/function-expression.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup lang="ts"></script>

<template>
<div @click=" ( x : never) => null">arrow</div>
<div @click=" function( a : unknown[]) {
console.log( 'abcdefg');
return;
}">anonymous function</div>
</template>

0 comments on commit 7afeafa

Please sign in to comment.