apiSetupHelpers.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import { ComponentPropsOptions } from '@vue/runtime-core'
  2. import { isArray, isPromise, isFunction } from '@vue/shared'
  3. import {
  4. getCurrentInstance,
  5. setCurrentInstance,
  6. SetupContext,
  7. createSetupContext,
  8. unsetCurrentInstance
  9. } from './component'
  10. import { EmitFn, EmitsOptions } from './componentEmits'
  11. import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps'
  12. import { warn } from './warning'
  13. // dev only
  14. const warnRuntimeUsage = (method: string) =>
  15. warn(
  16. `${method}() is a compiler-hint helper that is only usable inside ` +
  17. `<script setup> of a single file component. Its arguments should be ` +
  18. `compiled away and passing it at runtime has no effect.`
  19. )
  20. /**
  21. * Vue `<script setup>` compiler macro for declaring component props. The
  22. * expected argument is the same as the component `props` option.
  23. *
  24. * Example runtime declaration:
  25. * ```js
  26. * // using Array syntax
  27. * const props = defineProps(['foo', 'bar'])
  28. * // using Object syntax
  29. * const props = defineProps({
  30. * foo: String,
  31. * bar: {
  32. * type: Number,
  33. * required: true
  34. * }
  35. * })
  36. * ```
  37. *
  38. * Equivalent type-based declaration:
  39. * ```ts
  40. * // will be compiled into equivalent runtime declarations
  41. * const props = defineProps<{
  42. * foo?: string
  43. * bar: number
  44. * }>()
  45. * ```
  46. *
  47. * This is only usable inside `<script setup>`, is compiled away in the
  48. * output and should **not** be actually called at runtime.
  49. */
  50. // overload 1: runtime props w/ array
  51. export function defineProps<PropNames extends string = string>(
  52. props: PropNames[]
  53. ): Readonly<{ [key in PropNames]?: any }>
  54. // overload 2: runtime props w/ object
  55. export function defineProps<
  56. PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions
  57. >(props: PP): Readonly<ExtractPropTypes<PP>>
  58. // overload 3: typed-based declaration
  59. export function defineProps<TypeProps>(): Readonly<TypeProps>
  60. // implementation
  61. export function defineProps() {
  62. if (__DEV__) {
  63. warnRuntimeUsage(`defineProps`)
  64. }
  65. return null as any
  66. }
  67. /**
  68. * Vue `<script setup>` compiler macro for declaring a component's emitted
  69. * events. The expected argument is the same as the component `emits` option.
  70. *
  71. * Example runtime declaration:
  72. * ```js
  73. * const emit = defineEmits(['change', 'update'])
  74. * ```
  75. *
  76. * Example type-based declaration:
  77. * ```ts
  78. * const emit = defineEmits<{
  79. * (event: 'change'): void
  80. * (event: 'update', id: number): void
  81. * }>()
  82. *
  83. * emit('change')
  84. * emit('update', 1)
  85. * ```
  86. *
  87. * This is only usable inside `<script setup>`, is compiled away in the
  88. * output and should **not** be actually called at runtime.
  89. */
  90. // overload 1: runtime emits w/ array
  91. export function defineEmits<EE extends string = string>(
  92. emitOptions: EE[]
  93. ): EmitFn<EE[]>
  94. export function defineEmits<E extends EmitsOptions = EmitsOptions>(
  95. emitOptions: E
  96. ): EmitFn<E>
  97. export function defineEmits<TypeEmit>(): TypeEmit
  98. // implementation
  99. export function defineEmits() {
  100. if (__DEV__) {
  101. warnRuntimeUsage(`defineEmits`)
  102. }
  103. return null as any
  104. }
  105. /**
  106. * Vue `<script setup>` compiler macro for declaring a component's exposed
  107. * instance properties when it is accessed by a parent component via template
  108. * refs.
  109. *
  110. * `<script setup>` components are closed by default - i.e. varaibles inside
  111. * the `<script setup>` scope is not exposed to parent unless explicitly exposed
  112. * via `defineExpose`.
  113. *
  114. * This is only usable inside `<script setup>`, is compiled away in the
  115. * output and should **not** be actually called at runtime.
  116. */
  117. export function defineExpose(exposed?: Record<string, any>) {
  118. if (__DEV__) {
  119. warnRuntimeUsage(`defineExpose`)
  120. }
  121. }
  122. type NotUndefined<T> = T extends undefined ? never : T
  123. type InferDefaults<T> = {
  124. [K in keyof T]?: InferDefault<T, NotUndefined<T[K]>>
  125. }
  126. type InferDefault<P, T> = T extends
  127. | null
  128. | number
  129. | string
  130. | boolean
  131. | symbol
  132. | Function
  133. ? T
  134. : (props: P) => T
  135. type PropsWithDefaults<Base, Defaults> = Base & {
  136. [K in keyof Defaults]: K extends keyof Base ? NotUndefined<Base[K]> : never
  137. }
  138. /**
  139. * Vue `<script setup>` compiler macro for providing props default values when
  140. * using type-based `defineProps` declaration.
  141. *
  142. * Example usage:
  143. * ```ts
  144. * withDefaults(defineProps<{
  145. * size?: number
  146. * labels?: string[]
  147. * }>(), {
  148. * size: 3,
  149. * labels: () => ['default label']
  150. * })
  151. * ```
  152. *
  153. * This is only usable inside `<script setup>`, is compiled away in the output
  154. * and should **not** be actually called at runtime.
  155. */
  156. export function withDefaults<Props, Defaults extends InferDefaults<Props>>(
  157. props: Props,
  158. defaults: Defaults
  159. ): PropsWithDefaults<Props, Defaults> {
  160. if (__DEV__) {
  161. warnRuntimeUsage(`withDefaults`)
  162. }
  163. return null as any
  164. }
  165. export function useSlots(): SetupContext['slots'] {
  166. return getContext().slots
  167. }
  168. export function useAttrs(): SetupContext['attrs'] {
  169. return getContext().attrs
  170. }
  171. function getContext(): SetupContext {
  172. const i = getCurrentInstance()!
  173. if (__DEV__ && !i) {
  174. warn(`useContext() called without active instance.`)
  175. }
  176. return i.setupContext || (i.setupContext = createSetupContext(i))
  177. }
  178. /**
  179. * Runtime helper for merging default declarations. Imported by compiled code
  180. * only.
  181. * @internal
  182. */
  183. export function mergeDefaults(
  184. raw: ComponentPropsOptions,
  185. defaults: Record<string, any>
  186. ): ComponentObjectPropsOptions {
  187. const props = isArray(raw)
  188. ? raw.reduce(
  189. (normalized, p) => ((normalized[p] = {}), normalized),
  190. {} as ComponentObjectPropsOptions
  191. )
  192. : raw
  193. for (const key in defaults) {
  194. const opt = props[key]
  195. if (opt) {
  196. if (isArray(opt) || isFunction(opt)) {
  197. props[key] = { type: opt, default: defaults[key] }
  198. } else {
  199. opt.default = defaults[key]
  200. }
  201. } else if (opt === null) {
  202. props[key] = { default: defaults[key] }
  203. } else if (__DEV__) {
  204. warn(`props default key "${key}" has no corresponding declaration.`)
  205. }
  206. }
  207. return props
  208. }
  209. /**
  210. * Used to create a proxy for the rest element when destructuring props with
  211. * defineProps().
  212. * @internal
  213. */
  214. export function createPropsRestProxy(
  215. props: any,
  216. excludedKeys: string[]
  217. ): Record<string, any> {
  218. const ret: Record<string, any> = {}
  219. for (const key in props) {
  220. if (!excludedKeys.includes(key)) {
  221. Object.defineProperty(ret, key, {
  222. enumerable: true,
  223. get: () => props[key]
  224. })
  225. }
  226. }
  227. return ret
  228. }
  229. /**
  230. * `<script setup>` helper for persisting the current instance context over
  231. * async/await flows.
  232. *
  233. * `@vue/compiler-sfc` converts the following:
  234. *
  235. * ```ts
  236. * const x = await foo()
  237. * ```
  238. *
  239. * into:
  240. *
  241. * ```ts
  242. * let __temp, __restore
  243. * const x = (([__temp, __restore] = withAsyncContext(() => foo())),__temp=await __temp,__restore(),__temp)
  244. * ```
  245. * @internal
  246. */
  247. export function withAsyncContext(getAwaitable: () => any) {
  248. const ctx = getCurrentInstance()!
  249. if (__DEV__ && !ctx) {
  250. warn(
  251. `withAsyncContext called without active current instance. ` +
  252. `This is likely a bug.`
  253. )
  254. }
  255. let awaitable = getAwaitable()
  256. unsetCurrentInstance()
  257. if (isPromise(awaitable)) {
  258. awaitable = awaitable.catch(e => {
  259. setCurrentInstance(ctx)
  260. throw e
  261. })
  262. }
  263. return [awaitable, () => setCurrentInstance(ctx)]
  264. }