diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
index 4acc91a94..533252b6b 100644
--- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
@@ -33,16 +33,20 @@ 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 n0 = _createIf(() => (true), () => {
- const n3 = t0()
- const n2 = _createComponent(_component_Bar)
- _withDirectives(n2, [[_directive_hello, void 0, void 0, { world: true }]])
- _insert(n2, n3)
- return n3
- })
- return n0
- } }, null, true)
+ const n4 = _createComponent(_component_Comp, null, [
+ {
+ default: () => {
+ const n0 = _createIf(() => (true), () => {
+ const n3 = t0()
+ const n2 = _createComponent(_component_Bar)
+ _withDirectives(n2, [[_directive_hello, void 0, void 0, { world: true }]])
+ _insert(n2, n3)
+ return n3
+ })
+ return n0
+ }
+ }
+ ], 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..518b4408b 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
@@ -6,82 +6,90 @@ const t0 = _template("foo")
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
- const n2 = _createComponent(_component_Comp, null, null, [() => ({
- name: _ctx.name,
- fn: () => {
- const n0 = t0()
- return n0
- }
- })], true)
+ const n2 = _createComponent(_component_Comp, null, [
+ () => ({
+ name: _ctx.name,
+ fn: () => {
+ const n0 = t0()
+ return n0
+ }
+ })
+ ], 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) => ({
- name: item,
- fn: _withDestructure(({ bar }) => [bar], (_ctx0) => {
- const n0 = t0()
- return n0
- })
- })))], true)
+ const n2 = _createComponent(_component_Comp, null, [
+ () => (_createForSlots(_ctx.list, (item) => ({
+ name: item,
+ fn: _withDestructure(({ bar }) => [bar], (_ctx0) => {
+ const n0 = t0()
+ return n0
+ })
+ })))
+ ], 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) => ({
- name: index,
- fn: () => {
- const n0 = t0()
- return n0
- }
- })))], true)
+ const n2 = _createComponent(_component_Comp, null, [
+ () => (_createForSlots(_ctx.list, (_, __, index) => ({
+ name: index,
+ fn: () => {
+ const n0 = t0()
+ return n0
+ }
+ })))
+ ], 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
- ? {
- name: "condition",
- fn: () => {
- const n0 = t0()
- return n0
- }
- }
- : _ctx.anotherCondition
+ const n6 = _createComponent(_component_Comp, null, [
+ () => (_ctx.condition
? {
- name: "condition",
- fn: _withDestructure(({ foo, bar }) => [foo, bar], (_ctx0) => {
- const n2 = t1()
- return n2
- })
- }
- : {
name: "condition",
fn: () => {
- const n4 = t2()
- return n4
+ const n0 = t0()
+ return n0
}
- })], true)
+ }
+ : _ctx.anotherCondition
+ ? {
+ name: "condition",
+ fn: _withDestructure(({ foo, bar }) => [foo, bar], (_ctx0) => {
+ const n2 = t1()
+ return n2
+ })
+ }
+ : {
+ name: "condition",
+ fn: () => {
+ const n4 = t2()
+ return n4
+ }
+ })
+ ], true)
return n6
}"
`;
@@ -92,10 +100,14 @@ const t0 = _template("
")
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
- const n1 = _createComponent(_component_Comp, null, { default: () => {
- const n0 = t0()
- return n0
- } }, null, true)
+ const n1 = _createComponent(_component_Comp, null, [
+ {
+ default: () => {
+ const n0 = t0()
+ return n0
+ }
+ }
+ ], true)
return n1
}"
`;
@@ -108,79 +120,99 @@ const t2 = _template("")
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
- const n4 = _createComponent(_component_Comp, null, {
- one: () => {
- const n0 = t0()
- return n0
- },
- default: () => {
- const n2 = t1()
- const n3 = t2()
- return [n2, n3]
+ const n4 = _createComponent(_component_Comp, null, [
+ {
+ one: () => {
+ const n0 = t0()
+ return n0
+ },
+ default: () => {
+ const n2 = t1()
+ 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 n2 = t0()
- 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)
+ 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 n0 = _createTextNode(() => [_ctx0[0] + _ctx1[0] + _ctx.baz])
+ return n0
+ })
+ }
+ ])
+ const n3 = _createTextNode(() => [_ctx0[0] + _ctx.bar + _ctx.baz])
+ return [n1, n2, n3]
+ })
+ }
+ ], 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, { }, [() => ({
- name: _ctx.named,
- fn: _withDestructure(({ foo }) => [foo], (_ctx0) => {
- const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
- return n0
+ 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 n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
- return n0
- }) }, null, true)
+ const n1 = _createComponent(_component_Comp, null, [
+ {
+ named: _withDestructure(({ foo }) => [foo], (_ctx0) => {
+ const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
+ return n0
+ })
+ }
+ ], 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 n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
- return n0
- }) }, null, true)
+ const n1 = _createComponent(_component_Comp, null, [
+ {
+ default: _withDestructure(({ foo }) => [foo], (_ctx0) => {
+ const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
+ return n0
+ })
+ }
+ ], 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..b5445d60e 100644
--- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
@@ -1,7 +1,7 @@
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
import {
- DynamicSlotType,
IRNodeTypes,
+ IRSlotType,
transformChildren,
transformElement,
transformSlotOutlet,
@@ -42,14 +42,19 @@ describe('compiler: transform slot', () => {
id: 1,
tag: 'Comp',
props: [[]],
- slots: {
- default: {
- type: IRNodeTypes.BLOCK,
- dynamic: {
- children: [{ template: 0 }],
+ slots: [
+ {
+ slotType: IRSlotType.STATIC,
+ slots: {
+ default: {
+ type: IRNodeTypes.BLOCK,
+ dynamic: {
+ children: [{ template: 0 }],
+ },
+ },
},
},
- },
+ ],
},
])
expect(ir.block.returns).toEqual([1])
@@ -73,19 +78,24 @@ 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: [
+ {
+ slotType: IRSlotType.STATIC,
+ slots: {
+ default: {
+ type: IRNodeTypes.BLOCK,
+ props: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ foo }',
+ ast: {
+ type: 'ArrowFunctionExpression',
+ params: [{ type: 'ObjectPattern' }],
+ },
+ },
},
},
},
- },
+ ],
},
])
})
@@ -103,15 +113,20 @@ describe('compiler: transform slot', () => {
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Comp',
- slots: {
- named: {
- type: IRNodeTypes.BLOCK,
- props: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: '{ foo }',
+ slots: [
+ {
+ slotType: IRSlotType.STATIC,
+ slots: {
+ named: {
+ type: IRNodeTypes.BLOCK,
+ props: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ foo }',
+ },
+ },
},
},
- },
+ ],
},
])
})
@@ -130,7 +145,7 @@ describe('compiler: transform slot', () => {
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Comp',
- dynamicSlots: [
+ slots: [
{
name: {
type: NodeTypes.SIMPLE_EXPRESSION,
@@ -165,20 +180,25 @@ describe('compiler: transform slot', () => {
id: 4,
tag: 'Comp',
props: [[]],
- slots: {
- one: {
- type: IRNodeTypes.BLOCK,
- dynamic: {
- children: [{ template: 0 }],
+ slots: [
+ {
+ slotType: IRSlotType.STATIC,
+ 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 +227,41 @@ 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: [
+ {
+ slotType: IRSlotType.STATIC,
+ 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].slots.default.operation[0],
).toMatchObject({
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Inner',
- slots: {
- default: {
- type: IRNodeTypes.BLOCK,
- props: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: '{ bar }',
+ slots: [
+ {
+ slotType: IRSlotType.STATIC,
+ slots: {
+ default: {
+ type: IRNodeTypes.BLOCK,
+ props: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ bar }',
+ },
+ },
},
},
- },
+ ],
})
})
@@ -247,8 +277,7 @@ describe('compiler: transform slot', () => {
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Comp',
- slots: undefined,
- dynamicSlots: [
+ slots: [
{
name: {
type: NodeTypes.SIMPLE_EXPRESSION,
@@ -278,8 +307,7 @@ describe('compiler: transform slot', () => {
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Comp',
- slots: undefined,
- dynamicSlots: [
+ slots: [
{
name: {
type: NodeTypes.SIMPLE_EXPRESSION,
@@ -310,8 +338,7 @@ describe('compiler: transform slot', () => {
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Comp',
- slots: undefined,
- dynamicSlots: [
+ slots: [
{
name: {
type: NodeTypes.SIMPLE_EXPRESSION,
@@ -350,21 +377,20 @@ describe('compiler: transform slot', () => {
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Comp',
- slots: undefined,
- dynamicSlots: [
+ slots: [
{
- slotType: DynamicSlotType.CONDITIONAL,
+ slotType: IRSlotType.CONDITIONAL,
condition: { content: 'condition' },
positive: {
- slotType: DynamicSlotType.BASIC,
+ slotType: IRSlotType.DYNAMIC,
},
negative: {
- slotType: DynamicSlotType.CONDITIONAL,
+ slotType: IRSlotType.CONDITIONAL,
condition: { content: 'anotherCondition' },
positive: {
- slotType: DynamicSlotType.BASIC,
+ slotType: IRSlotType.DYNAMIC,
},
- negative: { slotType: DynamicSlotType.BASIC },
+ negative: { slotType: IRSlotType.DYNAMIC },
},
},
],
diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts
index b5773aa8d..e7b0bc0a8 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,
+ IRSlotType,
+ 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,51 +153,51 @@ function genModelModifiers(
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
}
-function genSlots(slots: ComponentSlots, context: CodegenContext) {
+function genRawSlots(slots: IRSlots[], context: CodegenContext) {
+ if (!slots.length) return
+ return genMulti(
+ DELIMITERS_ARRAY_NEWLINE,
+ ...slots.map(slot =>
+ slot.slotType === IRSlotType.STATIC
+ ? genStaticSlots(slot, context)
+ : genDynamicSlot(slot, context, true),
+ ),
+ )
+}
+
+function genStaticSlots({ slots }: IRSlotsStatic, context: CodegenContext) {
const names = Object.keys(slots)
return genMulti(
- names.length > 1 ? DELIMITERS_OBJECT_NEWLINE : DELIMITERS_OBJECT,
+ DELIMITERS_OBJECT_NEWLINE,
...names.map(name => [
- name,
- ': ',
+ `${name}: `,
...genSlotBlockWithProps(slots[name], context),
]),
)
}
-function genDynamicSlots(
- dynamicSlots: ComponentDynamicSlot[],
- context: CodegenContext,
-) {
- return genMulti(
- dynamicSlots.length > 1 ? DELIMITERS_ARRAY_NEWLINE : DELIMITERS_ARRAY,
- ...dynamicSlots.map(slot => genDynamicSlot(slot, context, true)),
- )
-}
-
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)
- case DynamicSlotType.LOOP:
- return top
- ? ['() => (', ...genLoopSlot(slot, context), ')']
- : genLoopSlot(slot, context)
- case DynamicSlotType.CONDITIONAL:
- return top
- ? ['() => (', ...genConditionalSlot(slot, context), ')']
- : genConditionalSlot(slot, context)
+ case IRSlotType.DYNAMIC:
+ frag = genBasicDynamicSlot(slot, context)
+ break
+ case IRSlotType.LOOP:
+ frag = genLoopSlot(slot, context)
+ break
+ case IRSlotType.CONDITIONAL:
+ frag = genConditionalSlot(slot, context)
+ break
}
+ return withFunction ? ['() => (', ...frag, ')'] : frag
}
function genBasicDynamicSlot(
- slot: ComponentBasicDynamicSlot,
+ slot: IRSlotDynamicBasic,
context: CodegenContext,
): CodeFragment[] {
const { name, fn } = slot
@@ -208,7 +209,7 @@ function genBasicDynamicSlot(
}
function genLoopSlot(
- slot: ComponentLoopDynamicSlot,
+ slot: IRSlotDynamicLoop,
context: CodegenContext,
): CodeFragment[] {
const { name, fn, loop } = slot
@@ -249,7 +250,7 @@ function genLoopSlot(
}
function genConditionalSlot(
- slot: ComponentConditionalDynamicSlot,
+ slot: IRSlotDynamicConditional,
context: CodegenContext,
): CodeFragment[] {
const { condition, positive, negative } = slot
@@ -266,10 +267,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..c2bd99f7d
--- /dev/null
+++ b/packages/compiler-vapor/src/ir/component.ts
@@ -0,0 +1,66 @@
+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 enum IRSlotType {
+ STATIC,
+ DYNAMIC,
+ LOOP,
+ CONDITIONAL,
+}
+export type IRSlotsStatic = {
+ slotType: IRSlotType.STATIC
+ slots: Record
+}
+export interface IRSlotDynamicBasic {
+ slotType: IRSlotType.DYNAMIC
+ name: SimpleExpressionNode
+ fn: SlotBlockIRNode
+}
+export interface IRSlotDynamicLoop {
+ slotType: IRSlotType.LOOP
+ name: SimpleExpressionNode
+ fn: SlotBlockIRNode
+ loop: IRFor
+}
+export interface IRSlotDynamicConditional {
+ slotType: IRSlotType.CONDITIONAL
+ condition: SimpleExpressionNode
+ positive: IRSlotDynamicBasic
+ negative?: IRSlotDynamicBasic | IRSlotDynamicConditional
+}
+
+export type IRSlotDynamic =
+ | IRSlotDynamicBasic
+ | IRSlotDynamicLoop
+ | IRSlotDynamicConditional
+export type IRSlots = IRSlotsStatic | IRSlotDynamic
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..c5c50ad9f 100644
--- a/packages/compiler-vapor/src/transforms/transformElement.ts
+++ b/packages/compiler-vapor/src/transforms/transformElement.ts
@@ -105,11 +105,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..d1bf1c6b0 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,19 @@ 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,
+ IRSlotType,
+ type IRSlots,
+ type IRSlotsStatic,
+ type SlotBlockIRNode,
type VaporDirectiveNode,
} from '../ir'
import { findDir, resolveExpression } from '../utils'
-// TODO dynamic slots
export const transformVSlot: NodeTransform = (node, context) => {
if (node.type !== NodeTypes.ELEMENT) return
@@ -37,163 +40,17 @@ export const transformVSlot: NodeTransform = (node, context) => {
parent.node.tagType === ElementTypes.COMPONENT
if (isComponent && children.length) {
- const arg = dir && dir.arg
- const slotName = arg ? arg.content : 'default'
-
- const nonSlotTemplateChildren = children.filter(
- n =>
- isNonWhitespaceContent(node) &&
- !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
- )
-
- const [block, onExit] = createSlotBlock(
+ return transformComponentSlot(
node,
dir,
context as TransformContext,
)
-
- const slots = (context.slots ||= {})
- const dynamicSlots = (context.dynamicSlots ||= [])
-
- return () => {
- onExit()
-
- let hasOtherSlots = !!Object.keys(slots).length
-
- if (dir && (hasOtherSlots || dynamicSlots.length)) {
- // 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
- }
-
- if (nonSlotTemplateChildren.length) {
- if (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,
- })
- }
- 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 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 [block, onExit] = createSlotBlock(
+ return transformTemplateSlot(
node,
dir,
context as TransformContext,
)
-
- arg &&= resolveExpression(arg)
-
- if ((!arg || arg.isStatic) && !vFor && !vIf && !vElse) {
- const slotName = arg ? arg.content : 'default'
-
- if (slots[slotName]) {
- context.options.onError(
- createCompilerError(
- ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES,
- dir.loc,
- ),
- )
- } else {
- slots[slotName] = block
- }
- } else if (vIf) {
- dynamicSlots.push({
- slotType: DynamicSlotType.CONDITIONAL,
- condition: vIf.exp!,
- positive: {
- slotType: DynamicSlotType.BASIC,
- name: arg!,
- fn: block,
- },
- })
- } else if (vElse) {
- const vIfIR = dynamicSlots[dynamicSlots.length - 1]
- if (vIfIR.slotType === DynamicSlotType.CONDITIONAL) {
- let ifNode = vIfIR
- while (
- ifNode.negative &&
- ifNode.negative.slotType === DynamicSlotType.CONDITIONAL
- )
- ifNode = ifNode.negative
- const negative:
- | ComponentBasicDynamicSlot
- | ComponentConditionalDynamicSlot = vElse.exp
- ? {
- slotType: DynamicSlotType.CONDITIONAL,
- condition: vElse.exp,
- positive: {
- slotType: DynamicSlotType.BASIC,
- name: arg!,
- fn: block,
- },
- }
- : {
- slotType: DynamicSlotType.BASIC,
- name: arg!,
- fn: block,
- }
- ifNode.negative = negative
- } else {
- context.options.onError(
- createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, vElse.loc),
- )
- }
- } else if (vFor) {
- if (vFor.forParseResult) {
- dynamicSlots.push({
- slotType: DynamicSlotType.LOOP,
- name: arg!,
- fn: block,
- loop: vFor.forParseResult as IRFor,
- })
- } else {
- context.options.onError(
- createCompilerError(
- ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION,
- vFor.loc,
- ),
- )
- }
- } else {
- dynamicSlots.push({
- slotType: DynamicSlotType.BASIC,
- name: arg!,
- fn: block,
- })
- }
-
- return () => onExit()
} else if (!isComponent && dir) {
context.options.onError(
createCompilerError(ErrorCodes.X_V_SLOT_MISPLACED, dir.loc),
@@ -201,12 +58,183 @@ export const transformVSlot: NodeTransform = (node, context) => {
}
}
+//
+function transformComponentSlot(
+ node: ElementNode,
+ dir: VaporDirectiveNode | undefined,
+ context: TransformContext,
+) {
+ const { children } = node
+ const arg = dir && dir.arg
+ const nonSlotTemplateChildren = children.filter(
+ n =>
+ isNonWhitespaceContent(node) &&
+ !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
+ )
+
+ const [block, onExit] = createSlotBlock(node, dir, context)
+
+ const { slots } = context
+
+ return () => {
+ onExit()
+
+ 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),
+ )
+ return
+ }
+
+ if (nonSlotTemplateChildren.length) {
+ if (hasStaticSlot(slots, 'default')) {
+ context.options.onError(
+ createCompilerError(
+ ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
+ nonSlotTemplateChildren[0].loc,
+ ),
+ )
+ } else {
+ registerSlot(slots, arg, block)
+ context.slots = slots
+ }
+ } else if (hasOtherSlots) {
+ context.slots = slots
+ }
+ }
+}
+
+//
+function transformTemplateSlot(
+ node: ElementNode,
+ dir: VaporDirectiveNode,
+ context: TransformContext,
+) {
+ 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
+ const [block, onExit] = createSlotBlock(node, dir, context)
+
+ 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, dir.loc),
+ )
+ } else {
+ registerSlot(slots, arg, block)
+ }
+ } else if (vIf) {
+ registerDynamicSlot(slots, {
+ slotType: IRSlotType.CONDITIONAL,
+ condition: vIf.exp!,
+ positive: {
+ slotType: IRSlotType.DYNAMIC,
+ name: arg!,
+ fn: block,
+ },
+ })
+ } else if (vElse) {
+ const vIfSlot = slots[slots.length - 1] as IRSlotDynamic
+ if (vIfSlot.slotType === IRSlotType.CONDITIONAL) {
+ let ifNode = vIfSlot
+ while (
+ ifNode.negative &&
+ ifNode.negative.slotType === IRSlotType.CONDITIONAL
+ )
+ ifNode = ifNode.negative
+ const negative: IRSlotDynamicBasic | IRSlotDynamicConditional = vElse.exp
+ ? {
+ slotType: IRSlotType.CONDITIONAL,
+ condition: vElse.exp,
+ positive: {
+ slotType: IRSlotType.DYNAMIC,
+ name: arg!,
+ fn: block,
+ },
+ }
+ : {
+ slotType: IRSlotType.DYNAMIC,
+ name: arg!,
+ fn: block,
+ }
+ ifNode.negative = negative
+ } else {
+ context.options.onError(
+ createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, vElse.loc),
+ )
+ }
+ } else if (vFor) {
+ if (vFor.forParseResult) {
+ registerDynamicSlot(slots, {
+ slotType: IRSlotType.LOOP,
+ name: arg!,
+ fn: block,
+ loop: vFor.forParseResult as IRFor,
+ })
+ } else {
+ context.options.onError(
+ createCompilerError(ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION, vFor.loc),
+ )
+ }
+ }
+
+ return onExit
+}
+
+function ensureStaticSlots(slots: IRSlots[]): IRSlotsStatic['slots'] {
+ let lastSlots = slots[slots.length - 1]
+ if (!slots.length || lastSlots.slotType !== IRSlotType.STATIC) {
+ slots.push(
+ (lastSlots = {
+ slotType: IRSlotType.STATIC,
+ slots: {},
+ }),
+ )
+ }
+ return lastSlots.slots
+}
+
+function registerSlot(
+ slots: IRSlots[],
+ name: SimpleExpressionNode | undefined,
+ block: SlotBlockIRNode,
+) {
+ const isStatic = !name || name.isStatic
+ if (isStatic) {
+ const staticSlots = ensureStaticSlots(slots)
+ staticSlots[name ? name.content : 'default'] = block
+ } else {
+ slots.push({
+ slotType: IRSlotType.DYNAMIC,
+ name: name!,
+ fn: block,
+ })
+ }
+}
+
+function registerDynamicSlot(allSlots: IRSlots[], dynamic: IRSlotDynamic) {
+ allSlots.push(dynamic)
+}
+
+function hasStaticSlot(slots: IRSlots[], name: string) {
+ return slots.some(slot => {
+ if (slot.slotType === IRSlotType.STATIC) return !!slot.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__/apiSetupContext.spec.ts b/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts
index 60d46d8e2..7bee76beb 100644
--- a/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts
+++ b/packages/runtime-vapor/__tests__/apiSetupContext.spec.ts
@@ -149,7 +149,7 @@ describe('api: setup context', () => {
const { html } = define({
render() {
- return createComponent(Child, null, null, [
+ return createComponent(Child, null, [
() => ({
name: 'foo',
fn: () => createTextNode(() => [id.value]),
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..2ea5715be 100644
--- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts
+++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts
@@ -2,6 +2,7 @@
import {
createComponent,
+ createForSlots,
createSlot,
createVaporApp,
defineComponent,
@@ -98,7 +99,7 @@ describe('component: slots', () => {
const { render } = define({
render() {
- return createComponent(Child, {}, { _: 2 as any }, [
+ return createComponent(Child, {}, [
() =>
flag1.value
? { name: 'one', fn: () => template('')() }
@@ -134,11 +135,11 @@ describe('component: slots', () => {
const { render } = define({
setup() {
- return createComponent(Child, {}, {}, [
+ return createComponent(Child, {}, [
() =>
flag1.value
- ? [{ name: 'header', fn: () => template('header')() }]
- : [{ name: 'footer', fn: () => template('footer')() }],
+ ? { name: 'header', fn: () => template('header')() }
+ : { name: 'footer', fn: () => template('footer')() },
])
},
})
@@ -172,35 +173,28 @@ describe('component: slots', () => {
const { instance } = define({
render() {
- return createComponent(
- Comp,
- {},
+ return createComponent(Comp, {}, [
{
default: () => {
instanceInDefaultSlot = getCurrentInstance()
return template('content')()
},
},
- [
- () => [
- {
- name: 'inVFor',
- fn: () => {
- instanceInVForSlot = getCurrentInstance()
- return template('content')()
- },
- },
- ],
- () => ({
- name: 'inVIf',
- key: '1',
- fn: () => {
- instanceInVIfSlot = getCurrentInstance()
- return template('content')()
- },
- }),
- ],
- )
+ () => ({
+ name: 'inVFor',
+ fn: () => {
+ instanceInVForSlot = getCurrentInstance()
+ return template('content')()
+ },
+ }),
+ () => ({
+ name: 'inVIf',
+ fn: () => {
+ instanceInVIfSlot = getCurrentInstance()
+ return template('content')()
+ },
+ }),
+ ])
},
}).render()
@@ -223,7 +217,7 @@ describe('component: slots', () => {
const { render } = define({
render() {
- return createComponent(Child, {}, {}, [
+ return createComponent(Child, {}, [
() => {
slotFn1()
return flag1.value
@@ -264,6 +258,39 @@ describe('component: slots', () => {
expect(slotFn2).toHaveBeenCalledTimes(2)
})
+ test('should work with createFlorSlots', async () => {
+ const loop = ref([1, 2, 3])
+
+ let instance: any
+ const Child = () => {
+ instance = getCurrentInstance()
+ return template('child')()
+ }
+
+ const { render } = define({
+ setup() {
+ return createComponent(Child, {}, [
+ () =>
+ createForSlots(loop.value, (item, i) => ({
+ name: item,
+ fn: () => template(item + i)(),
+ })),
+ ])
+ },
+ })
+ render()
+
+ expect(instance.slots).toHaveProperty('1')
+ expect(instance.slots).toHaveProperty('2')
+ expect(instance.slots).toHaveProperty('3')
+ loop.value.push(4)
+ await nextTick()
+ expect(instance.slots).toHaveProperty('4')
+ loop.value.shift()
+ await nextTick()
+ expect(instance.slots).not.toHaveProperty('1')
+ })
+
test.todo('should respect $stable flag', async () => {
// TODO: $stable flag?
})
@@ -327,9 +354,7 @@ describe('component: slots', () => {
})
const { host } = define(() => {
- return createComponent(
- Comp,
- {},
+ return createComponent(Comp, {}, [
{
header: withDestructure(
({ title }) => [title],
@@ -342,7 +367,7 @@ describe('component: slots', () => {
},
),
},
- )
+ ])
}).render()
expect(host.innerHTML).toBe('header
')
@@ -481,10 +506,10 @@ describe('component: slots', () => {
const { host } = define(() => {
// dynamic slot
- return createComponent(Comp, {}, {}, [
+ return createComponent(Comp, {}, [
() => ({
name: 'header',
- fn: props => template(props.title)(),
+ fn: (props: any) => template(props.title)(),
}),
])
}).render()
@@ -548,7 +573,7 @@ describe('component: slots', () => {
})
const { host } = define(() => {
- return createComponent(Child, {}, {}, [
+ return createComponent(Child, {}, [
() =>
flag1.value
? { name: 'one', fn: () => template('one content')() }
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..022f1dca3 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,75 @@ export type Slot = (
...args: IfAny
) => Block
-export type InternalSlots = {
- [name: string]: Slot | undefined
-}
+export type StaticSlots = Record
+export type DynamicSlot = { name: string; fn: Slot }
+export type DynamicSlotFn = () => DynamicSlot | DynamicSlot[] | undefined
+export type NormalizedRawSlots = Array
+export type RawSlots = NormalizedRawSlots | StaticSlots | null
-export type Slots = Readonly
-
-export interface DynamicSlot {
- name: string
- fn: Slot
-}
-
-type DynamicSlotFn = () => DynamicSlot | DynamicSlot[]
-
-export type DynamicSlots = DynamicSlotFn[]
+export const isDynamicSlotFn = isFunction as (
+ val: StaticSlots | DynamicSlotFn,
+) => val is DynamicSlotFn
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 === 1 && !isDynamicSlotFn(rawSlots[0])) {
+ instance.slots = rawSlots[0]
+ return
}
- if (dynamicSlots) {
- slots = shallowReactive(slots)
- const dynamicSlotRecords: Record[] = []
- dynamicSlots.forEach((fn, index) => {
+ const resolved: StaticSlots = (instance.slots = shallowReactive({}))
+ const keys: Set[] = []
+ rawSlots.forEach((slots, index) => {
+ const isDynamicSlot = isDynamicSlotFn(slots)
+ if (isDynamicSlot) {
firstEffect(instance, () => {
- const slotRecord = (dynamicSlotRecords[index] =
- dynamicSlotRecords[index] || {})
- const dynamicSlot: DynamicSlot | DynamicSlot[] =
- callWithAsyncErrorHandling(
- fn,
- instance,
- VaporErrorCodes.RENDER_FUNCTION,
- )
- // array of dynamic slot generated by
- if (isArray(dynamicSlot)) {
- for (let j = 0; j < dynamicSlot.length; j++) {
- slots[dynamicSlot[j].name] = withCtx(dynamicSlot[j].fn)
- slotRecord[dynamicSlot[j].name] = true
+ const recordNames = keys[index] || (keys[index] = new Set())
+ let dynamicSlot: ReturnType
+ if (isDynamicSlotFn(slots)) {
+ dynamicSlot = slots()
+ if (isArray(dynamicSlot)) {
+ for (const slot of dynamicSlot) {
+ registerSlot(slot.name, slot.fn, recordNames)
+ }
+ } else if (dynamicSlot) {
+ registerSlot(dynamicSlot.name, dynamicSlot.fn, recordNames)
}
- } else if (dynamicSlot) {
- // conditional single slot generated by
- slots[dynamicSlot.name] = withCtx(dynamicSlot.fn)
- slotRecord[dynamicSlot.name] = true
+ } else {
}
- // delete stale slots
- for (const key in slotRecord) {
+ for (const name of recordNames) {
if (
- slotRecord[key] &&
- !(dynamicSlot && isArray(dynamicSlot)
- ? dynamicSlot.some(s => s.name === key)
- : dynamicSlot.name === key)
+ !(isArray(dynamicSlot)
+ ? dynamicSlot.some(s => s.name === name)
+ : dynamicSlot && dynamicSlot.name === name)
) {
- slotRecord[key] = false
- delete slots[key]
+ recordNames.delete(name)
+ delete resolved[name]
}
}
})
- })
- }
+ } else {
+ for (const name in slots) {
+ registerSlot(name, slots[name])
+ }
+ }
+ })
- instance.slots = slots
+ function registerSlot(name: string, fn: Slot, recordNames?: Set) {
+ resolved[name] = withCtx(fn)
+ recordNames && recordNames.add(name)
+ }
function withCtx(fn: Slot): Slot {
return (...args: any[]) => {
const reset = setCurrentInstance(instance.parent!)
try {
- return fn(...args)
+ return fn(...(args as any))
} finally {
reset()
}