Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/pretty-paws-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@vue-macros/jsx-directive": patch
---

add lib option to support vue/vapor

4 changes: 4 additions & 0 deletions docs/features/jsx-directive.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ interface Options {
* @default 'v-'
*/
prefix?: string
/**
* @default 'vue'
*/
lib?: 'vue' | 'vue/vapor' | string
}
```

Expand Down
4 changes: 4 additions & 0 deletions docs/zh-CN/features/jsx-directive.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ interface Options {
* @default 'v-'
*/
prefix?: string
/**
* @default 'vue'
*/
lib?: 'vue' | 'vue/vapor' | string
}
```

Expand Down
14 changes: 7 additions & 7 deletions packages/jsx-directive/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
walkAST,
type CodeTransform,
} from '@vue-macros/common'
import type { OptionsResolved } from '..'
import { transformVFor } from './v-for'
import { transformVHtml } from './v-html'
import { transformVIf } from './v-if'
Expand All @@ -30,8 +31,7 @@ const onWithModifiersRegex = /^on[A-Z]\S*_\S+/
export function transformJsxDirective(
code: string,
id: string,
version: number,
prefix = 'v-',
options: OptionsResolved,
): CodeTransform | undefined {
const lang = getLang(id)

Expand All @@ -56,17 +56,17 @@ export function transformJsxDirective(
const s = new MagicStringAST(code)
for (const [ast, offset] of programs) {
s.offset = offset
transform(s, ast, version, prefix)
transform(s, ast, options)
}
return generateTransform(s, id)
}

function transform(
s: MagicStringAST,
program: Program,
version: number,
prefix = 'v-',
options: OptionsResolved,
) {
const { prefix, version } = options
const vIfMap = new Map<Node | null | undefined, JsxDirective[]>()
const vForNodes: JsxDirective[] = []
const vMemoNodes: (JsxDirective & {
Expand Down Expand Up @@ -212,12 +212,12 @@ function transform(
})

vIfMap.forEach((nodes) => transformVIf(nodes, s, version, prefix))
transformVFor(vForNodes, s, version)
transformVFor(vForNodes, s, options)
if (!version || version >= 3.2) transformVMemo(vMemoNodes, s, version)
transformVHtml(vHtmlNodes, s, version)
transformVOn(vOnNodes, s, version)
transformOnWithModifiers(onWithModifiers, s, version, prefix)
transformVSlot(vSlotMap, s, version, prefix)
transformVSlot(vSlotMap, s, options)
}

export function isVue2(version: number): boolean {
Expand Down
46 changes: 31 additions & 15 deletions packages/jsx-directive/src/core/v-for.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,15 @@ import {
importHelperFn,
type MagicStringAST,
} from '@vue-macros/common'
import type { OptionsResolved } from '..'
import { isVue2, type JsxDirective } from '.'

export function resolveVFor(
attribute: JsxDirective['attribute'],
{
s,
version,
vMemoAttribute,
}: {
s: MagicStringAST
version: number
vMemoAttribute?: JsxDirective['attribute']
},
node: JsxDirective['node'],
s: MagicStringAST,
{ lib, version }: OptionsResolved,
vMemoAttribute?: JsxDirective['attribute'],
): string {
if (attribute.value) {
let item, index, objectIndex, list
Expand All @@ -40,6 +36,10 @@ export function resolveVFor(
index ??= `${HELPER_PREFIX}index`
}

const params = `(${item}${
index ? `, ${index}` : ''
}${objectIndex ? `, ${objectIndex}` : ''})`

const renderList = isVue2(version)
? 'Array.from'
: importHelperFn(
Expand All @@ -49,9 +49,25 @@ export function resolveVFor(
version ? 'vue' : '@vue-macros/jsx-directive/helpers',
)

return `${renderList}(${list}, (${item}${
index ? `, ${index}` : ''
}${objectIndex ? `, ${objectIndex}` : ''}) => `
if (lib === 'vue/vapor') {
const key = node.openingElement.attributes.find(
(i) => i.type === 'JSXAttribute' && i.name.name === 'key',
)
if (
key?.type === 'JSXAttribute' &&
key.value?.type === 'JSXExpressionContainer' &&
key.value.expression
) {
s.appendLeft(
node.end!,
`, ${params} => (${s.sliceNode(key.value.expression)})`,
)
s.remove(key.start! - 1, key.end!)
}
return `${importHelperFn(s, 0, 'createFor', lib)}(() => ${list}, ${params} => `
} else {
return `${renderList}(${list}, ${params} => `
}
}
}

Expand All @@ -61,7 +77,7 @@ export function resolveVFor(
export function transformVFor(
nodes: JsxDirective[],
s: MagicStringAST,
version: number,
options: OptionsResolved,
): void {
if (nodes.length === 0) return

Expand All @@ -71,15 +87,15 @@ export function transformVFor(
)
s.appendLeft(
node.start!,
`${hasScope ? '{' : ''}${resolveVFor(attribute, { s, version, vMemoAttribute })}`,
`${hasScope ? '{' : ''}${resolveVFor(attribute, node, s, options, vMemoAttribute)}`,
)

const isTemplate =
node.type === 'JSXElement' &&
node.openingElement.name.type === 'JSXIdentifier' &&
node.openingElement.name.name === 'template'
if (isTemplate && node.closingElement) {
const content = isVue2(version) ? 'span' : ''
const content = isVue2(options.version) ? 'span' : ''
s.overwriteNode(node.openingElement.name, content)
s.overwriteNode(node.closingElement.name, content)
}
Expand Down
7 changes: 4 additions & 3 deletions packages/jsx-directive/src/core/v-slot.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { importHelperFn, type MagicStringAST } from '@vue-macros/common'
import type { OptionsResolved } from '..'
import { resolveVFor } from './v-for'
import { isVue2 } from '.'
import type { JSXAttribute, JSXElement, Node } from '@babel/types'
Expand All @@ -21,9 +22,9 @@ export type VSlotMap = Map<
export function transformVSlot(
nodeMap: VSlotMap,
s: MagicStringAST,
version: number,
prefix: string,
options: OptionsResolved,
): void {
const { version, prefix } = options
Array.from(nodeMap)
.reverse()
.forEach(([node, { attributeMap, vSlotAttribute }]) => {
Expand Down Expand Up @@ -52,7 +53,7 @@ export function transformVSlot(
if (vForAttribute) {
result.push(
'...Object.fromEntries(',
resolveVFor(vForAttribute, { s, version }),
resolveVFor(vForAttribute, node, s, { ...options, lib: 'vue' }),
'([',
)
}
Expand Down
14 changes: 11 additions & 3 deletions packages/jsx-directive/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ import {
} from 'unplugin'
import { transformJsxDirective } from './core'

export type Options = BaseOptions & { prefix?: string }
export type OptionsResolved = MarkRequired<Options, 'version'>
export type Options = BaseOptions & {
prefix?: string
lib?: 'vue' | 'vue/vapor' | (string & {})
}
export type OptionsResolved = MarkRequired<
Options,
'version' | 'prefix' | 'lib'
>

function resolveOptions(
options: Options,
Expand All @@ -32,6 +38,8 @@ function resolveOptions(
include,
exclude: [REGEX_NODE_MODULES, REGEX_SETUP_SFC],
...options,
prefix: options.prefix ?? 'v-',
lib: options.lib ?? 'vue',
version,
}
}
Expand All @@ -49,7 +57,7 @@ const plugin: UnpluginInstance<Options | undefined, false> = createUnplugin(

transformInclude: filter,
transform(code, id) {
return transformJsxDirective(code, id, options.version, options.prefix)
return transformJsxDirective(code, id, options)
},
}
},
Expand Down
45 changes: 45 additions & 0 deletions packages/jsx-directive/tests/__snapshots__/v-for.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,48 @@ defineRender(() => (
</script>
"
`;

exports[`jsx-vue-directive > vue/vapor v-for > ./fixtures/v-for/index.vue 1`] = `
"<script setup lang="tsx">
import { renderList as __MACROS_renderList } from "vue";
import { createFor as __MACROS_createFor } from "vue/vapor";
const map = new Map([
[1, '2'],
[3, '4'],
])
const set = new Set(['1', '2', '3'])
const object = { id: 1, name: 'admin' }
let selected = 0

defineRender(() => (
<>
{__MACROS_createFor(() => 4, (i) => <div>
<div>{i}</div>
</div>, (i) => (i))}

{__MACROS_createFor(() => object, (value, key, index) => <div>
{key}: {value}
</div>, (value, key, index) => (index))}

{__MACROS_createFor(() => [1, 2, 3][Symbol.iterator](), (i, index) => <div>
<div>{i}</div>
</div>, (i, index) => (index))}

{__MACROS_createFor(() => [1, 2, 3], (i, __MACROS_index) => <div v-memo={[selected === i]}>
<div>{i}</div>
</div>, (i, __MACROS_index) => (i))}

{(set) ? __MACROS_createFor(() => set, (i) => <div>
<div>{i}</div>
</div>, (i) => (i)) : null}

{__MACROS_createFor(() => map, ([key, value], index) => <div>
<div>
{key}: {value}
</div>
</div>, ([key, value], index) => (index))}
</>
))
</script>
"
`;
30 changes: 28 additions & 2 deletions packages/jsx-directive/tests/v-for.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ describe('jsx-vue-directive', () => {
query: '?raw',
import: 'default',
}),
(_, id, code) => transformJsxDirective(code, id, 2.7)?.code,
(_, id, code) =>
transformJsxDirective(code, id, {
version: 2.7,
lib: 'vue',
prefix: 'v-',
})?.code,
)
})

Expand All @@ -21,7 +26,28 @@ describe('jsx-vue-directive', () => {
query: '?raw',
import: 'default',
}),
(_, id, code) => transformJsxDirective(code, id, 3)?.code,
(_, id, code) =>
transformJsxDirective(code, id, {
version: 3,
lib: 'vue',
prefix: 'v-',
})?.code,
)
})

describe('vue/vapor v-for', async () => {
await testFixtures(
import.meta.glob<string>('./fixtures/v-for/*.{vue,jsx,tsx}', {
eager: true,
query: '?raw',
import: 'default',
}),
(_, id, code) =>
transformJsxDirective(code, id, {
version: 3,
lib: 'vue/vapor',
prefix: 'v-',
})?.code,
)
})
})
14 changes: 12 additions & 2 deletions packages/jsx-directive/tests/v-html.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ describe('jsx-vue-directive', () => {
query: '?raw',
import: 'default',
}),
(_, id, code) => transformJsxDirective(code, id, 3)?.code,
(_, id, code) =>
transformJsxDirective(code, id, {
version: 3,
lib: 'vue',
prefix: 'v-',
})?.code,
)
})

Expand All @@ -21,7 +26,12 @@ describe('jsx-vue-directive', () => {
query: '?raw',
import: 'default',
}),
(_, id, code) => transformJsxDirective(code, id, 2.7)?.code,
(_, id, code) =>
transformJsxDirective(code, id, {
version: 2.7,
lib: 'vue',
prefix: 'v-',
})?.code,
)
})
})
7 changes: 6 additions & 1 deletion packages/jsx-directive/tests/v-if.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ describe('jsx-vue-directive', () => {
query: '?raw',
import: 'default',
}),
(_, id, code) => transformJsxDirective(code, id, 2.7)?.code,
(_, id, code) =>
transformJsxDirective(code, id, {
version: 3,
lib: 'vue',
prefix: 'v-',
})?.code,
)
})
})
7 changes: 6 additions & 1 deletion packages/jsx-directive/tests/v-memo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ describe('jsx-vue-directive', () => {
query: '?raw',
import: 'default',
}),
(_, id, code) => transformJsxDirective(code, id, 3.2)?.code,
(_, id, code) =>
transformJsxDirective(code, id, {
version: 3.2,
lib: 'vue',
prefix: 'v-',
})?.code,
)
})
})
Loading
Loading