Skip to content

Commit d9d0112

Browse files
committed
fix: vapor transition multiple chilren check
1 parent 1511d6c commit d9d0112

File tree

4 files changed

+240
-16
lines changed

4 files changed

+240
-16
lines changed

packages/compiler-dom/src/transforms/Transition.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ export const transformTransition: NodeTransform = (node, context) => {
2424
export function postTransformTransition(
2525
node: ComponentNode,
2626
onError: (error: CompilerError) => void,
27+
hasMultipleChildren: (
28+
node: ComponentNode,
29+
) => boolean = defaultHasMultipleChildren,
2730
): () => void {
2831
return () => {
2932
if (!node.children.length) {
@@ -59,7 +62,9 @@ export function postTransformTransition(
5962
}
6063
}
6164

62-
function hasMultipleChildren(node: ComponentNode | IfBranchNode): boolean {
65+
function defaultHasMultipleChildren(
66+
node: ComponentNode | IfBranchNode,
67+
): boolean {
6368
// #1352 filter out potential comment nodes.
6469
const children = (node.children = node.children.filter(
6570
c =>
@@ -70,6 +75,7 @@ function hasMultipleChildren(node: ComponentNode | IfBranchNode): boolean {
7075
return (
7176
children.length !== 1 ||
7277
child.type === NodeTypes.FOR ||
73-
(child.type === NodeTypes.IF && child.branches.some(hasMultipleChildren))
78+
(child.type === NodeTypes.IF &&
79+
child.branches.some(defaultHasMultipleChildren))
7480
)
7581
}

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

+137-10
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,132 @@ describe('compiler: transition', () => {
5757
expect(code).contains('n0.$key = _ctx.key')
5858
})
5959

60-
test('warns if multiple children', () => {
60+
function checkWarning(template: string, shouldWarn = true) {
6161
const onError = vi.fn()
62-
compileWithElementTransform(
62+
compileWithElementTransform(template, { onError })
63+
if (shouldWarn) {
64+
expect(onError).toHaveBeenCalled()
65+
expect(onError.mock.calls).toMatchObject([
66+
[{ code: DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN }],
67+
])
68+
} else {
69+
expect(onError).not.toHaveBeenCalled()
70+
}
71+
}
72+
73+
test('warns if multiple children', () => {
74+
checkWarning(
6375
`<Transition>
6476
<h1>foo</h1>
6577
<h2>bar</h2>
6678
</Transition>`,
67-
{
68-
onError,
69-
},
79+
true,
80+
)
81+
})
82+
83+
test('warns with v-for', () => {
84+
checkWarning(
85+
`
86+
<transition>
87+
<div v-for="i in items">hey</div>
88+
</transition>
89+
`,
90+
true,
91+
)
92+
})
93+
94+
test('warns with multiple v-if + v-for', () => {
95+
checkWarning(
96+
`
97+
<transition>
98+
<div v-if="a" v-for="i in items">hey</div>
99+
<div v-else v-for="i in items">hey</div>
100+
</transition>
101+
`,
102+
true,
103+
)
104+
})
105+
106+
test('warns with template v-if', () => {
107+
checkWarning(
108+
`
109+
<transition>
110+
<template v-if="ok"></template>
111+
</transition>
112+
`,
113+
true,
114+
)
115+
})
116+
117+
test('warns with multiple templates', () => {
118+
checkWarning(
119+
`
120+
<transition>
121+
<template v-if="a"></template>
122+
<template v-else></template>
123+
</transition>
124+
`,
125+
true,
126+
)
127+
})
128+
129+
test('warns if multiple children with v-if', () => {
130+
checkWarning(
131+
`
132+
<transition>
133+
<div v-if="one">hey</div>
134+
<div v-if="other">hey</div>
135+
</transition>
136+
`,
137+
true,
138+
)
139+
})
140+
141+
test('does not warn with regular element', () => {
142+
checkWarning(
143+
`
144+
<transition>
145+
<div>hey</div>
146+
</transition>
147+
`,
148+
false,
149+
)
150+
})
151+
152+
test('does not warn with one single v-if', () => {
153+
checkWarning(
154+
`
155+
<transition>
156+
<div v-if="a">hey</div>
157+
</transition>
158+
`,
159+
false,
160+
)
161+
})
162+
163+
test('does not warn with v-if v-else-if v-else', () => {
164+
checkWarning(
165+
`
166+
<transition>
167+
<div v-if="a">hey</div>
168+
<div v-else-if="b">hey</div>
169+
<div v-else>hey</div>
170+
</transition>
171+
`,
172+
false,
173+
)
174+
})
175+
176+
test('does not warn with v-if v-else', () => {
177+
checkWarning(
178+
`
179+
<transition>
180+
<div v-if="a">hey</div>
181+
<div v-else>hey</div>
182+
</transition>
183+
`,
184+
false,
70185
)
71-
expect(onError).toHaveBeenCalled()
72-
expect(onError.mock.calls).toMatchObject([
73-
[{ code: DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN }],
74-
])
75186
})
76187

77188
test('inject persisted when child has v-show', () => {
@@ -84,5 +195,21 @@ describe('compiler: transition', () => {
84195
).toMatchSnapshot()
85196
})
86197

87-
// TODO more tests
198+
test('the v-if/else-if/else branches in Transition should ignore comments', () => {
199+
expect(
200+
compileWithElementTransform(`
201+
<transition>
202+
<div v-if="a">hey</div>
203+
<!-- this should be ignored -->
204+
<div v-else-if="b">hey</div>
205+
<!-- this should be ignored -->
206+
<div v-else>
207+
<p v-if="c"/>
208+
<!-- this should not be ignored -->
209+
<p v-else/>
210+
</div>
211+
</transition>
212+
`).code,
213+
).toMatchSnapshot()
214+
})
88215
})

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

+37
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,43 @@ export function render(_ctx) {
3535
}"
3636
`;
3737

38+
exports[`compiler: transition > the v-if/else-if/else branches in Transition should ignore comments 1`] = `
39+
"import { VaporTransition as _VaporTransition, createIf as _createIf, prepend as _prepend, createComponent as _createComponent, template as _template } from 'vue';
40+
const t0 = _template("<div>hey</div>")
41+
const t1 = _template("<p></p>")
42+
const t2 = _template("<div></div>")
43+
44+
export function render(_ctx) {
45+
const n16 = _createComponent(_VaporTransition, null, {
46+
"default": () => {
47+
const n0 = _createIf(() => (_ctx.a), () => {
48+
const n2 = t0()
49+
n2.$key = 2
50+
return n2
51+
}, () => _createIf(() => (_ctx.b), () => {
52+
const n5 = t0()
53+
n5.$key = 5
54+
return n5
55+
}, () => {
56+
const n14 = t2()
57+
const n9 = _createIf(() => (_ctx.c), () => {
58+
const n11 = t1()
59+
return n11
60+
}, () => {
61+
const n13 = t1()
62+
return n13
63+
})
64+
_prepend(n14, n9)
65+
n14.$key = 14
66+
return n14
67+
}))
68+
return [n0, n3, n7]
69+
}
70+
}, true)
71+
return n16
72+
}"
73+
`;
74+
3875
exports[`compiler: transition > work with dynamic keyed children 1`] = `
3976
"import { VaporTransition as _VaporTransition, createKeyedFragment as _createKeyedFragment, createComponent as _createComponent, template as _template } from 'vue';
4077
const t0 = _template("<h1>foo</h1>")
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,69 @@
11
import type { NodeTransform } from '@vue/compiler-vapor'
2-
import { ElementTypes, NodeTypes } from '@vue/compiler-core'
3-
import { isTransitionTag } from '../utils'
4-
import { postTransformTransition } from '@vue/compiler-dom'
2+
import { findDir, isTransitionTag } from '../utils'
3+
import {
4+
type ElementNode,
5+
ElementTypes,
6+
NodeTypes,
7+
isTemplateNode,
8+
postTransformTransition,
9+
} from '@vue/compiler-dom'
510

611
export const transformTransition: NodeTransform = (node, context) => {
712
if (
813
node.type === NodeTypes.ELEMENT &&
914
node.tagType === ElementTypes.COMPONENT
1015
) {
1116
if (isTransitionTag(node.tag)) {
12-
return postTransformTransition(node, context.options.onError)
17+
return postTransformTransition(
18+
node,
19+
context.options.onError,
20+
hasMultipleChildren,
21+
)
1322
}
1423
}
1524
}
25+
26+
function hasMultipleChildren(node: ElementNode): boolean {
27+
const children = (node.children = node.children.filter(
28+
c =>
29+
c.type !== NodeTypes.COMMENT &&
30+
!(c.type === NodeTypes.TEXT && !c.content.trim()),
31+
))
32+
33+
const first = children[0]
34+
35+
// template
36+
if (first && isTemplateNode(first)) {
37+
return true
38+
}
39+
40+
// has v-for
41+
if (
42+
children.length === 1 &&
43+
first.type === NodeTypes.ELEMENT &&
44+
findDir(first, 'for')
45+
) {
46+
return true
47+
}
48+
49+
const hasElse = (node: ElementNode) =>
50+
findDir(node, 'else-if') || findDir(node, 'else', true)
51+
52+
// has v-if/v-else-if/v-else
53+
if (
54+
children.length > 1 &&
55+
children.every(
56+
(c, index) =>
57+
c.type === NodeTypes.ELEMENT &&
58+
// not has v-for
59+
!findDir(c, 'for') &&
60+
// if the first child has v-if, the rest should also have v-else-if/v-else
61+
(index === 0 ? findDir(c, 'if') : hasElse(c)) &&
62+
!hasMultipleChildren(c),
63+
)
64+
) {
65+
return false
66+
}
67+
68+
return children.length > 1
69+
}

0 commit comments

Comments
 (0)