diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index 29a04bc77d5..6397df92f3c 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -68,7 +68,7 @@ export interface DirectiveTransformResult { ssrTagParts?: TemplateLiteral['elements'] } -// A structural directive transform is a technically a NodeTransform; +// A structural directive transform is technically also a NodeTransform; // Only v-if and v-for fall into this category. export type StructuralDirectiveTransform = ( node: ElementNode, diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts index 24b926f83bc..1a796186739 100644 --- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts @@ -306,27 +306,6 @@ describe('ssr: components', () => { `) }) - test('should inject attrs if root with coomments', () => { - expect(compile(`
`).code) - .toMatchInlineSnapshot(` - "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\") - - return function ssrRender(_ctx, _push, _parent, _attrs) { - _push(\`
\`) - }" - `) - }) - - test('should not inject attrs if not root', () => { - expect(compile(`
`).code) - .toMatchInlineSnapshot(` - " - return function ssrRender(_ctx, _push, _parent, _attrs) { - _push(\`
\`) - }" - `) - }) - // #5352 test('should push marker string if is slot root', () => { expect( diff --git a/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts b/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts new file mode 100644 index 00000000000..6cbb0d3a956 --- /dev/null +++ b/packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts @@ -0,0 +1,60 @@ +import { compile } from '../src' + +describe('ssr: attrs fallthrough', () => { + test('basic', () => { + expect(compile(`
`).code).toMatchInlineSnapshot(` + "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`
\`) + }" + `) + }) + + test('with comments', () => { + expect(compile(`
`).code).toMatchInlineSnapshot(` + "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`
\`) + }" + `) + }) + + // #5140 + test('should not inject to non-single-root if branches', () => { + expect(compile(`
`).code).toMatchInlineSnapshot(` + " + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + if (true) { + _push(\`
\`) + } else { + _push(\`\`) + } + _push(\`
\`) + }" + `) + }) + + test('fallthrough component content (root with coomments)', () => { + expect(compile(`
`).code) + .toMatchInlineSnapshot(` + "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`
\`) + }" + `) + }) + + test('should not inject to fallthrough component content if not root', () => { + expect(compile(`
`).code) + .toMatchInlineSnapshot(` + " + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`
\`) + }" + `) + }) +}) diff --git a/packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts b/packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts index 79ab24eccd7..2b5c350a557 100644 --- a/packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts +++ b/packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts @@ -46,6 +46,25 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => { } if (node.type === NodeTypes.IF_BRANCH && hasSingleChild(node)) { + // detect cases where the parent v-if is not the only root level node + let hasEncounteredIf = false + for (const c of filterChild(parent)) { + if ( + c.type === NodeTypes.IF || + (c.type === NodeTypes.ELEMENT && findDir(c, 'if')) + ) { + // multiple root v-if + if (hasEncounteredIf) return + hasEncounteredIf = true + } else if ( + // node before v-if + !hasEncounteredIf || + // non else nodes + !(c.type === NodeTypes.ELEMENT && findDir(c, /else/, true)) + ) { + return + } + } injectFallthroughAttrs(node.children[0]) } else if (hasSingleChild(parent)) { injectFallthroughAttrs(node)