mirror of https://github.com/vuejs/core.git
wip: vapor component props validation
This commit is contained in:
parent
8725954244
commit
93a16af08e
|
@ -217,7 +217,7 @@ export function initProps(
|
|||
|
||||
// validation
|
||||
if (__DEV__) {
|
||||
validateProps(rawProps || {}, props, instance)
|
||||
validateProps(rawProps || {}, props, instance.propsOptions[0]!)
|
||||
}
|
||||
|
||||
if (isStateful) {
|
||||
|
@ -371,7 +371,7 @@ export function updateProps(
|
|||
}
|
||||
|
||||
if (__DEV__) {
|
||||
validateProps(rawProps || {}, props, instance)
|
||||
validateProps(rawProps || {}, props, instance.propsOptions[0]!)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -691,23 +691,23 @@ function getType(ctor: Prop<any> | null): string {
|
|||
|
||||
/**
|
||||
* 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),
|
||||
)
|
||||
}
|
||||
|
@ -717,16 +717,16 @@ function validateProps(
|
|||
* 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
|
||||
|
@ -745,13 +745,13 @@ function validateProp(
|
|||
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 + '".')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -491,6 +491,7 @@ export {
|
|||
type NormalizedPropsOptions,
|
||||
baseNormalizePropsOptions,
|
||||
resolvePropValue,
|
||||
validateProps,
|
||||
} from './componentProps'
|
||||
export { baseEmit, isEmitListener } from './componentEmits'
|
||||
export { type SchedulerJob, queueJob } from './scheduler'
|
||||
|
|
|
@ -16,11 +16,13 @@ import {
|
|||
} 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'
|
||||
|
@ -208,31 +210,16 @@ export class VaporComponentInstance implements GenericComponentInstance {
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
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[]
|
||||
|
@ -174,3 +178,53 @@ function resolveDefault(
|
|||
) {
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue