UNPKG

8.32 kBJavaScriptView Raw
1/* @flow */
2
3export const emptyObject = Object.freeze({})
4
5// These helpers produce better VM code in JS engines due to their
6// explicitness and function inlining.
7export function isUndef (v: any): boolean %checks {
8 return v === undefined || v === null
9}
10
11export function isDef (v: any): boolean %checks {
12 return v !== undefined && v !== null
13}
14
15export function isTrue (v: any): boolean %checks {
16 return v === true
17}
18
19export function isFalse (v: any): boolean %checks {
20 return v === false
21}
22
23/**
24 * Check if value is primitive.
25 */
26export function isPrimitive (value: any): boolean %checks {
27 return (
28 typeof value === 'string' ||
29 typeof value === 'number' ||
30 // $flow-disable-line
31 typeof value === 'symbol' ||
32 typeof value === 'boolean'
33 )
34}
35
36/**
37 * Quick object check - this is primarily used to tell
38 * Objects from primitive values when we know the value
39 * is a JSON-compliant type.
40 */
41export function isObject (obj: mixed): boolean %checks {
42 return obj !== null && typeof obj === 'object'
43}
44
45/**
46 * Get the raw type string of a value, e.g., [object Object].
47 */
48const _toString = Object.prototype.toString
49
50export function toRawType (value: any): string {
51 return _toString.call(value).slice(8, -1)
52}
53
54/**
55 * Strict object type check. Only returns true
56 * for plain JavaScript objects.
57 */
58export function isPlainObject (obj: any): boolean {
59 return _toString.call(obj) === '[object Object]'
60}
61
62export function isRegExp (v: any): boolean {
63 return _toString.call(v) === '[object RegExp]'
64}
65
66/**
67 * Check if val is a valid array index.
68 */
69export function isValidArrayIndex (val: any): boolean {
70 const n = parseFloat(String(val))
71 return n >= 0 && Math.floor(n) === n && isFinite(val)
72}
73
74export function isPromise (val: any): boolean {
75 return (
76 isDef(val) &&
77 typeof val.then === 'function' &&
78 typeof val.catch === 'function'
79 )
80}
81
82/**
83 * Convert a value to a string that is actually rendered.
84 */
85export function toString (val: any): string {
86 return val == null
87 ? ''
88 : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
89 ? JSON.stringify(val, null, 2)
90 : String(val)
91}
92
93/**
94 * Convert an input value to a number for persistence.
95 * If the conversion fails, return original string.
96 */
97export function toNumber (val: string): number | string {
98 const n = parseFloat(val)
99 return isNaN(n) ? val : n
100}
101
102/**
103 * Make a map and return a function for checking if a key
104 * is in that map.
105 */
106export function makeMap (
107 str: string,
108 expectsLowerCase?: boolean
109): (key: string) => true | void {
110 const map = Object.create(null)
111 const list: Array<string> = str.split(',')
112 for (let i = 0; i < list.length; i++) {
113 map[list[i]] = true
114 }
115 return expectsLowerCase
116 ? val => map[val.toLowerCase()]
117 : val => map[val]
118}
119
120/**
121 * Check if a tag is a built-in tag.
122 */
123export const isBuiltInTag = makeMap('slot,component', true)
124
125/**
126 * Check if an attribute is a reserved attribute.
127 */
128export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')
129
130/**
131 * Remove an item from an array.
132 */
133export function remove (arr: Array<any>, item: any): Array<any> | void {
134 if (arr.length) {
135 const index = arr.indexOf(item)
136 if (index > -1) {
137 return arr.splice(index, 1)
138 }
139 }
140}
141
142/**
143 * Check whether an object has the property.
144 */
145const hasOwnProperty = Object.prototype.hasOwnProperty
146export function hasOwn (obj: Object | Array<*>, key: string): boolean {
147 return hasOwnProperty.call(obj, key)
148}
149
150/**
151 * Create a cached version of a pure function.
152 */
153export function cached<F: Function> (fn: F): F {
154 const cache = Object.create(null)
155 return (function cachedFn (str: string) {
156 const hit = cache[str]
157 return hit || (cache[str] = fn(str))
158 }: any)
159}
160
161/**
162 * Camelize a hyphen-delimited string.
163 */
164const camelizeRE = /-(\w)/g
165export const camelize = cached((str: string): string => {
166 return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
167})
168
169/**
170 * Capitalize a string.
171 */
172export const capitalize = cached((str: string): string => {
173 return str.charAt(0).toUpperCase() + str.slice(1)
174})
175
176/**
177 * Hyphenate a camelCase string.
178 */
179const hyphenateRE = /\B([A-Z])/g
180export const hyphenate = cached((str: string): string => {
181 return str.replace(hyphenateRE, '-$1').toLowerCase()
182})
183
184/**
185 * Simple bind polyfill for environments that do not support it,
186 * e.g., PhantomJS 1.x. Technically, we don't need this anymore
187 * since native bind is now performant enough in most browsers.
188 * But removing it would mean breaking code that was able to run in
189 * PhantomJS 1.x, so this must be kept for backward compatibility.
190 */
191
192/* istanbul ignore next */
193function polyfillBind (fn: Function, ctx: Object): Function {
194 function boundFn (a) {
195 const l = arguments.length
196 return l
197 ? l > 1
198 ? fn.apply(ctx, arguments)
199 : fn.call(ctx, a)
200 : fn.call(ctx)
201 }
202
203 boundFn._length = fn.length
204 return boundFn
205}
206
207function nativeBind (fn: Function, ctx: Object): Function {
208 return fn.bind(ctx)
209}
210
211export const bind = Function.prototype.bind
212 ? nativeBind
213 : polyfillBind
214
215/**
216 * Convert an Array-like object to a real Array.
217 */
218export function toArray (list: any, start?: number): Array<any> {
219 start = start || 0
220 let i = list.length - start
221 const ret: Array<any> = new Array(i)
222 while (i--) {
223 ret[i] = list[i + start]
224 }
225 return ret
226}
227
228/**
229 * Mix properties into target object.
230 */
231export function extend (to: Object, _from: ?Object): Object {
232 for (const key in _from) {
233 to[key] = _from[key]
234 }
235 return to
236}
237
238/**
239 * Merge an Array of Objects into a single Object.
240 */
241export function toObject (arr: Array<any>): Object {
242 const res = {}
243 for (let i = 0; i < arr.length; i++) {
244 if (arr[i]) {
245 extend(res, arr[i])
246 }
247 }
248 return res
249}
250
251/* eslint-disable no-unused-vars */
252
253/**
254 * Perform no operation.
255 * Stubbing args to make Flow happy without leaving useless transpiled code
256 * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).
257 */
258export function noop (a?: any, b?: any, c?: any) {}
259
260/**
261 * Always return false.
262 */
263export const no = (a?: any, b?: any, c?: any) => false
264
265/* eslint-enable no-unused-vars */
266
267/**
268 * Return the same value.
269 */
270export const identity = (_: any) => _
271
272/**
273 * Generate a string containing static keys from compiler modules.
274 */
275export function genStaticKeys (modules: Array<ModuleOptions>): string {
276 return modules.reduce((keys, m) => {
277 return keys.concat(m.staticKeys || [])
278 }, []).join(',')
279}
280
281/**
282 * Check if two values are loosely equal - that is,
283 * if they are plain objects, do they have the same shape?
284 */
285export function looseEqual (a: any, b: any): boolean {
286 if (a === b) return true
287 const isObjectA = isObject(a)
288 const isObjectB = isObject(b)
289 if (isObjectA && isObjectB) {
290 try {
291 const isArrayA = Array.isArray(a)
292 const isArrayB = Array.isArray(b)
293 if (isArrayA && isArrayB) {
294 return a.length === b.length && a.every((e, i) => {
295 return looseEqual(e, b[i])
296 })
297 } else if (a instanceof Date && b instanceof Date) {
298 return a.getTime() === b.getTime()
299 } else if (!isArrayA && !isArrayB) {
300 const keysA = Object.keys(a)
301 const keysB = Object.keys(b)
302 return keysA.length === keysB.length && keysA.every(key => {
303 return looseEqual(a[key], b[key])
304 })
305 } else {
306 /* istanbul ignore next */
307 return false
308 }
309 } catch (e) {
310 /* istanbul ignore next */
311 return false
312 }
313 } else if (!isObjectA && !isObjectB) {
314 return String(a) === String(b)
315 } else {
316 return false
317 }
318}
319
320/**
321 * Return the first index at which a loosely equal value can be
322 * found in the array (if value is a plain object, the array must
323 * contain an object of the same shape), or -1 if it is not present.
324 */
325export function looseIndexOf (arr: Array<mixed>, val: mixed): number {
326 for (let i = 0; i < arr.length; i++) {
327 if (looseEqual(arr[i], val)) return i
328 }
329 return -1
330}
331
332/**
333 * Ensure a function is called only once.
334 */
335export function once (fn: Function): Function {
336 let called = false
337 return function () {
338 if (!called) {
339 called = true
340 fn.apply(this, arguments)
341 }
342 }
343}