From b3b01af3a611b0514cc7127240a3db4e1f666b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Tue, 18 Jun 2024 00:09:24 +0800 Subject: [PATCH 01/10] refactor: component slots --- .../__snapshots__/compile.spec.ts.snap | 4 +- .../transformElement.spec.ts.snap | 38 +-- .../__snapshots__/vModel.spec.ts.snap | 12 +- .../__snapshots__/vOnce.spec.ts.snap | 2 +- .../__snapshots__/vSlot.spec.ts.snap | 58 ++--- .../transforms/transformElement.spec.ts | 4 +- .../__tests__/transforms/vSlot.spec.ts | 238 ++++++++++-------- .../src/generators/component.ts | 72 +++--- packages/compiler-vapor/src/ir/component.ts | 63 +++++ .../compiler-vapor/src/{ir.ts => ir/index.ts} | 73 +----- packages/compiler-vapor/src/transform.ts | 13 +- .../src/transforms/transformElement.ts | 5 +- .../compiler-vapor/src/transforms/vSlot.ts | 159 +++++++----- .../__tests__/componentAttrs.spec.ts | 4 - .../__tests__/componentProps.spec.ts | 1 - .../__tests__/componentSlots.spec.ts | 116 ++++----- .../__tests__/directives/vShow.spec.ts | 2 +- .../runtime-vapor/__tests__/dom/prop.spec.ts | 2 +- .../runtime-vapor/src/apiCreateComponent.ts | 6 +- .../runtime-vapor/src/apiCreateVaporApp.ts | 1 - packages/runtime-vapor/src/component.ts | 20 +- packages/runtime-vapor/src/componentSlots.ts | 92 +++---- 22 files changed, 499 insertions(+), 486 deletions(-) create mode 100644 packages/compiler-vapor/src/ir/component.ts rename packages/compiler-vapor/src/{ir.ts => ir/index.ts} (78%) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index 4acc91a94..213605764 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -33,7 +33,7 @@ export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const _directive_hello = _resolveDirective("hello") const _directive_test = _resolveDirective("test") - const n4 = _createComponent(_component_Comp, null, { default: () => { + const n4 = _createComponent(_component_Comp, null, [{ default: () => { const n0 = _createIf(() => (true), () => { const n3 = t0() const n2 = _createComponent(_component_Bar) @@ -42,7 +42,7 @@ export function render(_ctx) { return n3 }) return n0 - } }, null, true) + } }], true) _withDirectives(n4, [[_directive_test]]) return n4 }" 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 2d3bc2f23..0bd845763 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap @@ -5,7 +5,7 @@ exports[`compiler: element transform > component > do not resolve component from export function render(_ctx) { const _component_Example = _resolveComponent("Example") - const n0 = _createComponent(_component_Example, null, null, null, true) + const n0 = _createComponent(_component_Example, null, null, true) return n0 }" `; @@ -25,7 +25,7 @@ exports[`compiler: element transform > component > generate single root componen "import { createComponent as _createComponent } from 'vue/vapor'; export function render(_ctx) { - const n0 = _createComponent(_ctx.Comp, null, null, null, true) + const n0 = _createComponent(_ctx.Comp, null, null, true) return n0 }" `; @@ -35,21 +35,21 @@ exports[`compiler: element transform > component > import + resolve component 1` export function render(_ctx) { const _component_Foo = _resolveComponent("Foo") - const n0 = _createComponent(_component_Foo, null, null, null, true) + const n0 = _createComponent(_component_Foo, null, null, true) return n0 }" `; exports[`compiler: element transform > component > resolve component from setup bindings (inline const) 1`] = ` "(() => { - const n0 = _createComponent(Example, null, null, null, true) + const n0 = _createComponent(Example, null, null, true) return n0 })()" `; exports[`compiler: element transform > component > resolve component from setup bindings (inline) 1`] = ` "(() => { - const n0 = _createComponent(_unref(Example), null, null, null, true) + const n0 = _createComponent(_unref(Example), null, null, true) return n0 })()" `; @@ -58,14 +58,14 @@ exports[`compiler: element transform > component > resolve component from setup "import { createComponent as _createComponent } from 'vue/vapor'; export function render(_ctx) { - const n0 = _createComponent(_ctx.Example, null, null, null, true) + const n0 = _createComponent(_ctx.Example, null, null, true) return n0 }" `; exports[`compiler: element transform > component > resolve namespaced component from props bindings (inline) 1`] = ` "(() => { - const n0 = _createComponent(Foo.Example, null, null, null, true) + const n0 = _createComponent(Foo.Example, null, null, true) return n0 })()" `; @@ -74,14 +74,14 @@ exports[`compiler: element transform > component > resolve namespaced component "import { createComponent as _createComponent } from 'vue/vapor'; export function render(_ctx) { - const n0 = _createComponent(_ctx.Foo.Example, null, null, null, true) + const n0 = _createComponent(_ctx.Foo.Example, null, null, true) return n0 }" `; exports[`compiler: element transform > component > resolve namespaced component from setup bindings (inline const) 1`] = ` "(() => { - const n0 = _createComponent(Foo.Example, null, null, null, true) + const n0 = _createComponent(Foo.Example, null, null, true) return n0 })()" `; @@ -90,7 +90,7 @@ exports[`compiler: element transform > component > resolve namespaced component "import { createComponent as _createComponent } from 'vue/vapor'; export function render(_ctx) { - const n0 = _createComponent(_ctx.Foo.Example, null, null, null, true) + const n0 = _createComponent(_ctx.Foo.Example, null, null, true) return n0 }" `; @@ -102,7 +102,7 @@ export function render(_ctx) { const _component_Foo = _resolveComponent("Foo") const n0 = _createComponent(_component_Foo, [ { onBar: () => $event => (_ctx.handleBar($event)) } - ], null, null, true) + ], null, true) return n0 }" `; @@ -117,7 +117,7 @@ export function render(_ctx) { id: () => ("foo"), class: () => ("bar") } - ], null, null, true) + ], null, true) return n0 }" `; @@ -129,7 +129,7 @@ export function render(_ctx) { const _component_Foo = _resolveComponent("Foo") const n0 = _createComponent(_component_Foo, [ () => (_ctx.obj) - ], null, null, true) + ], null, true) return n0 }" `; @@ -142,7 +142,7 @@ export function render(_ctx) { const n0 = _createComponent(_component_Foo, [ { id: () => ("foo") }, () => (_ctx.obj) - ], null, null, true) + ], null, true) return n0 }" `; @@ -155,7 +155,7 @@ export function render(_ctx) { const n0 = _createComponent(_component_Foo, [ () => (_ctx.obj), { id: () => ("foo") } - ], null, null, true) + ], null, true) return n0 }" `; @@ -169,7 +169,7 @@ export function render(_ctx) { { id: () => ("foo") }, () => (_ctx.obj), { class: () => ("bar") } - ], null, null, true) + ], null, true) return n0 }" `; @@ -181,7 +181,7 @@ export function render(_ctx) { const _component_Foo = _resolveComponent("Foo") const n0 = _createComponent(_component_Foo, [ () => (_toHandlers(_ctx.obj)) - ], null, null, true) + ], null, true) return n0 }" `; @@ -195,7 +195,7 @@ export function render(_ctx) { const n0 = _createComponent(_component_Foo, [ () => ({ [_toHandlerKey(_ctx.foo-_ctx.bar)]: () => _ctx.bar }), () => ({ [_toHandlerKey(_ctx.baz)]: () => _ctx.qux }) - ], null, null, true) + ], null, true) return n0 }" `; @@ -208,7 +208,7 @@ export function render(_ctx) { const n0 = _createComponent(_component_Foo, [ () => ({ [_ctx.foo-_ctx.bar]: _ctx.bar }), () => ({ [_ctx.baz]: _ctx.qux }) - ], null, null, true) + ], null, true) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap index 62e0ece59..ee8fcf62d 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vModel.spec.ts.snap @@ -9,7 +9,7 @@ export function render(_ctx) { { modelValue: () => (_ctx.foo), "onUpdate:modelValue": () => $event => (_ctx.foo = $event), modelModifiers: () => ({ trim: true, "bar-baz": true }) } - ], null, null, true) + ], null, true) return n0 }" `; @@ -22,7 +22,7 @@ export function render(_ctx) { const n0 = _createComponent(_component_Comp, [ { modelValue: () => (_ctx.foo), "onUpdate:modelValue": () => $event => (_ctx.foo = $event) } - ], null, null, true) + ], null, true) return n0 }" `; @@ -41,7 +41,7 @@ export function render(_ctx) { "onUpdate:bar": () => $event => (_ctx.bar = $event), barModifiers: () => ({ number: true }) } - ], null, null, true) + ], null, true) return n0 }" `; @@ -54,7 +54,7 @@ export function render(_ctx) { const n0 = _createComponent(_component_Comp, [ { bar: () => (_ctx.foo), "onUpdate:bar": () => $event => (_ctx.foo = $event) } - ], null, null, true) + ], null, true) return n0 }" `; @@ -71,7 +71,7 @@ export function render(_ctx) { () => ({ [_ctx.bar]: _ctx.bar, ["onUpdate:" + _ctx.bar]: () => $event => (_ctx.bar = $event), [_ctx.bar + "Modifiers"]: () => ({ number: true }) }) - ], null, null, true) + ], null, true) return n0 }" `; @@ -84,7 +84,7 @@ export function render(_ctx) { const n0 = _createComponent(_component_Comp, [ () => ({ [_ctx.arg]: _ctx.foo, ["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event) }) - ], null, null, true) + ], null, true) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap index f4e42a895..68fe53dd8 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap @@ -44,7 +44,7 @@ export function render(_ctx) { const n1 = t0() const n0 = _createComponent(_component_Comp, [ { id: () => (_ctx.foo) } - ], null, null, null, true) + ], null, null, true) _insert(n0, n1) return n1 }" 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 8ac1e71f6..4fa8dcf19 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -6,60 +6,60 @@ const t0 = _template("foo") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n2 = _createComponent(_component_Comp, null, null, [() => ({ + const n2 = _createComponent(_component_Comp, null, [[() => ({ name: _ctx.name, fn: () => { const n0 = t0() return n0 } - })], true) + })]], true) return n2 }" `; exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = ` -"import { resolveComponent as _resolveComponent, createComponent as _createComponent, withDestructure as _withDestructure, createForSlots as _createForSlots, template as _template } from 'vue/vapor'; +"import { resolveComponent as _resolveComponent, withDestructure as _withDestructure, createForSlots as _createForSlots, createComponent as _createComponent, template as _template } from 'vue/vapor'; const t0 = _template("foo") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n2 = _createComponent(_component_Comp, null, null, [() => (_createForSlots(_ctx.list, (item) => ({ + const n2 = _createComponent(_component_Comp, null, [[() => (_createForSlots(_ctx.list, (item) => ({ name: item, fn: _withDestructure(({ bar }) => [bar], (_ctx0) => { const n0 = t0() return n0 }) - })))], true) + })))]], true) return n2 }" `; exports[`compiler: transform slot > dynamic slots name w/ v-for and provide absent key 1`] = ` -"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createForSlots as _createForSlots, template as _template } from 'vue/vapor'; +"import { resolveComponent as _resolveComponent, createForSlots as _createForSlots, createComponent as _createComponent, template as _template } from 'vue/vapor'; const t0 = _template("foo") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n2 = _createComponent(_component_Comp, null, null, [() => (_createForSlots(_ctx.list, (_, __, index) => ({ + const n2 = _createComponent(_component_Comp, null, [[() => (_createForSlots(_ctx.list, (_, __, index) => ({ name: index, fn: () => { const n0 = t0() return n0 } - })))], true) + })))]], true) return n2 }" `; exports[`compiler: transform slot > dynamic slots name w/ v-if / v-else[-if] 1`] = ` -"import { resolveComponent as _resolveComponent, createComponent as _createComponent, withDestructure as _withDestructure, template as _template } from 'vue/vapor'; +"import { resolveComponent as _resolveComponent, withDestructure as _withDestructure, createComponent as _createComponent, template as _template } from 'vue/vapor'; const t0 = _template("condition slot") const t1 = _template("another condition") const t2 = _template("else condition") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n6 = _createComponent(_component_Comp, null, null, [() => (_ctx.condition + const n6 = _createComponent(_component_Comp, null, [[() => (_ctx.condition ? { name: "condition", fn: () => { @@ -81,7 +81,7 @@ export function render(_ctx) { const n4 = t2() return n4 } - })], true) + })]], true) return n6 }" `; @@ -92,10 +92,10 @@ const t0 = _template("
") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n1 = _createComponent(_component_Comp, null, { default: () => { + const n1 = _createComponent(_component_Comp, null, [{ default: () => { const n0 = t0() return n0 - } }, null, true) + } }], true) return n1 }" `; @@ -108,7 +108,7 @@ const t2 = _template("") export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n4 = _createComponent(_component_Comp, null, { + const n4 = _createComponent(_component_Comp, null, [{ one: () => { const n0 = t0() return n0 @@ -118,69 +118,69 @@ export function render(_ctx) { const n3 = t2() return [n2, n3] } - }, null, true) + }], true) return n4 }" `; exports[`compiler: transform slot > nested slots scoping 1`] = ` -"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, withDestructure as _withDestructure, template as _template } from 'vue/vapor'; +"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, withDestructure as _withDestructure, createComponent as _createComponent, template as _template } from 'vue/vapor'; const t0 = _template(" ") export function render(_ctx) { const _component_Inner = _resolveComponent("Inner") const _component_Comp = _resolveComponent("Comp") - const n5 = _createComponent(_component_Comp, null, { default: _withDestructure(({ foo }) => [foo], (_ctx0) => { + const n5 = _createComponent(_component_Comp, null, [{ default: _withDestructure(({ foo }) => [foo], (_ctx0) => { const n2 = t0() - const n1 = _createComponent(_component_Inner, null, { default: _withDestructure(({ bar }) => [bar], (_ctx1) => { + const n1 = _createComponent(_component_Inner, null, [{ default: _withDestructure(({ bar }) => [bar], (_ctx1) => { const n0 = _createTextNode(() => [_ctx0[0] + _ctx1[0] + _ctx.baz]) return n0 - }) }) + }) }]) const n3 = _createTextNode(() => [_ctx0[0] + _ctx.bar + _ctx.baz]) return [n1, n2, n3] - }) }, null, true) + }) }], true) return n5 }" `; exports[`compiler: transform slot > on component dynamically named slot 1`] = ` -"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, withDestructure as _withDestructure } from 'vue/vapor'; +"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, withDestructure as _withDestructure, createComponent as _createComponent } from 'vue/vapor'; export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n1 = _createComponent(_component_Comp, null, { }, [() => ({ + const n1 = _createComponent(_component_Comp, null, [[() => ({ name: _ctx.named, fn: _withDestructure(({ foo }) => [foo], (_ctx0) => { const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar]) return n0 }) - })], true) + })]], true) return n1 }" `; exports[`compiler: transform slot > on component named slot 1`] = ` -"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, withDestructure as _withDestructure } from 'vue/vapor'; +"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, withDestructure as _withDestructure, createComponent as _createComponent } from 'vue/vapor'; export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n1 = _createComponent(_component_Comp, null, { named: _withDestructure(({ foo }) => [foo], (_ctx0) => { + const n1 = _createComponent(_component_Comp, null, [{ named: _withDestructure(({ foo }) => [foo], (_ctx0) => { const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar]) return n0 - }) }, null, true) + }) }], true) return n1 }" `; exports[`compiler: transform slot > on-component default slot 1`] = ` -"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, withDestructure as _withDestructure } from 'vue/vapor'; +"import { resolveComponent as _resolveComponent, createTextNode as _createTextNode, withDestructure as _withDestructure, createComponent as _createComponent } from 'vue/vapor'; export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") - const n1 = _createComponent(_component_Comp, null, { default: _withDestructure(({ foo }) => [foo], (_ctx0) => { + const n1 = _createComponent(_component_Comp, null, [{ default: _withDestructure(({ foo }) => [foo], (_ctx0) => { const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar]) return n0 - }) }, null, true) + }) }], true) return n1 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index bb5b8970f..746ac44cc 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -182,9 +182,7 @@ describe('compiler: element transform', () => { bindingMetadata: { Comp: BindingTypes.SETUP_CONST }, }) expect(code).toMatchSnapshot() - expect(code).contains( - '_createComponent(_ctx.Comp, null, null, null, true)', - ) + expect(code).contains('_createComponent(_ctx.Comp, null, null, true)') }) test('generate multi root component', () => { diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts index 966ecfcbc..717347a18 100644 --- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts @@ -42,14 +42,16 @@ describe('compiler: transform slot', () => { id: 1, tag: 'Comp', props: [[]], - slots: { - default: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], + slots: [ + { + default: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], + }, }, }, - }, + ], }, ]) expect(ir.block.returns).toEqual([1]) @@ -73,19 +75,21 @@ describe('compiler: transform slot', () => { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', props: [[]], - slots: { - default: { - type: IRNodeTypes.BLOCK, - props: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ foo }', - ast: { - type: 'ArrowFunctionExpression', - params: [{ type: 'ObjectPattern' }], + slots: [ + { + default: { + type: IRNodeTypes.BLOCK, + props: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: '{ foo }', + ast: { + type: 'ArrowFunctionExpression', + params: [{ type: 'ObjectPattern' }], + }, }, }, }, - }, + ], }, ]) }) @@ -103,15 +107,17 @@ describe('compiler: transform slot', () => { { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', - slots: { - named: { - type: IRNodeTypes.BLOCK, - props: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ foo }', + slots: [ + { + named: { + type: IRNodeTypes.BLOCK, + props: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: '{ foo }', + }, }, }, - }, + ], }, ]) }) @@ -130,21 +136,23 @@ describe('compiler: transform slot', () => { { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', - dynamicSlots: [ - { - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'named', - isStatic: false, - }, - fn: { - type: IRNodeTypes.BLOCK, - props: { + slots: [ + [ + { + name: { type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ foo }', + content: 'named', + isStatic: false, + }, + fn: { + type: IRNodeTypes.BLOCK, + props: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: '{ foo }', + }, }, }, - }, + ], ], }, ]) @@ -165,20 +173,22 @@ describe('compiler: transform slot', () => { id: 4, tag: 'Comp', props: [[]], - slots: { - one: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], + slots: [ + { + one: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], + }, }, - }, - default: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{}, { template: 1 }, { template: 2 }], + default: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{}, { template: 1 }, { template: 2 }], + }, }, }, - }, + ], }, ]) }) @@ -207,31 +217,35 @@ describe('compiler: transform slot', () => { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', props: [[]], - slots: { - default: { - type: IRNodeTypes.BLOCK, - props: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ foo }', + slots: [ + { + default: { + type: IRNodeTypes.BLOCK, + props: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: '{ foo }', + }, }, }, - }, + ], }, ]) expect( - (ir.block.operation[0] as any).slots.default.operation[0], + (ir.block.operation[0] as any).slots[0].default.operation[0], ).toMatchObject({ type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Inner', - slots: { - default: { - type: IRNodeTypes.BLOCK, - props: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ bar }', + slots: [ + { + default: { + type: IRNodeTypes.BLOCK, + props: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: '{ bar }', + }, }, }, - }, + ], }) }) @@ -247,16 +261,17 @@ describe('compiler: transform slot', () => { { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', - slots: undefined, - dynamicSlots: [ - { - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'name', - isStatic: false, + slots: [ + [ + { + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'name', + isStatic: false, + }, + fn: { type: IRNodeTypes.BLOCK }, }, - fn: { type: IRNodeTypes.BLOCK }, - }, + ], ], }, ]) @@ -278,21 +293,22 @@ describe('compiler: transform slot', () => { { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', - slots: undefined, - dynamicSlots: [ - { - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'item', - isStatic: false, - }, - fn: { type: IRNodeTypes.BLOCK }, - loop: { - source: { content: 'list' }, - value: { content: 'item' }, - index: undefined, + slots: [ + [ + { + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'item', + isStatic: false, + }, + fn: { type: IRNodeTypes.BLOCK }, + loop: { + source: { content: 'list' }, + value: { content: 'item' }, + index: undefined, + }, }, - }, + ], ], }, ]) @@ -310,23 +326,24 @@ describe('compiler: transform slot', () => { { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', - slots: undefined, - dynamicSlots: [ - { - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'index', - isStatic: false, - }, - fn: { type: IRNodeTypes.BLOCK }, - loop: { - source: { content: 'list' }, - value: undefined, - index: { + slots: [ + [ + { + name: { type: NodeTypes.SIMPLE_EXPRESSION, + content: 'index', + isStatic: false, + }, + fn: { type: IRNodeTypes.BLOCK }, + loop: { + source: { content: 'list' }, + value: undefined, + index: { + type: NodeTypes.SIMPLE_EXPRESSION, + }, }, }, - }, + ], ], }, ]) @@ -350,23 +367,24 @@ describe('compiler: transform slot', () => { { type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Comp', - slots: undefined, - dynamicSlots: [ - { - slotType: DynamicSlotType.CONDITIONAL, - condition: { content: 'condition' }, - positive: { - slotType: DynamicSlotType.BASIC, - }, - negative: { + slots: [ + [ + { slotType: DynamicSlotType.CONDITIONAL, - condition: { content: 'anotherCondition' }, + condition: { content: 'condition' }, positive: { slotType: DynamicSlotType.BASIC, }, - negative: { slotType: DynamicSlotType.BASIC }, + negative: { + slotType: DynamicSlotType.CONDITIONAL, + condition: { content: 'anotherCondition' }, + positive: { + slotType: DynamicSlotType.BASIC, + }, + negative: { slotType: DynamicSlotType.BASIC }, + }, }, - }, + ], ], }, ]) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index b5773aa8d..a4e6979ee 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -1,18 +1,19 @@ import { camelize, extend, isArray } from '@vue/shared' import type { CodegenContext } from '../generate' import { - type ComponentBasicDynamicSlot, - type ComponentConditionalDynamicSlot, - type ComponentDynamicSlot, - type ComponentLoopDynamicSlot, - type ComponentSlotBlockIRNode, - type ComponentSlots, type CreateComponentIRNode, DynamicSlotType, IRDynamicPropsKind, type IRProp, type IRProps, type IRPropsStatic, + type IRSlotDynamic, + type IRSlotDynamicBasic, + type IRSlotDynamicConditional, + type IRSlotDynamicLoop, + type IRSlots, + type IRSlotsStatic, + type SlotBlockIRNode, } from '../ir' import { type CodeFragment, @@ -45,8 +46,9 @@ export function genCreateComponent( const { vaporHelper } = context const tag = genTag() - const { root, slots, dynamicSlots, once } = oper - const rawProps = genRawProps(oper.props, context) + const { root, props, slots, once } = oper + const rawProps = genRawProps(props, context) + const rawSlots = genRawSlots(slots, context) return [ NEWLINE, @@ -55,8 +57,7 @@ export function genCreateComponent( vaporHelper('createComponent'), tag, rawProps, - slots && genSlots(slots, context), - dynamicSlots && genDynamicSlots(dynamicSlots, context), + rawSlots, root ? 'true' : false, once && 'true', ), @@ -152,20 +153,31 @@ function genModelModifiers( return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`] } -function genSlots(slots: ComponentSlots, context: CodegenContext) { +function genRawSlots(slots: IRSlots[], context: CodegenContext) { + if (!slots.length) return + return genMulti( + slots.length > 1 ? DELIMITERS_ARRAY_NEWLINE : DELIMITERS_ARRAY, + ...slots.map(slots => + isArray(slots) + ? genDynamicSlots(slots, context) + : genStaticSlots(slots, context), + ), + ) +} + +function genStaticSlots(slots: IRSlotsStatic, context: CodegenContext) { const names = Object.keys(slots) return genMulti( names.length > 1 ? DELIMITERS_OBJECT_NEWLINE : DELIMITERS_OBJECT, ...names.map(name => [ - name, - ': ', + `${name}: `, ...genSlotBlockWithProps(slots[name], context), ]), ) } function genDynamicSlots( - dynamicSlots: ComponentDynamicSlot[], + dynamicSlots: IRSlotDynamic[], context: CodegenContext, ) { return genMulti( @@ -175,28 +187,27 @@ function genDynamicSlots( } function genDynamicSlot( - slot: ComponentDynamicSlot, + slot: IRSlotDynamic, context: CodegenContext, - top = false, + withFunction = false, ): CodeFragment[] { + let frag: CodeFragment[] switch (slot.slotType) { case DynamicSlotType.BASIC: - return top - ? ['() => (', ...genBasicDynamicSlot(slot, context), ')'] - : genBasicDynamicSlot(slot, context) + frag = genBasicDynamicSlot(slot, context) + break case DynamicSlotType.LOOP: - return top - ? ['() => (', ...genLoopSlot(slot, context), ')'] - : genLoopSlot(slot, context) + frag = genLoopSlot(slot, context) + break case DynamicSlotType.CONDITIONAL: - return top - ? ['() => (', ...genConditionalSlot(slot, context), ')'] - : genConditionalSlot(slot, context) + frag = genConditionalSlot(slot, context) + break } + return withFunction ? ['() => (', ...frag, ')'] : frag } function genBasicDynamicSlot( - slot: ComponentBasicDynamicSlot, + slot: IRSlotDynamicBasic, context: CodegenContext, ): CodeFragment[] { const { name, fn } = slot @@ -208,7 +219,7 @@ function genBasicDynamicSlot( } function genLoopSlot( - slot: ComponentLoopDynamicSlot, + slot: IRSlotDynamicLoop, context: CodegenContext, ): CodeFragment[] { const { name, fn, loop } = slot @@ -249,7 +260,7 @@ function genLoopSlot( } function genConditionalSlot( - slot: ComponentConditionalDynamicSlot, + slot: IRSlotDynamicConditional, context: CodegenContext, ): CodeFragment[] { const { condition, positive, negative } = slot @@ -266,10 +277,7 @@ function genConditionalSlot( ] } -function genSlotBlockWithProps( - oper: ComponentSlotBlockIRNode, - context: CodegenContext, -) { +function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) { let isDestructureAssignment = false let rawProps: string | undefined let propsName: string | undefined diff --git a/packages/compiler-vapor/src/ir/component.ts b/packages/compiler-vapor/src/ir/component.ts new file mode 100644 index 000000000..59ba51e14 --- /dev/null +++ b/packages/compiler-vapor/src/ir/component.ts @@ -0,0 +1,63 @@ +import type { SimpleExpressionNode } from '@vue/compiler-dom' +import type { DirectiveTransformResult } from '../transform' +import type { BlockIRNode, IRFor } from './index' + +// props +export interface IRProp extends Omit { + values: SimpleExpressionNode[] +} + +export enum IRDynamicPropsKind { + EXPRESSION, // v-bind="value" + ATTRIBUTE, // v-bind:[foo]="value" +} + +export type IRPropsStatic = IRProp[] +export interface IRPropsDynamicExpression { + kind: IRDynamicPropsKind.EXPRESSION + value: SimpleExpressionNode + handler?: boolean +} +export interface IRPropsDynamicAttribute extends IRProp { + kind: IRDynamicPropsKind.ATTRIBUTE +} +export type IRProps = + | IRPropsStatic + | IRPropsDynamicAttribute + | IRPropsDynamicExpression + +// slots +export interface SlotBlockIRNode extends BlockIRNode { + props?: SimpleExpressionNode +} +export type IRSlotsStatic = Record + +export enum DynamicSlotType { + BASIC, + LOOP, + CONDITIONAL, +} +export interface IRSlotDynamicBasic { + slotType: DynamicSlotType.BASIC + name: SimpleExpressionNode + fn: SlotBlockIRNode +} +export interface IRSlotDynamicLoop { + slotType: DynamicSlotType.LOOP + name: SimpleExpressionNode + fn: SlotBlockIRNode + loop: IRFor +} +export interface IRSlotDynamicConditional { + slotType: DynamicSlotType.CONDITIONAL + condition: SimpleExpressionNode + positive: IRSlotDynamicBasic + negative?: IRSlotDynamicBasic | IRSlotDynamicConditional +} + +export type IRSlotDynamic = + | IRSlotDynamicBasic + | IRSlotDynamicLoop + | IRSlotDynamicConditional +export type IRSlotsDynamic = IRSlotDynamic[] +export type IRSlots = IRSlotsStatic | IRSlotsDynamic diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir/index.ts similarity index 78% rename from packages/compiler-vapor/src/ir.ts rename to packages/compiler-vapor/src/ir/index.ts index b55538b74..02318b634 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir/index.ts @@ -7,11 +7,10 @@ import type { TemplateChildNode, } from '@vue/compiler-dom' import type { Prettify } from '@vue/shared' -import type { - DirectiveTransform, - DirectiveTransformResult, - NodeTransform, -} from './transform' +import type { DirectiveTransform, NodeTransform } from '../transform' +import type { IRProp, IRProps, IRSlots } from './component' + +export * from './component' export enum IRNodeTypes { ROOT, @@ -88,29 +87,6 @@ export interface ForIRNode extends BaseIRNode, IRFor { once: boolean } -export interface IRProp extends Omit { - values: SimpleExpressionNode[] -} - -export enum IRDynamicPropsKind { - EXPRESSION, // v-bind="value" - ATTRIBUTE, // v-bind:[foo]="value" -} - -export type IRPropsStatic = IRProp[] -export interface IRPropsDynamicExpression { - kind: IRDynamicPropsKind.EXPRESSION - value: SimpleExpressionNode - handler?: boolean -} -export interface IRPropsDynamicAttribute extends IRProp { - kind: IRDynamicPropsKind.ATTRIBUTE -} -export type IRProps = - | IRPropsStatic - | IRPropsDynamicAttribute - | IRPropsDynamicExpression - export interface SetPropIRNode extends BaseIRNode { type: IRNodeTypes.SET_PROP element: number @@ -207,51 +183,12 @@ export interface WithDirectiveIRNode extends BaseIRNode { asset?: boolean } -export interface ComponentSlotBlockIRNode extends BlockIRNode { - props?: SimpleExpressionNode -} -export type ComponentSlots = Record - -export enum DynamicSlotType { - BASIC, - LOOP, - CONDITIONAL, -} - -export interface ComponentBasicDynamicSlot { - slotType: DynamicSlotType.BASIC - name: SimpleExpressionNode - fn: ComponentSlotBlockIRNode -} - -export interface ComponentLoopDynamicSlot { - slotType: DynamicSlotType.LOOP - name: SimpleExpressionNode - fn: ComponentSlotBlockIRNode - loop: IRFor -} - -export interface ComponentConditionalDynamicSlot { - slotType: DynamicSlotType.CONDITIONAL - condition: SimpleExpressionNode - positive: ComponentBasicDynamicSlot - negative?: ComponentBasicDynamicSlot | ComponentConditionalDynamicSlot -} - -export type ComponentDynamicSlot = - | ComponentBasicDynamicSlot - | ComponentLoopDynamicSlot - | ComponentConditionalDynamicSlot - export interface CreateComponentIRNode extends BaseIRNode { type: IRNodeTypes.CREATE_COMPONENT_NODE id: number tag: string props: IRProps[] - - slots?: ComponentSlots - dynamicSlots?: ComponentDynamicSlot[] - + slots: IRSlots[] asset: boolean root: boolean once: boolean diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 48b00ffb7..27bc690a9 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -16,12 +16,11 @@ import { import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared' import { type BlockIRNode, - type ComponentDynamicSlot, - type ComponentSlots, DynamicFlag, type HackOptions, type IRDynamicInfo, IRNodeTypes, + type IRSlots, type OperationNode, type RootIRNode, type VaporDirectiveNode, @@ -81,8 +80,7 @@ export class TransformContext { component: Set = this.ir.component directive: Set = this.ir.directive - slots?: ComponentSlots - dynamicSlots?: ComponentDynamicSlot[] + slots: IRSlots[] = [] private globalId = 0 @@ -96,14 +94,12 @@ export class TransformContext { } enterBlock(ir: BlockIRNode, isVFor: boolean = false): () => void { - const { block, template, dynamic, childrenTemplate, slots, dynamicSlots } = - this + const { block, template, dynamic, childrenTemplate, slots } = this this.block = ir this.dynamic = ir.dynamic this.template = '' this.childrenTemplate = [] - this.slots = undefined - this.dynamicSlots = undefined + this.slots = [] isVFor && this.inVFor++ return () => { // exit @@ -113,7 +109,6 @@ export class TransformContext { this.dynamic = dynamic this.childrenTemplate = childrenTemplate this.slots = slots - this.dynamicSlots = dynamicSlots isVFor && this.inVFor-- } } diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 173cdb58f..67ef676b2 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -97,6 +97,7 @@ function transformComponentElement( const root = context.root === context.parent && context.parent.node.children.length === 1 + // console.log(context.slots) context.registerOperation({ type: IRNodeTypes.CREATE_COMPONENT_NODE, id: context.reference(), @@ -105,11 +106,9 @@ function transformComponentElement( asset, root, slots: context.slots, - dynamicSlots: context.dynamicSlots, once: context.inVOnce, }) - context.slots = undefined - context.dynamicSlots = undefined + context.slots = [] } function resolveSetupReference(name: string, context: TransformContext) { diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts index 6a8e18eb6..337db49f9 100644 --- a/packages/compiler-vapor/src/transforms/vSlot.ts +++ b/packages/compiler-vapor/src/transforms/vSlot.ts @@ -3,6 +3,7 @@ import { ElementTypes, ErrorCodes, NodeTypes, + type SimpleExpressionNode, type TemplateChildNode, createCompilerError, isTemplateNode, @@ -11,17 +12,21 @@ import { import type { NodeTransform, TransformContext } from '../transform' import { newBlock } from './utils' import { - type ComponentBasicDynamicSlot, - type ComponentConditionalDynamicSlot, - type ComponentSlotBlockIRNode, DynamicFlag, DynamicSlotType, type IRFor, + type IRSlotDynamic, + type IRSlotDynamicBasic, + type IRSlotDynamicConditional, + type IRSlots, + type IRSlotsDynamic, + type IRSlotsStatic, + type SlotBlockIRNode, type VaporDirectiveNode, } from '../ir' import { findDir, resolveExpression } from '../utils' +import { isArray } from '@vue/shared' -// TODO dynamic slots export const transformVSlot: NodeTransform = (node, context) => { if (node.type !== NodeTypes.ELEMENT) return @@ -38,8 +43,6 @@ export const transformVSlot: NodeTransform = (node, context) => { if (isComponent && children.length) { const arg = dir && dir.arg - const slotName = arg ? arg.content : 'default' - const nonSlotTemplateChildren = children.filter( n => isNonWhitespaceContent(node) && @@ -52,72 +55,53 @@ export const transformVSlot: NodeTransform = (node, context) => { context as TransformContext, ) - const slots = (context.slots ||= {}) - const dynamicSlots = (context.dynamicSlots ||= []) + const slots = context.slots return () => { onExit() - let hasOtherSlots = !!Object.keys(slots).length - - if (dir && (hasOtherSlots || dynamicSlots.length)) { + const hasOtherSlots = !!slots.length + if (dir && hasOtherSlots) { // already has on-component slot - this is incorrect usage. context.options.onError( createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, dir.loc), ) - // discarding other slots, referenced how compiler-core implements - Object.keys(slots).forEach(slotName => delete slots[slotName]) - dynamicSlots.length = 0 - hasOtherSlots = false + // TODO remove old one } if (nonSlotTemplateChildren.length) { - if (slots.default) { + if (hasStaticSlot(slots, 'default')) { context.options.onError( createCompilerError( ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN, nonSlotTemplateChildren[0].loc, ), ) - } else if (!arg || arg.isStatic) { - slots[slotName] = block } else { - dynamicSlots.push({ - slotType: DynamicSlotType.BASIC, - name: arg, - fn: block, - }) + registerSlot(slots, arg, block) + context.slots = slots } - context.slots = slots } else if (hasOtherSlots) { context.slots = slots } - - if (dynamicSlots.length) context.dynamicSlots = dynamicSlots } } else if (isSlotTemplate && dir) { - let { arg } = dir - context.dynamic.flags |= DynamicFlag.NON_TEMPLATE + const arg = dir.arg && resolveExpression(dir.arg) const vFor = findDir(node, 'for') const vIf = findDir(node, 'if') const vElse = findDir(node, /^else(-if)?$/, true /* allowEmpty */) - const slots = context.slots! - const dynamicSlots = context.dynamicSlots! - + const slots = context.slots const [block, onExit] = createSlotBlock( node, dir, context as TransformContext, ) - arg &&= resolveExpression(arg) - - if ((!arg || arg.isStatic) && !vFor && !vIf && !vElse) { - const slotName = arg ? arg.content : 'default' - - if (slots[slotName]) { + if (!vFor && !vIf && !vElse) { + const slotName = arg ? arg.isStatic && arg.content : 'default' + if (slotName && hasStaticSlot(slots, slotName)) { context.options.onError( createCompilerError( ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES, @@ -125,10 +109,10 @@ export const transformVSlot: NodeTransform = (node, context) => { ), ) } else { - slots[slotName] = block + registerSlot(slots, arg, block) } } else if (vIf) { - dynamicSlots.push({ + registerDynamicSlot(slots, { slotType: DynamicSlotType.CONDITIONAL, condition: vIf.exp!, positive: { @@ -138,31 +122,31 @@ export const transformVSlot: NodeTransform = (node, context) => { }, }) } else if (vElse) { - const vIfIR = dynamicSlots[dynamicSlots.length - 1] - if (vIfIR.slotType === DynamicSlotType.CONDITIONAL) { - let ifNode = vIfIR + const lastSlots = slots[slots.length - 1] as IRSlotsDynamic + const vIfSlot = lastSlots[lastSlots.length - 1] + if (vIfSlot.slotType === DynamicSlotType.CONDITIONAL) { + let ifNode = vIfSlot while ( ifNode.negative && ifNode.negative.slotType === DynamicSlotType.CONDITIONAL ) ifNode = ifNode.negative - const negative: - | ComponentBasicDynamicSlot - | ComponentConditionalDynamicSlot = vElse.exp - ? { - slotType: DynamicSlotType.CONDITIONAL, - condition: vElse.exp, - positive: { + const negative: IRSlotDynamicBasic | IRSlotDynamicConditional = + vElse.exp + ? { + slotType: DynamicSlotType.CONDITIONAL, + condition: vElse.exp, + positive: { + slotType: DynamicSlotType.BASIC, + name: arg!, + fn: block, + }, + } + : { slotType: DynamicSlotType.BASIC, name: arg!, fn: block, - }, - } - : { - slotType: DynamicSlotType.BASIC, - name: arg!, - fn: block, - } + } ifNode.negative = negative } else { context.options.onError( @@ -171,7 +155,7 @@ export const transformVSlot: NodeTransform = (node, context) => { } } else if (vFor) { if (vFor.forParseResult) { - dynamicSlots.push({ + registerDynamicSlot(slots, { slotType: DynamicSlotType.LOOP, name: arg!, fn: block, @@ -185,15 +169,9 @@ export const transformVSlot: NodeTransform = (node, context) => { ), ) } - } else { - dynamicSlots.push({ - slotType: DynamicSlotType.BASIC, - name: arg!, - fn: block, - }) } - return () => onExit() + return onExit } else if (!isComponent && dir) { context.options.onError( createCompilerError(ErrorCodes.X_V_SLOT_MISPLACED, dir.loc), @@ -201,12 +179,61 @@ export const transformVSlot: NodeTransform = (node, context) => { } } +// +// function transformComponentSlot( +// node: ElementNode, +// dir: VaporDirectiveNode | undefined, +// context: TransformContext, +// ) { +// const { children } = node +// const { slots } = context + +// } + +function ensureSlots(slots: IRSlots[], isStatic: boolean): IRSlots { + let lastSlots = slots[slots.length - 1] + const isLastSlotsStatic = !isArray(lastSlots) + if (!slots.length || isStatic !== isLastSlotsStatic) { + slots.push((lastSlots = isStatic ? {} : [])) + } + return lastSlots +} + +function registerSlot( + allSlots: IRSlots[], + name: SimpleExpressionNode | undefined, + block: SlotBlockIRNode, +) { + const isStatic = !name || name.isStatic + const slots = ensureSlots(allSlots, isStatic) + if (isStatic) { + ;(slots as IRSlotsStatic)[name ? name.content : 'default'] = block + } else { + ;(slots as IRSlotsDynamic).push({ + slotType: DynamicSlotType.BASIC, + name: name!, + fn: block, + }) + } +} + +function registerDynamicSlot(allSlots: IRSlots[], dynamic: IRSlotDynamic) { + const slots = ensureSlots(allSlots, false) as IRSlotsDynamic + slots.push(dynamic) +} + +function hasStaticSlot(slots: IRSlots[], name: string) { + return slots.some(slots => { + if (!isArray(slots)) return !!slots[name] + }) +} + function createSlotBlock( slotNode: ElementNode, dir: VaporDirectiveNode | undefined, context: TransformContext, -): [ComponentSlotBlockIRNode, () => void] { - const block: ComponentSlotBlockIRNode = newBlock(slotNode) +): [SlotBlockIRNode, () => void] { + const block: SlotBlockIRNode = newBlock(slotNode) block.props = dir && dir.exp const exitBlock = context.enterBlock(block) return [block, exitBlock] diff --git a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts index 2ba81b81b..18effd8e0 100644 --- a/packages/runtime-vapor/__tests__/componentAttrs.spec.ts +++ b/packages/runtime-vapor/__tests__/componentAttrs.spec.ts @@ -40,7 +40,6 @@ describe('attribute fallthrough', () => { }, ], null, - null, true, ) }, @@ -85,7 +84,6 @@ describe('attribute fallthrough', () => { }, ], null, - null, true, ) }, @@ -123,7 +121,6 @@ describe('attribute fallthrough', () => { }, ], null, - null, true, ) return n0 @@ -146,7 +143,6 @@ describe('attribute fallthrough', () => { }, ], null, - null, true, ) }, diff --git a/packages/runtime-vapor/__tests__/componentProps.spec.ts b/packages/runtime-vapor/__tests__/componentProps.spec.ts index 38c031ddb..f5d4114e1 100644 --- a/packages/runtime-vapor/__tests__/componentProps.spec.ts +++ b/packages/runtime-vapor/__tests__/componentProps.spec.ts @@ -244,7 +244,6 @@ describe('component: props', () => { id: () => _ctx.id, }, null, - null, true, ) }, diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 56c8ac86c..1b27f8fc3 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -98,11 +98,14 @@ describe('component: slots', () => { const { render } = define({ render() { - return createComponent(Child, {}, { _: 2 as any }, [ - () => - flag1.value - ? { name: 'one', fn: () => template('')() } - : { name: 'two', fn: () => template('
')() }, + return createComponent(Child, {}, [ + { _: 2 as any }, + [ + () => + flag1.value + ? { name: 'one', fn: () => template('')() } + : { name: 'two', fn: () => template('
')() }, + ], ]) }, }) @@ -134,11 +137,13 @@ describe('component: slots', () => { const { render } = define({ setup() { - return createComponent(Child, {}, {}, [ - () => - flag1.value - ? [{ name: 'header', fn: () => template('header')() }] - : [{ name: 'footer', fn: () => template('footer')() }], + return createComponent(Child, {}, [ + [ + () => + flag1.value + ? { name: 'header', fn: () => template('header')() } + : { name: 'footer', fn: () => template('footer')() }, + ], ]) }, }) @@ -172,9 +177,7 @@ describe('component: slots', () => { const { instance } = define({ render() { - return createComponent( - Comp, - {}, + return createComponent(Comp, {}, [ { default: () => { instanceInDefaultSlot = getCurrentInstance() @@ -182,25 +185,22 @@ describe('component: slots', () => { }, }, [ - () => [ - { - name: 'inVFor', - fn: () => { - instanceInVForSlot = getCurrentInstance() - return template('content')() - }, + () => ({ + name: 'inVFor', + fn: () => { + instanceInVForSlot = getCurrentInstance() + return template('content')() }, - ], + }), () => ({ name: 'inVIf', - key: '1', fn: () => { instanceInVIfSlot = getCurrentInstance() return template('content')() }, }), ], - ) + ]) }, }).render() @@ -223,19 +223,21 @@ describe('component: slots', () => { const { render } = define({ render() { - return createComponent(Child, {}, {}, [ - () => { - slotFn1() - return flag1.value - ? { name: 'one', fn: () => template('one')() } - : { name: 'two', fn: () => template('two')() } - }, - () => { - slotFn2() - return flag2.value - ? { name: 'three', fn: () => template('three')() } - : { name: 'four', fn: () => template('four')() } - }, + return createComponent(Child, {}, [ + [ + () => { + slotFn1() + return flag1.value + ? { name: 'one', fn: () => template('one')() } + : { name: 'two', fn: () => template('two')() } + }, + () => { + slotFn2() + return flag2.value + ? { name: 'three', fn: () => template('three')() } + : { name: 'four', fn: () => template('four')() } + }, + ], ]) }, }) @@ -253,15 +255,15 @@ describe('component: slots', () => { expect(instance.slots).toHaveProperty('two') expect(instance.slots).toHaveProperty('three') expect(slotFn1).toHaveBeenCalledTimes(2) - expect(slotFn2).toHaveBeenCalledTimes(1) + expect(slotFn2).toHaveBeenCalledTimes(2) flag2.value = false await nextTick() expect(instance.slots).toHaveProperty('two') expect(instance.slots).toHaveProperty('four') - expect(slotFn1).toHaveBeenCalledTimes(2) - expect(slotFn2).toHaveBeenCalledTimes(2) + expect(slotFn1).toHaveBeenCalledTimes(3) + expect(slotFn2).toHaveBeenCalledTimes(3) }) test.todo('should respect $stable flag', async () => { @@ -313,7 +315,7 @@ describe('component: slots', () => { return createComponent(Comp, {}, { header: () => template('header')() }) }).render() - expect(host.innerHTML).toBe('
header
') + expect(host.innerHTML).toBe('
header
') }) test('slot should be render correctly with binds', async () => { @@ -327,9 +329,7 @@ describe('component: slots', () => { }) const { host } = define(() => { - return createComponent( - Comp, - {}, + return createComponent(Comp, {}, [ { header: withDestructure( ({ title }) => [title], @@ -342,10 +342,10 @@ describe('component: slots', () => { }, ), }, - ) + ]) }).render() - expect(host.innerHTML).toBe('

header

') + expect(host.innerHTML).toBe('

header

') }) test('dynamic slot props', async () => { @@ -481,11 +481,13 @@ describe('component: slots', () => { const { host } = define(() => { // dynamic slot - return createComponent(Comp, {}, {}, [ - () => ({ - name: 'header', - fn: props => template(props.title)(), - }), + return createComponent(Comp, {}, [ + [ + () => ({ + name: 'header', + fn: (props: any) => template(props.title)(), + }), + ], ]) }).render() @@ -530,7 +532,7 @@ describe('component: slots', () => { return createComponent(Comp, {}, {}) }).render() - expect(host.innerHTML).toBe('
fallback
') + expect(host.innerHTML).toBe('
fallback
') }) test('dynamic slot should be updated correctly', async () => { @@ -548,11 +550,13 @@ describe('component: slots', () => { }) const { host } = define(() => { - return createComponent(Child, {}, {}, [ - () => - flag1.value - ? { name: 'one', fn: () => template('one content')() } - : { name: 'two', fn: () => template('two content')() }, + return createComponent(Child, {}, [ + [ + () => + flag1.value + ? { name: 'one', fn: () => template('one content')() } + : { name: 'two', fn: () => template('two content')() }, + ], ]) }).render() diff --git a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts index d8ea73f20..59777e6fc 100644 --- a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts @@ -69,7 +69,7 @@ describe('directive: v-show', () => { const { instance, host } = define({ render() { const n1 = t1() - const n2 = createComponent(Child, [], null, null, true) + const n2 = createComponent(Child, [], null, true) withDirectives(n2, [[vShow, () => visible.value]]) on(n1 as HTMLElement, 'click', () => handleClick) return [n1, n2] diff --git a/packages/runtime-vapor/__tests__/dom/prop.spec.ts b/packages/runtime-vapor/__tests__/dom/prop.spec.ts index 8f9215a8c..ab5a3d4e6 100644 --- a/packages/runtime-vapor/__tests__/dom/prop.spec.ts +++ b/packages/runtime-vapor/__tests__/dom/prop.spec.ts @@ -18,7 +18,7 @@ import { getCurrentScope } from '@vue/reactivity' let removeComponentInstance = NOOP beforeEach(() => { - const instance = createComponentInstance((() => {}) as any, {}, null, null) + const instance = createComponentInstance((() => {}) as any, {}, null) const reset = setCurrentInstance(instance) const prev = getCurrentScope() instance.scope.on() diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index 64180729f..15a37133f 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -5,14 +5,13 @@ import { } from './component' import { setupComponent } from './apiRender' import type { RawProps } from './componentProps' -import type { DynamicSlots, Slots } from './componentSlots' +import type { RawSlots } from './componentSlots' import { withAttrs } from './componentAttrs' export function createComponent( comp: Component, rawProps: RawProps | null = null, - slots: Slots | null = null, - dynamicSlots: DynamicSlots | null = null, + slots: RawSlots | null = null, singleRoot: boolean = false, once: boolean = false, ) { @@ -21,7 +20,6 @@ export function createComponent( comp, singleRoot ? withAttrs(rawProps) : rawProps, slots, - dynamicSlots, once, ) setupComponent(instance, singleRoot) diff --git a/packages/runtime-vapor/src/apiCreateVaporApp.ts b/packages/runtime-vapor/src/apiCreateVaporApp.ts index e3ffef020..f89ca4641 100644 --- a/packages/runtime-vapor/src/apiCreateVaporApp.ts +++ b/packages/runtime-vapor/src/apiCreateVaporApp.ts @@ -112,7 +112,6 @@ export function createVaporApp( rootComponent, rootProps, null, - null, false, context, ) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 228c7b78a..dbbc61016 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -22,12 +22,7 @@ import { emit, normalizeEmitsOptions, } from './componentEmits' -import { - type DynamicSlots, - type InternalSlots, - type Slots, - initSlots, -} from './componentSlots' +import { type RawSlots, type StaticSlots, initSlots } from './componentSlots' import { VaporLifecycleHooks } from './apiLifecycle' import { warn } from './warning' import { @@ -51,7 +46,7 @@ export type SetupContext = E extends any attrs: Data emit: EmitFn expose: (exposed?: Record) => void - slots: Readonly + slots: Readonly } : never @@ -179,13 +174,13 @@ export interface ComponentInternalInstance { emit: EmitFn emitted: Record | null attrs: Data - slots: InternalSlots + slots: StaticSlots refs: Data // exposed properties via expose() exposed?: Record attrsProxy?: Data - slotsProxy?: Slots + slotsProxy?: StaticSlots // lifecycle isMounted: boolean @@ -266,8 +261,7 @@ let uid = 0 export function createComponentInstance( component: Component, rawProps: RawProps | null, - slots: Slots | null, - dynamicSlots: DynamicSlots | null, + slots: RawSlots | null, once: boolean = false, // application root node only appContext?: AppContext, @@ -363,7 +357,7 @@ export function createComponentInstance( } instance.scope = new BlockEffectScope(instance, parent && parent.scope) initProps(instance, rawProps, !isFunction(component), once) - initSlots(instance, slots, dynamicSlots) + initSlots(instance, slots) instance.emit = emit.bind(null, instance) return instance @@ -417,7 +411,7 @@ function getAttrsProxy(instance: ComponentInternalInstance): Data { /** * Dev-only */ -function getSlotsProxy(instance: ComponentInternalInstance): Slots { +function getSlotsProxy(instance: ComponentInternalInstance): StaticSlots { return ( instance.slotsProxy || (instance.slotsProxy = new Proxy(instance.slots, { diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index 7fde8e62b..84364b87f 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -13,7 +13,6 @@ import { import { type Block, type Fragment, fragmentKey } from './apiRender' import { firstEffect, renderEffect } from './renderEffect' import { createComment, createTextNode, insert, remove } from './dom/element' -import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling' import type { NormalizedRawProps } from './componentProps' import type { Data } from '@vue/runtime-shared' import { mergeProps } from './dom/prop' @@ -24,82 +23,61 @@ export type Slot = ( ...args: IfAny ) => Block -export type InternalSlots = { - [name: string]: Slot | undefined -} - -export type Slots = Readonly - +export type StaticSlots = Record export interface DynamicSlot { name: string fn: Slot } - -type DynamicSlotFn = () => DynamicSlot | DynamicSlot[] - +export type DynamicSlotFn = () => DynamicSlot | undefined export type DynamicSlots = DynamicSlotFn[] +export type NormalizedRawSlots = Array +export type RawSlots = NormalizedRawSlots | StaticSlots | null export function initSlots( instance: ComponentInternalInstance, - rawSlots: InternalSlots | null = null, - dynamicSlots: DynamicSlots | null = null, + rawSlots: RawSlots | null = null, ) { - let slots: InternalSlots = {} + if (!rawSlots) return + if (!isArray(rawSlots)) rawSlots = [rawSlots] - for (const key in rawSlots) { - const slot = rawSlots[key] - if (slot) { - slots[key] = withCtx(slot) - } + if (rawSlots.length === 0 && !isArray(rawSlots[0])) { + instance.slots = rawSlots[0] + return } - if (dynamicSlots) { - slots = shallowReactive(slots) - const dynamicSlotRecords: Record[] = [] - dynamicSlots.forEach((fn, index) => { - firstEffect(instance, () => { - const slotRecord = (dynamicSlotRecords[index] = - dynamicSlotRecords[index] || {}) - const dynamicSlot: DynamicSlot | DynamicSlot[] = - callWithAsyncErrorHandling( - fn, - instance, - VaporErrorCodes.RENDER_FUNCTION, - ) - // array of dynamic slot generated by