Skip to content

Commit 9c10b3b

Browse files
committed
fix(compiler): prevent v-for components from being single root
https://github.com/vuejs/core/pull/13149/files
1 parent 34c1c18 commit 9c10b3b

File tree

10 files changed

+83
-39
lines changed

10 files changed

+83
-39
lines changed

packages/compiler/src/ir/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export interface BaseIRNode {
4545
}
4646

4747
export interface RootNode {
48-
type: IRNodeTypes.ROOT
48+
type: IRNodeTypes.ROOT | JSXFragment['type']
4949
source: string
5050
children: JSXFragment['children']
5151
}

packages/compiler/src/transforms/transformElement.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,7 @@ export const transformElement: NodeTransform = (node, context) => {
6565
parent = parent.parent
6666
}
6767
const singleRoot =
68-
context.root === parent &&
69-
parent.node.children.filter((child) => !isJSXComponent(child)).length ===
70-
1
68+
context.root === parent && parent.node.type !== 'JSXFragment'
7169
;(isComponent ? transformComponentElement : transformNativeElement)(
7270
tag,
7371
propsResult,
@@ -110,7 +108,7 @@ function transformComponentElement(
110108
tag,
111109
props: propsResult[0] ? propsResult[1] : [propsResult[1]],
112110
asset,
113-
root: singleRoot,
111+
root: singleRoot && !context.inVFor,
114112
slots: [...context.slots],
115113
once: context.inVOnce,
116114
}
Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`compiler: element transform > Fragment should not mark as single root 1`] = `
4+
"
5+
const n0 = _createComponent(Comp)
6+
return n0
7+
"
8+
`;
9+
10+
exports[`compiler: element transform > generate single root component 1`] = `
11+
"
12+
const n0 = _createComponent(Comp, null, null, true)
13+
return n0
14+
"
15+
`;
16+
317
exports[`compiler: element transform > props merging: class 1`] = `
418
"
5-
const n0 = _createComponent(Foo, { class: () => (["foo", { bar: isBar }]) })
19+
const n0 = _createComponent(Foo, { class: () => (["foo", { bar: isBar }]) }, null, true)
620
return n0
721
"
822
`;
923

1024
exports[`compiler: element transform > props merging: style 1`] = `
1125
"
12-
const n0 = _createComponent(Foo, { style: () => (["color: green", { color: 'red' }]) })
26+
const n0 = _createComponent(Foo, { style: () => (["color: green", { color: 'red' }]) }, null, true)
27+
return n0
28+
"
29+
`;
30+
31+
exports[`compiler: element transform > v-for on component should not mark as single root 1`] = `
32+
"
33+
const n0 = _createFor(() => (items), (_for_item0) => {
34+
const n2 = _createComponent(Comp)
35+
return n2
36+
}, (item) => (item), 2)
1337
return n0
1438
"
1539
`;

packages/compiler/test/transforms/__snapshots__/vModel.spec.ts.snap

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ exports[`compiler: vModel transform > component > v-model for component should g
44
"
55
const n0 = _createComponent(Comp, { modelValue: () => (foo),
66
"onUpdate:modelValue": () => _value => (foo = _value),
7-
modelValueModifiers: () => ({ trim: true, "bar-baz": true }) })
7+
modelValueModifiers: () => ({ trim: true, "bar-baz": true }) }, null, true)
88
return n0
99
"
1010
`;
1111

1212
exports[`compiler: vModel transform > component > v-model for component should work 1`] = `
1313
"
1414
const n0 = _createComponent(Comp, { modelValue: () => (foo),
15-
"onUpdate:modelValue": () => _value => (foo = _value) })
15+
"onUpdate:modelValue": () => _value => (foo = _value) }, null, true)
1616
return n0
1717
"
1818
`;
@@ -26,15 +26,15 @@ exports[`compiler: vModel transform > component > v-model with arguments for com
2626
bar: () => (bar),
2727
"onUpdate:bar": () => _value => (bar = _value),
2828
barModifiers: () => ({ number: true })
29-
})
29+
}, null, true)
3030
return n0
3131
"
3232
`;
3333

3434
exports[`compiler: vModel transform > component > v-model with arguments for component should work 1`] = `
3535
"
3636
const n0 = _createComponent(Comp, { bar: () => (foo),
37-
"onUpdate:bar": () => _value => (foo = _value) })
37+
"onUpdate:bar": () => _value => (foo = _value) }, null, true)
3838
return n0
3939
"
4040
`;
@@ -48,7 +48,7 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
4848
() => ({ [bar.value]: bar,
4949
["onUpdate:" + bar.value]: () => _value => (bar = _value),
5050
[bar.value + "Modifiers"]: () => ({ number: true }) })
51-
] })
51+
] }, null, true)
5252
return n0
5353
"
5454
`;
@@ -58,7 +58,7 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
5858
const n0 = _createComponent(Comp, { $: [
5959
() => ({ [arg]: foo,
6060
["onUpdate:" + arg]: () => _value => (foo = _value) })
61-
] })
61+
] }, null, true)
6262
return n0
6363
"
6464
`;

packages/compiler/test/transforms/__snapshots__/vSlot.spec.ts.snap

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ exports[`compiler: transform slot > dynamic slots name 1`] = `
1212
}
1313
})
1414
]
15-
})
15+
}, true)
1616
return n4
1717
"
1818
`;
@@ -29,7 +29,7 @@ exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = `
2929
}
3030
})))
3131
]
32-
})
32+
}, true)
3333
return n4
3434
"
3535
`;
@@ -62,7 +62,7 @@ exports[`compiler: transform slot > dynamic slots name w/ v-if / v-else[-if] 1`]
6262
}
6363
})
6464
]
65-
})
65+
}, true)
6666
return n10
6767
"
6868
`;
@@ -74,7 +74,7 @@ exports[`compiler: transform slot > implicit default slot 1`] = `
7474
const n0 = t0()
7575
return n0
7676
}
77-
})
77+
}, true)
7878
return n1
7979
"
8080
`;
@@ -86,7 +86,7 @@ exports[`compiler: transform slot > named slots w/ comment 1`] = `
8686
const n3 = t0()
8787
return n3
8888
}
89-
})
89+
}, true)
9090
return n6
9191
"
9292
`;
@@ -103,7 +103,7 @@ exports[`compiler: transform slot > named slots w/ implicit default slot 1`] = `
103103
const n4 = t2()
104104
return [n3, n4]
105105
}
106-
})
106+
}, true)
107107
return n6
108108
"
109109
`;
@@ -115,7 +115,7 @@ exports[`compiler: transform slot > nested component slot 1`] = `
115115
const n0 = _createComponent(B)
116116
return n0
117117
}
118-
})
118+
}, true)
119119
return n1
120120
"
121121
`;
@@ -133,7 +133,7 @@ exports[`compiler: transform slot > nested slots scoping 1`] = `
133133
const n7 = _createNodes(() => (_slotProps0["foo"] + bar + baz))
134134
return [n5, n7]
135135
}
136-
})
136+
}, true)
137137
return n11
138138
"
139139
`;
@@ -150,7 +150,7 @@ exports[`compiler: transform slot > on component dynamically named slot 1`] = `
150150
}
151151
})
152152
]
153-
})
153+
}, true)
154154
return n1
155155
"
156156
`;
@@ -162,7 +162,7 @@ exports[`compiler: transform slot > on component named slot 1`] = `
162162
const n0 = _createNodes(() => (_slotProps0["foo"] + bar))
163163
return n0
164164
}
165-
})
165+
}, true)
166166
return n1
167167
"
168168
`;
@@ -174,7 +174,7 @@ exports[`compiler: transform slot > on-component default slot 1`] = `
174174
const n0 = _createNodes(() => (_slotProps0["foo"] + bar))
175175
return n0
176176
}
177-
})
177+
}, true)
178178
return n1
179179
"
180180
`;
@@ -185,7 +185,7 @@ exports[`compiler: transform slot > quote slot name 1`] = `
185185
"nav-bar-title-before": () => {
186186
return null
187187
}
188-
})
188+
}, true)
189189
return n1
190190
"
191191
`;

packages/compiler/test/transforms/__snapshots__/vSlots.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ exports[`compiler: transform v-slots > basic 1`] = `
66
$: [
77
{ default: ({ foo })=> <>{ foo + bar }</> }
88
]
9-
})
9+
}, true)
1010
return n0
1111
"
1212
`;

packages/compiler/test/transforms/_utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ export function makeCompile(options: CompilerOptions = {}) {
1313
plugins: ['jsx', 'typescript'],
1414
}).program
1515
let children!: JSXElement[] | JSXFragment['children']
16+
let tagType
1617
if (statement.type === 'ExpressionStatement') {
18+
tagType = statement.expression.type
1719
children =
1820
statement.expression.type === 'JSXFragment'
1921
? statement.expression.children
@@ -22,7 +24,7 @@ export function makeCompile(options: CompilerOptions = {}) {
2224
: []
2325
}
2426
const ast: RootNode = {
25-
type: IRNodeTypes.ROOT,
27+
type: tagType === 'JSXFragment' ? 'JSXFragment' : IRNodeTypes.ROOT,
2628
children,
2729
source,
2830
}

packages/compiler/test/transforms/transformElement.spec.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
// import {
2-
// type BindingMetadata,
3-
// NodeTypes,
4-
// } from '@vue/compiler-dom'
51
import { describe, expect, test } from 'vitest'
62
import {
7-
// IRDynamicPropsKind,
8-
// IRNodeTypes,
93
transformChildren,
104
transformElement,
115
transformText,
126
transformVBind,
7+
transformVFor,
138
transformVOn,
149
} from '../../src'
1510
import { makeCompile } from './_utils'
1611

1712
const compileWithElementTransform = makeCompile({
18-
nodeTransforms: [transformElement, transformChildren, transformText],
13+
nodeTransforms: [
14+
transformVFor,
15+
transformElement,
16+
transformChildren,
17+
transformText,
18+
],
1919
directiveTransforms: {
2020
bind: transformVBind,
2121
on: transformVOn,
@@ -28,7 +28,7 @@ describe('compiler: element transform', () => {
2828
const { code, helpers } = compileWithElementTransform(`<Foo/>`)
2929
expect(code).toMatchInlineSnapshot(`
3030
"
31-
const n0 = _createComponent(Foo)
31+
const n0 = _createComponent(Foo, null, null, true)
3232
return n0
3333
"
3434
`)
@@ -42,7 +42,7 @@ describe('compiler: element transform', () => {
4242
})
4343
expect(code).toMatchInlineSnapshot(`
4444
"
45-
const n0 = _createComponent(Foo.Example)
45+
const n0 = _createComponent(Foo.Example, null, null, true)
4646
return n0
4747
"
4848
`)
@@ -63,4 +63,24 @@ describe('compiler: element transform', () => {
6363
)
6464
expect(code).toMatchSnapshot()
6565
})
66+
67+
test('generate single root component', () => {
68+
const { code } = compileWithElementTransform(`<Comp/>`)
69+
expect(code).toMatchSnapshot()
70+
expect(code).contains('_createComponent(Comp, null, null, true)')
71+
})
72+
73+
test('Fragment should not mark as single root', () => {
74+
const { code } = compileWithElementTransform(`<><Comp/></>`)
75+
expect(code).toMatchSnapshot()
76+
expect(code).contains('_createComponent(Comp)')
77+
})
78+
79+
test('v-for on component should not mark as single root', () => {
80+
const { code } = compileWithElementTransform(
81+
`<Comp v-for={item in items} key={item}/>`,
82+
)
83+
expect(code).toMatchSnapshot()
84+
expect(code).contains('_createComponent(Comp)')
85+
})
6686
})

packages/compiler/test/transforms/vModel.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ describe('compiler: vModel transform', () => {
208208
expect(code).toMatchSnapshot()
209209
expect(code).contains(`modelValue: () => (foo),`)
210210
expect(code).contains(
211-
`"onUpdate:modelValue": () => _value => (foo = _value) })`,
211+
`"onUpdate:modelValue": () => _value => (foo = _value)`,
212212
)
213213
expect(ir.block.dynamic.children[0].operation).toMatchObject({
214214
type: IRNodeTypes.CREATE_COMPONENT_NODE,

packages/compiler/test/transforms/vSlot.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ describe('compiler: transform slot', () => {
177177
const n4 = t2()
178178
return [n3, n4]
179179
}
180-
})
180+
}, true)
181181
return n6
182182
"
183183
`)
@@ -327,7 +327,7 @@ describe('compiler: transform slot', () => {
327327
}
328328
})))
329329
]
330-
})
330+
}, true)
331331
return n4
332332
"
333333
`)

0 commit comments

Comments
 (0)