} from '@vue/runtime-dom'
import { type Block, isBlock } from './block'
import { pauseTracking, resetTracking } from '@vue/reactivity'
-import { EMPTY_OBJ, isFunction } from '@vue/shared'
+import { EMPTY_OBJ, hasOwn, isFunction } from '@vue/shared'
import {
type RawProps,
- getDynamicPropsHandlers,
- initStaticProps,
+ getPropsProxyHandlers,
+ normalizePropsOptions,
} from './componentProps'
import { setDynamicProp } from './dom/prop'
import { renderEffect } from './renderEffect'
this.rawProps = rawProps
this.provides = this.refs = EMPTY_OBJ
- this.emitted = this.ec = this.exposed = null
+ this.emitted = this.ec = this.exposed = this.propsDefaults = null
this.isMounted = this.isUnmounted = this.isDeactivated = false
// init props
- this.propsDefaults = null
+ const target = rawProps || EMPTY_OBJ
+ const handlers = getPropsProxyHandlers(comp, this)
+ this.props = comp.props ? new Proxy(target, handlers[0]!) : {}
+ this.attrs = new Proxy(target, handlers[1])
+
+ // determine fallthrough
this.hasFallthrough = false
- if (rawProps && rawProps.$) {
- // has dynamic props, use proxy
- const handlers = getDynamicPropsHandlers(comp, this)
- this.props = comp.props ? new Proxy(rawProps, handlers[0]!) : EMPTY_OBJ
- this.attrs = new Proxy(rawProps, handlers[1])
- this.hasFallthrough = true
- } else {
- this.props = {}
- this.attrs = {}
- this.hasFallthrough = initStaticProps(comp, rawProps, this)
+ if (rawProps) {
+ if (rawProps.$) {
+ this.hasFallthrough = true
+ } else {
+ // check if rawProps contains any keys not declared
+ const propsOptions = normalizePropsOptions(comp)[0]!
+ for (const key in rawProps) {
+ if (!hasOwn(propsOptions, key)) {
+ this.hasFallthrough = true
+ break
+ }
+ }
+ }
}
// TODO validate props
-import { EMPTY_ARR, NO, camelize, hasOwn, isFunction } from '@vue/shared'
+import { EMPTY_ARR, NO, hasOwn, isFunction } from '@vue/shared'
import type { VaporComponent, VaporComponentInstance } from './component'
import {
type NormalizedPropsOptions,
| (() => Record<string, unknown>)
| Record<string, () => unknown>
-export function initStaticProps(
- comp: VaporComponent,
- rawProps: RawProps | undefined,
- instance: VaporComponentInstance,
-): boolean {
- let hasAttrs = false
- const { props, attrs } = instance
- const [propsOptions, needCastKeys] = normalizePropsOptions(comp)
- const emitsOptions = normalizeEmitsOptions(comp)
-
- // for dev emit check
- if (__DEV__) {
- instance.propsOptions = normalizePropsOptions(comp)
- instance.emitsOptions = emitsOptions
- }
-
- for (const key in rawProps) {
- const normalizedKey = camelize(key)
- const needCast = needCastKeys && needCastKeys.includes(normalizedKey)
- const source = rawProps[key]
- if (propsOptions && normalizedKey in propsOptions) {
- Object.defineProperty(props, normalizedKey, {
- enumerable: true,
- get: needCast
- ? () =>
- resolvePropValue(
- propsOptions,
- normalizedKey,
- source(),
- instance,
- resolveDefault,
- )
- : source,
- })
- } else if (!isEmitListener(emitsOptions, key)) {
- Object.defineProperty(attrs, key, {
- enumerable: true,
- get: source,
- })
- hasAttrs = true
- }
- }
- for (const key in propsOptions) {
- if (!(key in props)) {
- props[key] = resolvePropValue(
- propsOptions,
- key,
- undefined,
- instance,
- resolveDefault,
- true,
- )
- }
- }
- return hasAttrs
-}
-
-function resolveDefault(
- factory: (props: Record<string, any>) => unknown,
- instance: VaporComponentInstance,
-) {
- return factory.call(null, instance.props)
-}
-
// TODO optimization: maybe convert functions into computeds
export function resolveSource(
source: Record<string, any> | (() => Record<string, any>),
const passThrough = (val: any) => val
-export function getDynamicPropsHandlers(
+export function getPropsProxyHandlers(
comp: VaporComponent,
instance: VaporComponentInstance,
): [ProxyHandler<RawProps> | null, ProxyHandler<RawProps>] {
if (comp.__propsHandlers) {
return comp.__propsHandlers
}
- let normalizedKeys: string[] | undefined
const propsOptions = normalizePropsOptions(comp)[0]
const emitsOptions = normalizeEmitsOptions(comp)
const isProp = propsOptions ? (key: string) => hasOwn(propsOptions, key) : NO
+ const castProp = propsOptions
+ ? (key: string, value: any, isAbsent = false) =>
+ resolvePropValue(
+ propsOptions,
+ key as string,
+ value,
+ instance,
+ resolveDefault,
+ isAbsent,
+ )
+ : passThrough
const getProp = (target: RawProps, key: string, asProp: boolean) => {
- if (key === '$') return
if (asProp) {
- if (!isProp(key)) return
+ if (!isProp(key) || key === '$') return
} else if (isProp(key) || isEmitListener(emitsOptions, key)) {
return
}
- const castProp = propsOptions
- ? (value: any, isAbsent = false) =>
- asProp
- ? resolvePropValue(
- propsOptions,
- key as string,
- value,
- instance,
- resolveDefault,
- isAbsent,
- )
- : value
- : passThrough
if (key in target) {
- return castProp(target[key as string]())
+ return castProp(key, target[key as string]())
}
const dynamicSources = target.$
if (dynamicSources) {
isDynamic = isFunction(source)
source = isDynamic ? (source as Function)() : source
if (hasOwn(source, key)) {
- return castProp(isDynamic ? source[key] : source[key]())
+ return castProp(key, isDynamic ? source[key] : source[key]())
}
}
}
- return castProp(undefined, true)
+ return castProp(key, undefined, true)
}
const propsHandlers = propsOptions
}
}
},
- ownKeys: () =>
- normalizedKeys || (normalizedKeys = Object.keys(propsOptions)),
+ ownKeys: () => Object.keys(propsOptions),
set: NO,
deleteProperty: NO,
} satisfies ProxyHandler<RawProps>)
return (comp.__propsHandlers = [propsHandlers, attrsHandlers])
}
-function normalizePropsOptions(comp: VaporComponent): NormalizedPropsOptions {
+export function normalizePropsOptions(
+ comp: VaporComponent,
+): NormalizedPropsOptions {
const cached = comp.__propsOptions
if (cached) return cached
return (comp.__propsOptions = [normalized, needCastKeys])
}
+
+function resolveDefault(
+ factory: (props: Record<string, any>) => unknown,
+ instance: VaporComponentInstance,
+) {
+ return factory.call(null, instance.props)
+}