Skip to content

Commit 584f25f

Browse files
fix(runtime-vapor): more accurate fallthrough attr support (#13972)
Co-authored-by: daiwei <[email protected]>
1 parent 35d135e commit 584f25f

File tree

23 files changed

+499
-114
lines changed

23 files changed

+499
-114
lines changed

packages/compiler-core/src/utils.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
type MemoExpression,
1313
NodeTypes,
1414
type ObjectExpression,
15+
type ParentNode,
1516
type Position,
1617
type Property,
1718
type RenderSlotCall,
@@ -568,4 +569,38 @@ export function getMemoedVNodeCall(
568569
}
569570
}
570571

572+
export function filterNonCommentChildren(
573+
node: ParentNode,
574+
): TemplateChildNode[] {
575+
return node.children.filter(n => n.type !== NodeTypes.COMMENT)
576+
}
577+
578+
export function hasSingleChild(node: ParentNode): boolean {
579+
return filterNonCommentChildren(node).length === 1
580+
}
581+
582+
export function isSingleIfBlock(parent: ParentNode): boolean {
583+
// detect cases where the parent v-if is not the only root level node
584+
let hasEncounteredIf = false
585+
for (const c of filterNonCommentChildren(parent)) {
586+
if (
587+
c.type === NodeTypes.IF ||
588+
(c.type === NodeTypes.ELEMENT && findDir(c, 'if'))
589+
) {
590+
// multiple root v-if
591+
if (hasEncounteredIf) return false
592+
hasEncounteredIf = true
593+
} else if (
594+
// node before v-if
595+
!hasEncounteredIf ||
596+
// non else nodes
597+
!(c.type === NodeTypes.ELEMENT && findDir(c, /^else(-if)?$/, true))
598+
) {
599+
return false
600+
}
601+
}
602+
603+
return true
604+
}
605+
571606
export const forAliasRE: RegExp = /([\s\S]*?)\s+(?:in|of)\s+(\S[\s\S]*)/

packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,16 @@ import {
22
ElementTypes,
33
type NodeTransform,
44
NodeTypes,
5-
type ParentNode,
65
type RootNode,
76
type TemplateChildNode,
87
createSimpleExpression,
8+
filterNonCommentChildren,
99
findDir,
10+
hasSingleChild,
11+
isSingleIfBlock,
1012
locStub,
1113
} from '@vue/compiler-dom'
1214

13-
const filterChild = (node: ParentNode) =>
14-
node.children.filter(n => n.type !== NodeTypes.COMMENT)
15-
16-
const hasSingleChild = (node: ParentNode): boolean =>
17-
filterChild(node).length === 1
18-
1915
export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
2016
// _attrs is provided as a function argument.
2117
// mark it as a known identifier so that it doesn't get prefixed by
@@ -32,7 +28,7 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
3228
node.tag === 'KeepAlive' ||
3329
node.tag === 'keep-alive')
3430
) {
35-
const rootChildren = filterChild(context.root)
31+
const rootChildren = filterNonCommentChildren(context.root)
3632
if (rootChildren.length === 1 && rootChildren[0] === node) {
3733
if (hasSingleChild(node)) {
3834
injectFallthroughAttrs(node.children[0])
@@ -47,26 +43,9 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
4743
}
4844

4945
if (node.type === NodeTypes.IF_BRANCH && hasSingleChild(node)) {
50-
// detect cases where the parent v-if is not the only root level node
51-
let hasEncounteredIf = false
52-
for (const c of filterChild(parent)) {
53-
if (
54-
c.type === NodeTypes.IF ||
55-
(c.type === NodeTypes.ELEMENT && findDir(c, 'if'))
56-
) {
57-
// multiple root v-if
58-
if (hasEncounteredIf) return
59-
hasEncounteredIf = true
60-
} else if (
61-
// node before v-if
62-
!hasEncounteredIf ||
63-
// non else nodes
64-
!(c.type === NodeTypes.ELEMENT && findDir(c, /else/, true))
65-
) {
66-
return
67-
}
46+
if (isSingleIfBlock(parent)) {
47+
injectFallthroughAttrs(node.children[0])
6848
}
69-
injectFallthroughAttrs(node.children[0])
7049
} else if (hasSingleChild(parent)) {
7150
injectFallthroughAttrs(node)
7251
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ exports[`compile > expression parsing > v-bind 1`] = `
284284
const n0 = t0()
285285
_renderEffect(() => {
286286
const _key = key.value
287-
_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }], true)
287+
_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }])
288288
})
289289
return n0
290290
"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ describe('compile', () => {
196196
expect(code).contains('const _key = key.value')
197197
expect(code).contains('_key+1')
198198
expect(code).contains(
199-
'_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }], true)',
199+
'_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }])',
200200
)
201201
})
202202

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ const t0 = _template("<div></div>", true)
483483
484484
export function render(_ctx) {
485485
const n0 = t0()
486-
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true))
486+
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj]))
487487
return n0
488488
}"
489489
`;
@@ -494,7 +494,7 @@ const t0 = _template("<div></div>", true)
494494
495495
export function render(_ctx) {
496496
const n0 = t0()
497-
_renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj], true))
497+
_renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj]))
498498
return n0
499499
}"
500500
`;
@@ -505,7 +505,7 @@ const t0 = _template("<div></div>", true)
505505
506506
export function render(_ctx) {
507507
const n0 = t0()
508-
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj, { id: "foo" }], true))
508+
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj, { id: "foo" }]))
509509
return n0
510510
}"
511511
`;
@@ -516,7 +516,7 @@ const t0 = _template("<div></div>", true)
516516
517517
export function render(_ctx) {
518518
const n0 = t0()
519-
_renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true))
519+
_renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj, { class: "bar" }]))
520520
return n0
521521
}"
522522
`;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function render(_ctx) {
3535

3636
exports[`compiler: template ref transform > ref + v-for 1`] = `
3737
"import { createTemplateRefSetter as _createTemplateRefSetter, createFor as _createFor, template as _template } from 'vue';
38-
const t0 = _template("<div></div>", true)
38+
const t0 = _template("<div></div>")
3939
4040
export function render(_ctx) {
4141
const _setTemplateRef = _createTemplateRefSetter()

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function render(_ctx) {
3737
const n0 = t0()
3838
_renderEffect(() => {
3939
const _key = _ctx.key
40-
_setDynamicProps(n0, [{ [_key+1]: _ctx.foo[_key+1]() }], true)
40+
_setDynamicProps(n0, [{ [_key+1]: _ctx.foo[_key+1]() }])
4141
})
4242
return n0
4343
}"
@@ -109,7 +109,7 @@ const t0 = _template("<div></div>", true)
109109
110110
export function render(_ctx) {
111111
const n0 = t0()
112-
_renderEffect(() => _setDynamicProps(n0, [{ foo: bar => _ctx.foo = bar }], true))
112+
_renderEffect(() => _setDynamicProps(n0, [{ foo: bar => _ctx.foo = bar }]))
113113
return n0
114114
}"
115115
`;
@@ -335,7 +335,7 @@ const t0 = _template("<div></div>", true)
335335
336336
export function render(_ctx) {
337337
const n0 = t0()
338-
_renderEffect(() => _setDynamicProps(n0, [{ [_camelize(_ctx.foo)]: _ctx.id }], true))
338+
_renderEffect(() => _setDynamicProps(n0, [{ [_camelize(_ctx.foo)]: _ctx.id }]))
339339
return n0
340340
}"
341341
`;
@@ -434,7 +434,7 @@ const t0 = _template("<div></div>", true)
434434
435435
export function render(_ctx) {
436436
const n0 = t0()
437-
_renderEffect(() => _setDynamicProps(n0, [{ ["." + _ctx.fooBar]: _ctx.id }], true))
437+
_renderEffect(() => _setDynamicProps(n0, [{ ["." + _ctx.fooBar]: _ctx.id }]))
438438
return n0
439439
}"
440440
`;
@@ -609,7 +609,7 @@ export function render(_ctx) {
609609
_renderEffect(() => {
610610
const _id = _ctx.id
611611
const _title = _ctx.title
612-
_setDynamicProps(n0, [{ [_id]: _id, [_title]: _title }], true)
612+
_setDynamicProps(n0, [{ [_id]: _id, [_title]: _title }])
613613
})
614614
return n0
615615
}"
@@ -623,7 +623,7 @@ export function render(_ctx) {
623623
const n0 = t0()
624624
_renderEffect(() => {
625625
const _id = _ctx.id
626-
_setDynamicProps(n0, [{ [_id]: _id, foo: "bar", checked: "" }], true)
626+
_setDynamicProps(n0, [{ [_id]: _id, foo: "bar", checked: "" }])
627627
})
628628
return n0
629629
}"
@@ -677,7 +677,7 @@ const t0 = _template("<svg></svg>", true, 1)
677677
678678
export function render(_ctx) {
679679
const n0 = t0()
680-
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true, true))
680+
_renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true))
681681
return n0
682682
}"
683683
`;

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
exports[`compiler: v-for > array de-structured value (with rest) 1`] = `
44
"import { txt as _txt, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
5-
const t0 = _template("<div> </div>", true)
5+
const t0 = _template("<div> </div>")
66
77
export function render(_ctx) {
88
const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
@@ -17,7 +17,7 @@ export function render(_ctx) {
1717

1818
exports[`compiler: v-for > array de-structured value 1`] = `
1919
"import { txt as _txt, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
20-
const t0 = _template("<div> </div>", true)
20+
const t0 = _template("<div> </div>")
2121
2222
export function render(_ctx) {
2323
const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
@@ -32,7 +32,7 @@ export function render(_ctx) {
3232

3333
exports[`compiler: v-for > basic v-for 1`] = `
3434
"import { txt as _txt, createInvoker as _createInvoker, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue';
35-
const t0 = _template("<div> </div>", true)
35+
const t0 = _template("<div> </div>")
3636
_delegateEvents("click")
3737
3838
export function render(_ctx) {
@@ -49,7 +49,7 @@ export function render(_ctx) {
4949

5050
exports[`compiler: v-for > key only binding pattern 1`] = `
5151
"import { txt as _txt, toDisplayString as _toDisplayString, setText as _setText, createFor as _createFor, template as _template } from 'vue';
52-
const t0 = _template("<tr> </tr>", true)
52+
const t0 = _template("<tr> </tr>")
5353
5454
export function render(_ctx) {
5555
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
@@ -64,7 +64,7 @@ export function render(_ctx) {
6464

6565
exports[`compiler: v-for > multi effect 1`] = `
6666
"import { setProp as _setProp, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
67-
const t0 = _template("<div></div>", true)
67+
const t0 = _template("<div></div>")
6868
6969
export function render(_ctx) {
7070
const n0 = _createFor(() => (_ctx.items), (_for_item0, _for_key0) => {
@@ -82,7 +82,7 @@ export function render(_ctx) {
8282
exports[`compiler: v-for > nested v-for 1`] = `
8383
"import { setInsertionState as _setInsertionState, txt as _txt, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
8484
const t0 = _template("<span> </span>")
85-
const t1 = _template("<div></div>", true)
85+
const t1 = _template("<div></div>")
8686
8787
export function render(_ctx) {
8888
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
@@ -102,7 +102,7 @@ export function render(_ctx) {
102102

103103
exports[`compiler: v-for > object de-structured value (with rest) 1`] = `
104104
"import { getRestElement as _getRestElement, txt as _txt, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
105-
const t0 = _template("<div> </div>", true)
105+
const t0 = _template("<div> </div>")
106106
107107
export function render(_ctx) {
108108
const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
@@ -117,7 +117,7 @@ export function render(_ctx) {
117117

118118
exports[`compiler: v-for > object de-structured value 1`] = `
119119
"import { txt as _txt, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
120-
const t0 = _template("<span> </span>", true)
120+
const t0 = _template("<span> </span>")
121121
122122
export function render(_ctx) {
123123
const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
@@ -132,7 +132,7 @@ export function render(_ctx) {
132132

133133
exports[`compiler: v-for > object value, key and index 1`] = `
134134
"import { txt as _txt, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
135-
const t0 = _template("<div> </div>", true)
135+
const t0 = _template("<div> </div>")
136136
137137
export function render(_ctx) {
138138
const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0, _for_index0) => {
@@ -147,7 +147,7 @@ export function render(_ctx) {
147147

148148
exports[`compiler: v-for > selector pattern 1`] = `
149149
"import { txt as _txt, toDisplayString as _toDisplayString, setText as _setText, createFor as _createFor, template as _template } from 'vue';
150-
const t0 = _template("<tr> </tr>", true)
150+
const t0 = _template("<tr> </tr>")
151151
152152
export function render(_ctx) {
153153
let _selector0_0
@@ -167,7 +167,7 @@ export function render(_ctx) {
167167

168168
exports[`compiler: v-for > selector pattern 2`] = `
169169
"import { setClass as _setClass, createFor as _createFor, template as _template } from 'vue';
170-
const t0 = _template("<tr></tr>", true)
170+
const t0 = _template("<tr></tr>")
171171
172172
export function render(_ctx) {
173173
let _selector0_0
@@ -186,7 +186,7 @@ export function render(_ctx) {
186186

187187
exports[`compiler: v-for > selector pattern 3`] = `
188188
"import { setClass as _setClass, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
189-
const t0 = _template("<tr></tr>", true)
189+
const t0 = _template("<tr></tr>")
190190
191191
export function render(_ctx) {
192192
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
@@ -203,7 +203,7 @@ export function render(_ctx) {
203203

204204
exports[`compiler: v-for > selector pattern 4`] = `
205205
"import { setClass as _setClass, createFor as _createFor, template as _template } from 'vue';
206-
const t0 = _template("<tr></tr>", true)
206+
const t0 = _template("<tr></tr>")
207207
208208
export function render(_ctx) {
209209
let _selector0_0
@@ -222,7 +222,7 @@ export function render(_ctx) {
222222

223223
exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
224224
"import { getDefaultValue as _getDefaultValue, txt as _txt, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
225-
const t0 = _template("<div> </div>", true)
225+
const t0 = _template("<div> </div>")
226226
227227
export function render(_ctx) {
228228
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
@@ -269,7 +269,7 @@ export function render(_ctx) {
269269

270270
exports[`compiler: v-for > w/o value 1`] = `
271271
"import { createFor as _createFor, template as _template } from 'vue';
272-
const t0 = _template("<div>item</div>", true)
272+
const t0 = _template("<div>item</div>")
273273
274274
export function render(_ctx) {
275275
const n0 = _createFor(() => (_ctx.items), (_for_item0) => {

0 commit comments

Comments
 (0)