1import { warn } from './debug'2import { observe, toggleObserving, shouldObserve } from '../observer/index'3import {4 hasOwn,5 isArray,6 isObject,7 isFunction,8 toRawType,9 hyphenate,10 capitalize,11 isPlainObject12} from 'shared/util'13import type { Component } from 'types/component'1415type PropOptions = {16 type: Function | Array<Function> | null17 default: any18 required?: boolean19 validator?: Function20}2122export function validateProp(23 key: string,24 propOptions: Object,25 propsData: Object,26 vm?: Component27): any {28 const prop = propOptions[key]29 const absent = !hasOwn(propsData, key)30 let value = propsData[key]31 // boolean casting32 const booleanIndex = getTypeIndex(Boolean, prop.type)33 if (booleanIndex > -1) {34 if (absent && !hasOwn(prop, 'default')) {35 value = false36 } else if (value === '' || value === hyphenate(key)) {37 // only cast empty string / same name to boolean if38 // boolean has higher priority39 const stringIndex = getTypeIndex(String, prop.type)40 if (stringIndex < 0 || booleanIndex < stringIndex) {41 value = true42 }43 }44 }45 // check default value46 if (value === undefined) {47 value = getPropDefaultValue(vm, prop, key)48 // since the default value is a fresh copy,49 // make sure to observe it.50 const prevShouldObserve = shouldObserve51 toggleObserving(true)52 observe(value)53 toggleObserving(prevShouldObserve)54 }55 if (__DEV__) {56 assertProp(prop, key, value, vm, absent)57 }58 return value59}6061/**62 * Get the default value of a prop.63 */64function getPropDefaultValue(65 vm: Component | undefined,66 prop: PropOptions,67 key: string68): any {69 // no default, return undefined70 if (!hasOwn(prop, 'default')) {71 return undefined72 }73 const def = prop.default74 // warn against non-factory defaults for Object & Array75 if (__DEV__ && isObject(def)) {76 warn(77 'Invalid default value for prop "' +78 key +79 '": ' +80 'Props with type Object/Array must use a factory function ' +81 'to return the default value.',82 vm83 )84 }85 // the raw prop value was also undefined from previous render,86 // return previous default value to avoid unnecessary watcher trigger87 if (88 vm &&89 vm.$options.propsData &&90 vm.$options.propsData[key] === undefined &&91 vm._props[key] !== undefined92 ) {93 return vm._props[key]94 }95 // call factory function for non-Function types96 // a value is Function if its prototype is function even across different execution context97 return isFunction(def) && getType(prop.type) !== 'Function'98 ? def.call(vm)99 : def100}101102/**103 * Assert whether a prop is valid.104 */105function assertProp(106 prop: PropOptions,107 name: string,108 value: any,109 vm?: Component,110 absent?: boolean111) {112 if (prop.required && absent) {113 warn('Missing required prop: "' + name + '"', vm)114 return115 }116 if (value == null && !prop.required) {117 return118 }119 let type = prop.type120 let valid = !type || (type as any) === true121 const expectedTypes: string[] = []122 if (type) {123 if (!isArray(type)) {124 type = [type]125 }126 for (let i = 0; i < type.length && !valid; i++) {127 const assertedType = assertType(value, type[i], vm)128 expectedTypes.push(assertedType.expectedType || '')129 valid = assertedType.valid130 }131 }132133 const haveExpectedTypes = expectedTypes.some(t => t)134 if (!valid && haveExpectedTypes) {135 warn(getInvalidTypeMessage(name, value, expectedTypes), vm)136 return137 }138 const validator = prop.validator139 if (validator) {140 if (!validator(value)) {141 warn(142 'Invalid prop: custom validator check failed for prop "' + name + '".',143 vm144 )145 }146 }147}148149const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/150151function assertType(152 value: any,153 type: Function,154 vm?: Component155): {156 valid: boolean157 expectedType: string158} {159 let valid160 const expectedType = getType(type)161 if (simpleCheckRE.test(expectedType)) {162 const t = typeof value163 valid = t === expectedType.toLowerCase()164 // for primitive wrapper objects165 if (!valid && t === 'object') {166 valid = value instanceof type167 }168 } else if (expectedType === 'Object') {169 valid = isPlainObject(value)170 } else if (expectedType === 'Array') {171 valid = isArray(value)172 } else {173 try {174 valid = value instanceof type175 } catch (e: any) {176 warn('Invalid prop type: "' + String(type) + '" is not a constructor', vm)177 valid = false178 }179 }180 return {181 valid,182 expectedType183 }184}185186const functionTypeCheckRE = /^\s*function (\w+)/187188/**189 * Use function string name to check built-in types,190 * because a simple equality check will fail when running191 * across different vms / iframes.192 */193function getType(fn) {194 const match = fn && fn.toString().match(functionTypeCheckRE)195 return match ? match[1] : ''196}197198function isSameType(a, b) {199 return getType(a) === getType(b)200}201202function getTypeIndex(type, expectedTypes): number {203 if (!isArray(expectedTypes)) {204 return isSameType(expectedTypes, type) ? 0 : -1205 }206 for (let i = 0, len = expectedTypes.length; i < len; i++) {207 if (isSameType(expectedTypes[i], type)) {208 return i209 }210 }211 return -1212}213214function getInvalidTypeMessage(name, value, expectedTypes) {215 let message =216 `Invalid prop: type check failed for prop "${name}".` +217 ` Expected ${expectedTypes.map(capitalize).join(', ')}`218 const expectedType = expectedTypes[0]219 const receivedType = toRawType(value)220 // check if we need to specify expected value221 if (222 expectedTypes.length === 1 &&223 isExplicable(expectedType) &&224 isExplicable(typeof value) &&225 !isBoolean(expectedType, receivedType)226 ) {227 message += ` with value ${styleValue(value, expectedType)}`228 }229 message += `, got ${receivedType} `230 // check if we need to specify received value231 if (isExplicable(receivedType)) {232 message += `with value ${styleValue(value, receivedType)}.`233 }234 return message235}236237function styleValue(value, type) {238 if (type === 'String') {239 return `"${value}"`240 } else if (type === 'Number') {241 return `${Number(value)}`242 } else {243 return `${value}`244 }245}246247const EXPLICABLE_TYPES = ['string', 'number', 'boolean']248function isExplicable(value) {249 return EXPLICABLE_TYPES.some(elem => value.toLowerCase() === elem)250}251252function isBoolean(...args) {253 return args.some(elem => elem.toLowerCase() === 'boolean')254}
Findings
✓ No findings reported for this file.