Skip to content

Commit ed43b67

Browse files
committed
fix(slot): enhance v-slot prop destructuring support
1 parent 0ab7e1b commit ed43b67

File tree

6 files changed

+311
-163
lines changed

6 files changed

+311
-163
lines changed

packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ export function render(_ctx) {
324324
const n1 = _createComponentWithFallback(_component_Comp, null, {
325325
"foo": _withVaporCtx((_slotProps0) => {
326326
const n0 = t0()
327-
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["a"] + _slotProps0["b"])))
327+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.a + _slotProps0.b)))
328328
return n0
329329
})
330330
}, true)

packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export function render(_ctx) {
5656
const n4 = _createComponentWithFallback(_component_Child, null, {
5757
"foo": _withVaporCtx((_slotProps0) => {
5858
const n0 = t0()
59-
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["msg"])))
59+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.msg)))
6060
return n0
6161
}),
6262
"bar": _withVaporCtx(() => {

packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function render(_ctx) {
3333
name: item,
3434
fn: _withVaporCtx((_slotProps0) => {
3535
const n0 = t0()
36-
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["bar"])))
36+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.bar)))
3737
return n0
3838
})
3939
})))
@@ -275,12 +275,12 @@ export function render(_ctx) {
275275
const n1 = _createComponentWithFallback(_component_Inner, null, {
276276
"default": _withVaporCtx((_slotProps1) => {
277277
const n0 = t0()
278-
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz)))
278+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _slotProps1.bar + _ctx.baz)))
279279
return n0
280280
})
281281
})
282282
const n3 = t0()
283-
_renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz)))
283+
_renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0.foo + _ctx.bar + _ctx.baz)))
284284
return [n1, n3]
285285
})
286286
}, true)
@@ -300,7 +300,7 @@ export function render(_ctx) {
300300
name: _ctx.named,
301301
fn: _withVaporCtx((_slotProps0) => {
302302
const n0 = t0()
303-
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
303+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar)))
304304
return n0
305305
})
306306
})
@@ -319,7 +319,7 @@ export function render(_ctx) {
319319
const n1 = _createComponentWithFallback(_component_Comp, null, {
320320
"named": _withVaporCtx((_slotProps0) => {
321321
const n0 = t0()
322-
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
322+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar)))
323323
return n0
324324
})
325325
}, true)
@@ -336,7 +336,7 @@ export function render(_ctx) {
336336
const n1 = _createComponentWithFallback(_component_Comp, null, {
337337
"default": _withVaporCtx((_slotProps0) => {
338338
const n0 = t0()
339-
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
339+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar)))
340340
return n0
341341
})
342342
}, true)
@@ -380,6 +380,91 @@ export function render(_ctx) {
380380
}"
381381
`;
382382
383+
exports[`compiler: transform slot > slot prop alias uses original key 1`] = `
384+
"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
385+
const t0 = _template(" ")
386+
387+
export function render(_ctx) {
388+
const _component_Comp = _resolveComponent("Comp")
389+
const n2 = _createComponentWithFallback(_component_Comp, null, {
390+
"default": _withVaporCtx((_slotProps0) => {
391+
const n0 = t0()
392+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.msg)))
393+
return n0
394+
})
395+
}, true)
396+
return n2
397+
}"
398+
`;
399+
400+
exports[`compiler: transform slot > slot prop computed key destructuring 1`] = `
401+
"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
402+
const t0 = _template(" ")
403+
404+
export function render(_ctx) {
405+
const _component_Comp = _resolveComponent("Comp")
406+
const n2 = _createComponentWithFallback(_component_Comp, null, {
407+
"default": _withVaporCtx((_slotProps0) => {
408+
const n0 = t0()
409+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0[_ctx.key])))
410+
return n0
411+
})
412+
}, true)
413+
return n2
414+
}"
415+
`;
416+
417+
exports[`compiler: transform slot > slot prop default value 1`] = `
418+
"import { resolveComponent as _resolveComponent, getDefaultValue as _getDefaultValue, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
419+
const t0 = _template(" ")
420+
421+
export function render(_ctx) {
422+
const _component_Comp = _resolveComponent("Comp")
423+
const n2 = _createComponentWithFallback(_component_Comp, null, {
424+
"default": _withVaporCtx((_slotProps0) => {
425+
const n0 = t0()
426+
_renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo, 1))))
427+
return n0
428+
})
429+
}, true)
430+
return n2
431+
}"
432+
`;
433+
434+
exports[`compiler: transform slot > slot prop nested destructuring 1`] = `
435+
"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
436+
const t0 = _template(" ")
437+
438+
export function render(_ctx) {
439+
const _component_Comp = _resolveComponent("Comp")
440+
const n2 = _createComponentWithFallback(_component_Comp, null, {
441+
"default": _withVaporCtx((_slotProps0) => {
442+
const n0 = t0()
443+
_renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo.bar)))
444+
return n0
445+
})
446+
}, true)
447+
return n2
448+
}"
449+
`;
450+
451+
exports[`compiler: transform slot > slot prop rest destructuring 1`] = `
452+
"import { resolveComponent as _resolveComponent, getRestElement as _getRestElement, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
453+
const t0 = _template(" ")
454+
455+
export function render(_ctx) {
456+
const _component_Comp = _resolveComponent("Comp")
457+
const n2 = _createComponentWithFallback(_component_Comp, null, {
458+
"default": _withVaporCtx((_slotProps0) => {
459+
const n0 = t0()
460+
_renderEffect(() => _setText(n0, _toDisplayString(_getRestElement(_slotProps0, ["foo"]).bar)))
461+
return n0
462+
})
463+
}, true)
464+
return n2
465+
}"
466+
`;
467+
383468
exports[`compiler: transform slot > with whitespace: 'preserve' > implicit default slot 1`] = `
384469
"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
385470
const t0 = _template(" Header ")

packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ describe('compiler: transform slot', () => {
6868
expect(code).toMatchSnapshot()
6969

7070
expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
71-
expect(code).contains(`_slotProps0["foo"] + _ctx.bar`)
71+
expect(code).contains(`_slotProps0.foo + _ctx.bar`)
7272

7373
expect(ir.block.dynamic.children[0].operation).toMatchObject({
7474
type: IRNodeTypes.CREATE_COMPONENT_NODE,
@@ -102,7 +102,7 @@ describe('compiler: transform slot', () => {
102102
expect(code).toMatchSnapshot()
103103

104104
expect(code).contains(`"named": _withVaporCtx((_slotProps0) =>`)
105-
expect(code).contains(`_slotProps0["foo"] + _ctx.bar`)
105+
expect(code).contains(`_slotProps0.foo + _ctx.bar`)
106106

107107
expect(ir.block.dynamic.children[0].operation).toMatchObject({
108108
type: IRNodeTypes.CREATE_COMPONENT_NODE,
@@ -131,7 +131,7 @@ describe('compiler: transform slot', () => {
131131
expect(code).toMatchSnapshot()
132132

133133
expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`)
134-
expect(code).contains(`_slotProps0["foo"] + _ctx.bar`)
134+
expect(code).contains(`_slotProps0.foo + _ctx.bar`)
135135

136136
expect(ir.block.dynamic.children[0].operation).toMatchObject({
137137
type: IRNodeTypes.CREATE_COMPONENT_NODE,
@@ -165,6 +165,56 @@ describe('compiler: transform slot', () => {
165165
expect(code).toMatchSnapshot()
166166
})
167167

168+
test('slot prop alias uses original key', () => {
169+
const { code } = compileWithSlots(
170+
`<Comp><template #default="{ msg: msg1 }">{{ msg1 }}</template></Comp>`,
171+
)
172+
173+
expect(code).toMatchSnapshot()
174+
expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
175+
expect(code).contains(`_slotProps0.msg`)
176+
})
177+
178+
test('slot prop nested destructuring', () => {
179+
const { code } = compileWithSlots(
180+
`<Comp><template #default="{ foo: { bar: baz } }">{{ baz }}</template></Comp>`,
181+
)
182+
183+
expect(code).toMatchSnapshot()
184+
expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
185+
expect(code).contains(`_slotProps0.foo.bar`)
186+
})
187+
188+
test('slot prop computed key destructuring', () => {
189+
const { code } = compileWithSlots(
190+
`<Comp><template #default="{ [key]: val }">{{ val }}</template></Comp>`,
191+
)
192+
193+
expect(code).toMatchSnapshot()
194+
expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
195+
expect(code).contains(`_slotProps0[_ctx.key]`)
196+
})
197+
198+
test('slot prop rest destructuring', () => {
199+
const { code } = compileWithSlots(
200+
`<Comp><template #default="{ foo, ...rest }">{{ rest.bar }}</template></Comp>`,
201+
)
202+
203+
expect(code).toMatchSnapshot()
204+
expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
205+
expect(code).contains(`_getRestElement(_slotProps0`)
206+
})
207+
208+
test('slot prop default value', () => {
209+
const { code } = compileWithSlots(
210+
`<Comp><template #default="{ foo = 1 }">{{ foo }}</template></Comp>`,
211+
)
212+
213+
expect(code).toMatchSnapshot()
214+
expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
215+
expect(code).contains(`_getDefaultValue(_slotProps0.foo, 1)`)
216+
})
217+
168218
test('named slots w/ implicit default slot', () => {
169219
const { ir, code } = compileWithSlots(
170220
`<Comp>
@@ -216,8 +266,8 @@ describe('compiler: transform slot', () => {
216266

217267
expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
218268
expect(code).contains(`"default": _withVaporCtx((_slotProps1) =>`)
219-
expect(code).contains(`_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz`)
220-
expect(code).contains(`_slotProps0["foo"] + _ctx.bar + _ctx.baz`)
269+
expect(code).contains(`_slotProps0.foo + _slotProps1.bar + _ctx.baz`)
270+
expect(code).contains(`_slotProps0.foo + _ctx.bar + _ctx.baz`)
221271

222272
const outerOp = ir.block.dynamic.children[0].operation
223273
expect(outerOp).toMatchObject({
@@ -293,7 +343,7 @@ describe('compiler: transform slot', () => {
293343
expect(code).toMatchSnapshot()
294344

295345
expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`)
296-
expect(code).contains(`_setText(n0, _toDisplayString(_slotProps0["bar"]))`)
346+
expect(code).contains(`_setText(n0, _toDisplayString(_slotProps0.bar))`)
297347

298348
expect(ir.block.dynamic.children[0].operation).toMatchObject({
299349
type: IRNodeTypes.CREATE_COMPONENT_NODE,

packages/compiler-vapor/src/generators/component.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@ import {
3434
createSimpleExpression,
3535
isMemberExpression,
3636
toValidAssetId,
37-
walkIdentifiers,
3837
} from '@vue/compiler-dom'
3938
import { genEventHandler } from './event'
4039
import { genDirectiveModifiers, genDirectivesForElement } from './directive'
4140
import { genBlock } from './block'
41+
import {
42+
type DestructureMap,
43+
type DestructureMapValue,
44+
buildDestructureIdMap,
45+
parseValueDestructure,
46+
} from './for'
4247
import { genModelHandler } from './vModel'
4348
import { isBuiltInComponent, isKeepAliveTag } from '../utils'
4449

@@ -405,40 +410,35 @@ function genConditionalSlot(
405410
}
406411

407412
function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) {
408-
let isDestructureAssignment = false
409-
let rawProps: string | undefined
410413
let propsName: string | undefined
411414
let exitScope: (() => void) | undefined
412415
let depth: number | undefined
413416
const { props, key, node } = oper
414-
const idsOfProps = new Set<string>()
417+
const idToPathMap: DestructureMap = props
418+
? parseValueDestructure(props, context)
419+
: new Map<string, DestructureMapValue | null>()
420+
415421
if (props) {
416-
rawProps = props.content
417-
if ((isDestructureAssignment = !!props.ast)) {
422+
if (props.ast) {
418423
;[depth, exitScope] = context.enterScope()
419424
propsName = `_slotProps${depth}`
420-
walkIdentifiers(
421-
props.ast,
422-
(id, _, __, ___, isLocal) => {
423-
if (isLocal) idsOfProps.add(id.name)
424-
},
425-
true,
426-
)
427425
} else {
428-
idsOfProps.add((propsName = rawProps))
426+
propsName = props.content
429427
}
430428
}
431429

432-
const idMap: Record<string, string | null> = {}
433-
434-
idsOfProps.forEach(
435-
id =>
436-
(idMap[id] = isDestructureAssignment
437-
? `${propsName}[${JSON.stringify(id)}]`
438-
: null),
430+
const idMap = buildDestructureIdMap(
431+
idToPathMap,
432+
propsName || '',
433+
context.options.expressionPlugins,
439434
)
435+
436+
if (propsName) {
437+
idMap[propsName] = null
438+
}
439+
440440
let blockFn = context.withId(
441-
() => genBlock(oper, context, [propsName]),
441+
() => genBlock(oper, context, propsName ? [propsName] : []),
442442
idMap,
443443
)
444444
exitScope && exitScope()

0 commit comments

Comments
 (0)