import { toRaw , shallowReactive , trigger , TriggerOpTypes } from '@vue/reactivity' ; import { EMPTY_OBJ , camelize , hyphenate , capitalize , isString , isFunction , isArray , isObject , hasOwn , toRawType , PatchFlags , makeMap , isReservedProp , EMPTY_ARR , def , extend , isOn } from '@vue/shared' ; import { warn } from './warning' ; import { Data , ComponentInternalInstance , ComponentOptions , ConcreteComponent , setCurrentInstance , unsetCurrentInstance } from './component' ; import { isEmitListener } from './componentEmits' ; import { InternalObjectKey } from './vnode' ; import { AppContext } from './apiCreateApp' ; import { createPropsDefaultThis } from './compat/props' ; import { isCompatEnabled , softAssertCompatEnabled } from './compat/compatConfig' ; import { DeprecationTypes } from './compat/compatConfig' ; import { shouldSkipAttr } from './compat/attrsFallthrough' ; export type ComponentPropsOptions < P = Data > = ComponentObjectPropsOptions < P > | string [ ] ; export type ComponentObjectPropsOptions < P = Data > = { [ K in keyof P ] : Prop < P [ K ] > | null ; } ; export type Prop < T , D = T > = PropOptions < T , D > | PropType < T > ; type DefaultFactory < T > = ( props : Data ) => T | null | undefined ; export interface PropOptions < T = any , D = T > { type ? : PropType < T > | true | null ; required ? : boolean ; default ? : D | DefaultFactory < D > | null | undefined | object ; validator ? ( value ) : boolean ; } export type PropType < T > = PropConstructor < T > | PropConstructor < T > [ ] ; type PropConstructor < T = any > = { new ( ... args ) : T & { } ; } | { ( ) : T ; } | PropMethod < T > ; type PropMethod < T , TConstructor = any > = [ T ] extends [ ( ... args : any ) => any ] ? { new ( ) : TConstructor ; ( ) : T ; readonly prototype : TConstructor ; } : never ; type RequiredKeys < T > = { [ K in keyof T ] : T [ K ] extends { required : true ; } | { default : any ; } | BooleanConstructor | { type : BooleanConstructor ; } ? T [ K ] extends { default : undefined | ( ( ) => undefined ) ; } ? never : K : never ; } [ keyof T ] ; type OptionalKeys < T > = Exclude < keyof T , RequiredKeys < T > > ; type DefaultKeys < T > = { [ K in keyof T ] : T [ K ] extends { default : any ; } | BooleanConstructor | { type : BooleanConstructor ; } ? T [ K ] extends { type : BooleanConstructor ; required : true ; } ? never : K : never ; } [ keyof T ] ; type InferPropType < T > = [ T ] extends [ null ] ? any : [ T ] extends [ { type : null | true ; } ] ? any : [ T ] extends [ ObjectConstructor | { type : ObjectConstructor ; } ] ? Record < string , any > : [ T ] extends [ BooleanConstructor | { type : BooleanConstructor ; } ] ? boolean : [ T ] extends [ DateConstructor | { type : DateConstructor ; } ] ? Date : [ T ] extends [ Prop < infer V , infer D > ] ? unknown extends V ? D : V : T ; export type ExtractPropTypes < O > = O extends object ? { [ K in keyof O ] ? : unknown ; } & { [ K in RequiredKeys < O > ] : InferPropType < O [ K ] > ; } & { [ K in OptionalKeys < O > ] ? : InferPropType < O [ K ] > ; } : { [ K in string ] : any ; } ; const enum BooleanFlags { shouldCast , shouldCastTrue } export type ExtractDefaultPropTypes < O > = O extends object ? { [ K in DefaultKeys < O > ] : InferPropType < O [ K ] > ; } : { } ; type NormalizedProp = null | ( PropOptions & { [ BooleanFlags . shouldCast ] ? : boolean ; [ BooleanFlags . shouldCastTrue ] ? : boolean ; } ) ; export type NormalizedProps = Record < string , NormalizedProp > ; export type NormalizedPropsOptions = [ NormalizedProps , string [ ] ] | [ ] ; export function initProps ( instance , rawProps , isStateful , isSSR = false ) { const props = { } ; const attrs = { } ; def ( attrs , InternalObjectKey , 1 ) ; instance . propsDefaults = Object . create ( null ) ; setFullProps ( instance , rawProps , props , attrs ) ; for ( const key in instance . propsOptions [ 0 ] ) { if ( ! ( key in props ) ) { props [ key ] = undefined ; } } if ( __DEV__ ) { validateProps ( rawProps || { } , props , instance ) ; } if ( isStateful ) { instance . props = isSSR ? props : shallowReactive ( props ) ; } else { if ( ! instance . type . props ) { instance . props = attrs ; } else { instance . props = props ; } } instance . attrs = attrs ; } export function updateProps ( instance , rawProps , rawPrevProps , optimized ) { const { props , attrs , vnode : { patchFlag } } = instance ; const rawCurrentProps = toRaw ( props ) ; const [ options ] = instance . propsOptions ; let hasAttrsChanged = false ; if ( ! ( __DEV__ && ( instance . type . __hmrId || ( instance . parent && instance . parent . type . __hmrId ) ) ) && ( optimized || patchFlag > 0 ) && ! ( patchFlag & PatchFlags . FULL_PROPS ) ) { if ( patchFlag & PatchFlags . PROPS ) { const propsToUpdate = instance . vnode . dynamicProps ! ; for ( let i = 0 ; i < propsToUpdate . length ; i ++ ) { let key = propsToUpdate [ i ] ; const value = rawProps ! [ key ] ; if ( options ) { if ( hasOwn ( attrs , key ) ) { if ( value !== attrs [ key ] ) { attrs [ key ] = value ; hasAttrsChanged = true ; } } else { const camelizedKey = camelize ( key ) ; props [ camelizedKey ] = resolvePropValue ( options , rawCurrentProps , camelizedKey , value , instance , false ) ; } } else { if ( __COMPAT__ ) { if ( isOn ( key ) && key . endsWith ( 'Native' ) ) { key = key . slice ( 0 , - 6 ) ; } else if ( shouldSkipAttr ( key , instance ) ) { continue ; } } if ( value !== attrs [ key ] ) { attrs [ key ] = value ; hasAttrsChanged = true ; } } } } } else { if ( setFullProps ( instance , rawProps , props , attrs ) ) { hasAttrsChanged = true ; } let kebabKey ; for ( const key in rawCurrentProps ) { if ( ! rawProps || ( ! hasOwn ( rawProps , key ) && ( ( kebabKey = hyphenate ( key ) ) === key || ! hasOwn ( rawProps , kebabKey ) ) ) ) { if ( options ) { if ( rawPrevProps && ( rawPrevProps [ key ] !== undefined || rawPrevProps [ kebabKey ! ] !== undefined ) ) { props [ key ] = resolvePropValue ( options , rawCurrentProps , key , undefined , instance , true ) ; } } else { delete props [ key ] ; } } } if ( attrs !== rawCurrentProps ) { for ( const key in attrs ) { if ( ! rawProps || ! hasOwn ( rawProps , key ) ) { delete attrs [ key ] ; hasAttrsChanged = true ; } } } } if ( hasAttrsChanged ) { trigger ( instance , TriggerOpTypes . SET , '$attrs' ) ; } if ( __DEV__ ) { validateProps ( rawProps || { } , props , instance ) ; } } function setFullProps ( instance , rawProps , props , attrs ) { const [ options , needCastKeys ] = instance . propsOptions ; let hasAttrsChanged = false ; let rawCastValues ; if ( rawProps ) { for ( let key in rawProps ) { if ( isReservedProp ( key ) ) { continue ; } if ( __COMPAT__ ) { if ( key . startsWith ( 'onHook:' ) ) { softAssertCompatEnabled ( DeprecationTypes . INSTANCE_EVENT_HOOKS , instance , key . slice ( 2 ) . toLowerCase ( ) ) ; } if ( key === 'inline-template' ) { continue ; } } const value = rawProps [ key ] ; let camelKey ; if ( options && hasOwn ( options , ( camelKey = camelize ( key ) ) ) ) { if ( ! needCastKeys || ! needCastKeys . includes ( camelKey ) ) { props [ camelKey ] = value ; } else { ; ( rawCastValues || ( rawCastValues = { } ) ) [ camelKey ] = value ; } } else if ( ! isEmitListener ( instance . emitsOptions , key ) ) { if ( __COMPAT__ ) { if ( isOn ( key ) && key . endsWith ( 'Native' ) ) { key = key . slice ( 0 , - 6 ) ; } else if ( shouldSkipAttr ( key , instance ) ) { continue ; } } if ( value !== attrs [ key ] ) { attrs [ key ] = value ; hasAttrsChanged = true ; } } } } if ( needCastKeys ) { const rawCurrentProps = toRaw ( props ) ; const castValues = rawCastValues || EMPTY_OBJ ; for ( let i = 0 ; i < needCastKeys . length ; i ++ ) { const key = needCastKeys [ i ] ; props [ key ] = resolvePropValue ( options ! , rawCurrentProps , key , castValues [ key ] , instance , ! hasOwn ( castValues , key ) ) ; } } return hasAttrsChanged ; } function resolvePropValue ( options , props , key , value , instance , isAbsent ) { const opt = options [ key ] ; if ( opt != null ) { const hasDefault = hasOwn ( opt , 'default' ) ; if ( hasDefault && value === undefined ) { const defaultValue = opt . default ; if ( opt . type !== Function && isFunction ( defaultValue ) ) { const { propsDefaults } = instance ; if ( key in propsDefaults ) { value = propsDefaults [ key ] ; } else { setCurrentInstance ( instance ) ; value = propsDefaults [ key ] = defaultValue . call ( __COMPAT__ && isCompatEnabled ( DeprecationTypes . PROPS_DEFAULT_THIS , instance ) ? createPropsDefaultThis ( instance , props , key ) : null , props ) ; unsetCurrentInstance ( ) ; } } else { value = defaultValue ; } } if ( opt [ BooleanFlags . shouldCast ] ) { if ( isAbsent && ! hasDefault ) { value = false ; } else if ( opt [ BooleanFlags . shouldCastTrue ] && ( value === '' || value === hyphenate ( key ) ) ) { value = true ; } } } return value ; } export function normalizePropsOptions ( comp , appContext , asMixin = false ) { const cache = appContext . propsCache ; const cached = cache . get ( comp ) ; if ( cached ) { return cached ; } const raw = comp . props ; const normalized = { } ; const needCastKeys = [ ] ; let hasExtends = false ; if ( __FEATURE_OPTIONS_API__ && ! isFunction ( comp ) ) { const extendProps = ( raw ) => { if ( __COMPAT__ && isFunction ( raw ) ) { raw = raw . options ; } hasExtends = true ; const [ props , keys ] = normalizePropsOptions ( raw , appContext , true ) ; extend ( normalized , props ) ; if ( keys ) needCastKeys . push ( ... keys ) ; } ; if ( ! asMixin && appContext . mixins . length ) { appContext . mixins . forEach ( extendProps ) ; } if ( comp . extends ) { extendProps ( comp . extends ) ; } if ( comp . mixins ) { comp . mixins . forEach ( extendProps ) ; } } if ( ! raw && ! hasExtends ) { cache . set ( comp , EMPTY_ARR as any ) ; return EMPTY_ARR as any ; } if ( isArray ( raw ) ) { for ( let i = 0 ; i < raw . length ; i ++ ) { if ( __DEV__ && ! isString ( raw [ i ] ) ) { warn ( `props must be strings when using array syntax.` , raw [ i ] ) ; } const normalizedKey = camelize ( raw [ i ] ) ; if ( validatePropName ( normalizedKey ) ) { normalized [ normalizedKey ] = EMPTY_OBJ ; } } } else if ( raw ) { if ( __DEV__ && ! isObject ( raw ) ) { warn ( `invalid props options` , raw ) ; } for ( const key in raw ) { const normalizedKey = camelize ( key ) ; if ( validatePropName ( normalizedKey ) ) { const opt = raw [ key ] ; const prop = ( normalized [ normalizedKey ] = isArray ( opt ) || isFunction ( opt ) ? { type : opt } : opt ) ; if ( prop ) { const booleanIndex = getTypeIndex ( Boolean , prop . type ) ; const stringIndex = getTypeIndex ( String , prop . type ) ; prop [ BooleanFlags . shouldCast ] = booleanIndex > - 1 ; prop [ BooleanFlags . shouldCastTrue ] = stringIndex < 0 || booleanIndex < stringIndex ; if ( booleanIndex > - 1 || hasOwn ( prop , 'default' ) ) { needCastKeys . push ( normalizedKey ) ; } } } } } const res = [ normalized , needCastKeys ] ; cache . set ( comp , res ) ; return res ; } function validatePropName ( key ) { if ( key [ 0 ] !== '$' ) { return true ; } else if ( __DEV__ ) { warn ( `Invalid prop name: "${ key }" is a reserved property.` ) ; } return false ; } function getType ( ctor ) { const match = ctor && ctor . toString ( ) . match ( /^\s*function (\w+)/ ) ; return match ? match [ 1 ] : ctor === null ? 'null' : '' ; } function isSameType ( a , b ) { return getType ( a ) === getType ( b ) ; } function getTypeIndex ( type , expectedTypes ) { if ( isArray ( expectedTypes ) ) { return expectedTypes . findIndex ( t => isSameType ( t , type ) ) ; } else if ( isFunction ( expectedTypes ) ) { return isSameType ( expectedTypes , type ) ? 0 : - 1 ; } return - 1 ; } /**
 * dev only
 */ function validateProps ( rawProps , props , instance ) { const resolvedValues = toRaw ( props ) ; const options = instance . propsOptions [ 0 ] ; for ( const key in options ) { let opt = options [ key ] ; if ( opt == null ) continue ; validateProp ( key , resolvedValues [ key ] , opt , ! hasOwn ( rawProps , key ) && ! hasOwn ( rawProps , hyphenate ( key ) ) ) ; } } /**
 * dev only
 */ function validateProp ( name , value , prop , isAbsent ) { const { type , required , validator } = prop ; if ( required && isAbsent ) { warn ( 'Missing required prop: "' + name + '"' ) ; return ; } if ( value == null && ! prop . required ) { return ; } if ( type != null && type !== true ) { let isValid = false ; const types = isArray ( type ) ? type : [ type ] ; const expectedTypes = [ ] ; for ( let i = 0 ; i < types . length && ! isValid ; i ++ ) { const { valid , expectedType } = assertType ( value , types [ i ] ) ; expectedTypes . push ( expectedType || '' ) ; isValid = valid ; } if ( ! isValid ) { warn ( getInvalidTypeMessage ( name , value , expectedTypes ) ) ; return ; } } if ( validator && ! validator ( value ) ) { warn ( 'Invalid prop: custom validator check failed for prop "' + name + '".' ) ; } } const isSimpleType = makeMap ( 'String,Number,Boolean,Function,Symbol,BigInt' ) ; type AssertionResult = { valid : boolean ; expectedType : string ; } ; /**
 * dev only
 */ function assertType ( value , type ) { let valid ; const expectedType = getType ( type ) ; if ( isSimpleType ( expectedType ) ) { const t = typeof value ; valid = t === expectedType . toLowerCase ( ) ; if ( ! valid && t === 'object' ) { valid = value instanceof type ; } } else if ( expectedType === 'Object' ) { valid = isObject ( value ) ; } else if ( expectedType === 'Array' ) { valid = isArray ( value ) ; } else if ( expectedType === 'null' ) { valid = value === null ; } else { valid = value instanceof type ; } return { valid , expectedType } ; } /**
 * dev only
 */ function getInvalidTypeMessage ( name , value , expectedTypes ) { let message = `Invalid prop: type check failed for prop "${ name }".` + ` Expected ${ expectedTypes . map ( capitalize ) . join ( ' | ' ) }` ; const expectedType = expectedTypes [ 0 ] ; const receivedType = toRawType ( value ) ; const expectedValue = styleValue ( value , expectedType ) ; const receivedValue = styleValue ( value , receivedType ) ; if ( expectedTypes . length === 1 && isExplicable ( expectedType ) && ! isBoolean ( expectedType , receivedType ) ) { message += ` with value ${ expectedValue }` ; } message += `, got ${ receivedType } ` ; if ( isExplicable ( receivedType ) ) { message += `with value ${ receivedValue }.` ; } return message ; } /**
 * dev only
 */ function styleValue ( value , type ) { if ( type === 'String' ) { return `"${ value }"` ; } else if ( type === 'Number' ) { return `${ Number ( value ) }` ; } else { return `${ value }` ; } } /**
 * dev only
 */ function isExplicable ( type ) { const explicitTypes = [ 'string' , 'number' , 'boolean' ] ; return explicitTypes . some ( elem => type . toLowerCase ( ) === elem ) ; } /**
 * dev only
 */ function isBoolean ( ... args ) { return args . some ( elem => elem . toLowerCase ( ) === 'boolean' ) ; }