8889841clang.ts000066600000002300150441737710006045 0ustar00/** * unicode letters used for parsing html tags, component names and property paths. * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname * skipping \u10000-\uEFFFF due to it freezing up PhantomJS */ export const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/ /** * Check if a string starts with $ or _ */ export function isReserved(str: string): boolean { const c = (str + '').charCodeAt(0) return c === 0x24 || c === 0x5f } /** * Define a property. */ export function def(obj: Object, key: string, val: any, enumerable?: boolean) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }) } /** * Parse simple path. */ const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_\\d]`) export function parsePath(path: string): any { if (bailRE.test(path)) { return } const segments = path.split('.') return function (obj) { for (let i = 0; i < segments.length; i++) { if (!obj) return obj = obj[segments[i]] } return obj } } index.ts000066600000000372150441737710006242 0ustar00export * from 'shared/util' export * from './lang' export * from './env' export * from './options' export * from './debug' export * from './props' export * from './error' export * from './next-tick' export { defineReactive } from '../observer/index' props.ts000066600000014437150441737710006305 0ustar00import { warn } from './debug' import { observe, toggleObserving, shouldObserve } from '../observer/index' import { hasOwn, isArray, isObject, isFunction, toRawType, hyphenate, capitalize, isPlainObject } from 'shared/util' import type { Component } from 'types/component' type PropOptions = { type: Function | Array | 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] // boolean casting const booleanIndex = getTypeIndex(Boolean, prop.type) if (booleanIndex > -1) { if (absent && !hasOwn(prop, 'default')) { value = false } else if (value === '' || value === hyphenate(key)) { // only cast empty string / same name to boolean if // boolean has higher priority const stringIndex = getTypeIndex(String, prop.type) if (stringIndex < 0 || booleanIndex < stringIndex) { 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 prevShouldObserve = shouldObserve toggleObserving(true) observe(value) toggleObserving(prevShouldObserve) } if (__DEV__) { assertProp(prop, key, value, vm, absent) } return value } /** * Get the default value of a prop. */ function getPropDefaultValue( vm: Component | undefined, 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 (__DEV__ && isObject(def)) { 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._props[key] !== undefined ) { return vm._props[key] } // call factory function for non-Function types // a value is Function if its prototype is function even across different execution context return isFunction(def) && getType(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 as any) === true const expectedTypes: string[] = [] if (type) { if (!isArray(type)) { type = [type] } for (let i = 0; i < type.length && !valid; i++) { const assertedType = assertType(value, type[i], vm) expectedTypes.push(assertedType.expectedType || '') valid = assertedType.valid } } const haveExpectedTypes = expectedTypes.some(t => t) if (!valid && haveExpectedTypes) { warn(getInvalidTypeMessage(name, value, expectedTypes), vm) return } const validator = prop.validator if (validator) { if (!validator(value)) { warn( 'Invalid prop: custom validator check failed for prop "' + name + '".', vm ) } } } const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/ function assertType( value: any, type: Function, vm?: Component ): { valid: boolean expectedType: string } { let valid const expectedType = getType(type) if (simpleCheckRE.test(expectedType)) { const t = typeof value valid = t === expectedType.toLowerCase() // for primitive wrapper objects if (!valid && t === 'object') { valid = value instanceof type } } else if (expectedType === 'Object') { valid = isPlainObject(value) } else if (expectedType === 'Array') { valid = isArray(value) } else { try { valid = value instanceof type } catch (e: any) { warn('Invalid prop type: "' + String(type) + '" is not a constructor', vm) valid = false } } return { valid, expectedType } } const functionTypeCheckRE = /^\s*function (\w+)/ /** * 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(functionTypeCheckRE) return match ? match[1] : '' } function isSameType(a, b) { return getType(a) === getType(b) } function getTypeIndex(type, expectedTypes): number { if (!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 = `Invalid prop: type check failed for prop "${name}".` + ` Expected ${expectedTypes.map(capitalize).join(', ')}` const expectedType = expectedTypes[0] const receivedType = toRawType(value) // check if we need to specify expected value if ( expectedTypes.length === 1 && isExplicable(expectedType) && isExplicable(typeof value) && !isBoolean(expectedType, receivedType) ) { message += ` with value ${styleValue(value, expectedType)}` } message += `, got ${receivedType} ` // check if we need to specify received value if (isExplicable(receivedType)) { message += `with value ${styleValue(value, receivedType)}.` } return message } function styleValue(value, type) { if (type === 'String') { return `"${value}"` } else if (type === 'Number') { return `${Number(value)}` } else { return `${value}` } } const EXPLICABLE_TYPES = ['string', 'number', 'boolean'] function isExplicable(value) { return EXPLICABLE_TYPES.some(elem => value.toLowerCase() === elem) } function isBoolean(...args) { return args.some(elem => elem.toLowerCase() === 'boolean') } error.ts000066600000004157150441737710006271 0ustar00import config from '../config' import { warn } from './debug' import { inBrowser } from './env' import { isPromise } from 'shared/util' import { pushTarget, popTarget } from '../observer/dep' export function handleError(err: Error, vm: any, info: string) { // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. // See: https://github.com/vuejs/vuex/issues/1505 pushTarget() try { if (vm) { let cur = vm while ((cur = cur.$parent)) { const hooks = cur.$options.errorCaptured if (hooks) { for (let i = 0; i < hooks.length; i++) { try { const capture = hooks[i].call(cur, err, vm, info) === false if (capture) return } catch (e: any) { globalHandleError(e, cur, 'errorCaptured hook') } } } } } globalHandleError(err, vm, info) } finally { popTarget() } } export function invokeWithErrorHandling( handler: Function, context: any, args: null | any[], vm: any, info: string ) { let res try { res = args ? handler.apply(context, args) : handler.call(context) if (res && !res._isVue && isPromise(res) && !(res as any)._handled) { res.catch(e => handleError(e, vm, info + ` (Promise/async)`)) // issue #9511 // avoid catch triggering multiple times when nested calls ;(res as any)._handled = true } } catch (e: any) { handleError(e, vm, info) } return res } function globalHandleError(err, vm, info) { if (config.errorHandler) { try { return config.errorHandler.call(null, err, vm, info) } catch (e: any) { // if the user intentionally throws the original error in the handler, // do not log it twice if (e !== err) { logError(e, null, 'config.errorHandler') } } } logError(err, vm, info) } function logError(err, vm, info) { if (__DEV__) { warn(`Error in ${info}: "${err.toString()}"`, vm) } /* istanbul ignore else */ if (inBrowser && typeof console !== 'undefined') { console.error(err) } else { throw err } } next-tick.ts000066600000007531150441737710007045 0ustar00/* globals MutationObserver */ import { noop } from 'shared/util' import { handleError } from './error' import { isIE, isIOS, isNative } from './env' export let isUsingMicroTask = false const callbacks: Array = [] let pending = false function flushCallbacks() { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } } // Here we have async deferring wrappers using microtasks. // In 2.5 we used (macro) tasks (in combination with microtasks). // However, it has subtle problems when state is changed right before repaint // (e.g. #6813, out-in transitions). // Also, using (macro) tasks in event handler would cause some weird behaviors // that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109). // So we now use microtasks everywhere, again. // A major drawback of this tradeoff is that there are some scenarios // where microtasks have too high a priority and fire in between supposedly // sequential events (e.g. #4521, #6690, which have workarounds) // or even between bubbling of the same event (#6566). let timerFunc // The nextTick behavior leverages the microtask queue, which can be accessed // via either native Promise.then or MutationObserver. // MutationObserver has wider support, however it is seriously bugged in // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It // completely stops working after triggering a few times... so, if native // Promise is available, we will use it: /* istanbul ignore next, $flow-disable-line */ if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) // In problematic UIWebViews, Promise.then doesn't completely break, but // it can get stuck in a weird state where callbacks are pushed into the // microtask queue but the queue isn't being flushed, until the browser // needs to do some other work, e.g. handle a timer. Therefore we can // "force" the microtask queue to be flushed by adding an empty timer. if (isIOS) setTimeout(noop) } isUsingMicroTask = true } else if ( !isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]') ) { // Use MutationObserver where native Promise is not available, // e.g. PhantomJS, iOS7, Android 4.4 // (#6466 MutationObserver is unreliable in IE11) let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // Fallback to setImmediate. // Technically it leverages the (macro) task queue, // but it is still a better choice than setTimeout. timerFunc = () => { setImmediate(flushCallbacks) } } else { // Fallback to setTimeout. timerFunc = () => { setTimeout(flushCallbacks, 0) } } export function nextTick(): Promise export function nextTick(this: T, cb: (this: T, ...args: any[]) => any): void export function nextTick(cb: (this: T, ...args: any[]) => any, ctx: T): void /** * @internal */ export function nextTick(cb?: (...args: any[]) => any, ctx?: object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e: any) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } // $flow-disable-line if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } } env.ts000066600000005311150441737710005721 0ustar00// can we use __proto__? export const hasProto = '__proto__' in {} // Browser environment sniffing export const inBrowser = typeof window !== 'undefined' export const UA = inBrowser && window.navigator.userAgent.toLowerCase() export const isIE = UA && /msie|trident/.test(UA) export const isIE9 = UA && UA.indexOf('msie 9.0') > 0 export const isEdge = UA && UA.indexOf('edge/') > 0 export const isAndroid = UA && UA.indexOf('android') > 0 export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA) export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge export const isPhantomJS = UA && /phantomjs/.test(UA) export const isFF = UA && UA.match(/firefox\/(\d+)/) // Firefox has a "watch" function on Object.prototype... // @ts-expect-error firebox support export const nativeWatch = {}.watch export let supportsPassive = false if (inBrowser) { try { const opts = {} Object.defineProperty(opts, 'passive', { get() { /* istanbul ignore next */ supportsPassive = true } } as object) // https://github.com/facebook/flow/issues/285 window.addEventListener('test-passive', null as any, opts) } catch (e: any) {} } // this needs to be lazy-evaled because vue may be required before // vue-server-renderer can set VUE_ENV let _isServer export const isServerRendering = () => { if (_isServer === undefined) { /* istanbul ignore if */ if (!inBrowser && typeof global !== 'undefined') { // detect presence of vue-server-renderer and avoid // Webpack shimming the process _isServer = global['process'] && global['process'].env.VUE_ENV === 'server' } else { _isServer = false } } return _isServer } // detect devtools export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__ /* istanbul ignore next */ export function isNative(Ctor: any): boolean { return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) } export const hasSymbol = typeof Symbol !== 'undefined' && isNative(Symbol) && typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys) let _Set // $flow-disable-line /* istanbul ignore if */ if (typeof Set !== 'undefined' && isNative(Set)) { // use native Set when available. _Set = Set } else { // a non-standard Set polyfill that only works with primitive keys. _Set = class Set implements SimpleSet { set: Record = Object.create(null) has(key: string | number) { return this.set[key] === true } add(key: string | number) { this.set[key] = true } clear() { this.set = Object.create(null) } } } export interface SimpleSet { has(key: string | number): boolean add(key: string | number): any clear(): void } export { _Set } perf.ts000066600000001070150441737710006063 0ustar00import { inBrowser } from './env' export let mark export let measure if (__DEV__) { const perf = inBrowser && window.performance /* istanbul ignore if */ if ( perf && // @ts-ignore perf.mark && // @ts-ignore perf.measure && // @ts-ignore perf.clearMarks && // @ts-ignore perf.clearMeasures ) { mark = tag => perf.mark(tag) measure = (name, startTag, endTag) => { perf.measure(name, startTag, endTag) perf.clearMarks(startTag) perf.clearMarks(endTag) // perf.clearMeasures(name) } } } debug.ts000066600000006014150441737710006220 0ustar00import config from '../config' import { noop, isArray, isFunction } from 'shared/util' import type { Component } from 'types/component' import { currentInstance } from 'v3/currentInstance' import { getComponentName } from '../vdom/create-component' export let warn: (msg: string, vm?: Component | null) => void = noop export let tip = noop export let generateComponentTrace: (vm: Component) => string // work around flow check export let formatComponentName: (vm: Component, includeFile?: false) => string if (__DEV__) { const hasConsole = typeof console !== 'undefined' const classifyRE = /(?:^|[-_])(\w)/g const classify = str => str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '') warn = (msg, vm = currentInstance) => { const trace = vm ? generateComponentTrace(vm) : '' if (config.warnHandler) { config.warnHandler.call(null, msg, vm, trace) } else if (hasConsole && !config.silent) { console.error(`[Vue warn]: ${msg}${trace}`) } } tip = (msg, vm) => { if (hasConsole && !config.silent) { console.warn(`[Vue tip]: ${msg}` + (vm ? generateComponentTrace(vm) : '')) } } formatComponentName = (vm, includeFile) => { if (vm.$root === vm) { return '' } const options = isFunction(vm) && (vm as any).cid != null ? (vm as any).options : vm._isVue ? vm.$options || (vm.constructor as any).options : vm let name = getComponentName(options) const file = options.__file if (!name && file) { const match = file.match(/([^/\\]+)\.vue$/) name = match && match[1] } return ( (name ? `<${classify(name)}>` : ``) + (file && includeFile !== false ? ` at ${file}` : '') ) } const repeat = (str, n) => { let res = '' while (n) { if (n % 2 === 1) res += str if (n > 1) str += str n >>= 1 } return res } generateComponentTrace = (vm: Component | undefined) => { if ((vm as any)._isVue && vm!.$parent) { const tree: any[] = [] let currentRecursiveSequence = 0 while (vm) { if (tree.length > 0) { const last = tree[tree.length - 1] if (last.constructor === vm.constructor) { currentRecursiveSequence++ vm = vm.$parent! continue } else if (currentRecursiveSequence > 0) { tree[tree.length - 1] = [last, currentRecursiveSequence] currentRecursiveSequence = 0 } } tree.push(vm) vm = vm.$parent! } return ( '\n\nfound in\n\n' + tree .map( (vm, i) => `${i === 0 ? '---> ' : repeat(' ', 5 + i * 2)}${ isArray(vm) ? `${formatComponentName(vm[0])}... (${ vm[1] } recursive calls)` : formatComponentName(vm) }` ) .join('\n') ) } else { return `\n\n(found in ${formatComponentName(vm!)})` } } } options.ts000066600000027364150441737710006640 0ustar00import config from '../config' import { warn } from './debug' import { set } from '../observer/index' import { unicodeRegExp } from './lang' import { nativeWatch, hasSymbol } from './env' import { isArray, isFunction } from 'shared/util' import { ASSET_TYPES, LIFECYCLE_HOOKS } from 'shared/constants' import { extend, hasOwn, camelize, toRawType, capitalize, isBuiltInTag, isPlainObject } from 'shared/util' import type { Component } from 'types/component' import type { ComponentOptions } from 'types/options' /** * Option overwriting strategies are functions that handle * how to merge a parent option value and a child option * value into the final value. */ const strats = config.optionMergeStrategies /** * Options with restrictions */ if (__DEV__) { strats.el = strats.propsData = function ( parent: any, child: any, vm: any, key: any ) { if (!vm) { warn( `option "${key}" can only be used during instance ` + 'creation with the `new` keyword.' ) } return defaultStrat(parent, child) } } /** * Helper that recursively merges two data objects together. */ function mergeData( to: Record, from: Record | null, recursive = true ): Record { if (!from) return to let key, toVal, fromVal const keys = hasSymbol ? (Reflect.ownKeys(from) as string[]) : Object.keys(from) for (let i = 0; i < keys.length; i++) { key = keys[i] // in case the object is already observed... if (key === '__ob__') continue toVal = to[key] fromVal = from[key] if (!recursive || !hasOwn(to, key)) { set(to, key, fromVal) } else if ( toVal !== fromVal && isPlainObject(toVal) && isPlainObject(fromVal) ) { mergeData(toVal, fromVal) } } return to } /** * Data */ export function mergeDataOrFn( parentVal: any, childVal: any, vm?: Component ): Function | null { if (!vm) { // in a Vue.extend merge, both should be functions if (!childVal) { return parentVal } if (!parentVal) { return childVal } // when parentVal & childVal are both present, // we need to return a function that returns the // merged result of both functions... no need to // check if parentVal is a function here because // it has to be a function to pass previous merges. return function mergedDataFn() { return mergeData( isFunction(childVal) ? childVal.call(this, this) : childVal, isFunction(parentVal) ? parentVal.call(this, this) : parentVal ) } } else { return function mergedInstanceDataFn() { // instance merge const instanceData = isFunction(childVal) ? childVal.call(vm, vm) : childVal const defaultData = isFunction(parentVal) ? parentVal.call(vm, vm) : parentVal if (instanceData) { return mergeData(instanceData, defaultData) } else { return defaultData } } } } strats.data = function ( parentVal: any, childVal: any, vm?: Component ): Function | null { if (!vm) { if (childVal && typeof childVal !== 'function') { __DEV__ && warn( 'The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm ) return parentVal } return mergeDataOrFn(parentVal, childVal) } return mergeDataOrFn(parentVal, childVal, vm) } /** * Hooks and props are merged as arrays. */ export function mergeLifecycleHook( parentVal: Array | null, childVal: Function | Array | null ): Array | null { const res = childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal return res ? dedupeHooks(res) : res } function dedupeHooks(hooks: any) { const res: Array = [] for (let i = 0; i < hooks.length; i++) { if (res.indexOf(hooks[i]) === -1) { res.push(hooks[i]) } } return res } LIFECYCLE_HOOKS.forEach(hook => { strats[hook] = mergeLifecycleHook }) /** * Assets * * When a vm is present (instance creation), we need to do * a three-way merge between constructor options, instance * options and parent options. */ function mergeAssets( parentVal: Object | null, childVal: Object | null, vm: Component | null, key: string ): Object { const res = Object.create(parentVal || null) if (childVal) { __DEV__ && assertObjectType(key, childVal, vm) return extend(res, childVal) } else { return res } } ASSET_TYPES.forEach(function (type) { strats[type + 's'] = mergeAssets }) /** * Watchers. * * Watchers hashes should not overwrite one * another, so we merge them as arrays. */ strats.watch = function ( parentVal: Record | null, childVal: Record | null, vm: Component | null, key: string ): Object | null { // work around Firefox's Object.prototype.watch... //@ts-expect-error work around if (parentVal === nativeWatch) parentVal = undefined //@ts-expect-error work around if (childVal === nativeWatch) childVal = undefined /* istanbul ignore if */ if (!childVal) return Object.create(parentVal || null) if (__DEV__) { assertObjectType(key, childVal, vm) } if (!parentVal) return childVal const ret: Record = {} extend(ret, parentVal) for (const key in childVal) { let parent = ret[key] const child = childVal[key] if (parent && !isArray(parent)) { parent = [parent] } ret[key] = parent ? parent.concat(child) : isArray(child) ? child : [child] } return ret } /** * Other object hashes. */ strats.props = strats.methods = strats.inject = strats.computed = function ( parentVal: Object | null, childVal: Object | null, vm: Component | null, key: string ): Object | null { if (childVal && __DEV__) { assertObjectType(key, childVal, vm) } if (!parentVal) return childVal const ret = Object.create(null) extend(ret, parentVal) if (childVal) extend(ret, childVal) return ret } strats.provide = function (parentVal: Object | null, childVal: Object | null) { if (!parentVal) return childVal return function () { const ret = Object.create(null) mergeData(ret, isFunction(parentVal) ? parentVal.call(this) : parentVal) if (childVal) { mergeData( ret, isFunction(childVal) ? childVal.call(this) : childVal, false // non-recursive ) } return ret } } /** * Default strategy. */ const defaultStrat = function (parentVal: any, childVal: any): any { return childVal === undefined ? parentVal : childVal } /** * Validate component names */ function checkComponents(options: Record) { for (const key in options.components) { validateComponentName(key) } } export function validateComponentName(name: string) { if ( !new RegExp(`^[a-zA-Z][\\-\\.0-9_${unicodeRegExp.source}]*$`).test(name) ) { warn( 'Invalid component name: "' + name + '". Component names ' + 'should conform to valid custom element name in html5 specification.' ) } if (isBuiltInTag(name) || config.isReservedTag(name)) { warn( 'Do not use built-in or reserved HTML elements as component ' + 'id: ' + name ) } } /** * Ensure all props option syntax are normalized into the * Object-based format. */ function normalizeProps(options: Record, vm?: Component | null) { const props = options.props if (!props) return const res: Record = {} let i, val, name if (isArray(props)) { i = props.length while (i--) { val = props[i] if (typeof val === 'string') { name = camelize(val) res[name] = { type: null } } else if (__DEV__) { warn('props must be strings when using array syntax.') } } } else if (isPlainObject(props)) { for (const key in props) { val = props[key] name = camelize(key) res[name] = isPlainObject(val) ? val : { type: val } } } else if (__DEV__) { warn( `Invalid value for option "props": expected an Array or an Object, ` + `but got ${toRawType(props)}.`, vm ) } options.props = res } /** * Normalize all injections into Object-based format */ function normalizeInject(options: Record, vm?: Component | null) { const inject = options.inject if (!inject) return const normalized: Record = (options.inject = {}) if (isArray(inject)) { for (let i = 0; i < inject.length; i++) { normalized[inject[i]] = { from: inject[i] } } } else if (isPlainObject(inject)) { for (const key in inject) { const val = inject[key] normalized[key] = isPlainObject(val) ? extend({ from: key }, val) : { from: val } } } else if (__DEV__) { warn( `Invalid value for option "inject": expected an Array or an Object, ` + `but got ${toRawType(inject)}.`, vm ) } } /** * Normalize raw function directives into object format. */ function normalizeDirectives(options: Record) { const dirs = options.directives if (dirs) { for (const key in dirs) { const def = dirs[key] if (isFunction(def)) { dirs[key] = { bind: def, update: def } } } } } function assertObjectType(name: string, value: any, vm: Component | null) { if (!isPlainObject(value)) { warn( `Invalid value for option "${name}": expected an Object, ` + `but got ${toRawType(value)}.`, vm ) } } /** * Merge two option objects into a new one. * Core utility used in both instantiation and inheritance. */ export function mergeOptions( parent: Record, child: Record, vm?: Component | null ): ComponentOptions { if (__DEV__) { checkComponents(child) } if (isFunction(child)) { // @ts-expect-error child = child.options } normalizeProps(child, vm) normalizeInject(child, vm) normalizeDirectives(child) // Apply extends and mixins on the child options, // but only if it is a raw options object that isn't // the result of another mergeOptions call. // Only merged options has the _base property. if (!child._base) { if (child.extends) { parent = mergeOptions(parent, child.extends, vm) } if (child.mixins) { for (let i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm) } } } const options: ComponentOptions = {} as any let key for (key in parent) { mergeField(key) } for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } } function mergeField(key: any) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) } return options } /** * Resolve an asset. * This function is used because child instances need access * to assets defined in its ancestor chain. */ export function resolveAsset( options: Record, type: string, id: string, warnMissing?: boolean ): any { /* istanbul ignore if */ if (typeof id !== 'string') { return } const assets = options[type] // check local registration variations first if (hasOwn(assets, id)) return assets[id] const camelizedId = camelize(id) if (hasOwn(assets, camelizedId)) return assets[camelizedId] const PascalCaseId = capitalize(camelizedId) if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId] // fallback to prototype chain const res = assets[id] || assets[camelizedId] || assets[PascalCaseId] if (__DEV__ && warnMissing && !res) { warn('Failed to resolve ' + type.slice(0, -1) + ': ' + id) } return res }