-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
/
Copy pathprops.js
182 lines (174 loc) · 4.6 KB
/
props.js
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/* @flow */
import { hasOwn, isObject, isPlainObject, capitalize, hyphenate } from 'shared/util'
import { observe, observerState } from '../observer/index'
import { warn } from './debug'
type PropOptions = {
type: Function | Array<Function> | null,
default: any,
required: ?boolean,
validator: ?Function
}
export function validateProp (
key: string,
propOptions: Object,
propsData: Object,
vm?: Component
): any {
const prop = propOptions[key]
const absent = !hasOwn(propsData, key)
let value = propsData[key]
// handle boolean props
if (isBooleanType(prop.type)) {
if (absent && !hasOwn(prop, 'default')) {
value = false
} else if (value === '' || value === hyphenate(key)) {
value = true
}
}
// check default value
if (value === undefined) {
value = getPropDefaultValue(vm, prop, key)
// since the default value is a fresh copy,
// make sure to observe it.
const prevShouldConvert = observerState.shouldConvert
observerState.shouldConvert = true
observe(value)
observerState.shouldConvert = prevShouldConvert
}
if (process.env.NODE_ENV !== 'production') {
assertProp(prop, key, value, vm, absent)
}
return value
}
/**
* Get the default value of a prop.
*/
function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): any {
// no default, return undefined
if (!hasOwn(prop, 'default')) {
return undefined
}
const def = prop.default
// warn against non-factory defaults for Object & Array
if (isObject(def)) {
process.env.NODE_ENV !== 'production' && warn(
'Invalid default value for prop "' + key + '": ' +
'Props with type Object/Array must use a factory function ' +
'to return the default value.',
vm
)
}
// the raw prop value was also undefined from previous render,
// return previous default value to avoid unnecessary watcher trigger
if (vm && vm.$options.propsData &&
vm.$options.propsData[key] === undefined &&
vm[key] !== undefined) {
return vm[key]
}
// call factory function for non-Function types
return typeof def === 'function' && prop.type !== Function
? def.call(vm)
: def
}
/**
* Assert whether a prop is valid.
*/
function assertProp (
prop: PropOptions,
name: string,
value: any,
vm: ?Component,
absent: boolean
) {
if (prop.required && absent) {
warn(
'Missing required prop: "' + name + '"',
vm
)
return
}
if (value == null && !prop.required) {
return
}
let type = prop.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) {
warn(
'Invalid prop: type check failed for prop "' + name + '".' +
' Expected ' + expectedTypes.map(capitalize).join(', ') +
', got ' + Object.prototype.toString.call(value).slice(8, -1) + '.',
vm
)
return
}
const validator = prop.validator
if (validator) {
if (!validator(value)) {
warn(
'Invalid prop: custom validator check failed for prop "' + name + '".',
vm
)
}
}
}
/**
* Assert the type of a value
*/
function assertType (value: any, type: Function): {
valid: boolean,
expectedType: ?string
} {
let valid
let expectedType = getType(type)
if (expectedType === 'String') {
valid = typeof value === (expectedType = 'string')
} else if (expectedType === 'Number') {
valid = typeof value === (expectedType = 'number')
} else if (expectedType === 'Boolean') {
valid = typeof value === (expectedType = 'boolean')
} else if (expectedType === 'Function') {
valid = typeof value === (expectedType = 'function')
} else if (expectedType === 'Object') {
valid = isPlainObject(value)
} else if (expectedType === 'Array') {
valid = Array.isArray(value)
} else {
valid = value instanceof type
}
return {
valid,
expectedType
}
}
/**
* Use function string name to check built-in types,
* because a simple equality check will fail when running
* across different vms / iframes.
*/
function getType (fn) {
const match = fn && fn.toString().match(/^\s*function (\w+)/)
return match && match[1]
}
function isBooleanType (fn) {
if (!Array.isArray(fn)) {
return getType(fn) === 'Boolean'
}
for (let i = 0, len = fn.length; i < len; i++) {
if (getType(fn[i]) === 'Boolean') {
return true
}
}
/* istanbul ignore next */
return false
}