import {
|
isFn,
|
hasOwn,
|
toRawType,
|
isPlainObject
|
} from 'uni-shared'
|
|
export default function validateParam (key, paramTypes, paramsData) {
|
const paramOptions = paramTypes[key]
|
const absent = !hasOwn(paramsData, key)
|
let value = paramsData[key]
|
|
const booleanIndex = getTypeIndex(Boolean, paramOptions.type)
|
if (booleanIndex > -1) {
|
if (absent && !hasOwn(paramOptions, 'default')) {
|
value = false
|
}
|
}
|
if (value === undefined) {
|
if (hasOwn(paramOptions, 'default')) {
|
const paramDefault = paramOptions.default
|
value = isFn(paramDefault) ? paramDefault() : paramDefault
|
paramsData[key] = value // 默认值
|
}
|
}
|
|
return assertParam(paramOptions, key, value, absent, paramsData)
|
}
|
|
function assertParam (
|
paramOptions,
|
name,
|
value,
|
absent,
|
paramsData
|
) {
|
if (paramOptions.required && absent) {
|
return `Missing required parameter \`${name}\``
|
}
|
|
if (value == null && !paramOptions.required) {
|
const validator = paramOptions.validator
|
if (validator) {
|
return validator(value, paramsData)
|
}
|
return
|
}
|
let type = paramOptions.type
|
let valid = !type || type === true
|
const expectedTypes = []
|
if (type) {
|
if (!Array.isArray(type)) {
|
type = [type]
|
}
|
for (let i = 0; i < type.length && !valid; i++) {
|
const assertedType = assertType(value, type[i])
|
expectedTypes.push(assertedType.expectedType || '')
|
valid = assertedType.valid
|
}
|
}
|
|
if (!valid) {
|
return getInvalidTypeMessage(name, value, expectedTypes)
|
}
|
|
const validator = paramOptions.validator
|
if (validator) {
|
return validator(value, paramsData)
|
}
|
}
|
|
const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/
|
|
function assertType (value, type) {
|
let valid
|
const expectedType = getType(type)
|
if (simpleCheckRE.test(expectedType)) {
|
const t = typeof value
|
valid = t === expectedType.toLowerCase()
|
if (!valid && t === 'object') {
|
valid = value instanceof type
|
}
|
} else if (value.byteLength >= 0) {
|
valid = true
|
} else if (expectedType === 'Object') {
|
valid = isPlainObject(value)
|
} else if (expectedType === 'Array') {
|
valid = Array.isArray(value)
|
} else {
|
// TODO 页面传入的ArrayBuffer使用instanceof ArrayBuffer返回false,暂做此修改
|
valid = value instanceof type || toRawType(value) === getType(type)
|
}
|
return {
|
valid,
|
expectedType
|
}
|
}
|
|
function getType (fn) {
|
const match = fn && fn.toString().match(/^\s*function (\w+)/)
|
return match ? match[1] : ''
|
}
|
|
function isSameType (a, b) {
|
return getType(a) === getType(b)
|
}
|
|
function getTypeIndex (type, expectedTypes) {
|
if (!Array.isArray(expectedTypes)) {
|
return isSameType(expectedTypes, type) ? 0 : -1
|
}
|
for (let i = 0, len = expectedTypes.length; i < len; i++) {
|
if (isSameType(expectedTypes[i], type)) {
|
return i
|
}
|
}
|
return -1
|
}
|
|
function getInvalidTypeMessage (name, value, expectedTypes) {
|
let message = `parameter \`${name}\`.` +
|
` Expected ${expectedTypes.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
|
}
|
|
function styleValue (value, type) {
|
if (type === 'String') {
|
return `"${value}"`
|
} else if (type === 'Number') {
|
return `${Number(value)}`
|
} else {
|
return `${value}`
|
}
|
}
|
|
const explicitTypes = ['string', 'number', 'boolean']
|
|
function isExplicable (value) {
|
return explicitTypes.some(elem => value.toLowerCase() === elem)
|
}
|
|
function isBoolean (...args) {
|
return args.some(elem => elem.toLowerCase() === 'boolean')
|
}
|