Skip to content

Commit 3b5e13c

Browse files
authored
fix(compiler-vapor): treat template v-for with single component child as component (#13914)
1 parent b65a6b8 commit 3b5e13c

File tree

3 files changed

+71
-2
lines changed

3 files changed

+71
-2
lines changed

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,38 @@ export function render(_ctx) {
235235
}"
236236
`;
237237

238+
exports[`compiler: v-for > v-for on component 1`] = `
239+
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
240+
const t0 = _template(" ")
241+
242+
export function render(_ctx) {
243+
const _component_Comp = _resolveComponent("Comp")
244+
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
245+
const n3 = _createComponentWithFallback(_component_Comp)
246+
const n2 = _child(n3)
247+
_renderEffect(() => _setText(n2, _toDisplayString(_for_item0.value)))
248+
return [n2, n3]
249+
}, undefined, 2)
250+
return n0
251+
}"
252+
`;
253+
254+
exports[`compiler: v-for > v-for on template with single component child 1`] = `
255+
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
256+
const t0 = _template(" ")
257+
258+
export function render(_ctx) {
259+
const _component_Comp = _resolveComponent("Comp")
260+
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
261+
const n3 = _createComponentWithFallback(_component_Comp)
262+
const n2 = _child(n3)
263+
_renderEffect(() => _setText(n2, _toDisplayString(_for_item0.value)))
264+
return [n2, n3]
265+
}, undefined, 2)
266+
return n0
267+
}"
268+
`;
269+
238270
exports[`compiler: v-for > w/o value 1`] = `
239271
"import { createFor as _createFor, template as _template } from 'vue';
240272
const t0 = _template("<div>item</div>", true)

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,4 +368,24 @@ describe('compiler: v-for', () => {
368368
index: undefined,
369369
})
370370
})
371+
372+
test('v-for on component', () => {
373+
const { code, ir } = compileWithVFor(
374+
`<Comp v-for="item in list">{{item}}</Comp>`,
375+
)
376+
expect(code).matchSnapshot()
377+
expect(
378+
(ir.block.dynamic.children[0].operation as ForIRNode).component,
379+
).toBe(true)
380+
})
381+
382+
test('v-for on template with single component child', () => {
383+
const { code, ir } = compileWithVFor(
384+
`<template v-for="item in list"><Comp>{{item}}</Comp></template>`,
385+
)
386+
expect(code).matchSnapshot()
387+
expect(
388+
(ir.block.dynamic.children[0].operation as ForIRNode).component,
389+
).toBe(true)
390+
})
371391
})

packages/compiler-vapor/src/transforms/vFor.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
type ElementNode,
33
ElementTypes,
44
ErrorCodes,
5+
NodeTypes,
56
type SimpleExpressionNode,
67
createCompilerError,
78
} from '@vue/compiler-dom'
@@ -28,7 +29,7 @@ export function processFor(
2829
node: ElementNode,
2930
dir: VaporDirectiveNode,
3031
context: TransformContext<ElementNode>,
31-
) {
32+
): (() => void) | undefined {
3233
if (!dir.exp) {
3334
context.options.onError(
3435
createCompilerError(ErrorCodes.X_V_FOR_NO_EXPRESSION, dir.loc),
@@ -47,7 +48,10 @@ export function processFor(
4748

4849
const keyProp = findProp(node, 'key')
4950
const keyProperty = keyProp && propToExpression(keyProp)
50-
const isComponent = node.tagType === ElementTypes.COMPONENT
51+
const isComponent =
52+
node.tagType === ElementTypes.COMPONENT ||
53+
// template v-for with a single component child
54+
isTemplateWithSingleComponent(node)
5155
context.node = node = wrapTemplate(node, ['for'])
5256
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
5357
const id = context.reference()
@@ -87,3 +91,16 @@ export function processFor(
8791
}
8892
}
8993
}
94+
95+
function isTemplateWithSingleComponent(node: ElementNode): boolean {
96+
if (node.tag !== 'template') return false
97+
98+
const nonCommentChildren = node.children.filter(
99+
c => c.type !== NodeTypes.COMMENT,
100+
)
101+
return (
102+
nonCommentChildren.length === 1 &&
103+
nonCommentChildren[0].type === NodeTypes.ELEMENT &&
104+
nonCommentChildren[0].tagType === ElementTypes.COMPONENT
105+
)
106+
}

0 commit comments

Comments
 (0)