// validation
if (__DEV__) {
- validateProps(rawProps || {}, props, instance)
+ validateProps(rawProps || {}, props, instance.propsOptions[0]!)
}
if (isStateful) {
}
if (__DEV__) {
- validateProps(rawProps || {}, props, instance)
+ validateProps(rawProps || {}, props, instance.propsOptions[0]!)
}
}
/**
* dev only
+ * @internal
*/
-function validateProps(
+export function validateProps(
rawProps: Data,
- props: Data,
- instance: ComponentInternalInstance,
-) {
- const resolvedValues = toRaw(props)
- const options = instance.propsOptions[0]
+ resolvedProps: Data,
+ options: NormalizedProps,
+): void {
+ resolvedProps = toRaw(resolvedProps)
const camelizePropsKey = Object.keys(rawProps).map(key => camelize(key))
for (const key in options) {
let opt = options[key]
if (opt == null) continue
validateProp(
key,
- resolvedValues[key],
+ resolvedProps[key],
opt,
- __DEV__ ? shallowReadonly(resolvedValues) : resolvedValues,
+ __DEV__ ? shallowReadonly(resolvedProps) : resolvedProps,
!camelizePropsKey.includes(key),
)
}
* dev only
*/
function validateProp(
- name: string,
+ key: string,
value: unknown,
- prop: PropOptions,
- props: Data,
+ propOptions: PropOptions,
+ resolvedProps: Data,
isAbsent: boolean,
) {
- const { type, required, validator, skipCheck } = prop
+ const { type, required, validator, skipCheck } = propOptions
// required!
if (required && isAbsent) {
- warn('Missing required prop: "' + name + '"')
+ warn('Missing required prop: "' + key + '"')
return
}
// missing but optional
isValid = valid
}
if (!isValid) {
- warn(getInvalidTypeMessage(name, value, expectedTypes))
+ warn(getInvalidTypeMessage(key, value, expectedTypes))
return
}
}
// custom validator
- if (validator && !validator(value, props)) {
- warn('Invalid prop: custom validator check failed for prop "' + name + '".')
+ if (validator && !validator(value, resolvedProps)) {
+ warn('Invalid prop: custom validator check failed for prop "' + key + '".')
}
}
type NormalizedPropsOptions,
baseNormalizePropsOptions,
resolvePropValue,
+ validateProps,
} from './componentProps'
export { baseEmit, isEmitListener } from './componentEmits'
export { type SchedulerJob, queueJob } from './scheduler'
} from '@vue/runtime-dom'
import { type Block, isBlock } from './block'
import { pauseTracking, resetTracking } from '@vue/reactivity'
-import { EMPTY_OBJ, hasOwn, isFunction } from '@vue/shared'
+import { EMPTY_OBJ, isFunction } from '@vue/shared'
import {
type RawProps,
getPropsProxyHandlers,
+ hasFallthroughAttrs,
normalizePropsOptions,
+ setupPropsValidation,
} from './componentProps'
import { setDynamicProp } from './dom/prop'
import { renderEffect } from './renderEffect'
const handlers = getPropsProxyHandlers(comp, this)
this.props = comp.props ? new Proxy(target, handlers[0]!) : {}
this.attrs = new Proxy(target, handlers[1])
+ this.hasFallthrough = hasFallthroughAttrs(comp, rawProps)
if (__DEV__) {
+ // validate props
+ if (rawProps) setupPropsValidation(this)
// cache normalized options for dev only emit check
this.propsOptions = normalizePropsOptions(comp)
this.emitsOptions = normalizeEmitsOptions(comp)
}
- // determine fallthrough
- this.hasFallthrough = false
- if (rawProps) {
- if (rawProps.$ || !comp.props) {
- 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
// TODO init slots
}
}
-import { EMPTY_ARR, NO, YES, hasOwn, isFunction } from '@vue/shared'
+import { EMPTY_ARR, NO, YES, extend, hasOwn, isFunction } from '@vue/shared'
import type { VaporComponent, VaporComponentInstance } from './component'
import {
type NormalizedPropsOptions,
baseNormalizePropsOptions,
isEmitListener,
+ popWarningContext,
+ pushWarningContext,
resolvePropValue,
+ validateProps,
} from '@vue/runtime-dom'
import { normalizeEmitsOptions } from './componentEmits'
+import { renderEffect } from './renderEffect'
export type RawProps = Record<string, () => unknown> & {
$?: DynamicPropsSource[]
) {
return factory.call(null, instance.props)
}
+
+export function hasFallthroughAttrs(
+ comp: VaporComponent,
+ rawProps: RawProps | undefined,
+): boolean {
+ if (rawProps) {
+ // determine fallthrough
+ if (rawProps.$ || !comp.props) {
+ return true
+ } else {
+ // check if rawProps contains any keys not declared
+ const propsOptions = normalizePropsOptions(comp)[0]
+ for (const key in rawProps) {
+ if (!hasOwn(propsOptions!, key)) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+/**
+ * dev only
+ */
+export function setupPropsValidation(instance: VaporComponentInstance): void {
+ const rawProps = instance.rawProps
+ if (!rawProps) return
+ renderEffect(() => {
+ const mergedRawProps = extend({}, rawProps)
+ if (rawProps.$) {
+ for (const source of rawProps.$) {
+ const isDynamic = isFunction(source)
+ const resolved = isDynamic ? source() : source
+ for (const key in resolved) {
+ mergedRawProps[key] = isDynamic
+ ? resolved[key]
+ : (resolved[key] as Function)()
+ }
+ }
+ }
+ pushWarningContext(instance)
+ validateProps(
+ mergedRawProps,
+ instance.props,
+ normalizePropsOptions(instance.type)[0]!,
+ )
+ popWarningContext()
+ })
+}