mirror of https://github.com/vuejs/core.git
refactor: component slots (#238)
Co-authored-by: Doctor Wu <doctorwu@moego.pet> Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
This commit is contained in:
parent
bbde386a7c
commit
97f0b3bc33
|
@ -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
|
||||
}"
|
||||
|
|
|
@ -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
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -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
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -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
|
||||
}"
|
||||
|
|
|
@ -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("<div></div>")
|
|||
|
||||
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("<span></span>")
|
|||
|
||||
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
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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 },
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<DirectiveTransformResult, 'value'> {
|
||||
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<string, SlotBlockIRNode>
|
||||
}
|
||||
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
|
|
@ -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<DirectiveTransformResult, 'value'> {
|
||||
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<string, ComponentSlotBlockIRNode>
|
||||
|
||||
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
|
|
@ -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<T extends AllNode = AllNode> {
|
|||
component: Set<string> = this.ir.component
|
||||
directive: Set<string> = this.ir.directive
|
||||
|
||||
slots?: ComponentSlots
|
||||
dynamicSlots?: ComponentDynamicSlot[]
|
||||
slots: IRSlots[] = []
|
||||
|
||||
private globalId = 0
|
||||
|
||||
|
@ -96,14 +94,12 @@ export class TransformContext<T extends AllNode = AllNode> {
|
|||
}
|
||||
|
||||
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<T extends AllNode = AllNode> {
|
|||
this.dynamic = dynamic
|
||||
this.childrenTemplate = childrenTemplate
|
||||
this.slots = slots
|
||||
this.dynamicSlots = dynamicSlots
|
||||
isVFor && this.inVFor--
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<ElementNode>,
|
||||
)
|
||||
|
||||
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<ElementNode>,
|
||||
)
|
||||
|
||||
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) => {
|
|||
}
|
||||
}
|
||||
|
||||
// <Foo v-slot:default>
|
||||
function transformComponentSlot(
|
||||
node: ElementNode,
|
||||
dir: VaporDirectiveNode | undefined,
|
||||
context: TransformContext<ElementNode>,
|
||||
) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// <template #foo>
|
||||
function transformTemplateSlot(
|
||||
node: ElementNode,
|
||||
dir: VaporDirectiveNode,
|
||||
context: TransformContext<ElementNode>,
|
||||
) {
|
||||
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<ElementNode>,
|
||||
): [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]
|
||||
|
|
|
@ -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]),
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
},
|
||||
|
|
|
@ -244,7 +244,6 @@ describe('component: props', () => {
|
|||
id: () => _ctx.id,
|
||||
},
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
)
|
||||
},
|
||||
|
|
|
@ -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('<span></span>')() }
|
||||
|
@ -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('<div><h1>header</h1></div>')
|
||||
|
@ -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')() }
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -112,7 +112,6 @@ export function createVaporApp(
|
|||
rootComponent,
|
||||
rootProps,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
context,
|
||||
)
|
||||
|
|
|
@ -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 = EmitsOptions> = E extends any
|
|||
attrs: Data
|
||||
emit: EmitFn<E>
|
||||
expose: (exposed?: Record<string, any>) => void
|
||||
slots: Readonly<InternalSlots>
|
||||
slots: Readonly<StaticSlots>
|
||||
}
|
||||
: never
|
||||
|
||||
|
@ -179,13 +174,13 @@ export interface ComponentInternalInstance {
|
|||
emit: EmitFn
|
||||
emitted: Record<string, boolean> | null
|
||||
attrs: Data
|
||||
slots: InternalSlots
|
||||
slots: StaticSlots
|
||||
refs: Data
|
||||
// exposed properties via expose()
|
||||
exposed?: Record<string, any>
|
||||
|
||||
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, {
|
||||
|
|
|
@ -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<T extends any = any> = (
|
|||
...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)>
|
||||
) => Block
|
||||
|
||||
export type InternalSlots = {
|
||||
[name: string]: Slot | undefined
|
||||
}
|
||||
export type StaticSlots = Record<string, Slot>
|
||||
export type DynamicSlot = { name: string; fn: Slot }
|
||||
export type DynamicSlotFn = () => DynamicSlot | DynamicSlot[] | undefined
|
||||
export type NormalizedRawSlots = Array<StaticSlots | DynamicSlotFn>
|
||||
export type RawSlots = NormalizedRawSlots | StaticSlots | null
|
||||
|
||||
export type Slots = Readonly<InternalSlots>
|
||||
|
||||
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<string, boolean>[] = []
|
||||
dynamicSlots.forEach((fn, index) => {
|
||||
const resolved: StaticSlots = (instance.slots = shallowReactive({}))
|
||||
const keys: Set<string>[] = []
|
||||
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 <template v-for="..." #[...]>
|
||||
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<DynamicSlotFn>
|
||||
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 <template v-if="..." #foo>
|
||||
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<string>) {
|
||||
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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue