Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.

Commit 2b0def3

Browse files
LittleSoundsxzz
andauthoredMay 2, 2024
feat(compiler-vapor): slot outlet (#182)
Co-authored-by: 三咲智子 Kevin Deng <[email protected]>
1 parent bfb5250 commit 2b0def3

File tree

11 files changed

+585
-6
lines changed

11 files changed

+585
-6
lines changed
 
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`compiler: transform <slot> outlets > default slot outlet 1`] = `
4+
"import { createSlot as _createSlot } from 'vue/vapor';
5+
6+
export function render(_ctx) {
7+
const n0 = _createSlot("default", null)
8+
return n0
9+
}"
10+
`;
11+
12+
exports[`compiler: transform <slot> outlets > default slot outlet with fallback 1`] = `
13+
"import { createSlot as _createSlot, template as _template } from 'vue/vapor';
14+
const t0 = _template("<div></div>")
15+
16+
export function render(_ctx) {
17+
const n0 = _createSlot("default", null, () => {
18+
const n2 = t0()
19+
return n2
20+
})
21+
return n0
22+
}"
23+
`;
24+
25+
exports[`compiler: transform <slot> outlets > default slot outlet with props 1`] = `
26+
"import { createSlot as _createSlot } from 'vue/vapor';
27+
28+
export function render(_ctx) {
29+
const n0 = _createSlot("default", [
30+
{
31+
foo: () => ("bar"),
32+
baz: () => (_ctx.qux),
33+
fooBar: () => (_ctx.foo-_ctx.bar)
34+
}
35+
])
36+
return n0
37+
}"
38+
`;
39+
40+
exports[`compiler: transform <slot> outlets > dynamically named slot outlet 1`] = `
41+
"import { createSlot as _createSlot } from 'vue/vapor';
42+
43+
export function render(_ctx) {
44+
const n0 = _createSlot(() => (_ctx.foo + _ctx.bar), null)
45+
return n0
46+
}"
47+
`;
48+
49+
exports[`compiler: transform <slot> outlets > dynamically named slot outlet with v-bind shorthand 1`] = `
50+
"import { createSlot as _createSlot } from 'vue/vapor';
51+
52+
export function render(_ctx) {
53+
const n0 = _createSlot(() => (_ctx.name), null)
54+
return n0
55+
}"
56+
`;
57+
58+
exports[`compiler: transform <slot> outlets > error on unexpected custom directive on <slot> 1`] = `
59+
"import { createSlot as _createSlot } from 'vue/vapor';
60+
61+
export function render(_ctx) {
62+
const n0 = _createSlot("default", null)
63+
return n0
64+
}"
65+
`;
66+
67+
exports[`compiler: transform <slot> outlets > error on unexpected custom directive with v-show on <slot> 1`] = `
68+
"import { createSlot as _createSlot } from 'vue/vapor';
69+
70+
export function render(_ctx) {
71+
const n0 = _createSlot("default", null)
72+
return n0
73+
}"
74+
`;
75+
76+
exports[`compiler: transform <slot> outlets > named slot outlet with fallback 1`] = `
77+
"import { createSlot as _createSlot, template as _template } from 'vue/vapor';
78+
const t0 = _template("<div></div>")
79+
80+
export function render(_ctx) {
81+
const n0 = _createSlot("foo", null, () => {
82+
const n2 = t0()
83+
return n2
84+
})
85+
return n0
86+
}"
87+
`;
88+
89+
exports[`compiler: transform <slot> outlets > statically named slot outlet 1`] = `
90+
"import { createSlot as _createSlot } from 'vue/vapor';
91+
92+
export function render(_ctx) {
93+
const n0 = _createSlot("foo", null)
94+
return n0
95+
}"
96+
`;
97+
98+
exports[`compiler: transform <slot> outlets > statically named slot outlet with props 1`] = `
99+
"import { createSlot as _createSlot } from 'vue/vapor';
100+
101+
export function render(_ctx) {
102+
const n0 = _createSlot("foo", [
103+
{
104+
foo: () => ("bar"),
105+
baz: () => (_ctx.qux)
106+
}
107+
])
108+
return n0
109+
}"
110+
`;
111+
112+
exports[`compiler: transform <slot> outlets > statically named slot outlet with v-bind="obj" 1`] = `
113+
"import { createSlot as _createSlot } from 'vue/vapor';
114+
115+
export function render(_ctx) {
116+
const n0 = _createSlot("foo", [
117+
{ foo: () => ("bar") },
118+
() => (_ctx.obj),
119+
{ baz: () => (_ctx.qux) }
120+
])
121+
return n0
122+
}"
123+
`;
124+
125+
exports[`compiler: transform <slot> outlets > statically named slot outlet with v-on 1`] = `
126+
"import { createSlot as _createSlot, toHandlers as _toHandlers } from 'vue/vapor';
127+
128+
export function render(_ctx) {
129+
const n0 = _createSlot("default", [
130+
{ onClick: () => _ctx.foo },
131+
() => (_toHandlers(_ctx.bar)),
132+
{ baz: () => (_ctx.qux) }
133+
])
134+
return n0
135+
}"
136+
`;
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
2+
import {
3+
IRNodeTypes,
4+
transformChildren,
5+
transformElement,
6+
transformSlotOutlet,
7+
transformText,
8+
transformVBind,
9+
transformVOn,
10+
transformVShow,
11+
} from '../../src'
12+
import { makeCompile } from './_utils'
13+
14+
const compileWithSlotsOutlet = makeCompile({
15+
nodeTransforms: [
16+
transformText,
17+
transformSlotOutlet,
18+
transformElement,
19+
transformChildren,
20+
],
21+
directiveTransforms: {
22+
bind: transformVBind,
23+
on: transformVOn,
24+
show: transformVShow,
25+
},
26+
})
27+
28+
describe('compiler: transform <slot> outlets', () => {
29+
test('default slot outlet', () => {
30+
const { ir, code, vaporHelpers } = compileWithSlotsOutlet(`<slot />`)
31+
expect(code).toMatchSnapshot()
32+
expect(vaporHelpers).toContain('createSlot')
33+
expect(ir.block.effect).toEqual([])
34+
expect(ir.block.operation).toMatchObject([
35+
{
36+
type: IRNodeTypes.SLOT_OUTLET_NODE,
37+
id: 0,
38+
name: {
39+
type: NodeTypes.SIMPLE_EXPRESSION,
40+
content: 'default',
41+
isStatic: true,
42+
},
43+
props: [],
44+
fallback: undefined,
45+
},
46+
])
47+
})
48+
49+
test('statically named slot outlet', () => {
50+
const { ir, code } = compileWithSlotsOutlet(`<slot name="foo" />`)
51+
expect(code).toMatchSnapshot()
52+
expect(ir.block.operation).toMatchObject([
53+
{
54+
type: IRNodeTypes.SLOT_OUTLET_NODE,
55+
id: 0,
56+
name: {
57+
type: NodeTypes.SIMPLE_EXPRESSION,
58+
content: 'foo',
59+
isStatic: true,
60+
},
61+
},
62+
])
63+
})
64+
65+
test('dynamically named slot outlet', () => {
66+
const { ir, code } = compileWithSlotsOutlet(`<slot :name="foo + bar" />`)
67+
expect(code).toMatchSnapshot()
68+
expect(ir.block.operation).toMatchObject([
69+
{
70+
type: IRNodeTypes.SLOT_OUTLET_NODE,
71+
id: 0,
72+
name: {
73+
type: NodeTypes.SIMPLE_EXPRESSION,
74+
content: 'foo + bar',
75+
isStatic: false,
76+
},
77+
},
78+
])
79+
})
80+
81+
test('dynamically named slot outlet with v-bind shorthand', () => {
82+
const { ir, code } = compileWithSlotsOutlet(`<slot :name />`)
83+
expect(code).toMatchSnapshot()
84+
expect(ir.block.operation).toMatchObject([
85+
{
86+
type: IRNodeTypes.SLOT_OUTLET_NODE,
87+
id: 0,
88+
name: {
89+
type: NodeTypes.SIMPLE_EXPRESSION,
90+
content: 'name',
91+
isStatic: false,
92+
},
93+
},
94+
])
95+
})
96+
97+
test('default slot outlet with props', () => {
98+
const { ir, code } = compileWithSlotsOutlet(
99+
`<slot foo="bar" :baz="qux" :foo-bar="foo-bar" />`,
100+
)
101+
expect(code).toMatchSnapshot()
102+
expect(ir.block.operation).toMatchObject([
103+
{
104+
type: IRNodeTypes.SLOT_OUTLET_NODE,
105+
name: { content: 'default' },
106+
props: [
107+
[
108+
{ key: { content: 'foo' }, values: [{ content: 'bar' }] },
109+
{ key: { content: 'baz' }, values: [{ content: 'qux' }] },
110+
{ key: { content: 'fooBar' }, values: [{ content: 'foo-bar' }] },
111+
],
112+
],
113+
},
114+
])
115+
})
116+
117+
test('statically named slot outlet with props', () => {
118+
const { ir, code } = compileWithSlotsOutlet(
119+
`<slot name="foo" foo="bar" :baz="qux" />`,
120+
)
121+
expect(code).toMatchSnapshot()
122+
expect(ir.block.operation).toMatchObject([
123+
{
124+
type: IRNodeTypes.SLOT_OUTLET_NODE,
125+
name: { content: 'foo' },
126+
props: [
127+
[
128+
{ key: { content: 'foo' }, values: [{ content: 'bar' }] },
129+
{ key: { content: 'baz' }, values: [{ content: 'qux' }] },
130+
],
131+
],
132+
},
133+
])
134+
})
135+
136+
test('statically named slot outlet with v-bind="obj"', () => {
137+
const { ir, code } = compileWithSlotsOutlet(
138+
`<slot name="foo" foo="bar" v-bind="obj" :baz="qux" />`,
139+
)
140+
expect(code).toMatchSnapshot()
141+
expect(ir.block.operation).toMatchObject([
142+
{
143+
type: IRNodeTypes.SLOT_OUTLET_NODE,
144+
name: { content: 'foo' },
145+
props: [
146+
[{ key: { content: 'foo' }, values: [{ content: 'bar' }] }],
147+
{ value: { content: 'obj', isStatic: false } },
148+
[{ key: { content: 'baz' }, values: [{ content: 'qux' }] }],
149+
],
150+
},
151+
])
152+
})
153+
154+
test('statically named slot outlet with v-on', () => {
155+
const { ir, code } = compileWithSlotsOutlet(
156+
`<slot @click="foo" v-on="bar" :baz="qux" />`,
157+
)
158+
expect(code).toMatchSnapshot()
159+
expect(ir.block.operation).toMatchObject([
160+
{
161+
type: IRNodeTypes.SLOT_OUTLET_NODE,
162+
props: [
163+
[{ key: { content: 'click' }, values: [{ content: 'foo' }] }],
164+
{ value: { content: 'bar' }, handler: true },
165+
[{ key: { content: 'baz' }, values: [{ content: 'qux' }] }],
166+
],
167+
},
168+
])
169+
})
170+
171+
test('default slot outlet with fallback', () => {
172+
const { ir, code } = compileWithSlotsOutlet(`<slot><div/></slot>`)
173+
expect(code).toMatchSnapshot()
174+
expect(ir.template[0]).toMatchObject('<div></div>')
175+
expect(ir.block.operation).toMatchObject([
176+
{
177+
type: IRNodeTypes.SLOT_OUTLET_NODE,
178+
id: 0,
179+
name: { content: 'default' },
180+
fallback: {
181+
type: IRNodeTypes.BLOCK,
182+
dynamic: {
183+
children: [{ template: 0, id: 2 }],
184+
},
185+
returns: [2],
186+
},
187+
},
188+
])
189+
})
190+
191+
test('named slot outlet with fallback', () => {
192+
const { ir, code } = compileWithSlotsOutlet(
193+
`<slot name="foo"><div/></slot>`,
194+
)
195+
expect(code).toMatchSnapshot()
196+
expect(ir.template[0]).toMatchObject('<div></div>')
197+
expect(ir.block.operation).toMatchObject([
198+
{
199+
type: IRNodeTypes.SLOT_OUTLET_NODE,
200+
id: 0,
201+
name: { content: 'foo' },
202+
fallback: {
203+
type: IRNodeTypes.BLOCK,
204+
dynamic: {
205+
children: [{ template: 0, id: 2 }],
206+
},
207+
returns: [2],
208+
},
209+
},
210+
])
211+
})
212+
213+
test('error on unexpected custom directive on <slot>', () => {
214+
const onError = vi.fn()
215+
const source = `<slot v-foo />`
216+
const index = source.indexOf('v-foo')
217+
const { code } = compileWithSlotsOutlet(source, { onError })
218+
expect(code).toMatchSnapshot()
219+
expect(onError.mock.calls[0][0]).toMatchObject({
220+
code: ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
221+
loc: {
222+
start: {
223+
offset: index,
224+
line: 1,
225+
column: index + 1,
226+
},
227+
end: {
228+
offset: index + 5,
229+
line: 1,
230+
column: index + 6,
231+
},
232+
},
233+
})
234+
})
235+
236+
test('error on unexpected custom directive with v-show on <slot>', () => {
237+
const onError = vi.fn()
238+
const source = `<slot v-show="ok" />`
239+
const index = source.indexOf('v-show="ok"')
240+
const { code } = compileWithSlotsOutlet(source, { onError })
241+
expect(code).toMatchSnapshot()
242+
expect(onError.mock.calls[0][0]).toMatchObject({
243+
code: ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
244+
loc: {
245+
start: {
246+
offset: index,
247+
line: 1,
248+
column: index + 1,
249+
},
250+
end: {
251+
offset: index + 11,
252+
line: 1,
253+
column: index + 12,
254+
},
255+
},
256+
})
257+
})
258+
})

‎packages/compiler-vapor/src/compile.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { transformVModel } from './transforms/vModel'
2727
import { transformVIf } from './transforms/vIf'
2828
import { transformVFor } from './transforms/vFor'
2929
import { transformComment } from './transforms/transformComment'
30+
import { transformSlotOutlet } from './transforms/transformSlotOutlet'
3031
import type { HackOptions } from './ir'
3132

3233
export { wrapTemplate } from './transforms/utils'
@@ -103,6 +104,7 @@ export function getBaseTransformPreset(
103104
transformOnce,
104105
transformVIf,
105106
transformVFor,
107+
transformSlotOutlet,
106108
transformTemplateRef,
107109
transformText,
108110
transformElement,

‎packages/compiler-vapor/src/generators/operation.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
buildCodeFragment,
1818
} from './utils'
1919
import { genCreateComponent } from './component'
20+
import { genSlotOutlet } from './slotOutlet'
2021

2122
export function genOperations(opers: OperationNode[], context: CodegenContext) {
2223
const [frag, push] = buildCodeFragment()
@@ -61,6 +62,8 @@ export function genOperation(
6162
return genCreateComponent(oper, context)
6263
case IRNodeTypes.DECLARE_OLD_REF:
6364
return genDeclareOldRef(oper)
65+
case IRNodeTypes.SLOT_OUTLET_NODE:
66+
return genSlotOutlet(oper, context)
6467
}
6568

6669
return []
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { CodegenContext } from '../generate'
2+
import type { SlotOutletIRNode } from '../ir'
3+
import { genBlock } from './block'
4+
import { genExpression } from './expression'
5+
import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils'
6+
import { genRawProps } from './component'
7+
8+
export function genSlotOutlet(oper: SlotOutletIRNode, context: CodegenContext) {
9+
const { vaporHelper } = context
10+
const { id, name, fallback } = oper
11+
const [frag, push] = buildCodeFragment()
12+
13+
const nameExpr = name.isStatic
14+
? genExpression(name, context)
15+
: ['() => (', ...genExpression(name, context), ')']
16+
17+
let fallbackArg: CodeFragment[] | undefined
18+
if (fallback) {
19+
fallbackArg = genBlock(fallback, context)
20+
}
21+
22+
push(
23+
NEWLINE,
24+
`const n${id} = `,
25+
...genCall(
26+
vaporHelper('createSlot'),
27+
nameExpr,
28+
genRawProps(oper.props, context) || 'null',
29+
fallbackArg,
30+
),
31+
)
32+
33+
return frag
34+
}

‎packages/compiler-vapor/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ export { transformVIf } from './transforms/vIf'
4848
export { transformVFor } from './transforms/vFor'
4949
export { transformVModel } from './transforms/vModel'
5050
export { transformComment } from './transforms/transformComment'
51+
export { transformSlotOutlet } from './transforms/transformSlotOutlet'

‎packages/compiler-vapor/src/ir.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export enum IRNodeTypes {
3030
PREPEND_NODE,
3131
CREATE_TEXT_NODE,
3232
CREATE_COMPONENT_NODE,
33+
SLOT_OUTLET_NODE,
3334

3435
WITH_DIRECTIVE,
3536
DECLARE_OLD_REF, // consider make it more general
@@ -214,6 +215,14 @@ export interface DeclareOldRefIRNode extends BaseIRNode {
214215
id: number
215216
}
216217

218+
export interface SlotOutletIRNode extends BaseIRNode {
219+
type: IRNodeTypes.SLOT_OUTLET_NODE
220+
id: number
221+
name: SimpleExpressionNode
222+
props: IRProps[]
223+
fallback?: BlockIRNode
224+
}
225+
217226
export type IRNode = OperationNode | RootIRNode
218227
export type OperationNode =
219228
| SetPropIRNode
@@ -232,6 +241,7 @@ export type OperationNode =
232241
| ForIRNode
233242
| CreateComponentIRNode
234243
| DeclareOldRefIRNode
244+
| SlotOutletIRNode
235245

236246
export enum DynamicFlag {
237247
NONE = 0,

‎packages/compiler-vapor/src/transforms/transformElement.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
type IRProp,
3030
type IRProps,
3131
type IRPropsDynamicAttribute,
32+
type IRPropsStatic,
3233
type VaporDirectiveNode,
3334
} from '../ir'
3435
import { EMPTY_EXPRESSION } from './utils'
@@ -125,7 +126,7 @@ function resolveSetupReference(name: string, context: TransformContext) {
125126

126127
function transformNativeElement(
127128
tag: string,
128-
propsResult: ReturnType<typeof buildProps>,
129+
propsResult: PropsResult,
129130
context: TransformContext<ElementNode>,
130131
) {
131132
const { scopeId } = context.options
@@ -179,9 +180,9 @@ function transformNativeElement(
179180

180181
export type PropsResult =
181182
| [dynamic: true, props: IRProps[], expressions: SimpleExpressionNode[]]
182-
| [dynamic: false, props: IRProp[]]
183+
| [dynamic: false, props: IRPropsStatic]
183184

184-
function buildProps(
185+
export function buildProps(
185186
node: ElementNode,
186187
context: TransformContext<ElementNode>,
187188
isComponent: boolean,
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import {
2+
type AttributeNode,
3+
type ElementNode,
4+
ElementTypes,
5+
ErrorCodes,
6+
NodeTypes,
7+
type SimpleExpressionNode,
8+
createCompilerError,
9+
createSimpleExpression,
10+
isStaticArgOf,
11+
isStaticExp,
12+
} from '@vue/compiler-core'
13+
import type { NodeTransform, TransformContext } from '../transform'
14+
import {
15+
type BlockIRNode,
16+
DynamicFlag,
17+
IRNodeTypes,
18+
type IRProps,
19+
type VaporDirectiveNode,
20+
type WithDirectiveIRNode,
21+
} from '../ir'
22+
import { camelize, extend } from '@vue/shared'
23+
import { newBlock } from './utils'
24+
import { buildProps } from './transformElement'
25+
26+
export const transformSlotOutlet: NodeTransform = (node, context) => {
27+
if (node.type !== NodeTypes.ELEMENT || node.tag !== 'slot') {
28+
return
29+
}
30+
const id = context.reference()
31+
context.dynamic.flags |= DynamicFlag.INSERT | DynamicFlag.NON_TEMPLATE
32+
const [fallback, exitBlock] = createFallback(
33+
node,
34+
context as TransformContext<ElementNode>,
35+
)
36+
37+
let slotName: SimpleExpressionNode | undefined
38+
const slotProps: (AttributeNode | VaporDirectiveNode)[] = []
39+
for (const prop of node.props as (AttributeNode | VaporDirectiveNode)[]) {
40+
if (prop.type === NodeTypes.ATTRIBUTE) {
41+
if (prop.value) {
42+
if (prop.name === 'name') {
43+
slotName = createSimpleExpression(prop.value.content, true, prop.loc)
44+
} else {
45+
slotProps.push(extend({}, prop, { name: camelize(prop.name) }))
46+
}
47+
}
48+
} else if (prop.name === 'bind' && isStaticArgOf(prop.arg, 'name')) {
49+
if (prop.exp) {
50+
slotName = prop.exp!
51+
} else {
52+
// v-bind shorthand syntax
53+
slotName = createSimpleExpression(
54+
camelize(prop.arg!.content),
55+
false,
56+
prop.arg!.loc,
57+
)
58+
slotName.ast = null
59+
}
60+
} else {
61+
let slotProp = prop
62+
if (
63+
slotProp.name === 'bind' &&
64+
slotProp.arg &&
65+
isStaticExp(slotProp.arg)
66+
) {
67+
slotProp = extend({}, prop, {
68+
arg: extend({}, slotProp.arg, {
69+
content: camelize(slotProp.arg!.content),
70+
}),
71+
})
72+
}
73+
slotProps.push(slotProp)
74+
}
75+
}
76+
77+
slotName ||= createSimpleExpression('default', true)
78+
let irProps: IRProps[] = []
79+
if (slotProps.length) {
80+
const [isDynamic, props] = buildProps(
81+
extend({}, node, { props: slotProps }),
82+
context as TransformContext<ElementNode>,
83+
true,
84+
)
85+
irProps = isDynamic ? props : [props]
86+
87+
const runtimeDirective = context.block.operation.find(
88+
(oper): oper is WithDirectiveIRNode =>
89+
oper.type === IRNodeTypes.WITH_DIRECTIVE && oper.element === id,
90+
)
91+
if (runtimeDirective) {
92+
context.options.onError(
93+
createCompilerError(
94+
ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
95+
runtimeDirective.dir.loc,
96+
),
97+
)
98+
}
99+
}
100+
101+
return () => {
102+
exitBlock && exitBlock()
103+
context.registerOperation({
104+
type: IRNodeTypes.SLOT_OUTLET_NODE,
105+
id,
106+
name: slotName,
107+
props: irProps,
108+
fallback,
109+
})
110+
}
111+
}
112+
113+
function createFallback(
114+
node: ElementNode,
115+
context: TransformContext<ElementNode>,
116+
): [block?: BlockIRNode, exit?: () => void] {
117+
if (!node.children.length) {
118+
return []
119+
}
120+
121+
context.node = node = extend({}, node, {
122+
type: NodeTypes.ELEMENT,
123+
tag: 'template',
124+
props: [],
125+
tagType: ElementTypes.TEMPLATE,
126+
children: node.children,
127+
})
128+
129+
const fallback = newBlock(node)
130+
const exitBlock = context.enterBlock(fallback)
131+
context.reference()
132+
return [fallback, exitBlock]
133+
}

‎packages/compiler-vapor/src/transforms/vBind.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
createCompilerError,
66
createSimpleExpression,
77
} from '@vue/compiler-dom'
8-
import { camelize } from '@vue/shared'
8+
import { camelize, extend } from '@vue/shared'
99
import type { DirectiveTransform, TransformContext } from '../transform'
1010
import { resolveExpression } from '../utils'
1111
import { isReservedProp } from './transformElement'
@@ -58,7 +58,7 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
5858
let camel = false
5959
if (modifiers.includes('camel')) {
6060
if (arg.isStatic) {
61-
arg.content = camelize(arg.content)
61+
arg = extend({}, arg, { content: camelize(arg.content) })
6262
} else {
6363
camel = true
6464
}

‎packages/compiler-vapor/src/transforms/vOn.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const delegatedEvents = /*#__PURE__*/ makeMap(
2020
export const transformVOn: DirectiveTransform = (dir, node, context) => {
2121
let { arg, exp, loc, modifiers } = dir
2222
const isComponent = node.tagType === ElementTypes.COMPONENT
23+
const isSlotOutlet = node.tag === 'slot'
2324

2425
if (!exp && !modifiers.length) {
2526
context.options.onError(
@@ -60,7 +61,7 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => {
6061
}
6162
}
6263

63-
if (isComponent) {
64+
if (isComponent || isSlotOutlet) {
6465
const handler = exp || EMPTY_EXPRESSION
6566
return {
6667
key: arg,

0 commit comments

Comments
 (0)
This repository has been archived.