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,7 +33,9 @@ export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const _directive_hello = _resolveDirective("hello")
|
const _directive_hello = _resolveDirective("hello")
|
||||||
const _directive_test = _resolveDirective("test")
|
const _directive_test = _resolveDirective("test")
|
||||||
const n4 = _createComponent(_component_Comp, null, { default: () => {
|
const n4 = _createComponent(_component_Comp, null, [
|
||||||
|
{
|
||||||
|
default: () => {
|
||||||
const n0 = _createIf(() => (true), () => {
|
const n0 = _createIf(() => (true), () => {
|
||||||
const n3 = t0()
|
const n3 = t0()
|
||||||
const n2 = _createComponent(_component_Bar)
|
const n2 = _createComponent(_component_Bar)
|
||||||
|
@ -42,7 +44,9 @@ export function render(_ctx) {
|
||||||
return n3
|
return n3
|
||||||
})
|
})
|
||||||
return n0
|
return n0
|
||||||
} }, null, true)
|
}
|
||||||
|
}
|
||||||
|
], true)
|
||||||
_withDirectives(n4, [[_directive_test]])
|
_withDirectives(n4, [[_directive_test]])
|
||||||
return n4
|
return n4
|
||||||
}"
|
}"
|
||||||
|
|
|
@ -5,7 +5,7 @@ exports[`compiler: element transform > component > do not resolve component from
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Example = _resolveComponent("Example")
|
const _component_Example = _resolveComponent("Example")
|
||||||
const n0 = _createComponent(_component_Example, null, null, null, true)
|
const n0 = _createComponent(_component_Example, null, null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -25,7 +25,7 @@ exports[`compiler: element transform > component > generate single root componen
|
||||||
"import { createComponent as _createComponent } from 'vue/vapor';
|
"import { createComponent as _createComponent } from 'vue/vapor';
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const n0 = _createComponent(_ctx.Comp, null, null, null, true)
|
const n0 = _createComponent(_ctx.Comp, null, null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -35,21 +35,21 @@ exports[`compiler: element transform > component > import + resolve component 1`
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Foo = _resolveComponent("Foo")
|
const _component_Foo = _resolveComponent("Foo")
|
||||||
const n0 = _createComponent(_component_Foo, null, null, null, true)
|
const n0 = _createComponent(_component_Foo, null, null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: element transform > component > resolve component from setup bindings (inline const) 1`] = `
|
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
|
return n0
|
||||||
})()"
|
})()"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: element transform > component > resolve component from setup bindings (inline) 1`] = `
|
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
|
return n0
|
||||||
})()"
|
})()"
|
||||||
`;
|
`;
|
||||||
|
@ -58,14 +58,14 @@ exports[`compiler: element transform > component > resolve component from setup
|
||||||
"import { createComponent as _createComponent } from 'vue/vapor';
|
"import { createComponent as _createComponent } from 'vue/vapor';
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const n0 = _createComponent(_ctx.Example, null, null, null, true)
|
const n0 = _createComponent(_ctx.Example, null, null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: element transform > component > resolve namespaced component from props bindings (inline) 1`] = `
|
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
|
return n0
|
||||||
})()"
|
})()"
|
||||||
`;
|
`;
|
||||||
|
@ -74,14 +74,14 @@ exports[`compiler: element transform > component > resolve namespaced component
|
||||||
"import { createComponent as _createComponent } from 'vue/vapor';
|
"import { createComponent as _createComponent } from 'vue/vapor';
|
||||||
|
|
||||||
export function render(_ctx) {
|
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
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: element transform > component > resolve namespaced component from setup bindings (inline const) 1`] = `
|
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
|
return n0
|
||||||
})()"
|
})()"
|
||||||
`;
|
`;
|
||||||
|
@ -90,7 +90,7 @@ exports[`compiler: element transform > component > resolve namespaced component
|
||||||
"import { createComponent as _createComponent } from 'vue/vapor';
|
"import { createComponent as _createComponent } from 'vue/vapor';
|
||||||
|
|
||||||
export function render(_ctx) {
|
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
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -102,7 +102,7 @@ export function render(_ctx) {
|
||||||
const _component_Foo = _resolveComponent("Foo")
|
const _component_Foo = _resolveComponent("Foo")
|
||||||
const n0 = _createComponent(_component_Foo, [
|
const n0 = _createComponent(_component_Foo, [
|
||||||
{ onBar: () => $event => (_ctx.handleBar($event)) }
|
{ onBar: () => $event => (_ctx.handleBar($event)) }
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -117,7 +117,7 @@ export function render(_ctx) {
|
||||||
id: () => ("foo"),
|
id: () => ("foo"),
|
||||||
class: () => ("bar")
|
class: () => ("bar")
|
||||||
}
|
}
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -129,7 +129,7 @@ export function render(_ctx) {
|
||||||
const _component_Foo = _resolveComponent("Foo")
|
const _component_Foo = _resolveComponent("Foo")
|
||||||
const n0 = _createComponent(_component_Foo, [
|
const n0 = _createComponent(_component_Foo, [
|
||||||
() => (_ctx.obj)
|
() => (_ctx.obj)
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -142,7 +142,7 @@ export function render(_ctx) {
|
||||||
const n0 = _createComponent(_component_Foo, [
|
const n0 = _createComponent(_component_Foo, [
|
||||||
{ id: () => ("foo") },
|
{ id: () => ("foo") },
|
||||||
() => (_ctx.obj)
|
() => (_ctx.obj)
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -155,7 +155,7 @@ export function render(_ctx) {
|
||||||
const n0 = _createComponent(_component_Foo, [
|
const n0 = _createComponent(_component_Foo, [
|
||||||
() => (_ctx.obj),
|
() => (_ctx.obj),
|
||||||
{ id: () => ("foo") }
|
{ id: () => ("foo") }
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -169,7 +169,7 @@ export function render(_ctx) {
|
||||||
{ id: () => ("foo") },
|
{ id: () => ("foo") },
|
||||||
() => (_ctx.obj),
|
() => (_ctx.obj),
|
||||||
{ class: () => ("bar") }
|
{ class: () => ("bar") }
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -181,7 +181,7 @@ export function render(_ctx) {
|
||||||
const _component_Foo = _resolveComponent("Foo")
|
const _component_Foo = _resolveComponent("Foo")
|
||||||
const n0 = _createComponent(_component_Foo, [
|
const n0 = _createComponent(_component_Foo, [
|
||||||
() => (_toHandlers(_ctx.obj))
|
() => (_toHandlers(_ctx.obj))
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -195,7 +195,7 @@ export function render(_ctx) {
|
||||||
const n0 = _createComponent(_component_Foo, [
|
const n0 = _createComponent(_component_Foo, [
|
||||||
() => ({ [_toHandlerKey(_ctx.foo-_ctx.bar)]: () => _ctx.bar }),
|
() => ({ [_toHandlerKey(_ctx.foo-_ctx.bar)]: () => _ctx.bar }),
|
||||||
() => ({ [_toHandlerKey(_ctx.baz)]: () => _ctx.qux })
|
() => ({ [_toHandlerKey(_ctx.baz)]: () => _ctx.qux })
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -208,7 +208,7 @@ export function render(_ctx) {
|
||||||
const n0 = _createComponent(_component_Foo, [
|
const n0 = _createComponent(_component_Foo, [
|
||||||
() => ({ [_ctx.foo-_ctx.bar]: _ctx.bar }),
|
() => ({ [_ctx.foo-_ctx.bar]: _ctx.bar }),
|
||||||
() => ({ [_ctx.baz]: _ctx.qux })
|
() => ({ [_ctx.baz]: _ctx.qux })
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -9,7 +9,7 @@ export function render(_ctx) {
|
||||||
{ modelValue: () => (_ctx.foo),
|
{ modelValue: () => (_ctx.foo),
|
||||||
"onUpdate:modelValue": () => $event => (_ctx.foo = $event),
|
"onUpdate:modelValue": () => $event => (_ctx.foo = $event),
|
||||||
modelModifiers: () => ({ trim: true, "bar-baz": true }) }
|
modelModifiers: () => ({ trim: true, "bar-baz": true }) }
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -22,7 +22,7 @@ export function render(_ctx) {
|
||||||
const n0 = _createComponent(_component_Comp, [
|
const n0 = _createComponent(_component_Comp, [
|
||||||
{ modelValue: () => (_ctx.foo),
|
{ modelValue: () => (_ctx.foo),
|
||||||
"onUpdate:modelValue": () => $event => (_ctx.foo = $event) }
|
"onUpdate:modelValue": () => $event => (_ctx.foo = $event) }
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -41,7 +41,7 @@ export function render(_ctx) {
|
||||||
"onUpdate:bar": () => $event => (_ctx.bar = $event),
|
"onUpdate:bar": () => $event => (_ctx.bar = $event),
|
||||||
barModifiers: () => ({ number: true })
|
barModifiers: () => ({ number: true })
|
||||||
}
|
}
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -54,7 +54,7 @@ export function render(_ctx) {
|
||||||
const n0 = _createComponent(_component_Comp, [
|
const n0 = _createComponent(_component_Comp, [
|
||||||
{ bar: () => (_ctx.foo),
|
{ bar: () => (_ctx.foo),
|
||||||
"onUpdate:bar": () => $event => (_ctx.foo = $event) }
|
"onUpdate:bar": () => $event => (_ctx.foo = $event) }
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -71,7 +71,7 @@ export function render(_ctx) {
|
||||||
() => ({ [_ctx.bar]: _ctx.bar,
|
() => ({ [_ctx.bar]: _ctx.bar,
|
||||||
["onUpdate:" + _ctx.bar]: () => $event => (_ctx.bar = $event),
|
["onUpdate:" + _ctx.bar]: () => $event => (_ctx.bar = $event),
|
||||||
[_ctx.bar + "Modifiers"]: () => ({ number: true }) })
|
[_ctx.bar + "Modifiers"]: () => ({ number: true }) })
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -84,7 +84,7 @@ export function render(_ctx) {
|
||||||
const n0 = _createComponent(_component_Comp, [
|
const n0 = _createComponent(_component_Comp, [
|
||||||
() => ({ [_ctx.arg]: _ctx.foo,
|
() => ({ [_ctx.arg]: _ctx.foo,
|
||||||
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event) })
|
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event) })
|
||||||
], null, null, true)
|
], null, true)
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function render(_ctx) {
|
||||||
const n1 = t0()
|
const n1 = t0()
|
||||||
const n0 = _createComponent(_component_Comp, [
|
const n0 = _createComponent(_component_Comp, [
|
||||||
{ id: () => (_ctx.foo) }
|
{ id: () => (_ctx.foo) }
|
||||||
], null, null, null, true)
|
], null, null, true)
|
||||||
_insert(n0, n1)
|
_insert(n0, n1)
|
||||||
return n1
|
return n1
|
||||||
}"
|
}"
|
||||||
|
|
|
@ -6,60 +6,67 @@ const t0 = _template("foo")
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n2 = _createComponent(_component_Comp, null, null, [() => ({
|
const n2 = _createComponent(_component_Comp, null, [
|
||||||
|
() => ({
|
||||||
name: _ctx.name,
|
name: _ctx.name,
|
||||||
fn: () => {
|
fn: () => {
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
return n0
|
return n0
|
||||||
}
|
}
|
||||||
})], true)
|
})
|
||||||
|
], true)
|
||||||
return n2
|
return n2
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = `
|
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")
|
const t0 = _template("foo")
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n2 = _createComponent(_component_Comp, null, null, [() => (_createForSlots(_ctx.list, (item) => ({
|
const n2 = _createComponent(_component_Comp, null, [
|
||||||
|
() => (_createForSlots(_ctx.list, (item) => ({
|
||||||
name: item,
|
name: item,
|
||||||
fn: _withDestructure(({ bar }) => [bar], (_ctx0) => {
|
fn: _withDestructure(({ bar }) => [bar], (_ctx0) => {
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
return n0
|
return n0
|
||||||
})
|
})
|
||||||
})))], true)
|
})))
|
||||||
|
], true)
|
||||||
return n2
|
return n2
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform slot > dynamic slots name w/ v-for and provide absent key 1`] = `
|
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")
|
const t0 = _template("foo")
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n2 = _createComponent(_component_Comp, null, null, [() => (_createForSlots(_ctx.list, (_, __, index) => ({
|
const n2 = _createComponent(_component_Comp, null, [
|
||||||
|
() => (_createForSlots(_ctx.list, (_, __, index) => ({
|
||||||
name: index,
|
name: index,
|
||||||
fn: () => {
|
fn: () => {
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
return n0
|
return n0
|
||||||
}
|
}
|
||||||
})))], true)
|
})))
|
||||||
|
], true)
|
||||||
return n2
|
return n2
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform slot > dynamic slots name w/ v-if / v-else[-if] 1`] = `
|
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 t0 = _template("condition slot")
|
||||||
const t1 = _template("another condition")
|
const t1 = _template("another condition")
|
||||||
const t2 = _template("else condition")
|
const t2 = _template("else condition")
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n6 = _createComponent(_component_Comp, null, null, [() => (_ctx.condition
|
const n6 = _createComponent(_component_Comp, null, [
|
||||||
|
() => (_ctx.condition
|
||||||
? {
|
? {
|
||||||
name: "condition",
|
name: "condition",
|
||||||
fn: () => {
|
fn: () => {
|
||||||
|
@ -81,7 +88,8 @@ export function render(_ctx) {
|
||||||
const n4 = t2()
|
const n4 = t2()
|
||||||
return n4
|
return n4
|
||||||
}
|
}
|
||||||
})], true)
|
})
|
||||||
|
], true)
|
||||||
return n6
|
return n6
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -92,10 +100,14 @@ const t0 = _template("<div></div>")
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n1 = _createComponent(_component_Comp, null, { default: () => {
|
const n1 = _createComponent(_component_Comp, null, [
|
||||||
|
{
|
||||||
|
default: () => {
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
return n0
|
return n0
|
||||||
} }, null, true)
|
}
|
||||||
|
}
|
||||||
|
], true)
|
||||||
return n1
|
return n1
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
@ -108,7 +120,8 @@ const t2 = _template("<span></span>")
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n4 = _createComponent(_component_Comp, null, {
|
const n4 = _createComponent(_component_Comp, null, [
|
||||||
|
{
|
||||||
one: () => {
|
one: () => {
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
return n0
|
return n0
|
||||||
|
@ -118,69 +131,88 @@ export function render(_ctx) {
|
||||||
const n3 = t2()
|
const n3 = t2()
|
||||||
return [n2, n3]
|
return [n2, n3]
|
||||||
}
|
}
|
||||||
}, null, true)
|
}
|
||||||
|
], true)
|
||||||
return n4
|
return n4
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform slot > nested slots scoping 1`] = `
|
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(" ")
|
const t0 = _template(" ")
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const _component_Inner = _resolveComponent("Inner")
|
const _component_Inner = _resolveComponent("Inner")
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n5 = _createComponent(_component_Comp, null, { default: _withDestructure(({ foo }) => [foo], (_ctx0) => {
|
const n5 = _createComponent(_component_Comp, null, [
|
||||||
|
{
|
||||||
|
default: _withDestructure(({ foo }) => [foo], (_ctx0) => {
|
||||||
const n2 = t0()
|
const n2 = t0()
|
||||||
const n1 = _createComponent(_component_Inner, null, { default: _withDestructure(({ bar }) => [bar], (_ctx1) => {
|
const n1 = _createComponent(_component_Inner, null, [
|
||||||
|
{
|
||||||
|
default: _withDestructure(({ bar }) => [bar], (_ctx1) => {
|
||||||
const n0 = _createTextNode(() => [_ctx0[0] + _ctx1[0] + _ctx.baz])
|
const n0 = _createTextNode(() => [_ctx0[0] + _ctx1[0] + _ctx.baz])
|
||||||
return n0
|
return n0
|
||||||
}) })
|
})
|
||||||
|
}
|
||||||
|
])
|
||||||
const n3 = _createTextNode(() => [_ctx0[0] + _ctx.bar + _ctx.baz])
|
const n3 = _createTextNode(() => [_ctx0[0] + _ctx.bar + _ctx.baz])
|
||||||
return [n1, n2, n3]
|
return [n1, n2, n3]
|
||||||
}) }, null, true)
|
})
|
||||||
|
}
|
||||||
|
], true)
|
||||||
return n5
|
return n5
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform slot > on component dynamically named slot 1`] = `
|
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) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n1 = _createComponent(_component_Comp, null, { }, [() => ({
|
const n1 = _createComponent(_component_Comp, null, [
|
||||||
|
() => ({
|
||||||
name: _ctx.named,
|
name: _ctx.named,
|
||||||
fn: _withDestructure(({ foo }) => [foo], (_ctx0) => {
|
fn: _withDestructure(({ foo }) => [foo], (_ctx0) => {
|
||||||
const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
|
const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
|
||||||
return n0
|
return n0
|
||||||
})
|
})
|
||||||
})], true)
|
})
|
||||||
|
], true)
|
||||||
return n1
|
return n1
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform slot > on component named slot 1`] = `
|
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) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n1 = _createComponent(_component_Comp, null, { named: _withDestructure(({ foo }) => [foo], (_ctx0) => {
|
const n1 = _createComponent(_component_Comp, null, [
|
||||||
|
{
|
||||||
|
named: _withDestructure(({ foo }) => [foo], (_ctx0) => {
|
||||||
const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
|
const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
|
||||||
return n0
|
return n0
|
||||||
}) }, null, true)
|
})
|
||||||
|
}
|
||||||
|
], true)
|
||||||
return n1
|
return n1
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform slot > on-component default slot 1`] = `
|
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) {
|
export function render(_ctx) {
|
||||||
const _component_Comp = _resolveComponent("Comp")
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
const n1 = _createComponent(_component_Comp, null, { default: _withDestructure(({ foo }) => [foo], (_ctx0) => {
|
const n1 = _createComponent(_component_Comp, null, [
|
||||||
|
{
|
||||||
|
default: _withDestructure(({ foo }) => [foo], (_ctx0) => {
|
||||||
const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
|
const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
|
||||||
return n0
|
return n0
|
||||||
}) }, null, true)
|
})
|
||||||
|
}
|
||||||
|
], true)
|
||||||
return n1
|
return n1
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -182,9 +182,7 @@ describe('compiler: element transform', () => {
|
||||||
bindingMetadata: { Comp: BindingTypes.SETUP_CONST },
|
bindingMetadata: { Comp: BindingTypes.SETUP_CONST },
|
||||||
})
|
})
|
||||||
expect(code).toMatchSnapshot()
|
expect(code).toMatchSnapshot()
|
||||||
expect(code).contains(
|
expect(code).contains('_createComponent(_ctx.Comp, null, null, true)')
|
||||||
'_createComponent(_ctx.Comp, null, null, null, true)',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('generate multi root component', () => {
|
test('generate multi root component', () => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
|
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
|
||||||
import {
|
import {
|
||||||
DynamicSlotType,
|
|
||||||
IRNodeTypes,
|
IRNodeTypes,
|
||||||
|
IRSlotType,
|
||||||
transformChildren,
|
transformChildren,
|
||||||
transformElement,
|
transformElement,
|
||||||
transformSlotOutlet,
|
transformSlotOutlet,
|
||||||
|
@ -42,6 +42,9 @@ describe('compiler: transform slot', () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
props: [[]],
|
props: [[]],
|
||||||
|
slots: [
|
||||||
|
{
|
||||||
|
slotType: IRSlotType.STATIC,
|
||||||
slots: {
|
slots: {
|
||||||
default: {
|
default: {
|
||||||
type: IRNodeTypes.BLOCK,
|
type: IRNodeTypes.BLOCK,
|
||||||
|
@ -51,6 +54,8 @@ describe('compiler: transform slot', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
])
|
])
|
||||||
expect(ir.block.returns).toEqual([1])
|
expect(ir.block.returns).toEqual([1])
|
||||||
expect(ir.block.dynamic).toMatchObject({
|
expect(ir.block.dynamic).toMatchObject({
|
||||||
|
@ -73,6 +78,9 @@ describe('compiler: transform slot', () => {
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
props: [[]],
|
props: [[]],
|
||||||
|
slots: [
|
||||||
|
{
|
||||||
|
slotType: IRSlotType.STATIC,
|
||||||
slots: {
|
slots: {
|
||||||
default: {
|
default: {
|
||||||
type: IRNodeTypes.BLOCK,
|
type: IRNodeTypes.BLOCK,
|
||||||
|
@ -87,6 +95,8 @@ describe('compiler: transform slot', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -103,6 +113,9 @@ describe('compiler: transform slot', () => {
|
||||||
{
|
{
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
|
slots: [
|
||||||
|
{
|
||||||
|
slotType: IRSlotType.STATIC,
|
||||||
slots: {
|
slots: {
|
||||||
named: {
|
named: {
|
||||||
type: IRNodeTypes.BLOCK,
|
type: IRNodeTypes.BLOCK,
|
||||||
|
@ -113,6 +126,8 @@ describe('compiler: transform slot', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -130,7 +145,7 @@ describe('compiler: transform slot', () => {
|
||||||
{
|
{
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
dynamicSlots: [
|
slots: [
|
||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
@ -165,6 +180,9 @@ describe('compiler: transform slot', () => {
|
||||||
id: 4,
|
id: 4,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
props: [[]],
|
props: [[]],
|
||||||
|
slots: [
|
||||||
|
{
|
||||||
|
slotType: IRSlotType.STATIC,
|
||||||
slots: {
|
slots: {
|
||||||
one: {
|
one: {
|
||||||
type: IRNodeTypes.BLOCK,
|
type: IRNodeTypes.BLOCK,
|
||||||
|
@ -180,6 +198,8 @@ describe('compiler: transform slot', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -207,6 +227,9 @@ describe('compiler: transform slot', () => {
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
props: [[]],
|
props: [[]],
|
||||||
|
slots: [
|
||||||
|
{
|
||||||
|
slotType: IRSlotType.STATIC,
|
||||||
slots: {
|
slots: {
|
||||||
default: {
|
default: {
|
||||||
type: IRNodeTypes.BLOCK,
|
type: IRNodeTypes.BLOCK,
|
||||||
|
@ -217,12 +240,17 @@ describe('compiler: transform slot', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
])
|
])
|
||||||
expect(
|
expect(
|
||||||
(ir.block.operation[0] as any).slots.default.operation[0],
|
(ir.block.operation[0] as any).slots[0].slots.default.operation[0],
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Inner',
|
tag: 'Inner',
|
||||||
|
slots: [
|
||||||
|
{
|
||||||
|
slotType: IRSlotType.STATIC,
|
||||||
slots: {
|
slots: {
|
||||||
default: {
|
default: {
|
||||||
type: IRNodeTypes.BLOCK,
|
type: IRNodeTypes.BLOCK,
|
||||||
|
@ -232,6 +260,8 @@ describe('compiler: transform slot', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -247,8 +277,7 @@ describe('compiler: transform slot', () => {
|
||||||
{
|
{
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
slots: undefined,
|
slots: [
|
||||||
dynamicSlots: [
|
|
||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
@ -278,8 +307,7 @@ describe('compiler: transform slot', () => {
|
||||||
{
|
{
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
slots: undefined,
|
slots: [
|
||||||
dynamicSlots: [
|
|
||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
@ -310,8 +338,7 @@ describe('compiler: transform slot', () => {
|
||||||
{
|
{
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
slots: undefined,
|
slots: [
|
||||||
dynamicSlots: [
|
|
||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
@ -350,21 +377,20 @@ describe('compiler: transform slot', () => {
|
||||||
{
|
{
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
tag: 'Comp',
|
tag: 'Comp',
|
||||||
slots: undefined,
|
slots: [
|
||||||
dynamicSlots: [
|
|
||||||
{
|
{
|
||||||
slotType: DynamicSlotType.CONDITIONAL,
|
slotType: IRSlotType.CONDITIONAL,
|
||||||
condition: { content: 'condition' },
|
condition: { content: 'condition' },
|
||||||
positive: {
|
positive: {
|
||||||
slotType: DynamicSlotType.BASIC,
|
slotType: IRSlotType.DYNAMIC,
|
||||||
},
|
},
|
||||||
negative: {
|
negative: {
|
||||||
slotType: DynamicSlotType.CONDITIONAL,
|
slotType: IRSlotType.CONDITIONAL,
|
||||||
condition: { content: 'anotherCondition' },
|
condition: { content: 'anotherCondition' },
|
||||||
positive: {
|
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 { camelize, extend, isArray } from '@vue/shared'
|
||||||
import type { CodegenContext } from '../generate'
|
import type { CodegenContext } from '../generate'
|
||||||
import {
|
import {
|
||||||
type ComponentBasicDynamicSlot,
|
|
||||||
type ComponentConditionalDynamicSlot,
|
|
||||||
type ComponentDynamicSlot,
|
|
||||||
type ComponentLoopDynamicSlot,
|
|
||||||
type ComponentSlotBlockIRNode,
|
|
||||||
type ComponentSlots,
|
|
||||||
type CreateComponentIRNode,
|
type CreateComponentIRNode,
|
||||||
DynamicSlotType,
|
|
||||||
IRDynamicPropsKind,
|
IRDynamicPropsKind,
|
||||||
type IRProp,
|
type IRProp,
|
||||||
type IRProps,
|
type IRProps,
|
||||||
type IRPropsStatic,
|
type IRPropsStatic,
|
||||||
|
type IRSlotDynamic,
|
||||||
|
type IRSlotDynamicBasic,
|
||||||
|
type IRSlotDynamicConditional,
|
||||||
|
type IRSlotDynamicLoop,
|
||||||
|
IRSlotType,
|
||||||
|
type IRSlots,
|
||||||
|
type IRSlotsStatic,
|
||||||
|
type SlotBlockIRNode,
|
||||||
} from '../ir'
|
} from '../ir'
|
||||||
import {
|
import {
|
||||||
type CodeFragment,
|
type CodeFragment,
|
||||||
|
@ -45,8 +46,9 @@ export function genCreateComponent(
|
||||||
const { vaporHelper } = context
|
const { vaporHelper } = context
|
||||||
|
|
||||||
const tag = genTag()
|
const tag = genTag()
|
||||||
const { root, slots, dynamicSlots, once } = oper
|
const { root, props, slots, once } = oper
|
||||||
const rawProps = genRawProps(oper.props, context)
|
const rawProps = genRawProps(props, context)
|
||||||
|
const rawSlots = genRawSlots(slots, context)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
NEWLINE,
|
NEWLINE,
|
||||||
|
@ -55,8 +57,7 @@ export function genCreateComponent(
|
||||||
vaporHelper('createComponent'),
|
vaporHelper('createComponent'),
|
||||||
tag,
|
tag,
|
||||||
rawProps,
|
rawProps,
|
||||||
slots && genSlots(slots, context),
|
rawSlots,
|
||||||
dynamicSlots && genDynamicSlots(dynamicSlots, context),
|
|
||||||
root ? 'true' : false,
|
root ? 'true' : false,
|
||||||
once && 'true',
|
once && 'true',
|
||||||
),
|
),
|
||||||
|
@ -152,51 +153,51 @@ function genModelModifiers(
|
||||||
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
|
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)
|
const names = Object.keys(slots)
|
||||||
return genMulti(
|
return genMulti(
|
||||||
names.length > 1 ? DELIMITERS_OBJECT_NEWLINE : DELIMITERS_OBJECT,
|
DELIMITERS_OBJECT_NEWLINE,
|
||||||
...names.map(name => [
|
...names.map(name => [
|
||||||
name,
|
`${name}: `,
|
||||||
': ',
|
|
||||||
...genSlotBlockWithProps(slots[name], context),
|
...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(
|
function genDynamicSlot(
|
||||||
slot: ComponentDynamicSlot,
|
slot: IRSlotDynamic,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
top = false,
|
withFunction = false,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
|
let frag: CodeFragment[]
|
||||||
switch (slot.slotType) {
|
switch (slot.slotType) {
|
||||||
case DynamicSlotType.BASIC:
|
case IRSlotType.DYNAMIC:
|
||||||
return top
|
frag = genBasicDynamicSlot(slot, context)
|
||||||
? ['() => (', ...genBasicDynamicSlot(slot, context), ')']
|
break
|
||||||
: genBasicDynamicSlot(slot, context)
|
case IRSlotType.LOOP:
|
||||||
case DynamicSlotType.LOOP:
|
frag = genLoopSlot(slot, context)
|
||||||
return top
|
break
|
||||||
? ['() => (', ...genLoopSlot(slot, context), ')']
|
case IRSlotType.CONDITIONAL:
|
||||||
: genLoopSlot(slot, context)
|
frag = genConditionalSlot(slot, context)
|
||||||
case DynamicSlotType.CONDITIONAL:
|
break
|
||||||
return top
|
|
||||||
? ['() => (', ...genConditionalSlot(slot, context), ')']
|
|
||||||
: genConditionalSlot(slot, context)
|
|
||||||
}
|
}
|
||||||
|
return withFunction ? ['() => (', ...frag, ')'] : frag
|
||||||
}
|
}
|
||||||
|
|
||||||
function genBasicDynamicSlot(
|
function genBasicDynamicSlot(
|
||||||
slot: ComponentBasicDynamicSlot,
|
slot: IRSlotDynamicBasic,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const { name, fn } = slot
|
const { name, fn } = slot
|
||||||
|
@ -208,7 +209,7 @@ function genBasicDynamicSlot(
|
||||||
}
|
}
|
||||||
|
|
||||||
function genLoopSlot(
|
function genLoopSlot(
|
||||||
slot: ComponentLoopDynamicSlot,
|
slot: IRSlotDynamicLoop,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const { name, fn, loop } = slot
|
const { name, fn, loop } = slot
|
||||||
|
@ -249,7 +250,7 @@ function genLoopSlot(
|
||||||
}
|
}
|
||||||
|
|
||||||
function genConditionalSlot(
|
function genConditionalSlot(
|
||||||
slot: ComponentConditionalDynamicSlot,
|
slot: IRSlotDynamicConditional,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const { condition, positive, negative } = slot
|
const { condition, positive, negative } = slot
|
||||||
|
@ -266,10 +267,7 @@ function genConditionalSlot(
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function genSlotBlockWithProps(
|
function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) {
|
||||||
oper: ComponentSlotBlockIRNode,
|
|
||||||
context: CodegenContext,
|
|
||||||
) {
|
|
||||||
let isDestructureAssignment = false
|
let isDestructureAssignment = false
|
||||||
let rawProps: string | undefined
|
let rawProps: string | undefined
|
||||||
let propsName: 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,
|
TemplateChildNode,
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import type { Prettify } from '@vue/shared'
|
import type { Prettify } from '@vue/shared'
|
||||||
import type {
|
import type { DirectiveTransform, NodeTransform } from '../transform'
|
||||||
DirectiveTransform,
|
import type { IRProp, IRProps, IRSlots } from './component'
|
||||||
DirectiveTransformResult,
|
|
||||||
NodeTransform,
|
export * from './component'
|
||||||
} from './transform'
|
|
||||||
|
|
||||||
export enum IRNodeTypes {
|
export enum IRNodeTypes {
|
||||||
ROOT,
|
ROOT,
|
||||||
|
@ -88,29 +87,6 @@ export interface ForIRNode extends BaseIRNode, IRFor {
|
||||||
once: boolean
|
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 {
|
export interface SetPropIRNode extends BaseIRNode {
|
||||||
type: IRNodeTypes.SET_PROP
|
type: IRNodeTypes.SET_PROP
|
||||||
element: number
|
element: number
|
||||||
|
@ -207,51 +183,12 @@ export interface WithDirectiveIRNode extends BaseIRNode {
|
||||||
asset?: boolean
|
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 {
|
export interface CreateComponentIRNode extends BaseIRNode {
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE
|
type: IRNodeTypes.CREATE_COMPONENT_NODE
|
||||||
id: number
|
id: number
|
||||||
tag: string
|
tag: string
|
||||||
props: IRProps[]
|
props: IRProps[]
|
||||||
|
slots: IRSlots[]
|
||||||
slots?: ComponentSlots
|
|
||||||
dynamicSlots?: ComponentDynamicSlot[]
|
|
||||||
|
|
||||||
asset: boolean
|
asset: boolean
|
||||||
root: boolean
|
root: boolean
|
||||||
once: boolean
|
once: boolean
|
|
@ -16,12 +16,11 @@ import {
|
||||||
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
|
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
type BlockIRNode,
|
type BlockIRNode,
|
||||||
type ComponentDynamicSlot,
|
|
||||||
type ComponentSlots,
|
|
||||||
DynamicFlag,
|
DynamicFlag,
|
||||||
type HackOptions,
|
type HackOptions,
|
||||||
type IRDynamicInfo,
|
type IRDynamicInfo,
|
||||||
IRNodeTypes,
|
IRNodeTypes,
|
||||||
|
type IRSlots,
|
||||||
type OperationNode,
|
type OperationNode,
|
||||||
type RootIRNode,
|
type RootIRNode,
|
||||||
type VaporDirectiveNode,
|
type VaporDirectiveNode,
|
||||||
|
@ -81,8 +80,7 @@ export class TransformContext<T extends AllNode = AllNode> {
|
||||||
component: Set<string> = this.ir.component
|
component: Set<string> = this.ir.component
|
||||||
directive: Set<string> = this.ir.directive
|
directive: Set<string> = this.ir.directive
|
||||||
|
|
||||||
slots?: ComponentSlots
|
slots: IRSlots[] = []
|
||||||
dynamicSlots?: ComponentDynamicSlot[]
|
|
||||||
|
|
||||||
private globalId = 0
|
private globalId = 0
|
||||||
|
|
||||||
|
@ -96,14 +94,12 @@ export class TransformContext<T extends AllNode = AllNode> {
|
||||||
}
|
}
|
||||||
|
|
||||||
enterBlock(ir: BlockIRNode, isVFor: boolean = false): () => void {
|
enterBlock(ir: BlockIRNode, isVFor: boolean = false): () => void {
|
||||||
const { block, template, dynamic, childrenTemplate, slots, dynamicSlots } =
|
const { block, template, dynamic, childrenTemplate, slots } = this
|
||||||
this
|
|
||||||
this.block = ir
|
this.block = ir
|
||||||
this.dynamic = ir.dynamic
|
this.dynamic = ir.dynamic
|
||||||
this.template = ''
|
this.template = ''
|
||||||
this.childrenTemplate = []
|
this.childrenTemplate = []
|
||||||
this.slots = undefined
|
this.slots = []
|
||||||
this.dynamicSlots = undefined
|
|
||||||
isVFor && this.inVFor++
|
isVFor && this.inVFor++
|
||||||
return () => {
|
return () => {
|
||||||
// exit
|
// exit
|
||||||
|
@ -113,7 +109,6 @@ export class TransformContext<T extends AllNode = AllNode> {
|
||||||
this.dynamic = dynamic
|
this.dynamic = dynamic
|
||||||
this.childrenTemplate = childrenTemplate
|
this.childrenTemplate = childrenTemplate
|
||||||
this.slots = slots
|
this.slots = slots
|
||||||
this.dynamicSlots = dynamicSlots
|
|
||||||
isVFor && this.inVFor--
|
isVFor && this.inVFor--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,11 +105,9 @@ function transformComponentElement(
|
||||||
asset,
|
asset,
|
||||||
root,
|
root,
|
||||||
slots: context.slots,
|
slots: context.slots,
|
||||||
dynamicSlots: context.dynamicSlots,
|
|
||||||
once: context.inVOnce,
|
once: context.inVOnce,
|
||||||
})
|
})
|
||||||
context.slots = undefined
|
context.slots = []
|
||||||
context.dynamicSlots = undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveSetupReference(name: string, context: TransformContext) {
|
function resolveSetupReference(name: string, context: TransformContext) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
ElementTypes,
|
ElementTypes,
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
|
type SimpleExpressionNode,
|
||||||
type TemplateChildNode,
|
type TemplateChildNode,
|
||||||
createCompilerError,
|
createCompilerError,
|
||||||
isTemplateNode,
|
isTemplateNode,
|
||||||
|
@ -11,17 +12,19 @@ import {
|
||||||
import type { NodeTransform, TransformContext } from '../transform'
|
import type { NodeTransform, TransformContext } from '../transform'
|
||||||
import { newBlock } from './utils'
|
import { newBlock } from './utils'
|
||||||
import {
|
import {
|
||||||
type ComponentBasicDynamicSlot,
|
|
||||||
type ComponentConditionalDynamicSlot,
|
|
||||||
type ComponentSlotBlockIRNode,
|
|
||||||
DynamicFlag,
|
DynamicFlag,
|
||||||
DynamicSlotType,
|
|
||||||
type IRFor,
|
type IRFor,
|
||||||
|
type IRSlotDynamic,
|
||||||
|
type IRSlotDynamicBasic,
|
||||||
|
type IRSlotDynamicConditional,
|
||||||
|
IRSlotType,
|
||||||
|
type IRSlots,
|
||||||
|
type IRSlotsStatic,
|
||||||
|
type SlotBlockIRNode,
|
||||||
type VaporDirectiveNode,
|
type VaporDirectiveNode,
|
||||||
} from '../ir'
|
} from '../ir'
|
||||||
import { findDir, resolveExpression } from '../utils'
|
import { findDir, resolveExpression } from '../utils'
|
||||||
|
|
||||||
// TODO dynamic slots
|
|
||||||
export const transformVSlot: NodeTransform = (node, context) => {
|
export const transformVSlot: NodeTransform = (node, context) => {
|
||||||
if (node.type !== NodeTypes.ELEMENT) return
|
if (node.type !== NodeTypes.ELEMENT) return
|
||||||
|
|
||||||
|
@ -37,129 +40,127 @@ export const transformVSlot: NodeTransform = (node, context) => {
|
||||||
parent.node.tagType === ElementTypes.COMPONENT
|
parent.node.tagType === ElementTypes.COMPONENT
|
||||||
|
|
||||||
if (isComponent && children.length) {
|
if (isComponent && children.length) {
|
||||||
const arg = dir && dir.arg
|
return transformComponentSlot(
|
||||||
const slotName = arg ? arg.content : 'default'
|
node,
|
||||||
|
dir,
|
||||||
|
context as TransformContext<ElementNode>,
|
||||||
|
)
|
||||||
|
} else if (isSlotTemplate && dir) {
|
||||||
|
return transformTemplateSlot(
|
||||||
|
node,
|
||||||
|
dir,
|
||||||
|
context as TransformContext<ElementNode>,
|
||||||
|
)
|
||||||
|
} else if (!isComponent && dir) {
|
||||||
|
context.options.onError(
|
||||||
|
createCompilerError(ErrorCodes.X_V_SLOT_MISPLACED, dir.loc),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <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(
|
const nonSlotTemplateChildren = children.filter(
|
||||||
n =>
|
n =>
|
||||||
isNonWhitespaceContent(node) &&
|
isNonWhitespaceContent(node) &&
|
||||||
!(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
|
!(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
|
||||||
)
|
)
|
||||||
|
|
||||||
const [block, onExit] = createSlotBlock(
|
const [block, onExit] = createSlotBlock(node, dir, context)
|
||||||
node,
|
|
||||||
dir,
|
|
||||||
context as TransformContext<ElementNode>,
|
|
||||||
)
|
|
||||||
|
|
||||||
const slots = (context.slots ||= {})
|
const { slots } = context
|
||||||
const dynamicSlots = (context.dynamicSlots ||= [])
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
onExit()
|
onExit()
|
||||||
|
|
||||||
let hasOtherSlots = !!Object.keys(slots).length
|
const hasOtherSlots = !!slots.length
|
||||||
|
if (dir && hasOtherSlots) {
|
||||||
if (dir && (hasOtherSlots || dynamicSlots.length)) {
|
|
||||||
// already has on-component slot - this is incorrect usage.
|
// already has on-component slot - this is incorrect usage.
|
||||||
context.options.onError(
|
context.options.onError(
|
||||||
createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, dir.loc),
|
createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, dir.loc),
|
||||||
)
|
)
|
||||||
// discarding other slots, referenced how compiler-core implements
|
return
|
||||||
Object.keys(slots).forEach(slotName => delete slots[slotName])
|
|
||||||
dynamicSlots.length = 0
|
|
||||||
hasOtherSlots = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonSlotTemplateChildren.length) {
|
if (nonSlotTemplateChildren.length) {
|
||||||
if (slots.default) {
|
if (hasStaticSlot(slots, 'default')) {
|
||||||
context.options.onError(
|
context.options.onError(
|
||||||
createCompilerError(
|
createCompilerError(
|
||||||
ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
|
ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
|
||||||
nonSlotTemplateChildren[0].loc,
|
nonSlotTemplateChildren[0].loc,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} else if (!arg || arg.isStatic) {
|
|
||||||
slots[slotName] = block
|
|
||||||
} else {
|
} else {
|
||||||
dynamicSlots.push({
|
registerSlot(slots, arg, block)
|
||||||
slotType: DynamicSlotType.BASIC,
|
|
||||||
name: arg,
|
|
||||||
fn: block,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
context.slots = slots
|
context.slots = slots
|
||||||
|
}
|
||||||
} else if (hasOtherSlots) {
|
} else if (hasOtherSlots) {
|
||||||
context.slots = slots
|
context.slots = slots
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dynamicSlots.length) context.dynamicSlots = dynamicSlots
|
|
||||||
}
|
}
|
||||||
} else if (isSlotTemplate && dir) {
|
}
|
||||||
let { arg } = dir
|
|
||||||
|
|
||||||
|
// <template #foo>
|
||||||
|
function transformTemplateSlot(
|
||||||
|
node: ElementNode,
|
||||||
|
dir: VaporDirectiveNode,
|
||||||
|
context: TransformContext<ElementNode>,
|
||||||
|
) {
|
||||||
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE
|
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE
|
||||||
|
|
||||||
|
const arg = dir.arg && resolveExpression(dir.arg)
|
||||||
const vFor = findDir(node, 'for')
|
const vFor = findDir(node, 'for')
|
||||||
const vIf = findDir(node, 'if')
|
const vIf = findDir(node, 'if')
|
||||||
const vElse = findDir(node, /^else(-if)?$/, true /* allowEmpty */)
|
const vElse = findDir(node, /^else(-if)?$/, true /* allowEmpty */)
|
||||||
const slots = context.slots!
|
const { slots } = context
|
||||||
const dynamicSlots = context.dynamicSlots!
|
const [block, onExit] = createSlotBlock(node, dir, context)
|
||||||
|
|
||||||
const [block, onExit] = createSlotBlock(
|
if (!vFor && !vIf && !vElse) {
|
||||||
node,
|
const slotName = arg ? arg.isStatic && arg.content : 'default'
|
||||||
dir,
|
if (slotName && hasStaticSlot(slots, slotName)) {
|
||||||
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(
|
context.options.onError(
|
||||||
createCompilerError(
|
createCompilerError(ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES, dir.loc),
|
||||||
ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES,
|
|
||||||
dir.loc,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
slots[slotName] = block
|
registerSlot(slots, arg, block)
|
||||||
}
|
}
|
||||||
} else if (vIf) {
|
} else if (vIf) {
|
||||||
dynamicSlots.push({
|
registerDynamicSlot(slots, {
|
||||||
slotType: DynamicSlotType.CONDITIONAL,
|
slotType: IRSlotType.CONDITIONAL,
|
||||||
condition: vIf.exp!,
|
condition: vIf.exp!,
|
||||||
positive: {
|
positive: {
|
||||||
slotType: DynamicSlotType.BASIC,
|
slotType: IRSlotType.DYNAMIC,
|
||||||
name: arg!,
|
name: arg!,
|
||||||
fn: block,
|
fn: block,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else if (vElse) {
|
} else if (vElse) {
|
||||||
const vIfIR = dynamicSlots[dynamicSlots.length - 1]
|
const vIfSlot = slots[slots.length - 1] as IRSlotDynamic
|
||||||
if (vIfIR.slotType === DynamicSlotType.CONDITIONAL) {
|
if (vIfSlot.slotType === IRSlotType.CONDITIONAL) {
|
||||||
let ifNode = vIfIR
|
let ifNode = vIfSlot
|
||||||
while (
|
while (
|
||||||
ifNode.negative &&
|
ifNode.negative &&
|
||||||
ifNode.negative.slotType === DynamicSlotType.CONDITIONAL
|
ifNode.negative.slotType === IRSlotType.CONDITIONAL
|
||||||
)
|
)
|
||||||
ifNode = ifNode.negative
|
ifNode = ifNode.negative
|
||||||
const negative:
|
const negative: IRSlotDynamicBasic | IRSlotDynamicConditional = vElse.exp
|
||||||
| ComponentBasicDynamicSlot
|
|
||||||
| ComponentConditionalDynamicSlot = vElse.exp
|
|
||||||
? {
|
? {
|
||||||
slotType: DynamicSlotType.CONDITIONAL,
|
slotType: IRSlotType.CONDITIONAL,
|
||||||
condition: vElse.exp,
|
condition: vElse.exp,
|
||||||
positive: {
|
positive: {
|
||||||
slotType: DynamicSlotType.BASIC,
|
slotType: IRSlotType.DYNAMIC,
|
||||||
name: arg!,
|
name: arg!,
|
||||||
fn: block,
|
fn: block,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
slotType: DynamicSlotType.BASIC,
|
slotType: IRSlotType.DYNAMIC,
|
||||||
name: arg!,
|
name: arg!,
|
||||||
fn: block,
|
fn: block,
|
||||||
}
|
}
|
||||||
|
@ -171,42 +172,69 @@ export const transformVSlot: NodeTransform = (node, context) => {
|
||||||
}
|
}
|
||||||
} else if (vFor) {
|
} else if (vFor) {
|
||||||
if (vFor.forParseResult) {
|
if (vFor.forParseResult) {
|
||||||
dynamicSlots.push({
|
registerDynamicSlot(slots, {
|
||||||
slotType: DynamicSlotType.LOOP,
|
slotType: IRSlotType.LOOP,
|
||||||
name: arg!,
|
name: arg!,
|
||||||
fn: block,
|
fn: block,
|
||||||
loop: vFor.forParseResult as IRFor,
|
loop: vFor.forParseResult as IRFor,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
context.options.onError(
|
context.options.onError(
|
||||||
createCompilerError(
|
createCompilerError(ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION, vFor.loc),
|
||||||
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 {
|
} else {
|
||||||
dynamicSlots.push({
|
slots.push({
|
||||||
slotType: DynamicSlotType.BASIC,
|
slotType: IRSlotType.DYNAMIC,
|
||||||
name: arg!,
|
name: name!,
|
||||||
fn: block,
|
fn: block,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return () => onExit()
|
function registerDynamicSlot(allSlots: IRSlots[], dynamic: IRSlotDynamic) {
|
||||||
} else if (!isComponent && dir) {
|
allSlots.push(dynamic)
|
||||||
context.options.onError(
|
}
|
||||||
createCompilerError(ErrorCodes.X_V_SLOT_MISPLACED, dir.loc),
|
|
||||||
)
|
function hasStaticSlot(slots: IRSlots[], name: string) {
|
||||||
}
|
return slots.some(slot => {
|
||||||
|
if (slot.slotType === IRSlotType.STATIC) return !!slot.slots[name]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSlotBlock(
|
function createSlotBlock(
|
||||||
slotNode: ElementNode,
|
slotNode: ElementNode,
|
||||||
dir: VaporDirectiveNode | undefined,
|
dir: VaporDirectiveNode | undefined,
|
||||||
context: TransformContext<ElementNode>,
|
context: TransformContext<ElementNode>,
|
||||||
): [ComponentSlotBlockIRNode, () => void] {
|
): [SlotBlockIRNode, () => void] {
|
||||||
const block: ComponentSlotBlockIRNode = newBlock(slotNode)
|
const block: SlotBlockIRNode = newBlock(slotNode)
|
||||||
block.props = dir && dir.exp
|
block.props = dir && dir.exp
|
||||||
const exitBlock = context.enterBlock(block)
|
const exitBlock = context.enterBlock(block)
|
||||||
return [block, exitBlock]
|
return [block, exitBlock]
|
||||||
|
|
|
@ -149,7 +149,7 @@ describe('api: setup context', () => {
|
||||||
|
|
||||||
const { html } = define({
|
const { html } = define({
|
||||||
render() {
|
render() {
|
||||||
return createComponent(Child, null, null, [
|
return createComponent(Child, null, [
|
||||||
() => ({
|
() => ({
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
fn: () => createTextNode(() => [id.value]),
|
fn: () => createTextNode(() => [id.value]),
|
||||||
|
|
|
@ -40,7 +40,6 @@ describe('attribute fallthrough', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -85,7 +84,6 @@ describe('attribute fallthrough', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -123,7 +121,6 @@ describe('attribute fallthrough', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
return n0
|
return n0
|
||||||
|
@ -146,7 +143,6 @@ describe('attribute fallthrough', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -244,7 +244,6 @@ describe('component: props', () => {
|
||||||
id: () => _ctx.id,
|
id: () => _ctx.id,
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createComponent,
|
createComponent,
|
||||||
|
createForSlots,
|
||||||
createSlot,
|
createSlot,
|
||||||
createVaporApp,
|
createVaporApp,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
|
@ -98,7 +99,7 @@ describe('component: slots', () => {
|
||||||
|
|
||||||
const { render } = define({
|
const { render } = define({
|
||||||
render() {
|
render() {
|
||||||
return createComponent(Child, {}, { _: 2 as any }, [
|
return createComponent(Child, {}, [
|
||||||
() =>
|
() =>
|
||||||
flag1.value
|
flag1.value
|
||||||
? { name: 'one', fn: () => template('<span></span>')() }
|
? { name: 'one', fn: () => template('<span></span>')() }
|
||||||
|
@ -134,11 +135,11 @@ describe('component: slots', () => {
|
||||||
|
|
||||||
const { render } = define({
|
const { render } = define({
|
||||||
setup() {
|
setup() {
|
||||||
return createComponent(Child, {}, {}, [
|
return createComponent(Child, {}, [
|
||||||
() =>
|
() =>
|
||||||
flag1.value
|
flag1.value
|
||||||
? [{ name: 'header', fn: () => template('header')() }]
|
? { name: 'header', fn: () => template('header')() }
|
||||||
: [{ name: 'footer', fn: () => template('footer')() }],
|
: { name: 'footer', fn: () => template('footer')() },
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -172,35 +173,28 @@ describe('component: slots', () => {
|
||||||
|
|
||||||
const { instance } = define({
|
const { instance } = define({
|
||||||
render() {
|
render() {
|
||||||
return createComponent(
|
return createComponent(Comp, {}, [
|
||||||
Comp,
|
|
||||||
{},
|
|
||||||
{
|
{
|
||||||
default: () => {
|
default: () => {
|
||||||
instanceInDefaultSlot = getCurrentInstance()
|
instanceInDefaultSlot = getCurrentInstance()
|
||||||
return template('content')()
|
return template('content')()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[
|
() => ({
|
||||||
() => [
|
|
||||||
{
|
|
||||||
name: 'inVFor',
|
name: 'inVFor',
|
||||||
fn: () => {
|
fn: () => {
|
||||||
instanceInVForSlot = getCurrentInstance()
|
instanceInVForSlot = getCurrentInstance()
|
||||||
return template('content')()
|
return template('content')()
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
],
|
|
||||||
() => ({
|
() => ({
|
||||||
name: 'inVIf',
|
name: 'inVIf',
|
||||||
key: '1',
|
|
||||||
fn: () => {
|
fn: () => {
|
||||||
instanceInVIfSlot = getCurrentInstance()
|
instanceInVIfSlot = getCurrentInstance()
|
||||||
return template('content')()
|
return template('content')()
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
])
|
||||||
)
|
|
||||||
},
|
},
|
||||||
}).render()
|
}).render()
|
||||||
|
|
||||||
|
@ -223,7 +217,7 @@ describe('component: slots', () => {
|
||||||
|
|
||||||
const { render } = define({
|
const { render } = define({
|
||||||
render() {
|
render() {
|
||||||
return createComponent(Child, {}, {}, [
|
return createComponent(Child, {}, [
|
||||||
() => {
|
() => {
|
||||||
slotFn1()
|
slotFn1()
|
||||||
return flag1.value
|
return flag1.value
|
||||||
|
@ -264,6 +258,39 @@ describe('component: slots', () => {
|
||||||
expect(slotFn2).toHaveBeenCalledTimes(2)
|
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 () => {
|
test.todo('should respect $stable flag', async () => {
|
||||||
// TODO: $stable flag?
|
// TODO: $stable flag?
|
||||||
})
|
})
|
||||||
|
@ -327,9 +354,7 @@ describe('component: slots', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const { host } = define(() => {
|
const { host } = define(() => {
|
||||||
return createComponent(
|
return createComponent(Comp, {}, [
|
||||||
Comp,
|
|
||||||
{},
|
|
||||||
{
|
{
|
||||||
header: withDestructure(
|
header: withDestructure(
|
||||||
({ title }) => [title],
|
({ title }) => [title],
|
||||||
|
@ -342,7 +367,7 @@ describe('component: slots', () => {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
])
|
||||||
}).render()
|
}).render()
|
||||||
|
|
||||||
expect(host.innerHTML).toBe('<div><h1>header</h1></div>')
|
expect(host.innerHTML).toBe('<div><h1>header</h1></div>')
|
||||||
|
@ -481,10 +506,10 @@ describe('component: slots', () => {
|
||||||
|
|
||||||
const { host } = define(() => {
|
const { host } = define(() => {
|
||||||
// dynamic slot
|
// dynamic slot
|
||||||
return createComponent(Comp, {}, {}, [
|
return createComponent(Comp, {}, [
|
||||||
() => ({
|
() => ({
|
||||||
name: 'header',
|
name: 'header',
|
||||||
fn: props => template(props.title)(),
|
fn: (props: any) => template(props.title)(),
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
}).render()
|
}).render()
|
||||||
|
@ -548,7 +573,7 @@ describe('component: slots', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const { host } = define(() => {
|
const { host } = define(() => {
|
||||||
return createComponent(Child, {}, {}, [
|
return createComponent(Child, {}, [
|
||||||
() =>
|
() =>
|
||||||
flag1.value
|
flag1.value
|
||||||
? { name: 'one', fn: () => template('one content')() }
|
? { name: 'one', fn: () => template('one content')() }
|
||||||
|
|
|
@ -69,7 +69,7 @@ describe('directive: v-show', () => {
|
||||||
const { instance, host } = define({
|
const { instance, host } = define({
|
||||||
render() {
|
render() {
|
||||||
const n1 = t1()
|
const n1 = t1()
|
||||||
const n2 = createComponent(Child, [], null, null, true)
|
const n2 = createComponent(Child, [], null, true)
|
||||||
withDirectives(n2, [[vShow, () => visible.value]])
|
withDirectives(n2, [[vShow, () => visible.value]])
|
||||||
on(n1 as HTMLElement, 'click', () => handleClick)
|
on(n1 as HTMLElement, 'click', () => handleClick)
|
||||||
return [n1, n2]
|
return [n1, n2]
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { getCurrentScope } from '@vue/reactivity'
|
||||||
|
|
||||||
let removeComponentInstance = NOOP
|
let removeComponentInstance = NOOP
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const instance = createComponentInstance((() => {}) as any, {}, null, null)
|
const instance = createComponentInstance((() => {}) as any, {}, null)
|
||||||
const reset = setCurrentInstance(instance)
|
const reset = setCurrentInstance(instance)
|
||||||
const prev = getCurrentScope()
|
const prev = getCurrentScope()
|
||||||
instance.scope.on()
|
instance.scope.on()
|
||||||
|
|
|
@ -5,14 +5,13 @@ import {
|
||||||
} from './component'
|
} from './component'
|
||||||
import { setupComponent } from './apiRender'
|
import { setupComponent } from './apiRender'
|
||||||
import type { RawProps } from './componentProps'
|
import type { RawProps } from './componentProps'
|
||||||
import type { DynamicSlots, Slots } from './componentSlots'
|
import type { RawSlots } from './componentSlots'
|
||||||
import { withAttrs } from './componentAttrs'
|
import { withAttrs } from './componentAttrs'
|
||||||
|
|
||||||
export function createComponent(
|
export function createComponent(
|
||||||
comp: Component,
|
comp: Component,
|
||||||
rawProps: RawProps | null = null,
|
rawProps: RawProps | null = null,
|
||||||
slots: Slots | null = null,
|
slots: RawSlots | null = null,
|
||||||
dynamicSlots: DynamicSlots | null = null,
|
|
||||||
singleRoot: boolean = false,
|
singleRoot: boolean = false,
|
||||||
once: boolean = false,
|
once: boolean = false,
|
||||||
) {
|
) {
|
||||||
|
@ -21,7 +20,6 @@ export function createComponent(
|
||||||
comp,
|
comp,
|
||||||
singleRoot ? withAttrs(rawProps) : rawProps,
|
singleRoot ? withAttrs(rawProps) : rawProps,
|
||||||
slots,
|
slots,
|
||||||
dynamicSlots,
|
|
||||||
once,
|
once,
|
||||||
)
|
)
|
||||||
setupComponent(instance, singleRoot)
|
setupComponent(instance, singleRoot)
|
||||||
|
|
|
@ -112,7 +112,6 @@ export function createVaporApp(
|
||||||
rootComponent,
|
rootComponent,
|
||||||
rootProps,
|
rootProps,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
false,
|
false,
|
||||||
context,
|
context,
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,12 +22,7 @@ import {
|
||||||
emit,
|
emit,
|
||||||
normalizeEmitsOptions,
|
normalizeEmitsOptions,
|
||||||
} from './componentEmits'
|
} from './componentEmits'
|
||||||
import {
|
import { type RawSlots, type StaticSlots, initSlots } from './componentSlots'
|
||||||
type DynamicSlots,
|
|
||||||
type InternalSlots,
|
|
||||||
type Slots,
|
|
||||||
initSlots,
|
|
||||||
} from './componentSlots'
|
|
||||||
import { VaporLifecycleHooks } from './apiLifecycle'
|
import { VaporLifecycleHooks } from './apiLifecycle'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import {
|
import {
|
||||||
|
@ -51,7 +46,7 @@ export type SetupContext<E = EmitsOptions> = E extends any
|
||||||
attrs: Data
|
attrs: Data
|
||||||
emit: EmitFn<E>
|
emit: EmitFn<E>
|
||||||
expose: (exposed?: Record<string, any>) => void
|
expose: (exposed?: Record<string, any>) => void
|
||||||
slots: Readonly<InternalSlots>
|
slots: Readonly<StaticSlots>
|
||||||
}
|
}
|
||||||
: never
|
: never
|
||||||
|
|
||||||
|
@ -179,13 +174,13 @@ export interface ComponentInternalInstance {
|
||||||
emit: EmitFn
|
emit: EmitFn
|
||||||
emitted: Record<string, boolean> | null
|
emitted: Record<string, boolean> | null
|
||||||
attrs: Data
|
attrs: Data
|
||||||
slots: InternalSlots
|
slots: StaticSlots
|
||||||
refs: Data
|
refs: Data
|
||||||
// exposed properties via expose()
|
// exposed properties via expose()
|
||||||
exposed?: Record<string, any>
|
exposed?: Record<string, any>
|
||||||
|
|
||||||
attrsProxy?: Data
|
attrsProxy?: Data
|
||||||
slotsProxy?: Slots
|
slotsProxy?: StaticSlots
|
||||||
|
|
||||||
// lifecycle
|
// lifecycle
|
||||||
isMounted: boolean
|
isMounted: boolean
|
||||||
|
@ -266,8 +261,7 @@ let uid = 0
|
||||||
export function createComponentInstance(
|
export function createComponentInstance(
|
||||||
component: Component,
|
component: Component,
|
||||||
rawProps: RawProps | null,
|
rawProps: RawProps | null,
|
||||||
slots: Slots | null,
|
slots: RawSlots | null,
|
||||||
dynamicSlots: DynamicSlots | null,
|
|
||||||
once: boolean = false,
|
once: boolean = false,
|
||||||
// application root node only
|
// application root node only
|
||||||
appContext?: AppContext,
|
appContext?: AppContext,
|
||||||
|
@ -363,7 +357,7 @@ export function createComponentInstance(
|
||||||
}
|
}
|
||||||
instance.scope = new BlockEffectScope(instance, parent && parent.scope)
|
instance.scope = new BlockEffectScope(instance, parent && parent.scope)
|
||||||
initProps(instance, rawProps, !isFunction(component), once)
|
initProps(instance, rawProps, !isFunction(component), once)
|
||||||
initSlots(instance, slots, dynamicSlots)
|
initSlots(instance, slots)
|
||||||
instance.emit = emit.bind(null, instance)
|
instance.emit = emit.bind(null, instance)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
@ -417,7 +411,7 @@ function getAttrsProxy(instance: ComponentInternalInstance): Data {
|
||||||
/**
|
/**
|
||||||
* Dev-only
|
* Dev-only
|
||||||
*/
|
*/
|
||||||
function getSlotsProxy(instance: ComponentInternalInstance): Slots {
|
function getSlotsProxy(instance: ComponentInternalInstance): StaticSlots {
|
||||||
return (
|
return (
|
||||||
instance.slotsProxy ||
|
instance.slotsProxy ||
|
||||||
(instance.slotsProxy = new Proxy(instance.slots, {
|
(instance.slotsProxy = new Proxy(instance.slots, {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
import { type Block, type Fragment, fragmentKey } from './apiRender'
|
import { type Block, type Fragment, fragmentKey } from './apiRender'
|
||||||
import { firstEffect, renderEffect } from './renderEffect'
|
import { firstEffect, renderEffect } from './renderEffect'
|
||||||
import { createComment, createTextNode, insert, remove } from './dom/element'
|
import { createComment, createTextNode, insert, remove } from './dom/element'
|
||||||
import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
|
|
||||||
import type { NormalizedRawProps } from './componentProps'
|
import type { NormalizedRawProps } from './componentProps'
|
||||||
import type { Data } from '@vue/runtime-shared'
|
import type { Data } from '@vue/runtime-shared'
|
||||||
import { mergeProps } from './dom/prop'
|
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)>
|
...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)>
|
||||||
) => Block
|
) => Block
|
||||||
|
|
||||||
export type InternalSlots = {
|
export type StaticSlots = Record<string, Slot>
|
||||||
[name: string]: Slot | undefined
|
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 const isDynamicSlotFn = isFunction as (
|
||||||
|
val: StaticSlots | DynamicSlotFn,
|
||||||
export interface DynamicSlot {
|
) => val is DynamicSlotFn
|
||||||
name: string
|
|
||||||
fn: Slot
|
|
||||||
}
|
|
||||||
|
|
||||||
type DynamicSlotFn = () => DynamicSlot | DynamicSlot[]
|
|
||||||
|
|
||||||
export type DynamicSlots = DynamicSlotFn[]
|
|
||||||
|
|
||||||
export function initSlots(
|
export function initSlots(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
rawSlots: InternalSlots | null = null,
|
rawSlots: RawSlots | null = null,
|
||||||
dynamicSlots: DynamicSlots | null = null,
|
|
||||||
) {
|
) {
|
||||||
let slots: InternalSlots = {}
|
if (!rawSlots) return
|
||||||
|
if (!isArray(rawSlots)) rawSlots = [rawSlots]
|
||||||
|
|
||||||
for (const key in rawSlots) {
|
if (rawSlots.length === 1 && !isDynamicSlotFn(rawSlots[0])) {
|
||||||
const slot = rawSlots[key]
|
instance.slots = rawSlots[0]
|
||||||
if (slot) {
|
return
|
||||||
slots[key] = withCtx(slot)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dynamicSlots) {
|
const resolved: StaticSlots = (instance.slots = shallowReactive({}))
|
||||||
slots = shallowReactive(slots)
|
const keys: Set<string>[] = []
|
||||||
const dynamicSlotRecords: Record<string, boolean>[] = []
|
rawSlots.forEach((slots, index) => {
|
||||||
dynamicSlots.forEach((fn, index) => {
|
const isDynamicSlot = isDynamicSlotFn(slots)
|
||||||
|
if (isDynamicSlot) {
|
||||||
firstEffect(instance, () => {
|
firstEffect(instance, () => {
|
||||||
const slotRecord = (dynamicSlotRecords[index] =
|
const recordNames = keys[index] || (keys[index] = new Set())
|
||||||
dynamicSlotRecords[index] || {})
|
let dynamicSlot: ReturnType<DynamicSlotFn>
|
||||||
const dynamicSlot: DynamicSlot | DynamicSlot[] =
|
if (isDynamicSlotFn(slots)) {
|
||||||
callWithAsyncErrorHandling(
|
dynamicSlot = slots()
|
||||||
fn,
|
|
||||||
instance,
|
|
||||||
VaporErrorCodes.RENDER_FUNCTION,
|
|
||||||
)
|
|
||||||
// array of dynamic slot generated by <template v-for="..." #[...]>
|
|
||||||
if (isArray(dynamicSlot)) {
|
if (isArray(dynamicSlot)) {
|
||||||
for (let j = 0; j < dynamicSlot.length; j++) {
|
for (const slot of dynamicSlot) {
|
||||||
slots[dynamicSlot[j].name] = withCtx(dynamicSlot[j].fn)
|
registerSlot(slot.name, slot.fn, recordNames)
|
||||||
slotRecord[dynamicSlot[j].name] = true
|
|
||||||
}
|
}
|
||||||
} else if (dynamicSlot) {
|
} else if (dynamicSlot) {
|
||||||
// conditional single slot generated by <template v-if="..." #foo>
|
registerSlot(dynamicSlot.name, dynamicSlot.fn, recordNames)
|
||||||
slots[dynamicSlot.name] = withCtx(dynamicSlot.fn)
|
|
||||||
slotRecord[dynamicSlot.name] = true
|
|
||||||
}
|
}
|
||||||
// delete stale slots
|
} else {
|
||||||
for (const key in slotRecord) {
|
}
|
||||||
|
for (const name of recordNames) {
|
||||||
if (
|
if (
|
||||||
slotRecord[key] &&
|
!(isArray(dynamicSlot)
|
||||||
!(dynamicSlot && isArray(dynamicSlot)
|
? dynamicSlot.some(s => s.name === name)
|
||||||
? dynamicSlot.some(s => s.name === key)
|
: dynamicSlot && dynamicSlot.name === name)
|
||||||
: dynamicSlot.name === key)
|
|
||||||
) {
|
) {
|
||||||
slotRecord[key] = false
|
recordNames.delete(name)
|
||||||
delete slots[key]
|
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 {
|
function withCtx(fn: Slot): Slot {
|
||||||
return (...args: any[]) => {
|
return (...args: any[]) => {
|
||||||
const reset = setCurrentInstance(instance.parent!)
|
const reset = setCurrentInstance(instance.parent!)
|
||||||
try {
|
try {
|
||||||
return fn(...args)
|
return fn(...(args as any))
|
||||||
} finally {
|
} finally {
|
||||||
reset()
|
reset()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue