componentProps.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { EMPTY_ARR, NO, YES, hasOwn, isFunction } from '@vue/shared'
  2. import type { VaporComponent, VaporComponentInstance } from './component'
  3. import {
  4. type NormalizedPropsOptions,
  5. baseNormalizePropsOptions,
  6. isEmitListener,
  7. resolvePropValue,
  8. } from '@vue/runtime-dom'
  9. import { normalizeEmitsOptions } from './componentEmits'
  10. export type RawProps = Record<string, () => unknown> & {
  11. $?: DynamicPropsSource[]
  12. }
  13. type DynamicPropsSource =
  14. | (() => Record<string, unknown>)
  15. | Record<string, () => unknown>
  16. // TODO optimization: maybe convert functions into computeds
  17. export function resolveSource(
  18. source: Record<string, any> | (() => Record<string, any>),
  19. ): Record<string, any> {
  20. return isFunction(source) ? source() : source
  21. }
  22. const passThrough = (val: any) => val
  23. export function getPropsProxyHandlers(
  24. comp: VaporComponent,
  25. instance: VaporComponentInstance,
  26. ): [ProxyHandler<RawProps> | null, ProxyHandler<RawProps>] {
  27. if (comp.__propsHandlers) {
  28. return comp.__propsHandlers
  29. }
  30. const propsOptions = normalizePropsOptions(comp)[0]
  31. const emitsOptions = normalizeEmitsOptions(comp)
  32. const isProp = propsOptions ? (key: string) => hasOwn(propsOptions, key) : NO
  33. const isAttr = propsOptions
  34. ? (key: string) =>
  35. key !== '$' && !isProp(key) && !isEmitListener(emitsOptions, key)
  36. : YES
  37. const castProp = propsOptions
  38. ? (value: any, key: string, isAbsent = false) =>
  39. resolvePropValue(
  40. propsOptions,
  41. key as string,
  42. value,
  43. instance,
  44. resolveDefault,
  45. isAbsent,
  46. )
  47. : passThrough
  48. const getProp = (target: RawProps, key: string, asProp: boolean) => {
  49. if (asProp) {
  50. if (!isProp(key) || key === '$') return
  51. } else if (isProp(key) || isEmitListener(emitsOptions, key)) {
  52. return
  53. }
  54. const dynamicSources = target.$
  55. if (dynamicSources) {
  56. let i = dynamicSources.length
  57. let source, isDynamic
  58. while (i--) {
  59. source = dynamicSources[i]
  60. isDynamic = isFunction(source)
  61. source = isDynamic ? (source as Function)() : source
  62. if (hasOwn(source, key)) {
  63. return castProp(isDynamic ? source[key] : source[key](), key)
  64. }
  65. }
  66. }
  67. if (key in target) {
  68. return castProp(target[key as string](), key)
  69. }
  70. return castProp(undefined, key, true)
  71. }
  72. const propsHandlers = propsOptions
  73. ? ({
  74. get: (target, key: string) => getProp(target, key, true),
  75. has: (_, key: string) => isProp(key),
  76. getOwnPropertyDescriptor(target, key: string) {
  77. if (isProp(key)) {
  78. return {
  79. configurable: true,
  80. enumerable: true,
  81. get: () => getProp(target, key, true),
  82. }
  83. }
  84. },
  85. ownKeys: () => Object.keys(propsOptions),
  86. set: NO,
  87. deleteProperty: NO,
  88. } satisfies ProxyHandler<RawProps>)
  89. : null
  90. const hasAttr = (target: RawProps, key: string) => {
  91. if (isAttr(key)) {
  92. const dynamicSources = target.$
  93. if (dynamicSources) {
  94. let i = dynamicSources.length
  95. while (i--) {
  96. if (hasOwn(resolveSource(dynamicSources[i]), key)) {
  97. return true
  98. }
  99. }
  100. }
  101. return hasOwn(target, key)
  102. } else {
  103. return false
  104. }
  105. }
  106. const attrsHandlers = {
  107. get: (target, key: string) => {
  108. return getProp(target, key, false)
  109. },
  110. has: hasAttr,
  111. getOwnPropertyDescriptor(target, key: string) {
  112. if (hasAttr(target, key)) {
  113. return {
  114. configurable: true,
  115. enumerable: true,
  116. get: () => getProp(target, key, false),
  117. }
  118. }
  119. },
  120. ownKeys(target) {
  121. const keys: string[] = []
  122. for (const key in target) {
  123. if (isAttr(key)) keys.push(key)
  124. }
  125. const dynamicSources = target.$
  126. if (dynamicSources) {
  127. let i = dynamicSources.length
  128. let source
  129. while (i--) {
  130. source = resolveSource(dynamicSources[i])
  131. for (const key in source) {
  132. if (isAttr(key)) keys.push(key)
  133. }
  134. }
  135. }
  136. return Array.from(new Set(keys))
  137. },
  138. set: NO,
  139. deleteProperty: NO,
  140. } satisfies ProxyHandler<RawProps>
  141. return (comp.__propsHandlers = [propsHandlers, attrsHandlers])
  142. }
  143. export function normalizePropsOptions(
  144. comp: VaporComponent,
  145. ): NormalizedPropsOptions {
  146. const cached = comp.__propsOptions
  147. if (cached) return cached
  148. const raw = comp.props
  149. if (!raw) return EMPTY_ARR as []
  150. const normalized: NormalizedPropsOptions[0] = {}
  151. const needCastKeys: NormalizedPropsOptions[1] = []
  152. baseNormalizePropsOptions(raw, normalized, needCastKeys)
  153. return (comp.__propsOptions = [normalized, needCastKeys])
  154. }
  155. function resolveDefault(
  156. factory: (props: Record<string, any>) => unknown,
  157. instance: VaporComponentInstance,
  158. ) {
  159. return factory.call(null, instance.props)
  160. }