2021-09-27 18:24:21 +00:00
|
|
|
import { ComponentPropsOptions } from '@vue/runtime-core'
|
|
|
|
import { isArray, isPromise, isFunction } from '@vue/shared'
|
2020-11-26 14:25:35 +00:00
|
|
|
import {
|
|
|
|
getCurrentInstance,
|
2021-07-06 18:31:53 +00:00
|
|
|
setCurrentInstance,
|
2020-11-26 14:25:35 +00:00
|
|
|
SetupContext,
|
2021-07-07 13:07:19 +00:00
|
|
|
createSetupContext,
|
|
|
|
unsetCurrentInstance
|
2020-11-26 14:25:35 +00:00
|
|
|
} from './component'
|
2020-11-24 20:12:59 +00:00
|
|
|
import { EmitFn, EmitsOptions } from './componentEmits'
|
2021-09-27 18:24:21 +00:00
|
|
|
import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps'
|
2020-11-24 20:12:59 +00:00
|
|
|
import { warn } from './warning'
|
|
|
|
|
2021-06-27 01:11:57 +00:00
|
|
|
// dev only
|
|
|
|
const warnRuntimeUsage = (method: string) =>
|
|
|
|
warn(
|
|
|
|
`${method}() is a compiler-hint helper that is only usable inside ` +
|
|
|
|
`<script setup> of a single file component. Its arguments should be ` +
|
|
|
|
`compiled away and passing it at runtime has no effect.`
|
|
|
|
)
|
2021-06-25 23:31:47 +00:00
|
|
|
|
2020-11-24 20:12:59 +00:00
|
|
|
/**
|
2021-06-27 01:11:57 +00:00
|
|
|
* Vue `<script setup>` compiler macro for declaring component props. The
|
|
|
|
* expected argument is the same as the component `props` option.
|
|
|
|
*
|
|
|
|
* Example runtime declaration:
|
|
|
|
* ```js
|
|
|
|
* // using Array syntax
|
|
|
|
* const props = defineProps(['foo', 'bar'])
|
|
|
|
* // using Object syntax
|
|
|
|
* const props = defineProps({
|
|
|
|
* foo: String,
|
|
|
|
* bar: {
|
|
|
|
* type: Number,
|
|
|
|
* required: true
|
|
|
|
* }
|
|
|
|
* })
|
|
|
|
* ```
|
|
|
|
*
|
2021-09-05 22:02:50 +00:00
|
|
|
* Equivalent type-based declaration:
|
2021-06-27 01:11:57 +00:00
|
|
|
* ```ts
|
|
|
|
* // will be compiled into equivalent runtime declarations
|
|
|
|
* const props = defineProps<{
|
|
|
|
* foo?: string
|
|
|
|
* bar: number
|
|
|
|
* }>()
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* This is only usable inside `<script setup>`, is compiled away in the
|
|
|
|
* output and should **not** be actually called at runtime.
|
2020-11-24 20:12:59 +00:00
|
|
|
*/
|
2021-06-27 01:11:57 +00:00
|
|
|
// overload 1: runtime props w/ array
|
|
|
|
export function defineProps<PropNames extends string = string>(
|
|
|
|
props: PropNames[]
|
|
|
|
): Readonly<{ [key in PropNames]?: any }>
|
|
|
|
// overload 2: runtime props w/ object
|
2020-11-24 20:12:59 +00:00
|
|
|
export function defineProps<
|
2021-06-27 01:11:57 +00:00
|
|
|
PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions
|
|
|
|
>(props: PP): Readonly<ExtractPropTypes<PP>>
|
|
|
|
// overload 3: typed-based declaration
|
|
|
|
export function defineProps<TypeProps>(): Readonly<TypeProps>
|
2020-11-24 20:12:59 +00:00
|
|
|
// implementation
|
2020-11-26 15:01:36 +00:00
|
|
|
export function defineProps() {
|
|
|
|
if (__DEV__) {
|
2021-06-27 01:11:57 +00:00
|
|
|
warnRuntimeUsage(`defineProps`)
|
2020-11-24 20:12:59 +00:00
|
|
|
}
|
2020-11-24 21:55:43 +00:00
|
|
|
return null as any
|
2020-11-24 20:12:59 +00:00
|
|
|
}
|
|
|
|
|
2021-06-27 01:11:57 +00:00
|
|
|
/**
|
|
|
|
* Vue `<script setup>` compiler macro for declaring a component's emitted
|
|
|
|
* events. The expected argument is the same as the component `emits` option.
|
|
|
|
*
|
|
|
|
* Example runtime declaration:
|
|
|
|
* ```js
|
|
|
|
* const emit = defineEmits(['change', 'update'])
|
|
|
|
* ```
|
|
|
|
*
|
2021-09-05 22:02:50 +00:00
|
|
|
* Example type-based declaration:
|
2021-06-27 01:11:57 +00:00
|
|
|
* ```ts
|
|
|
|
* const emit = defineEmits<{
|
|
|
|
* (event: 'change'): void
|
|
|
|
* (event: 'update', id: number): void
|
|
|
|
* }>()
|
|
|
|
*
|
|
|
|
* emit('change')
|
|
|
|
* emit('update', 1)
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* This is only usable inside `<script setup>`, is compiled away in the
|
|
|
|
* output and should **not** be actually called at runtime.
|
|
|
|
*/
|
|
|
|
// overload 1: runtime emits w/ array
|
|
|
|
export function defineEmits<EE extends string = string>(
|
|
|
|
emitOptions: EE[]
|
|
|
|
): EmitFn<EE[]>
|
|
|
|
export function defineEmits<E extends EmitsOptions = EmitsOptions>(
|
|
|
|
emitOptions: E
|
|
|
|
): EmitFn<E>
|
|
|
|
export function defineEmits<TypeEmit>(): TypeEmit
|
2020-11-24 20:12:59 +00:00
|
|
|
// implementation
|
2021-06-22 19:02:56 +00:00
|
|
|
export function defineEmits() {
|
2020-11-26 15:01:36 +00:00
|
|
|
if (__DEV__) {
|
2021-06-27 01:11:57 +00:00
|
|
|
warnRuntimeUsage(`defineEmits`)
|
2020-11-24 20:12:59 +00:00
|
|
|
}
|
2020-11-24 21:55:43 +00:00
|
|
|
return null as any
|
2020-11-24 20:12:59 +00:00
|
|
|
}
|
|
|
|
|
2021-06-27 01:11:57 +00:00
|
|
|
/**
|
|
|
|
* Vue `<script setup>` compiler macro for declaring a component's exposed
|
|
|
|
* instance properties when it is accessed by a parent component via template
|
|
|
|
* refs.
|
|
|
|
*
|
2022-01-21 06:18:34 +00:00
|
|
|
* `<script setup>` components are closed by default - i.e. variables inside
|
2021-06-27 01:11:57 +00:00
|
|
|
* the `<script setup>` scope is not exposed to parent unless explicitly exposed
|
|
|
|
* via `defineExpose`.
|
|
|
|
*
|
|
|
|
* This is only usable inside `<script setup>`, is compiled away in the
|
|
|
|
* output and should **not** be actually called at runtime.
|
|
|
|
*/
|
2022-01-21 06:18:34 +00:00
|
|
|
export function defineExpose<
|
|
|
|
Exposed extends Record<string, any> = Record<string, any>
|
|
|
|
>(exposed?: Exposed) {
|
2021-06-25 20:18:21 +00:00
|
|
|
if (__DEV__) {
|
2021-06-27 01:11:57 +00:00
|
|
|
warnRuntimeUsage(`defineExpose`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type NotUndefined<T> = T extends undefined ? never : T
|
|
|
|
|
|
|
|
type InferDefaults<T> = {
|
2021-11-15 03:09:00 +00:00
|
|
|
[K in keyof T]?: InferDefault<T, NotUndefined<T[K]>>
|
2021-06-27 01:11:57 +00:00
|
|
|
}
|
|
|
|
|
2021-11-15 03:09:00 +00:00
|
|
|
type InferDefault<P, T> = T extends
|
2021-11-25 09:33:29 +00:00
|
|
|
| null
|
2021-11-15 03:09:00 +00:00
|
|
|
| number
|
|
|
|
| string
|
|
|
|
| boolean
|
|
|
|
| symbol
|
|
|
|
| Function
|
2022-05-23 00:28:39 +00:00
|
|
|
? T | ((props: P) => T)
|
2021-11-15 03:09:00 +00:00
|
|
|
: (props: P) => T
|
|
|
|
|
|
|
|
type PropsWithDefaults<Base, Defaults> = Base & {
|
|
|
|
[K in keyof Defaults]: K extends keyof Base ? NotUndefined<Base[K]> : never
|
|
|
|
}
|
2021-06-27 01:11:57 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Vue `<script setup>` compiler macro for providing props default values when
|
2021-09-05 22:02:50 +00:00
|
|
|
* using type-based `defineProps` declaration.
|
2021-06-27 01:11:57 +00:00
|
|
|
*
|
|
|
|
* Example usage:
|
|
|
|
* ```ts
|
|
|
|
* withDefaults(defineProps<{
|
|
|
|
* size?: number
|
|
|
|
* labels?: string[]
|
|
|
|
* }>(), {
|
|
|
|
* size: 3,
|
|
|
|
* labels: () => ['default label']
|
|
|
|
* })
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* This is only usable inside `<script setup>`, is compiled away in the output
|
|
|
|
* and should **not** be actually called at runtime.
|
|
|
|
*/
|
|
|
|
export function withDefaults<Props, Defaults extends InferDefaults<Props>>(
|
|
|
|
props: Props,
|
|
|
|
defaults: Defaults
|
|
|
|
): PropsWithDefaults<Props, Defaults> {
|
|
|
|
if (__DEV__) {
|
|
|
|
warnRuntimeUsage(`withDefaults`)
|
2021-06-25 20:18:21 +00:00
|
|
|
}
|
2021-06-27 01:11:57 +00:00
|
|
|
return null as any
|
2021-06-25 20:18:21 +00:00
|
|
|
}
|
|
|
|
|
2021-06-27 01:11:57 +00:00
|
|
|
export function useSlots(): SetupContext['slots'] {
|
|
|
|
return getContext().slots
|
|
|
|
}
|
|
|
|
|
|
|
|
export function useAttrs(): SetupContext['attrs'] {
|
|
|
|
return getContext().attrs
|
|
|
|
}
|
|
|
|
|
2021-06-23 14:31:32 +00:00
|
|
|
function getContext(): SetupContext {
|
2020-11-26 14:25:35 +00:00
|
|
|
const i = getCurrentInstance()!
|
|
|
|
if (__DEV__ && !i) {
|
|
|
|
warn(`useContext() called without active instance.`)
|
|
|
|
}
|
|
|
|
return i.setupContext || (i.setupContext = createSetupContext(i))
|
2020-11-24 20:12:59 +00:00
|
|
|
}
|
2021-06-23 01:00:26 +00:00
|
|
|
|
2021-06-27 01:11:57 +00:00
|
|
|
/**
|
|
|
|
* Runtime helper for merging default declarations. Imported by compiled code
|
|
|
|
* only.
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
export function mergeDefaults(
|
2021-09-27 18:24:21 +00:00
|
|
|
raw: ComponentPropsOptions,
|
2021-06-27 01:11:57 +00:00
|
|
|
defaults: Record<string, any>
|
2021-09-27 18:24:21 +00:00
|
|
|
): ComponentObjectPropsOptions {
|
|
|
|
const props = isArray(raw)
|
|
|
|
? raw.reduce(
|
|
|
|
(normalized, p) => ((normalized[p] = {}), normalized),
|
|
|
|
{} as ComponentObjectPropsOptions
|
|
|
|
)
|
|
|
|
: raw
|
2021-06-27 01:11:57 +00:00
|
|
|
for (const key in defaults) {
|
2021-09-27 18:24:21 +00:00
|
|
|
const opt = props[key]
|
|
|
|
if (opt) {
|
|
|
|
if (isArray(opt) || isFunction(opt)) {
|
|
|
|
props[key] = { type: opt, default: defaults[key] }
|
|
|
|
} else {
|
|
|
|
opt.default = defaults[key]
|
|
|
|
}
|
|
|
|
} else if (opt === null) {
|
2021-06-27 01:11:57 +00:00
|
|
|
props[key] = { default: defaults[key] }
|
|
|
|
} else if (__DEV__) {
|
|
|
|
warn(`props default key "${key}" has no corresponding declaration.`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return props
|
2021-06-23 01:00:26 +00:00
|
|
|
}
|
2021-06-29 13:24:12 +00:00
|
|
|
|
2021-09-27 18:24:21 +00:00
|
|
|
/**
|
|
|
|
* Used to create a proxy for the rest element when destructuring props with
|
|
|
|
* defineProps().
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
export function createPropsRestProxy(
|
|
|
|
props: any,
|
|
|
|
excludedKeys: string[]
|
|
|
|
): Record<string, any> {
|
|
|
|
const ret: Record<string, any> = {}
|
|
|
|
for (const key in props) {
|
|
|
|
if (!excludedKeys.includes(key)) {
|
|
|
|
Object.defineProperty(ret, key, {
|
|
|
|
enumerable: true,
|
|
|
|
get: () => props[key]
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2021-06-29 13:24:12 +00:00
|
|
|
/**
|
2021-07-06 18:31:53 +00:00
|
|
|
* `<script setup>` helper for persisting the current instance context over
|
|
|
|
* async/await flows.
|
|
|
|
*
|
|
|
|
* `@vue/compiler-sfc` converts the following:
|
|
|
|
*
|
|
|
|
* ```ts
|
|
|
|
* const x = await foo()
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* into:
|
|
|
|
*
|
|
|
|
* ```ts
|
|
|
|
* let __temp, __restore
|
|
|
|
* const x = (([__temp, __restore] = withAsyncContext(() => foo())),__temp=await __temp,__restore(),__temp)
|
|
|
|
* ```
|
|
|
|
* @internal
|
2021-06-29 13:24:12 +00:00
|
|
|
*/
|
2021-07-06 18:31:53 +00:00
|
|
|
export function withAsyncContext(getAwaitable: () => any) {
|
2021-07-07 13:07:19 +00:00
|
|
|
const ctx = getCurrentInstance()!
|
|
|
|
if (__DEV__ && !ctx) {
|
|
|
|
warn(
|
|
|
|
`withAsyncContext called without active current instance. ` +
|
|
|
|
`This is likely a bug.`
|
|
|
|
)
|
|
|
|
}
|
2021-07-06 18:31:53 +00:00
|
|
|
let awaitable = getAwaitable()
|
2021-07-07 13:07:19 +00:00
|
|
|
unsetCurrentInstance()
|
2021-07-06 18:31:53 +00:00
|
|
|
if (isPromise(awaitable)) {
|
|
|
|
awaitable = awaitable.catch(e => {
|
|
|
|
setCurrentInstance(ctx)
|
|
|
|
throw e
|
|
|
|
})
|
2021-06-29 18:21:31 +00:00
|
|
|
}
|
2021-07-06 18:31:53 +00:00
|
|
|
return [awaitable, () => setCurrentInstance(ctx)]
|
2021-06-29 13:24:12 +00:00
|
|
|
}
|