Skip to content

Commit 516bc54

Browse files
committed
fix(compiler-ssr): fix wrong attrs fallthrough on non-single-root v-if branches
fix #5140
1 parent e8ceac7 commit 516bc54

File tree

4 files changed

+80
-22
lines changed

4 files changed

+80
-22
lines changed

packages/compiler-core/src/transform.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export interface DirectiveTransformResult {
6868
ssrTagParts?: TemplateLiteral['elements']
6969
}
7070

71-
// A structural directive transform is a technically a NodeTransform;
71+
// A structural directive transform is technically also a NodeTransform;
7272
// Only v-if and v-for fall into this category.
7373
export type StructuralDirectiveTransform = (
7474
node: ElementNode,

packages/compiler-ssr/__tests__/ssrComponent.spec.ts

-21
Original file line numberDiff line numberDiff line change
@@ -306,27 +306,6 @@ describe('ssr: components', () => {
306306
`)
307307
})
308308

309-
test('should inject attrs if root with coomments', () => {
310-
expect(compile(`<!--root--><transition><div/></transition>`).code)
311-
.toMatchInlineSnapshot(`
312-
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
313-
314-
return function ssrRender(_ctx, _push, _parent, _attrs) {
315-
_push(\`<!--[--><!--root--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
316-
}"
317-
`)
318-
})
319-
320-
test('should not inject attrs if not root', () => {
321-
expect(compile(`<div/><transition><div/></transition>`).code)
322-
.toMatchInlineSnapshot(`
323-
"
324-
return function ssrRender(_ctx, _push, _parent, _attrs) {
325-
_push(\`<!--[--><div></div><div></div><!--]-->\`)
326-
}"
327-
`)
328-
})
329-
330309
// #5352
331310
test('should push marker string if is slot root', () => {
332311
expect(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { compile } from '../src'
2+
3+
describe('ssr: attrs fallthrough', () => {
4+
test('basic', () => {
5+
expect(compile(`<div/>`).code).toMatchInlineSnapshot(`
6+
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
7+
8+
return function ssrRender(_ctx, _push, _parent, _attrs) {
9+
_push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
10+
}"
11+
`)
12+
})
13+
14+
test('with comments', () => {
15+
expect(compile(`<!--!--><div/>`).code).toMatchInlineSnapshot(`
16+
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
17+
18+
return function ssrRender(_ctx, _push, _parent, _attrs) {
19+
_push(\`<!--[--><!--!--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
20+
}"
21+
`)
22+
})
23+
24+
// #5140
25+
test('should not inject to non-single-root if branches', () => {
26+
expect(compile(`<div v-if="true"/><div/>`).code).toMatchInlineSnapshot(`
27+
"
28+
return function ssrRender(_ctx, _push, _parent, _attrs) {
29+
_push(\`<!--[-->\`)
30+
if (true) {
31+
_push(\`<div></div>\`)
32+
} else {
33+
_push(\`<!---->\`)
34+
}
35+
_push(\`<div></div><!--]-->\`)
36+
}"
37+
`)
38+
})
39+
40+
test('fallthrough component content (root with coomments)', () => {
41+
expect(compile(`<!--root--><transition><div/></transition>`).code)
42+
.toMatchInlineSnapshot(`
43+
"const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
44+
45+
return function ssrRender(_ctx, _push, _parent, _attrs) {
46+
_push(\`<!--[--><!--root--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
47+
}"
48+
`)
49+
})
50+
51+
test('should not inject to fallthrough component content if not root', () => {
52+
expect(compile(`<div/><transition><div/></transition>`).code)
53+
.toMatchInlineSnapshot(`
54+
"
55+
return function ssrRender(_ctx, _push, _parent, _attrs) {
56+
_push(\`<!--[--><div></div><div></div><!--]-->\`)
57+
}"
58+
`)
59+
})
60+
})

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

+19
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,25 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
4646
}
4747

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

0 commit comments

Comments
 (0)