params.js 3.9 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
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 (expectedType === 'Object') {
    valid = isPlainObject(value)
  } else if (expectedType === 'Array') {
    valid = Array.isArray(value)
  } else {
    valid = value instanceof 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')
}