Skip to content

Commit ac5494c

Browse files
authored
feat(jsx-directive): add v-on directive with object syntax (#578)
1 parent 3915dbf commit ac5494c

File tree

19 files changed

+619
-475
lines changed

19 files changed

+619
-475
lines changed

.changeset/quiet-bottles-fold.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@vue-macros/jsx-directive': minor
3+
'@vue-macros/volar': minor
4+
---
5+
6+
add `v-on` directive with object syntax

docs/features/jsx-directive.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,18 @@ Vue built-in directives for JSX.
1010
| `v-else-if` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
1111
| `v-else` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
1212
| `v-for` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
13+
| `v-on` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
1314
| `v-slot` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
1415
| `v-html` | :white_check_mark: | :white_check_mark: | / |
1516
| `v-once` | :white_check_mark: | :x: | / |
1617
| `v-memo` | :white_check_mark: | :x: | / |
1718

19+
::: warning
20+
21+
`v-on` only supports binding to an object of event / listener pairs without an argument.
22+
23+
:::
24+
1825
## Usage
1926

2027
```vue
@@ -38,7 +45,9 @@ defineRender(() => (
3845
{i}
3946
</div>
4047
41-
<Child v-slot={props}>{props}</Child>
48+
<Child v-on={{ submit: () => {} }} v-slot={props}>
49+
{props}
50+
</Child>
4251
</>
4352
))
4453
</script>

docs/zh-CN/features/jsx-directive.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@
1010
| `v-else-if` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
1111
| `v-else` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
1212
| `v-for` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
13+
| `v-on` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
1314
| `v-slot` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
1415
| `v-html` | :white_check_mark: | :white_check_mark: | / |
1516
| `v-once` | :white_check_mark: | :x: | / |
1617
| `v-memo` | :white_check_mark: | :x: | / |
1718

18-
## Usage
19+
::: warning
20+
21+
`v-on` 仅支持绑定不带参数的事件/监听器对的对象。
22+
23+
:::
24+
25+
## 用法
1926

2027
```vue
2128
<script setup lang="tsx">
@@ -38,13 +45,15 @@ defineRender(() => (
3845
{i}
3946
</div>
4047
41-
<Child v-slot={props}>{props}</Child>
48+
<Child v-on={{ submit: () => {} }} v-slot={props}>
49+
{props}
50+
</Child>
4251
</>
4352
))
4453
</script>
4554
```
4655

47-
## Volar Configuration
56+
## Volar 配置
4857

4958
```jsonc {6}
5059
// tsconfig.json

packages/jsx-directive/src/core/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { transformVFor } from './v-for'
1212
import { transformVMemo } from './v-memo'
1313
import { transformVHtml } from './v-html'
1414
import { transformVSlot } from './v-slot'
15+
import { transformVOn } from './v-on'
1516
import type { JSXAttribute, JSXElement, Node, Program } from '@babel/types'
1617

1718
export type JsxDirectiveNode = {
@@ -51,7 +52,9 @@ export function transformJsxDirective(
5152

5253
const s = new MagicString(code)
5354
for (const { ast, offset } of asts) {
54-
if (!/\sv-(if|for|memo|once|html|slot)/.test(s.sliceNode(ast, { offset })))
55+
if (
56+
!/\sv-(if|for|memo|once|html|slot|on)/.test(s.sliceNode(ast, { offset }))
57+
)
5558
continue
5659

5760
const vIfMap = new Map<Node, JsxDirectiveNode[]>()
@@ -61,6 +64,7 @@ export function transformJsxDirective(
6164
})[] = []
6265
const vHtmlNodes: JsxDirectiveNode[] = []
6366
const vSlotSet = new Set<JSXElement>()
67+
const vOnNodes: JsxDirectiveNode[] = []
6468
walkAST<Node>(ast, {
6569
enter(node, parent) {
6670
if (node.type !== 'JSXElement') return
@@ -97,6 +101,12 @@ export function transformJsxDirective(
97101
: node,
98102
)
99103
}
104+
if (attribute.name.name === 'v-on') {
105+
vOnNodes.push({
106+
node,
107+
attribute,
108+
})
109+
}
100110
}
101111

102112
if (vIfAttribute) {
@@ -131,6 +141,7 @@ export function transformJsxDirective(
131141
version >= 3.2 && transformVMemo(vMemoNodes, s, offset)
132142
transformVHtml(vHtmlNodes, s, offset, version)
133143
transformVSlot(Array.from(vSlotSet), s, offset, version)
144+
transformVOn(vOnNodes, s, offset, version)
134145
}
135146

136147
return generateTransform(s, id)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { HELPER_PREFIX, type MagicString } from '@vue-macros/common'
2+
import type { JsxDirectiveNode } from '.'
3+
4+
export function transformVOn(
5+
nodes: JsxDirectiveNode[],
6+
s: MagicString,
7+
offset: number,
8+
version: number,
9+
) {
10+
if (nodes.length > 0 && version >= 3)
11+
s.prependRight(
12+
offset,
13+
`const ${HELPER_PREFIX}transformVOn = (obj) => Object.entries(obj).reduce((res, [key, value]) => (res['on' + key[0].toUpperCase() + key.slice(1)] = value, res), {})`,
14+
)
15+
16+
nodes.forEach(({ attribute }) => {
17+
if (version < 3) {
18+
s.remove(attribute.start! + offset, attribute.start! + offset + 2)
19+
return
20+
}
21+
22+
s.overwriteNode(
23+
attribute,
24+
`{...${HELPER_PREFIX}transformVOn(${s.slice(
25+
attribute.value!.start! + offset + 1,
26+
attribute.value!.end! + offset - 1,
27+
)})}`,
28+
{ offset },
29+
)
30+
})
31+
}

packages/jsx-directive/src/core/v-slot.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@ export function transformVSlot(
106106
if (attribute) {
107107
s.overwriteNode(attribute, result, { offset })
108108
} else {
109-
s.appendLeft(node.openingElement.end! + offset - 1, ` ${result}`)
109+
s.overwrite(
110+
node.openingElement.end! + offset - 1,
111+
node.openingElement.end! + offset,
112+
` ${result}>`,
113+
)
110114
}
111115
})
112116
}

0 commit comments

Comments
 (0)