export const emptyObject: Record = Object.freeze({}) export const isArray = Array.isArray // These helpers produce better VM code in JS engines due to their // explicitness and function inlining. export function isUndef(v: any): v is undefined | null { return v === undefined || v === null } export function isDef(v: T): v is NonNullable { return v !== undefined && v !== null } export function isTrue(v: any): boolean { return v === true } export function isFalse(v: any): boolean { return v === false } /** * Check if value is primitive. */ export function isPrimitive(value: any): boolean { return ( typeof value === 'string' || typeof value === 'number' || // $flow-disable-line typeof value === 'symbol' || typeof value === 'boolean' ) } export function isFunction(value: any): value is (...args: any[]) => any { return typeof value === 'function' } /** * Quick object check - this is primarily used to tell * objects from primitive values when we know the value * is a JSON-compliant type. */ export function isObject(obj: any): boolean { return obj !== null && typeof obj === 'object' } /** * Get the raw type string of a value, e.g., [object Object]. */ const _toString = Object.prototype.toString export function toRawType(value: any): string { return _toString.call(value).slice(8, -1) } /** * Strict object type check. Only returns true * for plain JavaScript objects. */ export function isPlainObject(obj: any): boolean { return _toString.call(obj) === '[object Object]' } export function isRegExp(v: any): v is RegExp { return _toString.call(v) === '[object RegExp]' } /** * Check if val is a valid array index. */ export function isValidArrayIndex(val: any): boolean { const n = parseFloat(String(val)) return n >= 0 && Math.floor(n) === n && isFinite(val) } export function isPromise(val: any): val is Promise { return ( isDef(val) && typeof val.then === 'function' && typeof val.catch === 'function' ) } /** * Convert a value to a string that is actually rendered. */ export function toString(val: any): string { return val == null ? '' : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) ? JSON.stringify(val, replacer, 2) : String(val) } function replacer(_key: string, val: any): any { // avoid circular deps from v3 if (val && val.__v_isRef) { return val.value } return val } /** * Convert an input value to a number for persistence. * If the conversion fails, return original string. */ export function toNumber(val: string): number | string { const n = parseFloat(val) return isNaN(n) ? val : n } /** * Make a map and return a function for checking if a key * is in that map. */ export function makeMap( str: string, expectsLowerCase?: boolean ): (key: string) => true | undefined { const map = Object.create(null) const list: Array = str.split(',') for (let i = 0; i < list.length; i++) { map[list[i]] = true } return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val] } /** * Check if a tag is a built-in tag. */ export const isBuiltInTag = makeMap('slot,component', true) /** * Check if an attribute is a reserved attribute. */ export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is') /** * Remove an item from an array. */ export function remove(arr: Array, item: any): Array | void { const len = arr.length if (len) { // fast path for the only / last item if (item === arr[len - 1]) { arr.length = len - 1 return } const index = arr.indexOf(item) if (index > -1) { return arr.splice(index, 1) } } } /** * Check whether an object has the property. */ const hasOwnProperty = Object.prototype.hasOwnProperty export function hasOwn(obj: Object | Array, key: string): boolean { return hasOwnProperty.call(obj, key) } /** * Create a cached version of a pure function. */ export function cached(fn: (str: string) => R): (sr: string) => R { const cache: Record = Object.create(null) return function cachedFn(str: string) { const hit = cache[str] return hit || (cache[str] = fn(str)) } } /** * Camelize a hyphen-delimited string. */ const camelizeRE = /-(\w)/g export const camelize = cached((str: string): string => { return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')) }) /** * Capitalize a string. */ export const capitalize = cached((str: string): string => { return str.charAt(0).toUpperCase() + str.slice(1) }) /** * Hyphenate a camelCase string. */ const hyphenateRE = /\B([A-Z])/g export const hyphenate = cached((str: string): string => { return str.replace(hyphenateRE, '-$1').toLowerCase() }) /** * Simple bind polyfill for environments that do not support it, * e.g., PhantomJS 1.x. Technically, we don't need this anymore * since native bind is now performant enough in most browsers. * But removing it would mean breaking code that was able to run in * PhantomJS 1.x, so this must be kept for backward compatibility. */ /* istanbul ignore next */ function polyfillBind(fn: Function, ctx: Object): Function { function boundFn(a: any) { const l = arguments.length return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx) } boundFn._length = fn.length return boundFn } function nativeBind(fn: Function, ctx: Object): Function { return fn.bind(ctx) } // @ts-expect-error bind cannot be `undefined` export const bind = Function.prototype.bind ? nativeBind : polyfillBind /** * Convert an Array-like object to a real Array. */ export function toArray(list: any, start?: number): Array { start = start || 0 let i = list.length - start const ret: Array = new Array(i) while (i--) { ret[i] = list[i + start] } return ret } /** * Mix properties into target object. */ export function extend( to: Record, _from?: Record ): Record { for (const key in _from) { to[key] = _from[key] } return to } /** * Merge an Array of Objects into a single Object. */ export function toObject(arr: Array): object { const res = {} for (let i = 0; i < arr.length; i++) { if (arr[i]) { extend(res, arr[i]) } } return res } /* eslint-disable no-unused-vars */ /** * Perform no operation. * Stubbing args to make Flow happy without leaving useless transpiled code * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/). */ export function noop(a?: any, b?: any, c?: any) {} /** * Always return false. */ export const no = (a?: any, b?: any, c?: any) => false /* eslint-enable no-unused-vars */ /** * Return the same value. */ export const identity = (_: any) => _ /** * Generate a string containing static keys from compiler modules. */ export function genStaticKeys( modules: Array<{ staticKeys?: string[] } /* ModuleOptions */> ): string { return modules .reduce((keys, m) => keys.concat(m.staticKeys || []), []) .join(',') } /** * Check if two values are loosely equal - that is, * if they are plain objects, do they have the same shape? */ export function looseEqual(a: any, b: any): boolean { if (a === b) return true const isObjectA = isObject(a) const isObjectB = isObject(b) if (isObjectA && isObjectB) { try { const isArrayA = Array.isArray(a) const isArrayB = Array.isArray(b) if (isArrayA && isArrayB) { return ( a.length === b.length && a.every((e: any, i: any) => { return looseEqual(e, b[i]) }) ) } else if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime() } else if (!isArrayA && !isArrayB) { const keysA = Object.keys(a) const keysB = Object.keys(b) return ( keysA.length === keysB.length && keysA.every(key => { return looseEqual(a[key], b[key]) }) ) } else { /* istanbul ignore next */ return false } } catch (e: any) { /* istanbul ignore next */ return false } } else if (!isObjectA && !isObjectB) { return String(a) === String(b) } else { return false } } /** * Return the first index at which a loosely equal value can be * found in the array (if value is a plain object, the array must * contain an object of the same shape), or -1 if it is not present. */ export function looseIndexOf(arr: Array, val: unknown): number { for (let i = 0; i < arr.length; i++) { if (looseEqual(arr[i], val)) return i } return -1 } /** * Ensure a function is called only once. */ export function once any>(fn: T): T { let called = false return function () { if (!called) { called = true fn.apply(this, arguments as any) } } as any } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#polyfill export function hasChanged(x: unknown, y: unknown): boolean { if (x === y) { return x === 0 && 1 / x !== 1 / (y as number) } else { return x === x || y === y } }