From 29149fb4f725e7f4532f3fb58ba9ffd397a13708 Mon Sep 17 00:00:00 2001 From: Cynthia Date: Sun, 20 Jul 2025 20:51:32 +0200 Subject: [PATCH 1/4] fix(compile-vapor): don't generate unnecessary closing tags in templates --- .../transformElement.spec.ts.snap | 47 ++++++++++++++----- .../transforms/transformElement.spec.ts | 33 +++++++++++-- .../src/transforms/transformElement.ts | 13 +++-- 3 files changed, 73 insertions(+), 20 deletions(-) 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 7aa56aa9c2f..00bf4b5eaf1 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap @@ -337,9 +337,9 @@ export function render(_ctx) { exports[`compiler: element transform > invalid html nesting 1`] = ` "import { insert as _insert, template as _template } from 'vue'; -const t0 = _template("
123
") -const t1 = _template("

") -const t2 = _template("
") +const t0 = _template("
123") +const t1 = _template("

") +const t2 = _template("

") export function render(_ctx) { const n1 = t1() @@ -352,9 +352,30 @@ export function render(_ctx) { }" `; +exports[`compiler: element transform > multiple roots 1`] = ` +"import { template as _template } from 'vue'; +const t0 = _template("
") + +export function render(_ctx) { + const n0 = t0() + const n1 = t0() + return [n0, n1] +}" +`; + +exports[`compiler: element transform > props + child 1`] = ` +"import { template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + return n0 +}" +`; + exports[`compiler: element transform > props + children 1`] = ` "import { template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() @@ -364,7 +385,7 @@ export function render(_ctx) { exports[`compiler: element transform > props merging: class 1`] = ` "import { setClass as _setClass, renderEffect as _renderEffect, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() @@ -375,7 +396,7 @@ export function render(_ctx) { exports[`compiler: element transform > props merging: event handlers 1`] = ` "import { withKeys as _withKeys, delegate as _delegate, delegateEvents as _delegateEvents, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) _delegateEvents("click") export function render(_ctx) { @@ -388,7 +409,7 @@ export function render(_ctx) { exports[`compiler: element transform > props merging: style 1`] = ` "import { setStyle as _setStyle, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() @@ -399,7 +420,7 @@ export function render(_ctx) { exports[`compiler: element transform > static props 1`] = ` "import { template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() @@ -409,7 +430,7 @@ export function render(_ctx) { exports[`compiler: element transform > v-bind="obj" 1`] = ` "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() @@ -420,7 +441,7 @@ export function render(_ctx) { exports[`compiler: element transform > v-bind="obj" after static prop 1`] = ` "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() @@ -431,7 +452,7 @@ export function render(_ctx) { exports[`compiler: element transform > v-bind="obj" before static prop 1`] = ` "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() @@ -442,7 +463,7 @@ export function render(_ctx) { exports[`compiler: element transform > v-bind="obj" between static props 1`] = ` "import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() @@ -453,7 +474,7 @@ export function render(_ctx) { exports[`compiler: element transform > v-on="obj" 1`] = ` "import { setDynamicEvents as _setDynamicEvents, renderEffect as _renderEffect, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const n0 = t0() diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index a693db4ad39..c079096b39c 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -576,19 +576,44 @@ describe('compiler: element transform', () => { `
`, ) - const template = '
' + const template = '
' expect(code).toMatchSnapshot() expect(code).contains(JSON.stringify(template)) expect(ir.template).toMatchObject([template]) expect(ir.block.effect).lengthOf(0) }) - test('props + children', () => { + test('props + child', () => { const { code, ir } = compileWithElementTransform( `
`, ) - const template = '
' + const template = '
' + expect(code).toMatchSnapshot() + expect(code).contains(JSON.stringify(template)) + expect(ir.template).toMatchObject([template]) + expect(ir.block.effect).lengthOf(0) + }) + + test('props + children', () => { + const { code, ir } = compileWithElementTransform( + `
`, + ) + + const template = + '
' + expect(code).toMatchSnapshot() + expect(code).contains(JSON.stringify(template)) + expect(ir.template).toMatchObject([template]) + expect(ir.block.effect).lengthOf(0) + }) + + test('multiple roots', () => { + const { code, ir } = compileWithElementTransform( + `
`, + ) + + const template = '
' expect(code).toMatchSnapshot() expect(code).contains(JSON.stringify(template)) expect(ir.template).toMatchObject([template]) @@ -937,7 +962,7 @@ describe('compiler: element transform', () => { `, ) expect(code).toMatchSnapshot() - expect(ir.template).toEqual(['
123
', '

', '
']) + expect(ir.template).toEqual(['
123', '

', '

']) expect(ir.block.dynamic).toMatchObject({ children: [ { id: 1, template: 1, children: [{ id: 0, template: 0 }] }, diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 05153e729af..88b99b26a36 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -67,7 +67,12 @@ export const transformElement: NodeTransform = (node, context) => { getEffectIndex, ) - let { parent } = context + let { index, parent } = context + const isLastChild = + parent && + parent.node.children.filter(child => child.type !== NodeTypes.COMMENT) + .length === + index + 1 while ( parent && parent.parent && @@ -94,6 +99,8 @@ export const transformElement: NodeTransform = (node, context) => { node as PlainElementNode, propsResult, singleRoot, + // Multi-root always generates dedicated templates for each root + isLastChild || context.root === parent, context, getEffectIndex, ) @@ -196,6 +203,7 @@ function transformNativeElement( node: PlainElementNode, propsResult: PropsResult, singleRoot: boolean, + isLastChild: boolean, context: TransformContext, getEffectIndex: () => number, ) { @@ -244,8 +252,7 @@ function transformNativeElement( } template += `>` + context.childrenTemplate.join('') - // TODO remove unnecessary close tag, e.g. if it's the last element of the template - if (!isVoidTag(tag)) { + if (!isVoidTag(tag) && !isLastChild) { template += `` } From ebdb381b9c3a38556b0167f1244495eed2603272 Mon Sep 17 00:00:00 2001 From: Cynthia Date: Sun, 20 Jul 2025 20:52:20 +0200 Subject: [PATCH 2/4] chore(compiler-vapor): update snapshots and tests --- .../__snapshots__/compile.spec.ts.snap | 46 ++++---- .../compiler-vapor/__tests__/compile.spec.ts | 2 +- .../__snapshots__/expression.spec.ts.snap | 6 +- .../transformChildren.spec.ts.snap | 10 +- .../__snapshots__/transformRef.spec.ts.snap | 8 +- .../transformSlotOutlet.spec.ts.snap | 8 +- .../transformTemplateRef.spec.ts.snap | 10 +- .../__snapshots__/vBind.spec.ts.snap | 102 +++++++++--------- .../__snapshots__/vFor.spec.ts.snap | 32 +++--- .../__snapshots__/vHtml.spec.ts.snap | 6 +- .../transforms/__snapshots__/vIf.spec.ts.snap | 34 +++--- .../__snapshots__/vModel.spec.ts.snap | 4 +- .../transforms/__snapshots__/vOn.spec.ts.snap | 66 ++++++------ .../__snapshots__/vOnce.spec.ts.snap | 18 ++-- .../__snapshots__/vShow.spec.ts.snap | 2 +- .../__snapshots__/vSlot.spec.ts.snap | 8 +- .../__snapshots__/vText.spec.ts.snap | 6 +- .../transforms/transformSlotOutlet.spec.ts | 8 +- .../transforms/transformTemplateRef.spec.ts | 6 +- .../__tests__/transforms/vBind.spec.ts | 6 +- .../__tests__/transforms/vFor.spec.ts | 4 +- .../__tests__/transforms/vHtml.spec.ts | 4 +- .../__tests__/transforms/vIf.spec.ts | 18 ++-- .../__tests__/transforms/vSlot.spec.ts | 4 +- .../__tests__/transforms/vText.spec.ts | 4 +- 25 files changed, 211 insertions(+), 211 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index b10a98d32cb..3085967fc0f 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -2,7 +2,7 @@ exports[`compile > bindings 1`] = ` "import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -14,7 +14,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > custom directive > basic 1`] = ` "import { resolveDirective as _resolveDirective, withVaporDirectives as _withVaporDirectives, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx) { const _directive_test = _resolveDirective("test") @@ -27,7 +27,7 @@ export function render(_ctx) { exports[`compile > custom directive > component 1`] = ` "import { resolveComponent as _resolveComponent, resolveDirective as _resolveDirective, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, withVaporDirectives as _withVaporDirectives, createIf as _createIf, template as _template } from 'vue'; -const t0 = _template("
") +const t0 = _template("
") export function render(_ctx) { const _component_Bar = _resolveComponent("Bar") @@ -53,7 +53,7 @@ export function render(_ctx) { exports[`compile > directives > custom directive > basic 1`] = ` "import { withVaporDirectives as _withVaporDirectives, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -64,7 +64,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > directives > custom directive > binding value 1`] = ` "import { withVaporDirectives as _withVaporDirectives, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -75,7 +75,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > directives > custom directive > dynamic parameters 1`] = ` "import { withVaporDirectives as _withVaporDirectives, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -86,7 +86,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > directives > custom directive > modifiers 1`] = ` "import { withVaporDirectives as _withVaporDirectives, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -97,7 +97,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > directives > custom directive > modifiers w/o binding 1`] = ` "import { withVaporDirectives as _withVaporDirectives, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -108,7 +108,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > directives > custom directive > static parameters 1`] = ` "import { withVaporDirectives as _withVaporDirectives, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -119,7 +119,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > directives > custom directive > static parameters and modifiers 1`] = ` "import { withVaporDirectives as _withVaporDirectives, template as _template } from 'vue'; -const t0 = _template("
", true) +const t0 = _template("
", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -130,7 +130,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > directives > v-cloak > basic 1`] = ` "import { template as _template } from 'vue'; -const t0 = _template("
test
", true) +const t0 = _template("
test", true) export function render(_ctx) { const n0 = t0() @@ -140,7 +140,7 @@ export function render(_ctx) { exports[`compile > directives > v-pre > basic 1`] = ` "import { template as _template } from 'vue'; -const t0 = _template("
{{ bar }}
", true) +const t0 = _template("
{{ bar }}", true) export function render(_ctx, $props, $emit, $attrs, $slots) { const n0 = t0() @@ -150,8 +150,8 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { exports[`compile > directives > v-pre > should not affect siblings after it 1`] = ` "import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; -const t0 = _template("
{{ bar }}
") -const t1 = _template("
") +const t0 = _template("
{{ bar }}") +const t1 = _template("
") export function render(_ctx, $props, $emit, $attrs, $slots) { const _component_Comp = _resolveComponent("Comp") @@ -181,7 +181,7 @@ export function render(_ctx) { exports[`compile > dynamic root nodes and interpolation 1`] = ` "import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue'; -const t0 = _template("", true) +const t0 = _template("
", true) +const t0 = _template("
") +const t1 = _template("