Skip to content

Commit

Permalink
fix(runtime-core): properly merge props and emits options from mixins (
Browse files Browse the repository at this point in the history
  • Loading branch information
himself65 authored Apr 10, 2023
1 parent 2ce23f0 commit c94ef02
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 7 deletions.
54 changes: 53 additions & 1 deletion packages/runtime-core/__tests__/componentEmits.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
h,
nodeOps,
toHandlers,
nextTick
nextTick,
ComponentPublicInstance
} from '@vue/runtime-test'
import { isEmitListener } from '../src/componentEmits'

Expand Down Expand Up @@ -454,4 +455,55 @@ describe('component: emit', () => {
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)
})
})
9 changes: 7 additions & 2 deletions packages/runtime-core/src/apiSetupHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,15 @@ function getContext(): SetupContext {
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
Expand Down
36 changes: 32 additions & 4 deletions packages/runtime-core/src/componentOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ import {
import {
ComponentObjectPropsOptions,
ExtractPropTypes,
ExtractDefaultPropTypes
ExtractDefaultPropTypes,
ComponentPropsOptions
} from './componentProps'
import { EmitsOptions, EmitsToProps } from './componentEmits'
import { Directive } from './directives'
Expand All @@ -75,6 +76,7 @@ import {
import { OptionMergeFunction } from './apiCreateApp'
import { LifecycleHooks } from './enums'
import { SlotsType } from './componentSlots'
import { normalizePropsOrEmits } from './apiSetupHelpers'

/**
* Interface for declaring custom options.
Expand Down Expand Up @@ -1069,8 +1071,8 @@ export function mergeOptions(

export const internalOptionMergeStrats: Record<string, Function> = {
data: mergeDataFn,
props: mergeObjectOptions, // TODO
emits: mergeObjectOptions, // TODO
props: mergeEmitsOrPropsOptions,
emits: mergeEmitsOrPropsOptions,
// objects
methods: mergeObjectOptions,
computed: mergeObjectOptions,
Expand Down Expand Up @@ -1147,7 +1149,33 @@ function mergeAsArray<T = Function>(to: T[] | T | undefined, from: T | T[]) {
}

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(
Expand Down

0 comments on commit c94ef02

Please sign in to comment.