From 656f52d03c0e9f740310e739fc5b8a7098f00a9d Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 4 Dec 2025 14:48:18 +0800 Subject: [PATCH] refactor: conditionally generate `withVaporCtx` for slots only when they contain components or slot outlets. --- .../__snapshots__/compile.spec.ts.snap | 6 +- .../__snapshots__/scopeId.spec.ts.snap | 26 +- .../compiler-vapor/__tests__/scopeId.spec.ts | 10 +- .../TransformTransition.spec.ts.snap | 36 +- .../__snapshots__/vSlot.spec.ts.snap | 378 ++++++++++++++---- .../__tests__/transforms/vSlot.spec.ts | 183 ++++++++- .../src/generators/component.ts | 92 ++++- .../__tests__/apiSetupContext.spec.ts | 5 +- .../__tests__/componentAttrs.spec.ts | 5 +- .../__tests__/componentSlots.spec.ts | 78 ++-- .../__tests__/components/Teleport.spec.ts | 5 +- .../__tests__/customElement.spec.ts | 14 +- .../__tests__/dom/templateRef.spec.ts | 13 +- .../__tests__/helpers/useCssVars.spec.ts | 7 +- .../runtime-vapor/__tests__/scopeId.spec.ts | 4 +- 15 files changed, 657 insertions(+), 205 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index cb7b5de868..56a1e8c726 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -316,17 +316,17 @@ export function render(_ctx) { `; exports[`compile > expression parsing > v-slot 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n1 = _createComponentWithFallback(_component_Comp, null, { - "foo": _withVaporCtx((_slotProps0) => { + "foo": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.a + _slotProps0.b))) return n0 - }) + } }, true) return n1 }" diff --git a/packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap index c49073973a..5a39958186 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap @@ -1,23 +1,23 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`scopeId compiler support > should wrap default slot 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template("
") export function render(_ctx) { const _component_Child = _resolveComponent("Child") const n1 = _createComponentWithFallback(_component_Child, null, { - "default": _withVaporCtx(() => { + "default": () => { const n0 = t0() return n0 - }) + } }, true) return n1 }" `; exports[`scopeId compiler support > should wrap dynamic slots 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template("
") export function render(_ctx) { @@ -27,18 +27,18 @@ export function render(_ctx) { () => (_ctx.ok ? { name: "foo", - fn: _withVaporCtx(() => { + fn: () => { const n0 = t0() return n0 - }) + } } : void 0), () => (_createForSlots(_ctx.list, (i) => ({ name: i, - fn: _withVaporCtx(() => { + fn: () => { const n2 = t0() return n2 - }) + } }))) ] }, true) @@ -47,22 +47,22 @@ export function render(_ctx) { `; exports[`scopeId compiler support > should wrap named slots 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") const t1 = _template("
") export function render(_ctx) { const _component_Child = _resolveComponent("Child") const n4 = _createComponentWithFallback(_component_Child, null, { - "foo": _withVaporCtx((_slotProps0) => { + "foo": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.msg))) return n0 - }), - "bar": _withVaporCtx(() => { + }, + "bar": () => { const n2 = t1() return n2 - }) + } }, true) return n4 }" diff --git a/packages/compiler-vapor/__tests__/scopeId.spec.ts b/packages/compiler-vapor/__tests__/scopeId.spec.ts index 8dfcd0c0d3..a35d57deea 100644 --- a/packages/compiler-vapor/__tests__/scopeId.spec.ts +++ b/packages/compiler-vapor/__tests__/scopeId.spec.ts @@ -18,7 +18,7 @@ function compile(template: string | RootNode, options: CompilerOptions = {}) { describe('scopeId compiler support', () => { test('should wrap default slot', () => { const code = compile(`
`) - expect(code).toMatch(`"default": _withVaporCtx(() => {`) + expect(code).toMatch(`"default": () => {`) expect(code).toMatchSnapshot() }) @@ -34,8 +34,8 @@ describe('scopeId compiler support', () => { scopeId: 'test', }, ) - expect(code).toMatch(`"foo": _withVaporCtx((_slotProps0) => {`) - expect(code).toMatch(`"bar": _withVaporCtx(() => {`) + expect(code).toMatch(`"foo": (_slotProps0) => {`) + expect(code).toMatch(`"bar": () => {`) expect(code).toMatchSnapshot() }) @@ -51,8 +51,8 @@ describe('scopeId compiler support', () => { scopeId: 'test', }, ) - expect(code).toMatch(/name: "foo",\s+fn: _withVaporCtx\(/) - expect(code).toMatch(/name: i,\s+fn: _withVaporCtx\(/) + expect(code).toMatch(/name: "foo",\s+fn: \(/) + expect(code).toMatch(/name: i,\s+fn: \(/) expect(code).toMatchSnapshot() }) }) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap index 9a1a369c29..a621f5a6ec 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap @@ -1,46 +1,46 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`compiler: transition > basic 1`] = ` -"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, withVaporCtx as _withVaporCtx, createComponent as _createComponent, template as _template } from 'vue'; +"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue'; const t0 = _template("

foo

") export function render(_ctx) { const n1 = _createComponent(_VaporTransition, { persisted: () => ("") }, { - "default": _withVaporCtx(() => { + "default": () => { const n0 = t0() _applyVShow(n0, () => (_ctx.show)) return n0 - }) + } }, true) return n1 }" `; exports[`compiler: transition > inject persisted when child has v-show 1`] = ` -"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, withVaporCtx as _withVaporCtx, createComponent as _createComponent, template as _template } from 'vue'; +"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue'; const t0 = _template("
") export function render(_ctx) { const n1 = _createComponent(_VaporTransition, { persisted: () => ("") }, { - "default": _withVaporCtx(() => { + "default": () => { const n0 = t0() _applyVShow(n0, () => (_ctx.ok)) return n0 - }) + } }, true) return n1 }" `; exports[`compiler: transition > the v-if/else-if/else branches in Transition should ignore comments 1`] = ` -"import { VaporTransition as _VaporTransition, setInsertionState as _setInsertionState, createIf as _createIf, withVaporCtx as _withVaporCtx, createComponent as _createComponent, template as _template } from 'vue'; +"import { VaporTransition as _VaporTransition, setInsertionState as _setInsertionState, createIf as _createIf, createComponent as _createComponent, template as _template } from 'vue'; const t0 = _template("
hey
") const t1 = _template("

") const t2 = _template("
") export function render(_ctx) { const n16 = _createComponent(_VaporTransition, null, { - "default": _withVaporCtx(() => { + "default": () => { const n0 = _createIf(() => (_ctx.a), () => { const n2 = t0() n2.$key = 2 @@ -63,14 +63,14 @@ export function render(_ctx) { return n14 })) return [n0, n3, n7] - }) + } }, true) return n16 }" `; exports[`compiler: transition > v-show + appear 1`] = ` -"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, withVaporCtx as _withVaporCtx, createComponent as _createComponent, template as _template } from 'vue'; +"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue'; const t0 = _template("

foo

") export function render(_ctx) { @@ -79,11 +79,11 @@ export function render(_ctx) { appear: () => (""), persisted: () => ("") }, { - "default": _withVaporCtx(() => { + "default": () => { const n0 = t0() deferredApplyVShows.push(() => _applyVShow(n0, () => (_ctx.show))) return n0 - }) + } }, true) deferredApplyVShows.forEach(fn => fn()) return n1 @@ -91,37 +91,37 @@ export function render(_ctx) { `; exports[`compiler: transition > work with dynamic keyed children 1`] = ` -"import { VaporTransition as _VaporTransition, createKeyedFragment as _createKeyedFragment, withVaporCtx as _withVaporCtx, createComponent as _createComponent, template as _template } from 'vue'; +"import { VaporTransition as _VaporTransition, createKeyedFragment as _createKeyedFragment, createComponent as _createComponent, template as _template } from 'vue'; const t0 = _template("

foo

") export function render(_ctx) { const n1 = _createComponent(_VaporTransition, null, { - "default": _withVaporCtx(() => { + "default": () => { return _createKeyedFragment(() => _ctx.key, () => { const n0 = t0() n0.$key = _ctx.key return n0 }) - }) + } }, true) return n1 }" `; exports[`compiler: transition > work with v-if 1`] = ` -"import { VaporTransition as _VaporTransition, createIf as _createIf, withVaporCtx as _withVaporCtx, createComponent as _createComponent, template as _template } from 'vue'; +"import { VaporTransition as _VaporTransition, createIf as _createIf, createComponent as _createComponent, template as _template } from 'vue'; const t0 = _template("

foo

") export function render(_ctx) { const n3 = _createComponent(_VaporTransition, null, { - "default": _withVaporCtx(() => { + "default": () => { const n0 = _createIf(() => (_ctx.show), () => { const n2 = t0() n2.$key = 2 return n2 }) return n0 - }) + } }, true) return n3 }" diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index 95c90354c6..12decc8b82 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`compiler: transform slot > dynamic slots name 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template("foo") export function render(_ctx) { @@ -10,10 +10,10 @@ export function render(_ctx) { $: [ () => ({ name: _ctx.name, - fn: _withVaporCtx(() => { + fn: () => { const n0 = t0() return n0 - }) + } }) ] }, true) @@ -22,7 +22,7 @@ export function render(_ctx) { `; exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { @@ -31,11 +31,11 @@ export function render(_ctx) { $: [ () => (_createForSlots(_ctx.list, (item) => ({ name: item, - fn: _withVaporCtx((_slotProps0) => { + fn: (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.bar))) return n0 - }) + } }))) ] }, true) @@ -44,7 +44,7 @@ export function render(_ctx) { `; exports[`compiler: transform slot > dynamic slots name w/ v-for and provide absent key 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template("foo") export function render(_ctx) { @@ -53,10 +53,10 @@ export function render(_ctx) { $: [ () => (_createForSlots(_ctx.list, (_, __, index) => ({ name: index, - fn: _withVaporCtx(() => { + fn: () => { const n0 = t0() return n0 - }) + } }))) ] }, true) @@ -65,7 +65,7 @@ export function render(_ctx) { `; exports[`compiler: transform slot > dynamic slots name w/ v-if / v-else[-if] 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template("condition slot") const t1 = _template("another condition") const t2 = _template("else condition") @@ -77,25 +77,25 @@ export function render(_ctx) { () => (_ctx.condition ? { name: "condition", - fn: _withVaporCtx(() => { + fn: () => { const n0 = t0() return n0 - }) + } } : _ctx.anotherCondition ? { name: "condition", - fn: _withVaporCtx((_slotProps0) => { + fn: (_slotProps0) => { const n2 = t1() return n2 - }) + } } : { name: "condition", - fn: _withVaporCtx(() => { + fn: () => { const n4 = t2() return n4 - }) + } }) ] }, true) @@ -190,23 +190,23 @@ export function render(_ctx) { `; exports[`compiler: transform slot > implicit default slot 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template("
") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n1 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx(() => { + "default": () => { const n0 = t0() return n0 - }) + } }, true) return n1 }" `; exports[`compiler: transform slot > named slots w/ implicit default slot 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template("foo") const t1 = _template("bar") const t2 = _template("") @@ -214,30 +214,30 @@ const t2 = _template("") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n4 = _createComponentWithFallback(_component_Comp, null, { - "one": _withVaporCtx(() => { + "one": () => { const n0 = t0() return n0 - }), - "default": _withVaporCtx(() => { + }, + "default": () => { const n2 = t1() const n3 = t2() return [n2, n3] - }) + } }, true) return n4 }" `; exports[`compiler: transform slot > nested component should not inherit parent slots 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, withVaporCtx as _withVaporCtx } from 'vue'; export function render(_ctx) { const _component_Bar = _resolveComponent("Bar") const _component_Foo = _resolveComponent("Foo") const n2 = _createComponentWithFallback(_component_Foo, null, { - "header": _withVaporCtx(() => { + "header": () => { return null - }), + }, "default": _withVaporCtx(() => { const n1 = _createComponentWithFallback(_component_Bar) return n1 @@ -264,7 +264,7 @@ export function render(_ctx) { `; exports[`compiler: transform slot > nested slots scoping 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, withVaporCtx as _withVaporCtx, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { @@ -273,11 +273,11 @@ export function render(_ctx) { const n5 = _createComponentWithFallback(_component_Comp, null, { "default": _withVaporCtx((_slotProps0) => { const n1 = _createComponentWithFallback(_component_Inner, null, { - "default": _withVaporCtx((_slotProps1) => { + "default": (_slotProps1) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _slotProps1.bar + _ctx.baz))) return n0 - }) + } }) const n3 = t0() _renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0.foo + _ctx.bar + _ctx.baz))) @@ -289,7 +289,7 @@ export function render(_ctx) { `; exports[`compiler: transform slot > on component dynamically named slot 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { @@ -298,11 +298,11 @@ export function render(_ctx) { $: [ () => ({ name: _ctx.named, - fn: _withVaporCtx((_slotProps0) => { + fn: (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar))) return n0 - }) + } }) ] }, true) @@ -311,48 +311,48 @@ export function render(_ctx) { `; exports[`compiler: transform slot > on component named slot 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n1 = _createComponentWithFallback(_component_Comp, null, { - "named": _withVaporCtx((_slotProps0) => { + "named": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar))) return n0 - }) + } }, true) return n1 }" `; exports[`compiler: transform slot > on-component default slot 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n1 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar))) return n0 - }) + } }, true) return n1 }" `; exports[`compiler: transform slot > quote slot name 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue'; export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n1 = _createComponentWithFallback(_component_Comp, null, { - "nav-bar-title-before": _withVaporCtx(() => { + "nav-bar-title-before": () => { return null - }) + } }, true) return n1 }" @@ -381,143 +381,143 @@ export function render(_ctx) { `; exports[`compiler: transform slot > slot prop alias uses original key 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n2 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.msg))) return n0 - }) + } }, true) return n2 }" `; exports[`compiler: transform slot > slot prop array rest destructuring 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n2 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.arr.slice(1)[0]))) return n0 - }) + } }, true) return n2 }" `; exports[`compiler: transform slot > slot prop computed key destructuring 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n2 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0[_ctx.key]))) return n0 - }) + } }, true) return n2 }" `; exports[`compiler: transform slot > slot prop default value 1`] = ` -"import { resolveComponent as _resolveComponent, getDefaultValue as _getDefaultValue, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, getDefaultValue as _getDefaultValue, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n2 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo, 1)))) return n0 - }) + } }, true) return n2 }" `; exports[`compiler: transform slot > slot prop nested default value 1`] = ` -"import { resolveComponent as _resolveComponent, getDefaultValue as _getDefaultValue, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, getDefaultValue as _getDefaultValue, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n2 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo[0], 1) + _getDefaultValue(_slotProps0.baz.qux, 2)))) return n0 - }) + } }, true) return n2 }" `; exports[`compiler: transform slot > slot prop nested destructuring 1`] = ` -"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n2 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo.bar))) return n0 - }) + } }, true) return n2 }" `; exports[`compiler: transform slot > slot prop rest destructuring 1`] = ` -"import { resolveComponent as _resolveComponent, getRestElement as _getRestElement, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, getRestElement as _getRestElement, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n2 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_getRestElement(_slotProps0, ["foo"]).bar))) return n0 - }) + } }, true) return n2 }" `; exports[`compiler: transform slot > slot prop rest with computed keys preserved 1`] = ` -"import { resolveComponent as _resolveComponent, getRestElement as _getRestElement, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, getRestElement as _getRestElement, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n2 = _createComponentWithFallback(_component_Comp, null, { - "default": _withVaporCtx((_slotProps0) => { + "default": (_slotProps0) => { const n0 = t0() _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _getRestElement(_slotProps0, ["foo", _ctx.key]).other))) return n0 - }) + } }, true) return n2 }" `; exports[`compiler: transform slot > with whitespace: 'preserve' > implicit default slot 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" Header ") const t1 = _template(" ") const t2 = _template("

") @@ -525,58 +525,276 @@ const t2 = _template("

") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n4 = _createComponentWithFallback(_component_Comp, null, { - "header": _withVaporCtx(() => { + "header": () => { const n0 = t0() return n0 - }), - "default": _withVaporCtx(() => { + }, + "default": () => { const n2 = t1() const n3 = t2() return [n2, n3] - }) + } }, true) return n4 }" `; exports[`compiler: transform slot > with whitespace: 'preserve' > named default slot + implicit whitespace content 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" Header ") const t1 = _template(" Default ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n5 = _createComponentWithFallback(_component_Comp, null, { - "header": _withVaporCtx(() => { + "header": () => { const n0 = t0() return n0 - }), - "default": _withVaporCtx(() => { + }, + "default": () => { const n3 = t1() return n3 - }) + } }, true) return n5 }" `; exports[`compiler: transform slot > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = ` -"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" Header ") const t1 = _template(" Footer ") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n5 = _createComponentWithFallback(_component_Comp, null, { - "header": _withVaporCtx(() => { + "header": () => { const n0 = t0() return n0 - }), - "footer": _withVaporCtx(() => { + }, + "footer": () => { const n3 = t1() return n3 + } + }, true) + return n5 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with component inside v-for should have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, createFor as _createFor, withVaporCtx as _withVaporCtx, template as _template } from 'vue'; +const t0 = _template("
") + +export function render(_ctx) { + const _component_ChildComp = _resolveComponent("ChildComp") + const _component_Comp = _resolveComponent("Comp") + const n5 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx(() => { + const n0 = _createFor(() => (_ctx.items), (_for_item0) => { + const n3 = t0() + _setInsertionState(n3, null, true) + const n2 = _createComponentWithFallback(_component_ChildComp) + return n3 + }) + return n0 + }) + }, true) + return n5 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with component inside v-if should have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, createIf as _createIf, withVaporCtx as _withVaporCtx, template as _template } from 'vue'; +const t0 = _template("
") + +export function render(_ctx) { + const _component_ChildComp = _resolveComponent("ChildComp") + const _component_Comp = _resolveComponent("Comp") + const n5 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx(() => { + const n0 = _createIf(() => (_ctx.show), () => { + const n3 = t0() + _setInsertionState(n3, null, true) + const n2 = _createComponentWithFallback(_component_ChildComp) + return n3 + }) + return n0 + }) + }, true) + return n5 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with component should have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, withVaporCtx as _withVaporCtx } from 'vue'; + +export function render(_ctx) { + const _component_ChildComp = _resolveComponent("ChildComp") + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx(() => { + const n0 = _createComponentWithFallback(_component_ChildComp) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with custom element inside v-if should have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createPlainElement as _createPlainElement, createIf as _createIf, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template("
") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n5 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx(() => { + const n0 = _createIf(() => (_ctx.show), () => { + const n3 = t0() + _setInsertionState(n3, null, true) + const n2 = _createPlainElement("my-element") + return n3 + }) + return n0 }) }, true) return n5 }" `; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with custom element should have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, createPlainElement as _createPlainElement, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue'; + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx(() => { + const n0 = _createPlainElement("my-element") + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with nested v-if containing component should have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, createIf as _createIf, withVaporCtx as _withVaporCtx, template as _template } from 'vue'; +const t0 = _template("") +const t1 = _template("
") + +export function render(_ctx) { + const _component_ChildComp = _resolveComponent("ChildComp") + const _component_Comp = _resolveComponent("Comp") + const n8 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx(() => { + const n0 = _createIf(() => (_ctx.a), () => { + const n6 = t1() + _setInsertionState(n6, null, true) + const n2 = _createIf(() => (_ctx.b), () => { + const n5 = t0() + _setInsertionState(n5, null, true) + const n4 = _createComponentWithFallback(_component_ChildComp) + return n5 + }) + return n6 + }) + return n0 + }) + }, true) + return n8 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with only static elements should not have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template("
static content
") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": () => { + const n0 = t0() + return n0 + } + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with only text interpolation should not have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": () => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_ctx.message))) + return n0 + } + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with slot outlet should have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, createSlot as _createSlot, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue'; + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx(() => { + const n0 = _createSlot("default", null) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with v-for but no component should not have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, txt as _txt, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template("
") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n4 = _createComponentWithFallback(_component_Comp, null, { + "default": () => { + const n0 = _createFor(() => (_ctx.items), (_for_item0) => { + const n2 = t0() + const x2 = _txt(n2) + _renderEffect(() => _setText(x2, _toDisplayString(_for_item0.value))) + return n2 + }) + const x0 = _txt(n0) + _renderEffect(() => _setText(x0, _toDisplayString(_ctx.item))) + return n0 + } + }, true) + return n4 +}" +`; + +exports[`compiler: transform slot > withVaporCtx optimization > slot with v-if but no component should not have withVaporCtx 1`] = ` +"import { resolveComponent as _resolveComponent, createIf as _createIf, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template("
content
") +const t1 = _template("fallback") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n7 = _createComponentWithFallback(_component_Comp, null, { + "default": () => { + const n0 = _createIf(() => (_ctx.show), () => { + const n2 = t0() + return n2 + }, () => { + const n4 = t1() + return n4 + }) + return n0 + } + }, true) + return n7 +}" +`; diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts index b70ee26970..89bdc335aa 100644 --- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts @@ -67,7 +67,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_slotProps0.foo + _ctx.bar`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ @@ -101,7 +101,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"named": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"named": (_slotProps0) =>`) expect(code).contains(`_slotProps0.foo + _ctx.bar`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ @@ -130,7 +130,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`fn: (_slotProps0) =>`) expect(code).contains(`_slotProps0.foo + _ctx.bar`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ @@ -171,7 +171,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_slotProps0.msg`) }) @@ -181,7 +181,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_slotProps0.foo.bar`) }) @@ -191,7 +191,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_slotProps0[_ctx.key]`) }) @@ -201,7 +201,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_getRestElement(_slotProps0`) }) @@ -211,7 +211,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_slotProps0.arr.slice(1)`) }) @@ -221,7 +221,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_getDefaultValue(_slotProps0.foo, 1)`) }) @@ -231,7 +231,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_getDefaultValue(_slotProps0.foo[0], 1)`) expect(code).contains(`_getDefaultValue(_slotProps0.baz.qux, 2)`) }) @@ -242,7 +242,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_getRestElement(_slotProps0, ["foo", _ctx.key])`) }) @@ -296,7 +296,7 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) - expect(code).contains(`"default": _withVaporCtx((_slotProps1) =>`) + expect(code).contains(`"default": (_slotProps1) =>`) expect(code).contains(`_slotProps0.foo + _slotProps1.bar + _ctx.baz`) expect(code).contains(`_slotProps0.foo + _ctx.bar + _ctx.baz`) @@ -373,7 +373,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`fn: (_slotProps0) =>`) expect(code).contains(`_setText(n0, _toDisplayString(_slotProps0.bar))`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ @@ -437,7 +437,7 @@ describe('compiler: transform slot', () => { ) expect(code).toMatchSnapshot() - expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`fn: (_slotProps0) =>`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.CREATE_COMPONENT_NODE, @@ -685,4 +685,159 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() }) }) + + describe('withVaporCtx optimization', () => { + test('slot with only static elements should not have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).not.toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with component should have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with slot outlet should have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with component inside v-if should have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with component inside v-for should have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with nested v-if containing component should have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with only text interpolation should not have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).not.toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with v-if but no component should not have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).not.toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with v-for but no component should not have withVaporCtx', () => { + const { code } = compileWithSlots(` + + + + `) + expect(code).not.toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with custom element should have withVaporCtx', () => { + const { code } = compileWithSlots( + ` + + + + `, + { + isCustomElement: tag => tag.startsWith('my-'), + }, + ) + expect(code).toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + + test('slot with custom element inside v-if should have withVaporCtx', () => { + const { code } = compileWithSlots( + ` + + + + `, + { + isCustomElement: tag => tag.startsWith('my-'), + }, + ) + expect(code).toContain('withVaporCtx') + expect(code).toMatchSnapshot() + }) + }) }) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index c4e9061345..37af449eb0 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -1,8 +1,12 @@ import { camelize, extend, getModifierPropName, isArray } from '@vue/shared' import type { CodegenContext } from '../generate' import { + type BlockIRNode, type CreateComponentIRNode, + type ForIRNode, + type IRDynamicInfo, IRDynamicPropsKind, + IRNodeTypes, type IRProp, type IRProps, type IRPropsStatic, @@ -13,6 +17,8 @@ import { IRSlotType, type IRSlots, type IRSlotsStatic, + type IfIRNode, + type OperationNode, type SlotBlockIRNode, } from '../ir' import { @@ -45,7 +51,7 @@ import { parseValueDestructure, } from './for' import { genModelHandler } from './vModel' -import { isBuiltInComponent, isKeepAliveTag } from '../utils' +import { isBuiltInComponent } from '../utils' export function genCreateComponent( operation: CreateComponentIRNode, @@ -462,10 +468,88 @@ function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) { ] } - if (node.type === NodeTypes.ELEMENT && !isKeepAliveTag(node.tag)) { - // wrap with withVaporCtx to ensure correct currentInstance inside slot - blockFn = [`${context.helper('withVaporCtx')}(`, ...blockFn, `)`] + if (node.type === NodeTypes.ELEMENT) { + // wrap with withVaporCtx to track slot owner for: + // 1. createSlot to get correct rawSlots in forwarded slots + // 2. scopeId inheritance for components created inside slots + // Skip if slot content has no components or slot outlets + if (needsVaporCtx(oper)) { + blockFn = [`${context.helper('withVaporCtx')}(`, ...blockFn, `)`] + } } return blockFn } + +/** + * Check if a slot block needs withVaporCtx wrapper. + * Returns true if the block contains: + * - Component creation (needs scopeId inheritance) + * - Slot outlet (needs rawSlots from slot owner) + */ +function needsVaporCtx(block: BlockIRNode): boolean { + return hasComponentOrSlotInBlock(block) +} + +function hasComponentOrSlotInBlock(block: BlockIRNode): boolean { + // Check operations array + if (hasComponentOrSlotInOperations(block.operation)) return true + // Check dynamic children (components are often stored here) + return hasComponentOrSlotInDynamic(block.dynamic) +} + +function hasComponentOrSlotInDynamic(dynamic: IRDynamicInfo): boolean { + // Check operation in this dynamic node + if (dynamic.operation) { + const type = dynamic.operation.type + if ( + type === IRNodeTypes.CREATE_COMPONENT_NODE || + type === IRNodeTypes.SLOT_OUTLET_NODE + ) { + return true + } + if (type === IRNodeTypes.IF) { + if (hasComponentOrSlotInIf(dynamic.operation as IfIRNode)) return true + } + if (type === IRNodeTypes.FOR) { + if (hasComponentOrSlotInBlock((dynamic.operation as ForIRNode).render)) + return true + } + } + // Recursively check children + for (const child of dynamic.children) { + if (hasComponentOrSlotInDynamic(child)) return true + } + return false +} + +function hasComponentOrSlotInOperations(operations: OperationNode[]): boolean { + for (const op of operations) { + switch (op.type) { + case IRNodeTypes.CREATE_COMPONENT_NODE: + case IRNodeTypes.SLOT_OUTLET_NODE: + return true + case IRNodeTypes.IF: + if (hasComponentOrSlotInIf(op as IfIRNode)) return true + break + case IRNodeTypes.FOR: + if (hasComponentOrSlotInBlock((op as ForIRNode).render)) return true + break + } + } + return false +} + +function hasComponentOrSlotInIf(node: IfIRNode): boolean { + if (hasComponentOrSlotInBlock(node.positive)) return true + if (node.negative) { + if ('positive' in node.negative) { + // nested IfIRNode + return hasComponentOrSlotInIf(node.negative as IfIRNode) + } else { + // BlockIRNode + return hasComponentOrSlotInBlock(node.negative as BlockIRNode) + } + } + return false +} diff --git a/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts b/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts index d694214322..5e0d5d98aa 100644 --- a/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts +++ b/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts @@ -10,7 +10,6 @@ import { setDynamicProps, setText, template, - withVaporCtx, } from '../src' import { nextTick, reactive, ref, watchEffect } from '@vue/runtime-dom' import { makeRender } from './_utils' @@ -114,11 +113,11 @@ describe('api: setup context', () => { inheritAttrs: false, setup(_: any, { attrs }: any) { const n0 = createComponent(Wrapper, null, { - default: withVaporCtx(() => { + default: () => { const n0 = template('
')() as HTMLDivElement renderEffect(() => setDynamicProps(n0, [attrs])) return n0 - }), + }, }) return n0 }, diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index d82837ffa7..e5838e91ff 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -20,7 +20,6 @@ import { setProp, setStyle, template, - withVaporCtx, } from '../src' import { makeRender } from './_utils' import { stringifyStyle } from '@vue/shared' @@ -1087,10 +1086,10 @@ describe('attribute fallthrough', () => { () => 'button', null, { - default: withVaporCtx(() => { + default: () => { const n0 = createSlot('default', null) return n0 - }), + }, }, true, ) diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 74b6a9b3b9..db2171b583 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -178,7 +178,7 @@ describe('component: slots', () => { const { host } = define(() => { return createComponent(Comp, null, { - header: withVaporCtx(() => template('header')()), + header: () => template('header')(), }) }).render() @@ -225,7 +225,7 @@ describe('component: slots', () => { ) define(() => createComponent(Comp, null, { - default: withVaporCtx((_props: any) => ((props = _props), [])), + default: (_props: any) => ((props = _props), []), }), ).render() @@ -253,7 +253,7 @@ describe('component: slots', () => { ) define(() => createComponent(Comp, null, { - default: withVaporCtx((_props: any) => ((props = _props), [])), + default: (_props: any) => ((props = _props), []), }), ).render() @@ -286,13 +286,13 @@ describe('component: slots', () => { $: [ () => ({ name: 'header', - fn: withVaporCtx((props: any) => { + fn: (props: any) => { const el = template('

')() renderEffect(() => { setElementText(el, props.title) }) return el - }), + }, }), ], }) @@ -321,8 +321,8 @@ describe('component: slots', () => { const { host } = define(() => { return createComponent(Comp, null, { - header: withVaporCtx(() => template('header')()), - footer: withVaporCtx(() => template('footer')()), + header: () => template('header')(), + footer: () => template('footer')(), }) }).render() @@ -371,11 +371,11 @@ describe('component: slots', () => { flag1.value ? { name: 'one', - fn: withVaporCtx(() => template('one content')()), + fn: () => template('one content')(), } : { name: 'two', - fn: withVaporCtx(() => template('two content')()), + fn: () => template('two content')(), }, ], }) @@ -420,8 +420,8 @@ describe('component: slots', () => { Child, {}, { - one: withVaporCtx(() => template('one content')()), - two: withVaporCtx(() => template('two content')()), + one: () => template('one content')(), + two: () => template('two content')(), }, ) }).render() @@ -468,14 +468,14 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { return createIf( () => toggle.value, () => { return document.createTextNode('content') }, ) - }), + }, }) }, }).render() @@ -505,14 +505,14 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { return createIf( () => toggle.value, () => { return document.createTextNode('content') }, ) - }), + }, }) }, }).render() @@ -546,9 +546,9 @@ describe('component: slots', () => { (toggle.value ? { name: val.value, - fn: withVaporCtx(() => { + fn: () => { return template('

')() - }), + }, } : void 0) as DynamicSlot, ], @@ -574,9 +574,9 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { return template('')() - }), + }, }) }, }).render() @@ -598,14 +598,14 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { return createIf( () => toggle.value, () => { return document.createTextNode('content') }, ) - }), + }, }) }, }).render() @@ -636,7 +636,7 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { return createIf( () => outerShow.value, () => { @@ -648,7 +648,7 @@ describe('component: slots', () => { ) }, ) - }), + }, }) }, }).render() @@ -693,7 +693,7 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { const n2 = createFor( () => items.value, for_item0 => { @@ -706,7 +706,7 @@ describe('component: slots', () => { }, ) return n2 - }), + }, }) }, }).render() @@ -739,7 +739,7 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { const n2 = createFor( () => items.value, for_item0 => { @@ -752,7 +752,7 @@ describe('component: slots', () => { }, ) return n2 - }), + }, }) }, }).render() @@ -794,12 +794,12 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { const n3 = template('
')() as any const x3 = txt(n3) as any renderEffect(() => setText(x3, toDisplayString(count.value))) return n3 - }), + }, }) }, }).render() @@ -843,11 +843,11 @@ describe('component: slots', () => { Parent, null, { - foo: withVaporCtx(() => { + foo: () => { const n0 = template(' ')() as any renderEffect(() => setText(n0, foo.value)) return n0 - }), + }, }, true, ) @@ -888,16 +888,16 @@ describe('component: slots', () => { Parent, null, { - foo: withVaporCtx(() => { + foo: () => { const n0 = template(' ')() as any renderEffect(() => setText(n0, foo.value)) return n0 - }), - default: withVaporCtx(() => { + }, + default: () => { const n3 = template(' ')() as any renderEffect(() => setText(n3, foo.value)) return n3 - }), + }, }, true, ) @@ -936,7 +936,7 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Parent, null, { - default: withVaporCtx(() => template('')()), + default: () => template('')(), }) }, }).render() @@ -976,7 +976,7 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Parent, null, { - default: withVaporCtx(() => template('')()), + default: () => template('')(), }) }, }).render() @@ -1057,8 +1057,8 @@ describe('component: slots', () => { const { html } = define({ setup() { return createComponent(Child, null, { - default: withVaporCtx(() => template('default')()), - foo: withVaporCtx(() => template('foo')()), + default: () => template('default')(), + foo: () => template('foo')(), }) }, }).render() diff --git a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts index f6b47220e7..c8f77f29af 100644 --- a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts @@ -4,7 +4,6 @@ import { createComponent as createComp, createComponent, } from '../../src/component' -import { withVaporCtx } from '../../src' import { type VaporDirective, VaporTeleport, @@ -719,11 +718,11 @@ function runSharedTests(deferMode: boolean): void { VaporTeleport, { to: () => svg.value }, { - default: withVaporCtx(() => { + default: () => { const n3 = template('', false, 1)() as any _setTemplateRef(n3, circle, undefined, undefined, 'circle') return n3 - }), + }, }, ) return n4 diff --git a/packages/runtime-vapor/__tests__/customElement.spec.ts b/packages/runtime-vapor/__tests__/customElement.spec.ts index faad57aa52..fecbf51780 100644 --- a/packages/runtime-vapor/__tests__/customElement.spec.ts +++ b/packages/runtime-vapor/__tests__/customElement.spec.ts @@ -1407,7 +1407,7 @@ describe('defineVaporCustomElement', () => { return createPlainElement('my-parent', null, { default: withVaporCtx(() => createPlainElement('my-child', null, { - default: withVaporCtx(() => template('default')()), + default: () => template('default')(), }), ), }) @@ -1461,7 +1461,7 @@ describe('defineVaporCustomElement', () => { return createPlainElement('my-el-teleport-parent', null, { default: withVaporCtx(() => createPlainElement('my-el-teleport-child', null, { - default: withVaporCtx(() => template('default')()), + default: () => template('default')(), }), ), }) @@ -1505,10 +1505,10 @@ describe('defineVaporCustomElement', () => { const App = { setup() { return createPlainElement('my-el-two-teleport-child', null, { - default: withVaporCtx(() => [ + default: () => [ template('
header
')(), template('body')(), - ]), + ], }) }, } @@ -1556,10 +1556,10 @@ describe('defineVaporCustomElement', () => { const App = { setup() { return createPlainElement('my-el-two-teleport-child-0', null, { - default: withVaporCtx(() => [ + default: () => [ template('
header
')(), template('body')(), - ]), + ], }) }, } @@ -1591,7 +1591,7 @@ describe('defineVaporCustomElement', () => { const ChildWrapper = { setup() { return createPlainElement('my-el-child-shadow-false', null, { - default: withVaporCtx(() => template('child')()), + default: () => template('child')(), }) }, } diff --git a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts index 536ae965eb..c86eb48de0 100644 --- a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts +++ b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts @@ -12,7 +12,6 @@ import { insert, renderEffect, template, - withVaporCtx, } from '../../src' import { compile, makeRender, runtimeDom, runtimeVapor } from '../_utils' import { @@ -613,11 +612,11 @@ describe('api: template ref', () => { render() { const setRef = createTemplateRefSetter() const n0 = createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { n = document.createElement('div') setRef(n, 'foo') return n - }), + }, }) return n0 }, @@ -641,11 +640,11 @@ describe('api: template ref', () => { setup() { const setRef = createTemplateRefSetter() const n0 = createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { n = document.createElement('div') setRef(n, r) return n - }), + }, }) return n0 }, @@ -670,11 +669,11 @@ describe('api: template ref', () => { r = useTemplateRef('foo') const setRef = createTemplateRefSetter() const n0 = createComponent(Child, null, { - default: withVaporCtx(() => { + default: () => { n = document.createElement('div') setRef(n, 'foo') return n - }), + }, }) return n0 }, diff --git a/packages/runtime-vapor/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-vapor/__tests__/helpers/useCssVars.spec.ts index 1f73df6e0a..0c5b50725c 100644 --- a/packages/runtime-vapor/__tests__/helpers/useCssVars.spec.ts +++ b/packages/runtime-vapor/__tests__/helpers/useCssVars.spec.ts @@ -9,7 +9,6 @@ import { setStyle, template, useVaporCssVars, - withVaporCtx, } from '@vue/runtime-vapor' import { nextTick, onMounted, reactive, ref } from '@vue/runtime-core' import { makeRender } from '../_utils' @@ -246,14 +245,14 @@ describe('useVaporCssVars', () => { VaporTeleport, { to: () => target }, { - default: withVaporCtx(() => { + default: () => { const n0 = template('
', true)() const n1 = createIf( () => toggle.value, () => template('
', true)(), ) return [n0, n1] - }), + }, }, ) }, @@ -286,7 +285,7 @@ describe('useVaporCssVars', () => { VaporTeleport, { to: () => target, disabled: () => true }, { - default: withVaporCtx(() => template('
', true)()), + default: () => template('
', true)(), }, ) }, diff --git a/packages/runtime-vapor/__tests__/scopeId.spec.ts b/packages/runtime-vapor/__tests__/scopeId.spec.ts index 4689c1b6b7..04b54e7d1c 100644 --- a/packages/runtime-vapor/__tests__/scopeId.spec.ts +++ b/packages/runtime-vapor/__tests__/scopeId.spec.ts @@ -293,9 +293,9 @@ describe('scopeId', () => { Slotted, null, { - default: withVaporCtx(() => { + default: () => { return template('
')() - }), + }, }, true, ) -- 2.47.3