exposePropsOnRenderContext,
exposeSetupStateOnRenderContext
} from './componentProxy'
-import { ComponentPropsOptions, initProps } from './componentProps'
+import {
+ ComponentPropsOptions,
+ NormalizedPropsOptions,
+ initProps
+} from './componentProps'
import { Slots, initSlots, InternalSlots } from './componentSlots'
import { warn } from './warning'
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
// Note: can't mark this whole interface internal because some public interfaces
// extend it.
-export interface SFCInternalOptions {
+export interface ComponentInternalOptions {
+ /**
+ * @internal
+ */
+ __props?: NormalizedPropsOptions | []
/**
* @internal
*/
export interface FunctionalComponent<
P = {},
E extends EmitsOptions = Record<string, any>
-> extends SFCInternalOptions {
+> extends ComponentInternalOptions {
// use of any here is intentional so it can be a valid JSX Element constructor
(props: P, ctx: SetupContext<E>): any
props?: ComponentPropsOptions<P>
ComponentInternalInstance,
Data,
SetupContext,
- SFCInternalOptions,
+ ComponentInternalOptions,
PublicAPIComponent,
Component
} from './component'
EE extends string = string
>
extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
- SFCInternalOptions,
+ ComponentInternalOptions,
ComponentCustomOptions {
setup?: (
this: void,
mixins,
extends: extendsOptions,
// state
- props: propsOptions,
data: dataOptions,
computed: computedOptions,
methods,
const checkDuplicateProperties = __DEV__ ? createDuplicateChecker() : null
- if (__DEV__ && propsOptions) {
- for (const key in normalizePropsOptions(propsOptions)[0]) {
- checkDuplicateProperties!(OptionTypes.PROPS, key)
+ if (__DEV__) {
+ const propsOptions = normalizePropsOptions(options)[0]
+ if (propsOptions) {
+ for (const key in propsOptions) {
+ checkDuplicateProperties!(OptionTypes.PROPS, key)
+ }
}
}
def
} from '@vue/shared'
import { warn } from './warning'
-import { Data, ComponentInternalInstance } from './component'
+import {
+ Data,
+ ComponentInternalInstance,
+ ComponentOptions,
+ Component
+} from './component'
import { isEmitListener } from './componentEmits'
import { InternalObjectKey } from './vnode'
// normalized value is a tuple of the actual normalized options
// and an array of prop keys that need value casting (booleans and defaults)
-type NormalizedPropsOptions = [Record<string, NormalizedProp>, string[]]
+export type NormalizedPropsOptions = [Record<string, NormalizedProp>, string[]]
export function initProps(
instance: ComponentInternalInstance,
const attrs: Data = {}
def(attrs, InternalObjectKey, 1)
setFullProps(instance, rawProps, props, attrs)
- const options = instance.type.props
// validation
- if (__DEV__ && options && rawProps) {
- validateProps(props, options)
+ if (__DEV__) {
+ validateProps(props, instance.type)
}
if (isStateful) {
// stateful
instance.props = isSSR ? props : shallowReactive(props)
} else {
- if (!options) {
+ if (!instance.type.props) {
// functional w/ optional props, props === attrs
instance.props = attrs
} else {
attrs,
vnode: { patchFlag }
} = instance
- const rawOptions = instance.type.props
const rawCurrentProps = toRaw(props)
- const [options] = normalizePropsOptions(rawOptions)
+ const [options] = normalizePropsOptions(instance.type)
if ((optimized || patchFlag > 0) && !(patchFlag & PatchFlags.FULL_PROPS)) {
if (patchFlag & PatchFlags.PROPS) {
}
}
- if (__DEV__ && rawOptions && rawProps) {
- validateProps(props, rawOptions)
+ if (__DEV__ && rawProps) {
+ validateProps(props, instance.type)
}
}
props: Data,
attrs: Data
) {
- const [options, needCastKeys] = normalizePropsOptions(
- instance.type.props
- )
+ const [options, needCastKeys] = normalizePropsOptions(instance.type)
const emits = instance.type.emits
if (rawProps) {
}
export function normalizePropsOptions(
- raw: ComponentPropsOptions | undefined
+ comp: Component
): NormalizedPropsOptions | [] {
- if (!raw) {
- return EMPTY_ARR as any
- }
- if ((raw as any)._n) {
- return (raw as any)._n
+ if (comp.__props) {
+ return comp.__props
}
+
+ const raw = comp.props
const normalized: NormalizedPropsOptions[0] = {}
const needCastKeys: NormalizedPropsOptions[1] = []
+
+ // apply mixin/extends props
+ let hasExtends = false
+ if (__FEATURE_OPTIONS__ && !isFunction(comp)) {
+ const extendProps = (raw: ComponentOptions) => {
+ const [props, keys] = normalizePropsOptions(raw)
+ Object.assign(normalized, props)
+ if (keys) needCastKeys.push(...keys)
+ }
+ if (comp.extends) {
+ hasExtends = true
+ extendProps(comp.extends)
+ }
+ if (comp.mixins) {
+ hasExtends = true
+ comp.mixins.forEach(extendProps)
+ }
+ }
+
+ if (!raw && !hasExtends) {
+ return (comp.__props = EMPTY_ARR)
+ }
+
if (isArray(raw)) {
for (let i = 0; i < raw.length; i++) {
if (__DEV__ && !isString(raw[i])) {
normalized[normalizedKey] = EMPTY_OBJ
}
}
- } else {
+ } else if (raw) {
if (__DEV__ && !isObject(raw)) {
warn(`invalid props options`, raw)
}
}
}
const normalizedEntry: NormalizedPropsOptions = [normalized, needCastKeys]
- def(raw, '_n', normalizedEntry)
+ comp.__props = normalizedEntry
return normalizedEntry
}
return -1
}
-function validateProps(props: Data, rawOptions: ComponentPropsOptions) {
+/**
+ * dev only
+ */
+function validateProps(props: Data, comp: Component) {
const rawValues = toRaw(props)
- const options = normalizePropsOptions(rawOptions)[0]
+ const options = normalizePropsOptions(comp)[0]
for (const key in options) {
let opt = options[key]
if (opt == null) continue
}
}
+/**
+ * dev only
+ */
function validatePropName(key: string) {
if (key[0] !== '$') {
return true
return false
}
+/**
+ * dev only
+ */
function validateProp(
name: string,
value: unknown,
expectedType: string
}
+/**
+ * dev only
+ */
function assertType(value: unknown, type: PropConstructor): AssertionResult {
let valid
const expectedType = getType(type)
}
}
+/**
+ * dev only
+ */
function getInvalidTypeMessage(
name: string,
value: unknown,
return message
}
+/**
+ * dev only
+ */
function styleValue(value: unknown, type: string): string {
if (type === 'String') {
return `"${value}"`
}
}
+/**
+ * dev only
+ */
function isExplicable(type: string): boolean {
const explicitTypes = ['string', 'number', 'boolean']
return explicitTypes.some(elem => type.toLowerCase() === elem)
}
+/**
+ * dev only
+ */
function isBoolean(...args: string[]): boolean {
return args.some(elem => elem.toLowerCase() === 'boolean')
}
// only cache other properties when instance has declared (thus stable)
// props
type.props &&
- hasOwn(normalizePropsOptions(type.props)[0]!, key)
+ hasOwn(normalizePropsOptions(type)[0]!, key)
) {
accessCache![key] = AccessTypes.PROPS
return props![key]
accessCache![key] !== undefined ||
(data !== EMPTY_OBJ && hasOwn(data, key)) ||
(setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
- (type.props && hasOwn(normalizePropsOptions(type.props)[0]!, key)) ||
+ (type.props && hasOwn(normalizePropsOptions(type)[0]!, key)) ||
hasOwn(ctx, key) ||
hasOwn(publicPropertiesMap, key) ||
hasOwn(appContext.config.globalProperties, key)
export function exposePropsOnRenderContext(
instance: ComponentInternalInstance
) {
- const {
- ctx,
- type: { props: propsOptions }
- } = instance
+ const { ctx, type } = instance
+ const propsOptions = normalizePropsOptions(type)[0]
if (propsOptions) {
- Object.keys(normalizePropsOptions(propsOptions)[0]!).forEach(key => {
+ Object.keys(propsOptions).forEach(key => {
Object.defineProperty(ctx, key, {
enumerable: true,
configurable: true,