h,
nodeOps,
toHandlers,
- nextTick
+ nextTick,
+ ComponentPublicInstance
} from '@vue/runtime-test'
import { isEmitListener } from '../src/componentEmits'
await nextTick()
expect(fn).not.toHaveBeenCalled()
})
+
+ test('merge string array emits', async () => {
+ const ComponentA = defineComponent({
+ emits: ['one', 'two']
+ })
+ const ComponentB = defineComponent({
+ emits: ['three']
+ })
+ const renderFn = vi.fn(function (this: ComponentPublicInstance) {
+ expect(this.$options.emits).toEqual(['one', 'two', 'three'])
+ return h('div')
+ })
+ const ComponentC = defineComponent({
+ render: renderFn,
+ mixins: [ComponentA, ComponentB]
+ })
+ const el = nodeOps.createElement('div')
+ expect(renderFn).toHaveBeenCalledTimes(0)
+ render(h(ComponentC), el)
+ expect(renderFn).toHaveBeenCalledTimes(1)
+ })
+
+ test('merge object emits', async () => {
+ const twoFn = vi.fn((v: unknown) => !v)
+ const ComponentA = defineComponent({
+ emits: {
+ one: null,
+ two: twoFn
+ }
+ })
+ const ComponentB = defineComponent({
+ emits: ['three']
+ })
+ const renderFn = vi.fn(function (this: ComponentPublicInstance) {
+ expect(this.$options.emits).toEqual({
+ one: null,
+ two: twoFn,
+ three: null
+ })
+ expect(this.$options.emits.two).toBe(twoFn)
+ return h('div')
+ })
+ const ComponentC = defineComponent({
+ render: renderFn,
+ mixins: [ComponentA, ComponentB]
+ })
+ const el = nodeOps.createElement('div')
+ expect(renderFn).toHaveBeenCalledTimes(0)
+ render(h(ComponentC), el)
+ expect(renderFn).toHaveBeenCalledTimes(1)
+ })
})
return i.setupContext || (i.setupContext = createSetupContext(i))
}
-function normalizePropsOrEmits(props: ComponentPropsOptions | EmitsOptions) {
+/**
+ * @internal
+ */
+export function normalizePropsOrEmits(
+ props: ComponentPropsOptions | EmitsOptions
+) {
return isArray(props)
? props.reduce(
- (normalized, p) => ((normalized[p] = {}), normalized),
+ (normalized, p) => ((normalized[p] = null), normalized),
{} as ComponentObjectPropsOptions | ObjectEmitsOptions
)
: props
import {
ComponentObjectPropsOptions,
ExtractPropTypes,
- ExtractDefaultPropTypes
+ ExtractDefaultPropTypes,
+ ComponentPropsOptions
} from './componentProps'
import { EmitsOptions, EmitsToProps } from './componentEmits'
import { Directive } from './directives'
import { OptionMergeFunction } from './apiCreateApp'
import { LifecycleHooks } from './enums'
import { SlotsType } from './componentSlots'
+import { normalizePropsOrEmits } from './apiSetupHelpers'
/**
* Interface for declaring custom options.
export const internalOptionMergeStrats: Record<string, Function> = {
data: mergeDataFn,
- props: mergeObjectOptions, // TODO
- emits: mergeObjectOptions, // TODO
+ props: mergeEmitsOrPropsOptions,
+ emits: mergeEmitsOrPropsOptions,
// objects
methods: mergeObjectOptions,
computed: mergeObjectOptions,
}
function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
- return to ? extend(extend(Object.create(null), to), from) : from
+ return to ? extend(Object.create(null), to, from) : from
+}
+
+function mergeEmitsOrPropsOptions(
+ to: EmitsOptions | undefined,
+ from: EmitsOptions | undefined
+): EmitsOptions | undefined
+function mergeEmitsOrPropsOptions(
+ to: ComponentPropsOptions | undefined,
+ from: ComponentPropsOptions | undefined
+): ComponentPropsOptions | undefined
+function mergeEmitsOrPropsOptions(
+ to: ComponentPropsOptions | EmitsOptions | undefined,
+ from: ComponentPropsOptions | EmitsOptions | undefined
+) {
+ if (to) {
+ if (isArray(to) && isArray(from)) {
+ return [...new Set([...to, ...from])]
+ }
+ return extend(
+ Object.create(null),
+ normalizePropsOrEmits(to),
+ normalizePropsOrEmits(from ?? {})
+ )
+ } else {
+ return from
+ }
}
function mergeWatchOptions(