Skip to content

Commit dc4c425

Browse files
committed
feat: support object slots
1 parent 9551d0b commit dc4c425

File tree

7 files changed

+89
-8
lines changed

7 files changed

+89
-8
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,16 @@
124124
},
125125
"dependencies": {
126126
"@vue-macros/common": "^1.10.0",
127+
"html-tags": "^3.3.1",
128+
"svg-tags": "^1.0.0",
127129
"unplugin": "^1.5.1"
128130
},
129131
"devDependencies": {
130132
"@antfu/eslint-config": "^2.6.2",
131133
"@nuxt/kit": "^3.8.2",
132134
"@nuxt/schema": "^3.8.2",
133135
"@types/node": "^20.10.3",
136+
"@types/svg-tags": "^1.0.2",
134137
"bumpp": "^9.2.0",
135138
"chalk": "^5.3.0",
136139
"eslint": "^8.55.0",

playground/virtual-dom/App.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { ref } from 'vue'
33
import For from './for.vue'
44
import Bind from './bind.vue'
5+
import Slot from './slot.vue'
56
67
const count = ref(1)
78
function Comp({ icon, getChildren }: any, { slots }: any) {
@@ -217,6 +218,14 @@ defineRender((
217218
: null)
218219
}
219220
</div>
221+
222+
<div>
223+
v-slot:
224+
<Slot v-slot={scope}>
225+
default slot:
226+
{scope}
227+
</Slot>
228+
</div>
220229
</form>
221230
</>
222231
),

playground/virtual-dom/slot.vue

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="tsx">
2+
function Comp(_: any, { slots }: any) {
3+
return (
4+
<span>
5+
<template v-for={(Slot, slotName) in slots} key={slotName}>
6+
<slot name={slotName} />
7+
</template>
8+
</span>
9+
)
10+
}
11+
12+
const getSlots = () => ({ slots: { default: () => <span>default slot</span> } })
13+
defineRender(
14+
() => (
15+
<Comp>
16+
{getSlots().slots}
17+
</Comp>
18+
),
19+
)
20+
</script>

pnpm-lock.yaml

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/common.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
1-
import type { ArrowFunctionExpression, CallExpression, ConditionalExpression, Expression, FunctionExpression, LogicalExpression, Node } from '@babel/types'
1+
import type { ArrowFunctionExpression, CallExpression, ConditionalExpression, Expression, FunctionExpression, JSXElement, JSXFragment, JSXOpeningElement, LogicalExpression, Node } from '@babel/types'
22
import type { MagicString } from '@vue-macros/common'
3+
import htmlTags from 'html-tags'
4+
import svgTags from 'svg-tags'
5+
6+
export function isComponent(node: JSXOpeningElement) {
7+
if (node.name.type === 'JSXIdentifier') {
8+
const name = node.name.name
9+
return (
10+
!htmlTags.includes(name as htmlTags.htmlTags)
11+
&& !svgTags.includes(name)
12+
)
13+
}
14+
else {
15+
return node.name.type === 'JSXMemberExpression'
16+
}
17+
}
318

419
export function addAttribute(
520
node: Node,
@@ -58,7 +73,7 @@ export function isJSXExpression(node?: Node | null): boolean {
5873
)
5974
}
6075

61-
export function isJSXElement(node?: Node | null): boolean {
76+
export function isJSXElement(node?: Node | null): node is JSXElement | JSXFragment {
6277
return !!node && (
6378
node.type === 'JSXElement'
6479
|| node.type === 'JSXFragment'

src/core/transform.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { Node } from '@babel/types'
99
import type { Options } from '../types'
1010
import { transformVIf } from './v-if'
1111
import { transformVFor } from './v-for'
12-
import { isConditionalExpression, isFunctionExpression, isJSXElement, isJSXExpression, isLogicalExpression, isMapCallExpression } from './common'
12+
import { isComponent, isConditionalExpression, isFunctionExpression, isJSXElement, isJSXExpression, isLogicalExpression, isMapCallExpression } from './common'
1313

1414
export type RootNodes = {
1515
node: Node
@@ -124,8 +124,20 @@ export function transformVueJsxVapor(
124124
s.removeNode(node),
125125
)
126126
}
127+
else if (parent?.type === 'JSXElement'
128+
&& isComponent(parent.openingElement)
129+
&& parent.children.filter(child => s.sliceNode(child).trim()).length === 1
130+
) {
131+
rootNodes.unshift({
132+
node: node.expression,
133+
isAttributeValue: true,
134+
})
135+
s.prepend(`const _toSlots = s => (Object.prototype.toString.call(s) === '[object Object]' && !s?.__v_isVNode) ? s : { default: typeof s === 'function' ? s: () => s };`)
136+
s.overwrite(node.start!, node.expression.start!, `<template v-for="(slot, slotName) in _toSlots(`)
137+
s.overwrite(node.expression.end!, node.end!, `)" v-slot:[slotName]="scope" :key="slotName"><component :is="slot" v-bind="scope" /></template>`)
138+
}
127139
else if (!isJSXExpression(node.expression)) {
128-
s.overwrite(node.start!, node.start! + 1, '<component :is="__createTextVNode(')
140+
s.overwrite(node.start!, node.start! + 1, '<component :is="_resolveJSXExpression(')
129141
s.overwrite(node.end! - 1, node.end!, ')" />')
130142

131143
rootNodes.unshift({
@@ -158,7 +170,7 @@ export function transformVueJsxVapor(
158170
.replace('_cache', '_cache = []')
159171
.replaceAll(/_ctx\.(?!\$slots)/g, '')
160172
.replaceAll(/{ "v\d+\-bind": ([\s\S]*) }/g, '$1')
161-
.replaceAll(/_resolveComponent\("(.*)"\)/g, ($0, $1) => `(() => { try { return ${$1} } catch { return ${$0} } })()`)
173+
.replaceAll(/(?<!const )_component_(\w*)/g, ($0, $1) => `(() => { try { return ${$1.replaceAll(/(?<=\w)46(?=\w)/g, '.')} } catch { return ${$0} } })()`)
162174
return runtime === '"vue"' ? `(${code})()` : code
163175
}
164176
else {
@@ -184,7 +196,7 @@ export function transformVueJsxVapor(
184196
importSet.add('createTextVNode as _createTextVNode')
185197
importSet.add('toDisplayString as _toDisplayString')
186198
s.prepend(
187-
`const __createTextVNode = (node) => node?.__v_isVNode || typeof node === 'function' || (Array.isArray(node) && node[0]?.__v_isVNode) ? node : _createTextVNode(_toDisplayString(node));`,
199+
`const _resolveJSXExpression = (node) => node?.__v_isVNode || typeof node === 'function' || (Array.isArray(node) && node[0]?.__v_isVNode) ? node : _createTextVNode(_toDisplayString(node));`,
188200
)
189201
}
190202

src/core/v-if.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function transformVIf(
2828
s.remove(start!, consequent.start!)
2929
}
3030
else {
31-
overwrite(start, consequent.start!, `<template${directive}>${isJSXExpressionConsequent ? '' : '<component :is="__createTextVNode('}`, s)
31+
overwrite(start, consequent.start!, `<template${directive}>${isJSXExpressionConsequent ? '' : '<component :is="_resolveJSXExpression('}`, s)
3232
}
3333

3434
if (isJSXElement(alternate)) {
@@ -51,7 +51,7 @@ export function transformVIf(
5151
`${isJSXElementConsequent
5252
? ''
5353
: `${isJSXExpressionConsequent ? '' : ')" />'
54-
}</template>`}<template v-else>${isJSXExpressionAlternate ? '' : '<component :is="__createTextVNode('}`,
54+
}</template>`}<template v-else>${isJSXExpressionAlternate ? '' : '<component :is="_resolveJSXExpression('}`,
5555
s,
5656
)
5757
overwrite(alternate.end!, end, `${isJSXExpressionAlternate ? '' : ')" />'}</template>`, s)

0 commit comments

Comments
 (0)