wip(vapor): align compiler with new props runtime behavior

This commit is contained in:
Evan You 2024-12-04 20:55:10 +08:00
parent 59b1aeda51
commit 23ba438be1
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
10 changed files with 198 additions and 198 deletions

View File

@ -100,9 +100,7 @@ exports[`compiler: element transform > component > should wrap as function if v-
export function render(_ctx) { 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)) }, null, true)
{ onBar: () => $event => (_ctx.handleBar($event)) }
], null, true)
return n0 return n0
}" }"
`; `;
@ -112,12 +110,10 @@ exports[`compiler: element transform > component > static props 1`] = `
export function render(_ctx) { export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo") const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [ const n0 = _createComponent(_component_Foo, {
{ id: () => ("foo"),
id: () => ("foo"), class: () => ("bar")
class: () => ("bar") }, null, true)
}
], null, true)
return n0 return n0
}" }"
`; `;
@ -127,9 +123,9 @@ exports[`compiler: element transform > component > v-bind="obj" 1`] = `
export function render(_ctx) { 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, true) ] }, null, true)
return n0 return n0
}" }"
`; `;
@ -139,10 +135,12 @@ exports[`compiler: element transform > component > v-bind="obj" after static pro
export function render(_ctx) { export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo") const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [ const n0 = _createComponent(_component_Foo, {
{ id: () => ("foo") }, id: () => ("foo"),
() => (_ctx.obj) $: [
], null, true) () => (_ctx.obj)
]
}, null, true)
return n0 return n0
}" }"
`; `;
@ -152,10 +150,10 @@ exports[`compiler: element transform > component > v-bind="obj" before static pr
export function render(_ctx) { 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),
{ id: () => ("foo") } { id: () => ("foo") }
], null, true) ] }, null, true)
return n0 return n0
}" }"
`; `;
@ -165,11 +163,13 @@ exports[`compiler: element transform > component > v-bind="obj" between static p
export function render(_ctx) { export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo") const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [ const n0 = _createComponent(_component_Foo, {
{ id: () => ("foo") }, id: () => ("foo"),
() => (_ctx.obj), $: [
{ class: () => ("bar") } () => (_ctx.obj),
], null, true) { class: () => ("bar") }
]
}, null, true)
return n0 return n0
}" }"
`; `;
@ -179,9 +179,9 @@ exports[`compiler: element transform > component > v-on="obj" 1`] = `
export function render(_ctx) { 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, true) ] }, null, true)
return n0 return n0
}" }"
`; `;
@ -192,10 +192,10 @@ import { resolveComponent as _resolveComponent, createComponent as _createCompon
export function render(_ctx) { export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo") const _component_Foo = _resolveComponent("Foo")
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, true) ] }, null, true)
return n0 return n0
}" }"
`; `;
@ -205,10 +205,10 @@ exports[`compiler: element transform > component with dynamic prop arguments 1`]
export function render(_ctx) { 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.foo-_ctx.bar]: _ctx.bar }), () => ({ [_ctx.foo-_ctx.bar]: _ctx.bar }),
() => ({ [_ctx.baz]: _ctx.qux }) () => ({ [_ctx.baz]: _ctx.qux })
], null, true) ] }, null, true)
return n0 return n0
}" }"
`; `;
@ -245,9 +245,7 @@ exports[`compiler: element transform > dynamic component > normal component with
export function render(_ctx) { export function render(_ctx) {
const _component_custom_input = _resolveComponent("custom-input") const _component_custom_input = _resolveComponent("custom-input")
const n0 = _createComponent(_component_custom_input, [ const n0 = _createComponent(_component_custom_input, { is: () => ("foo") }, null, true)
{ is: () => ("foo") }
], null, true)
return n0 return n0
}" }"
`; `;

View File

@ -27,9 +27,7 @@ exports[`compiler: transform <slot> outlets > default slot outlet with props & f
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = _createSlot("default", [ const n0 = _createSlot("default", { foo: () => (_ctx.bar) }, () => {
{ foo: () => (_ctx.bar) }
], () => {
const n2 = t0() const n2 = t0()
return n2 return n2
}) })
@ -41,13 +39,11 @@ exports[`compiler: transform <slot> outlets > default slot outlet with props 1`]
"import { createSlot as _createSlot } from 'vue/vapor'; "import { createSlot as _createSlot } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
const n0 = _createSlot("default", [ const n0 = _createSlot("default", {
{ foo: () => ("bar"),
foo: () => ("bar"), baz: () => (_ctx.qux),
baz: () => (_ctx.qux), fooBar: () => (_ctx.foo-_ctx.bar)
fooBar: () => (_ctx.foo-_ctx.bar) })
}
])
return n0 return n0
}" }"
`; `;
@ -107,9 +103,7 @@ exports[`compiler: transform <slot> outlets > named slot outlet with props & fal
const t0 = _template("<div></div>") const t0 = _template("<div></div>")
export function render(_ctx) { export function render(_ctx) {
const n0 = _createSlot("foo", [ const n0 = _createSlot("foo", { foo: () => (_ctx.bar) }, () => {
{ foo: () => (_ctx.bar) }
], () => {
const n2 = t0() const n2 = t0()
return n2 return n2
}) })
@ -130,12 +124,10 @@ exports[`compiler: transform <slot> outlets > statically named slot outlet with
"import { createSlot as _createSlot } from 'vue/vapor'; "import { createSlot as _createSlot } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
const n0 = _createSlot("foo", [ const n0 = _createSlot("foo", {
{ foo: () => ("bar"),
foo: () => ("bar"), baz: () => (_ctx.qux)
baz: () => (_ctx.qux) })
}
])
return n0 return n0
}" }"
`; `;
@ -144,11 +136,13 @@ exports[`compiler: transform <slot> outlets > statically named slot outlet with
"import { createSlot as _createSlot } from 'vue/vapor'; "import { createSlot as _createSlot } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
const n0 = _createSlot("foo", [ const n0 = _createSlot("foo", {
{ foo: () => ("bar") }, foo: () => ("bar"),
() => (_ctx.obj), $: [
{ baz: () => (_ctx.qux) } () => (_ctx.obj),
]) { baz: () => (_ctx.qux) }
]
})
return n0 return n0
}" }"
`; `;
@ -157,11 +151,13 @@ exports[`compiler: transform <slot> outlets > statically named slot outlet with
"import { createSlot as _createSlot, toHandlers as _toHandlers } from 'vue/vapor'; "import { createSlot as _createSlot, toHandlers as _toHandlers } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
const n0 = _createSlot("default", [ const n0 = _createSlot("default", {
{ onClick: () => _ctx.foo }, onClick: () => _ctx.foo,
() => (_toHandlers(_ctx.bar)), $: [
{ baz: () => (_ctx.qux) } () => (_toHandlers(_ctx.bar)),
]) { baz: () => (_ctx.qux) }
]
})
return n0 return n0
}" }"
`; `;

View File

@ -464,9 +464,7 @@ exports[`compiler v-bind > number value 1`] = `
export function render(_ctx) { export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp") const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [ const n0 = _createComponent(_component_Comp, { depth: () => (0) }, null, true)
{ depth: () => (0) }
], null, true)
return n0 return n0
}" }"
`; `;

View File

@ -5,11 +5,9 @@ exports[`compiler: vModel transform > component > v-model for component should g
export function render(_ctx) { export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp") const _component_Comp = _resolveComponent("Comp")
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), modelModifiers: () => ({ trim: true, "bar-baz": true }) }, null, true)
modelModifiers: () => ({ trim: true, "bar-baz": true }) }
], null, true)
return n0 return n0
}" }"
`; `;
@ -19,10 +17,8 @@ exports[`compiler: vModel transform > component > v-model for component should w
export function render(_ctx) { export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp") const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [ const n0 = _createComponent(_component_Comp, { modelValue: () => (_ctx.foo),
{ modelValue: () => (_ctx.foo), "onUpdate:modelValue": () => $event => (_ctx.foo = $event) }, null, true)
"onUpdate:modelValue": () => $event => (_ctx.foo = $event) }
], null, true)
return n0 return n0
}" }"
`; `;
@ -32,16 +28,14 @@ exports[`compiler: vModel transform > component > v-model with arguments for com
export function render(_ctx) { export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp") const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [ const n0 = _createComponent(_component_Comp, {
{ foo: () => (_ctx.foo),
foo: () => (_ctx.foo), "onUpdate:foo": () => $event => (_ctx.foo = $event),
"onUpdate:foo": () => $event => (_ctx.foo = $event), fooModifiers: () => ({ trim: true }),
fooModifiers: () => ({ trim: true }), bar: () => (_ctx.bar),
bar: () => (_ctx.bar), "onUpdate:bar": () => $event => (_ctx.bar = $event),
"onUpdate:bar": () => $event => (_ctx.bar = $event), barModifiers: () => ({ number: true })
barModifiers: () => ({ number: true }) }, null, true)
}
], null, true)
return n0 return n0
}" }"
`; `;
@ -51,10 +45,8 @@ exports[`compiler: vModel transform > component > v-model with arguments for com
export function render(_ctx) { export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp") const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [ const n0 = _createComponent(_component_Comp, { bar: () => (_ctx.foo),
{ bar: () => (_ctx.foo), "onUpdate:bar": () => $event => (_ctx.foo = $event) }, null, true)
"onUpdate:bar": () => $event => (_ctx.foo = $event) }
], null, true)
return n0 return n0
}" }"
`; `;
@ -64,14 +56,14 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
export function render(_ctx) { export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp") const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [ const n0 = _createComponent(_component_Comp, { $: [
() => ({ [_ctx.foo]: _ctx.foo, () => ({ [_ctx.foo]: _ctx.foo,
["onUpdate:" + _ctx.foo]: () => $event => (_ctx.foo = $event), ["onUpdate:" + _ctx.foo]: () => $event => (_ctx.foo = $event),
[_ctx.foo + "Modifiers"]: () => ({ trim: true }) }), [_ctx.foo + "Modifiers"]: () => ({ trim: true }) }),
() => ({ [_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, true) ] }, null, true)
return n0 return n0
}" }"
`; `;
@ -81,10 +73,10 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
export function render(_ctx) { export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp") const _component_Comp = _resolveComponent("Comp")
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, true) ] }, null, true)
return n0 return n0
}" }"
`; `;

View File

@ -43,9 +43,7 @@ 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 = t0() const n1 = t0()
const n0 = _createComponent(_component_Comp, [ const n0 = _createComponent(_component_Comp, { id: () => (_ctx.foo) }, null, null, true)
{ id: () => (_ctx.foo) }
], null, null, true)
_insert(n0, n1) _insert(n0, n1)
return n1 return n1
}" }"

View File

@ -199,12 +199,10 @@ describe('compiler: element transform', () => {
) )
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(code).contains(`[ expect(code).contains(`{
{ id: () => ("foo"),
id: () => ("foo"), class: () => ("bar")
class: () => ("bar") }`)
}
]`)
expect(ir.block.operation).toMatchObject([ expect(ir.block.operation).toMatchObject([
{ {
@ -273,10 +271,12 @@ describe('compiler: element transform', () => {
`<Foo id="foo" v-bind="obj" />`, `<Foo id="foo" v-bind="obj" />`,
) )
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(code).contains(`[ expect(code).contains(`{
{ id: () => ("foo") }, id: () => ("foo"),
() => (_ctx.obj) $: [
]`) () => (_ctx.obj)
]
}`)
expect(ir.block.operation).toMatchObject([ expect(ir.block.operation).toMatchObject([
{ {
type: IRNodeTypes.CREATE_COMPONENT_NODE, type: IRNodeTypes.CREATE_COMPONENT_NODE,
@ -321,11 +321,13 @@ describe('compiler: element transform', () => {
`<Foo id="foo" v-bind="obj" class="bar" />`, `<Foo id="foo" v-bind="obj" class="bar" />`,
) )
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(code).contains(`[ expect(code).contains(`{
{ id: () => ("foo") }, id: () => ("foo"),
() => (_ctx.obj), $: [
{ class: () => ("bar") } () => (_ctx.obj),
]`) { class: () => ("bar") }
]
}`)
expect(ir.block.operation).toMatchObject([ expect(ir.block.operation).toMatchObject([
{ {
type: IRNodeTypes.CREATE_COMPONENT_NODE, type: IRNodeTypes.CREATE_COMPONENT_NODE,

View File

@ -208,9 +208,9 @@ describe('compiler: vModel transform', () => {
test('v-model for component should work', () => { test('v-model for component should work', () => {
const { code, ir } = compileWithVModel('<Comp v-model="foo" />') const { code, ir } = compileWithVModel('<Comp v-model="foo" />')
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(code).contains(`modelValue: () => (_ctx.foo),`)
expect(code).contains( expect(code).contains(
`modelValue: () => (_ctx.foo), `"onUpdate:modelValue": () => $event => (_ctx.foo = $event)`,
"onUpdate:modelValue": () => $event => (_ctx.foo = $event)`,
) )
expect(ir.block.operation).toMatchObject([ expect(ir.block.operation).toMatchObject([
{ {
@ -233,9 +233,9 @@ describe('compiler: vModel transform', () => {
test('v-model with arguments for component should work', () => { test('v-model with arguments for component should work', () => {
const { code, ir } = compileWithVModel('<Comp v-model:bar="foo" />') const { code, ir } = compileWithVModel('<Comp v-model:bar="foo" />')
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
expect(code).contains(`bar: () => (_ctx.foo),`)
expect(code).contains( expect(code).contains(
`bar: () => (_ctx.foo), `"onUpdate:bar": () => $event => (_ctx.foo = $event)`,
"onUpdate:bar": () => $event => (_ctx.foo = $event)`,
) )
expect(ir.block.operation).toMatchObject([ expect(ir.block.operation).toMatchObject([
{ {

View File

@ -40,19 +40,19 @@ import { genModelHandler } from './modelValue'
import { genBlock } from './block' import { genBlock } from './block'
export function genCreateComponent( export function genCreateComponent(
oper: CreateComponentIRNode, operation: CreateComponentIRNode,
context: CodegenContext, context: CodegenContext,
): CodeFragment[] { ): CodeFragment[] {
const { vaporHelper } = context const { vaporHelper } = context
const tag = genTag() const tag = genTag()
const { root, props, slots, once } = oper const { root, props, slots, once } = operation
const rawProps = genRawProps(props, context) const rawProps = genRawProps(props, context)
const rawSlots = genRawSlots(slots, context) const rawSlots = genRawSlots(slots, context)
return [ return [
NEWLINE, NEWLINE,
`const n${oper.id} = `, `const n${operation.id} = `,
...genCall( ...genCall(
vaporHelper('createComponent'), vaporHelper('createComponent'),
tag, tag,
@ -61,20 +61,20 @@ export function genCreateComponent(
root ? 'true' : false, root ? 'true' : false,
once && 'true', once && 'true',
), ),
...genDirectivesForElement(oper.id, context), ...genDirectivesForElement(operation.id, context),
] ]
function genTag() { function genTag() {
if (oper.dynamic) { if (operation.dynamic) {
return genCall( return genCall(
vaporHelper('resolveDynamicComponent'), vaporHelper('resolveDynamicComponent'),
genExpression(oper.dynamic, context), genExpression(operation.dynamic, context),
) )
} else if (oper.asset) { } else if (operation.asset) {
return toValidAssetId(oper.tag, 'component') return toValidAssetId(operation.tag, 'component')
} else { } else {
return genExpression( return genExpression(
extend(createSimpleExpression(oper.tag, false), { ast: null }), extend(createSimpleExpression(operation.tag, false), { ast: null }),
context, context,
) )
} }
@ -85,41 +85,65 @@ export function genRawProps(
props: IRProps[], props: IRProps[],
context: CodegenContext, context: CodegenContext,
): CodeFragment[] | undefined { ): CodeFragment[] | undefined {
const { vaporHelper } = context const staticProps = props[0]
const frag = props if (isArray(staticProps)) {
.map(props => { if (!staticProps.length && props.length === 1) {
if (isArray(props)) { return
if (!props.length) return }
return genStaticProps(props, context) return genStaticProps(
} else { staticProps,
let expr: CodeFragment[] context,
if (props.kind === IRDynamicPropsKind.ATTRIBUTE) genDynamicProps(props.slice(1), context),
expr = genMulti(DELIMITERS_OBJECT, genProp(props, context))
else {
expr = genExpression(props.value, context)
if (props.handler) expr = genCall(vaporHelper('toHandlers'), expr)
}
return ['() => (', ...expr, ')']
}
})
.filter(
Boolean as any as (v: CodeFragment[] | undefined) => v is CodeFragment[],
) )
if (frag.length) { } else if (props.length) {
return genMulti(DELIMITERS_ARRAY_NEWLINE, ...frag) // all dynamic
return genStaticProps([], context, genDynamicProps(props, context))
} }
} }
function genStaticProps( function genStaticProps(
props: IRPropsStatic, props: IRPropsStatic,
context: CodegenContext, context: CodegenContext,
dynamicProps?: CodeFragment[],
): CodeFragment[] { ): CodeFragment[] {
const args = props.map(prop => genProp(prop, context, true))
if (dynamicProps) {
args.push([`$: `, ...dynamicProps])
}
return genMulti( return genMulti(
props.length > 1 ? DELIMITERS_OBJECT_NEWLINE : DELIMITERS_OBJECT, args.length > 1 ? DELIMITERS_OBJECT_NEWLINE : DELIMITERS_OBJECT,
...props.map(prop => genProp(prop, context, true)), ...args,
) )
} }
function genDynamicProps(
props: IRProps[],
context: CodegenContext,
): CodeFragment[] | undefined {
const { vaporHelper } = context
const frags: CodeFragment[][] = []
for (const p of props) {
let expr: CodeFragment[]
if (isArray(p)) {
if (p.length) {
frags.push(genStaticProps(p, context))
}
continue
} else {
if (p.kind === IRDynamicPropsKind.ATTRIBUTE)
expr = genMulti(DELIMITERS_OBJECT, genProp(p, context))
else {
expr = genExpression(p.value, context)
if (p.handler) expr = genCall(vaporHelper('toHandlers'), expr)
}
}
frags.push(['() => (', ...expr, ')'])
}
if (frags.length) {
return genMulti(DELIMITERS_ARRAY_NEWLINE, ...frags)
}
}
function genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) { function genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) {
const values = genPropValue(prop.values, context) const values = genPropValue(prop.values, context)
return [ return [

View File

@ -76,10 +76,11 @@ export function createComponent(
// check if we are the single root of the parent // check if we are the single root of the parent
// if yes, inject parent attrs as dynamic props source // if yes, inject parent attrs as dynamic props source
if (isSingleRoot && currentInstance && currentInstance.hasFallthrough) { if (isSingleRoot && currentInstance && currentInstance.hasFallthrough) {
const attrs = currentInstance.attrs
if (rawProps) { if (rawProps) {
;(rawProps.$ || (rawProps.$ = [])).push(currentInstance.attrs) ;(rawProps.$ || (rawProps.$ = [])).push(() => attrs)
} else { } else {
rawProps = { $: [currentInstance.attrs] } rawProps = { $: [() => attrs] } as RawProps
} }
} }

View File

@ -8,14 +8,13 @@ import {
} from '@vue/runtime-dom' } from '@vue/runtime-dom'
import { normalizeEmitsOptions } from './componentEmits' import { normalizeEmitsOptions } from './componentEmits'
export interface RawProps { export type RawProps = Record<string, () => unknown> & {
[key: string]: PropSource
$?: DynamicPropsSource[] $?: DynamicPropsSource[]
} }
type PropSource<T = any> = T | (() => T) type DynamicPropsSource =
| (() => Record<string, unknown>)
type DynamicPropsSource = PropSource<Record<string, any>> | Record<string, () => unknown>
export function initStaticProps( export function initStaticProps(
comp: VaporComponent, comp: VaporComponent,
@ -38,40 +37,24 @@ export function initStaticProps(
const needCast = needCastKeys && needCastKeys.includes(normalizedKey) const needCast = needCastKeys && needCastKeys.includes(normalizedKey)
const source = rawProps[key] const source = rawProps[key]
if (propsOptions && normalizedKey in propsOptions) { if (propsOptions && normalizedKey in propsOptions) {
if (isFunction(source)) { Object.defineProperty(props, normalizedKey, {
Object.defineProperty(props, normalizedKey, { enumerable: true,
enumerable: true, get: needCast
get: needCast ? () =>
? () => resolvePropValue(
resolvePropValue( propsOptions,
propsOptions, normalizedKey,
normalizedKey, source(),
source(), instance,
instance, resolveDefault,
resolveDefault, )
) : source,
: source, })
})
} else {
props[normalizedKey] = needCast
? resolvePropValue(
propsOptions,
normalizedKey,
source,
instance,
resolveDefault,
)
: source
}
} else if (!isEmitListener(emitsOptions, key)) { } else if (!isEmitListener(emitsOptions, key)) {
if (isFunction(source)) { Object.defineProperty(attrs, key, {
Object.defineProperty(attrs, key, { enumerable: true,
enumerable: true, get: source,
get: source, })
})
} else {
attrs[normalizedKey] = source
}
hasAttrs = true hasAttrs = true
} }
} }
@ -98,7 +81,9 @@ function resolveDefault(
} }
// TODO optimization: maybe convert functions into computeds // TODO optimization: maybe convert functions into computeds
export function resolveSource(source: PropSource): Record<string, any> { export function resolveSource(
source: Record<string, any> | (() => Record<string, any>),
): Record<string, any> {
return isFunction(source) ? source() : source return isFunction(source) ? source() : source
} }
@ -138,15 +123,18 @@ export function getDynamicPropsHandlers(
: passThrough : passThrough
if (key in target) { if (key in target) {
return castProp(resolveSource(target[key as string])) return castProp(target[key as string]())
} }
if (target.$) { const dynamicSources = target.$
let i = target.$.length if (dynamicSources) {
let source let i = dynamicSources.length
let source, isDynamic
while (i--) { while (i--) {
source = resolveSource(target.$[i]) source = dynamicSources[i]
isDynamic = isFunction(source)
source = isDynamic ? (source as Function)() : source
if (hasOwn(source, key)) { if (hasOwn(source, key)) {
return castProp(source[key]) return castProp(isDynamic ? source[key] : source[key]())
} }
} }
} }
@ -174,12 +162,14 @@ export function getDynamicPropsHandlers(
: null : null
const hasAttr = (target: RawProps, key: string) => { const hasAttr = (target: RawProps, key: string) => {
if (key === '$' || isProp(key) || isEmitListener(emitsOptions, key)) if (key === '$' || isProp(key) || isEmitListener(emitsOptions, key)) {
return false return false
if (target.$) { }
let i = target.$.length const dynamicSources = target.$
if (dynamicSources) {
let i = dynamicSources.length
while (i--) { while (i--) {
if (hasOwn(resolveSource(target.$[i]), key)) { if (hasOwn(resolveSource(dynamicSources[i]), key)) {
return true return true
} }
} }
@ -201,10 +191,11 @@ export function getDynamicPropsHandlers(
}, },
ownKeys(target) { ownKeys(target) {
const keys = Object.keys(target) const keys = Object.keys(target)
if (target.$) { const dynamicSources = target.$
let i = target.$.length if (dynamicSources) {
let i = dynamicSources.length
while (i--) { while (i--) {
keys.push(...Object.keys(resolveSource(target.$[i]))) keys.push(...Object.keys(resolveSource(dynamicSources[i])))
} }
} }
return keys.filter(key => hasAttr(target, key)) return keys.filter(key => hasAttr(target, key))