diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index ddf9b0eed8d..7ab8a07ecea 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -12,6 +12,7 @@ import { type MemoExpression, NodeTypes, type ObjectExpression, + type ParentNode, type Position, type Property, type RenderSlotCall, @@ -568,4 +569,36 @@ export function getMemoedVNodeCall( } } +export function filterCommentChildren(node: ParentNode): TemplateChildNode[] { + return node.children.filter(n => n.type !== NodeTypes.COMMENT) +} + +export function hasSingleChild(node: ParentNode): boolean { + return filterCommentChildren(node).length === 1 +} + +export function isSingleIfBlock(parent: ParentNode): boolean { + // detect cases where the parent v-if is not the only root level node + let hasEncounteredIf = false + for (const c of filterCommentChildren(parent)) { + if ( + c.type === NodeTypes.IF || + (c.type === NodeTypes.ELEMENT && findDir(c, 'if')) + ) { + // multiple root v-if + if (hasEncounteredIf) return false + hasEncounteredIf = true + } else if ( + // node before v-if + !hasEncounteredIf || + // non else nodes + !(c.type === NodeTypes.ELEMENT && findDir(c, /^else(-if)?$/, true)) + ) { + return false + } + } + + return true +} + export const forAliasRE: RegExp = /([\s\S]*?)\s+(?:in|of)\s+(\S[\s\S]*)/ diff --git a/packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts b/packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts index b1aac0d74c2..a9b8c8f4421 100644 --- a/packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts +++ b/packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts @@ -2,20 +2,16 @@ import { ElementTypes, type NodeTransform, NodeTypes, - type ParentNode, type RootNode, type TemplateChildNode, createSimpleExpression, + filterCommentChildren, findDir, + hasSingleChild, + isSingleIfBlock, locStub, } from '@vue/compiler-dom' -const filterChild = (node: ParentNode) => - node.children.filter(n => n.type !== NodeTypes.COMMENT) - -const hasSingleChild = (node: ParentNode): boolean => - filterChild(node).length === 1 - export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => { // _attrs is provided as a function argument. // mark it as a known identifier so that it doesn't get prefixed by @@ -32,7 +28,7 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => { node.tag === 'KeepAlive' || node.tag === 'keep-alive') ) { - const rootChildren = filterChild(context.root) + const rootChildren = filterCommentChildren(context.root) if (rootChildren.length === 1 && rootChildren[0] === node) { if (hasSingleChild(node)) { injectFallthroughAttrs(node.children[0]) @@ -47,26 +43,9 @@ 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 - } + if (isSingleIfBlock(parent)) { + injectFallthroughAttrs(node.children[0]) } - injectFallthroughAttrs(node.children[0]) } else if (hasSingleChild(parent)) { injectFallthroughAttrs(node) } diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index 9f2183ce83e..3d5467594a7 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -260,7 +260,7 @@ exports[`compile > expression parsing > v-bind 1`] = ` const n0 = t0() _renderEffect(() => { const _key = key.value - _setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }], true) + _setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }]) }) return n0 " diff --git a/packages/compiler-vapor/__tests__/compile.spec.ts b/packages/compiler-vapor/__tests__/compile.spec.ts index 7963a9e98c2..9f4598181b0 100644 --- a/packages/compiler-vapor/__tests__/compile.spec.ts +++ b/packages/compiler-vapor/__tests__/compile.spec.ts @@ -196,7 +196,7 @@ describe('compile', () => { expect(code).contains('const _key = key.value') expect(code).contains('_key+1') expect(code).contains( - '_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }], true)', + '_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }])', ) }) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap index 3188a866070..ea88da22f9d 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap @@ -413,7 +413,7 @@ const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() - _renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true)) + _renderEffect(() => _setDynamicProps(n0, [_ctx.obj])) return n0 }" `; @@ -424,7 +424,7 @@ const t0 = _template("", true) export function render(_ctx) { const n0 = t0() - _renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj], true)) + _renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj])) return n0 }" `; @@ -435,7 +435,7 @@ const t0 = _template("", true) export function render(_ctx) { const n0 = t0() - _renderEffect(() => _setDynamicProps(n0, [_ctx.obj, { id: "foo" }], true)) + _renderEffect(() => _setDynamicProps(n0, [_ctx.obj, { id: "foo" }])) return n0 }" `; @@ -446,7 +446,7 @@ const t0 = _template("", true) export function render(_ctx) { const n0 = t0() - _renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj, { class: "bar" }], true)) + _renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, _ctx.obj, { class: "bar" }])) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap index 7184446fc09..4ca43957a2d 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap @@ -35,7 +35,7 @@ export function render(_ctx) { exports[`compiler: template ref transform > ref + v-for 1`] = ` "import { createTemplateRefSetter as _createTemplateRefSetter, createFor as _createFor, template as _template } from 'vue'; -const t0 = _template("", true) +const t0 = _template("") export function render(_ctx) { const _setTemplateRef = _createTemplateRefSetter() diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap index 4ea0db55fe5..cc55b4f6ca9 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -23,7 +23,7 @@ export function render(_ctx) { const n0 = t0() _renderEffect(() => { const _key = _ctx.key - _setDynamicProps(n0, [{ [_key+1]: _ctx.foo[_key+1]() }], true) + _setDynamicProps(n0, [{ [_key+1]: _ctx.foo[_key+1]() }]) }) return n0 }" @@ -80,7 +80,7 @@ const t0 = _template("", true) export function render(_ctx) { const n0 = t0() - _renderEffect(() => _setDynamicProps(n0, [{ foo: bar => _ctx.foo = bar }], true)) + _renderEffect(() => _setDynamicProps(n0, [{ foo: bar => _ctx.foo = bar }])) return n0 }" `; @@ -306,7 +306,7 @@ const t0 = _template("", true) export function render(_ctx) { const n0 = t0() - _renderEffect(() => _setDynamicProps(n0, [{ [_camelize(_ctx.foo)]: _ctx.id }], true)) + _renderEffect(() => _setDynamicProps(n0, [{ [_camelize(_ctx.foo)]: _ctx.id }])) return n0 }" `; @@ -405,7 +405,7 @@ const t0 = _template("", true) export function render(_ctx) { const n0 = t0() - _renderEffect(() => _setDynamicProps(n0, [{ ["." + _ctx.fooBar]: _ctx.id }], true)) + _renderEffect(() => _setDynamicProps(n0, [{ ["." + _ctx.fooBar]: _ctx.id }])) return n0 }" `; @@ -569,7 +569,7 @@ export function render(_ctx) { _renderEffect(() => { const _id = _ctx.id const _title = _ctx.title - _setDynamicProps(n0, [{ [_id]: _id, [_title]: _title }], true) + _setDynamicProps(n0, [{ [_id]: _id, [_title]: _title }]) }) return n0 }" @@ -583,7 +583,7 @@ export function render(_ctx) { const n0 = t0() _renderEffect(() => { const _id = _ctx.id - _setDynamicProps(n0, [{ [_id]: _id, foo: "bar", checked: "" }], true) + _setDynamicProps(n0, [{ [_id]: _id, foo: "bar", checked: "" }]) }) return n0 }" diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index 4b1574e5d25..7f797e66130 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -2,7 +2,7 @@ exports[`compiler: v-for > array de-structured value (with rest) 1`] = ` "import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue'; -const t0 = _template("", true) +const t2 = _template("
") export function render(_ctx) { const n0 = _createIf(() => (_ctx.ok), () => { @@ -82,8 +180,8 @@ export function render(_ctx) { exports[`compiler: v-if > v-if + v-else 1`] = ` "import { createIf as _createIf, template as _template } from 'vue'; -const t0 = _template("") -const t1 = _template("") +const t0 = _template("", true) +const t1 = _template("", true) export function render(_ctx) { const n0 = _createIf(() => (_ctx.ok), () => { @@ -99,8 +197,8 @@ export function render(_ctx) { exports[`compiler: v-if > v-if + v-else-if + v-else 1`] = ` "import { createIf as _createIf, template as _template } from 'vue'; -const t0 = _template("") -const t1 = _template("") +const t0 = _template("", true) +const t1 = _template("", true) const t2 = _template("fine") export function render(_ctx) { @@ -120,8 +218,8 @@ export function render(_ctx) { exports[`compiler: v-if > v-if + v-else-if 1`] = ` "import { createIf as _createIf, template as _template } from 'vue'; -const t0 = _template("") -const t1 = _template("") +const t0 = _template("", true) +const t1 = _template("", true) export function render(_ctx) { const n0 = _createIf(() => (_ctx.ok), () => { @@ -160,3 +258,22 @@ export function render(_ctx) { return n8 }" `; + +exports[`compiler: v-if > v-if and extra at root 1`] = ` +"import { createIf as _createIf, template as _template } from 'vue'; +const t0 = _template("