From 9ed2809822a466ac0ac89d8513861d75e8bd7ae1 Mon Sep 17 00:00:00 2001 From: himself65 Date: Sat, 8 Apr 2023 14:59:53 -0500 Subject: [PATCH 1/9] fix(runtime-core): merge emits strategy --- packages/runtime-core/src/componentOptions.ts | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 481c2adb67e..c54045b166a 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -53,7 +53,11 @@ import { ExtractPropTypes, ExtractDefaultPropTypes } from './componentProps' -import { EmitsOptions, EmitsToProps } from './componentEmits' +import { + EmitsOptions, + EmitsToProps, + ObjectEmitsOptions +} from './componentEmits' import { Directive } from './directives' import { CreateComponentPublicInstance, @@ -1070,7 +1074,7 @@ export function mergeOptions( export const internalOptionMergeStrats: Record = { data: mergeDataFn, props: mergeObjectOptions, // TODO - emits: mergeObjectOptions, // TODO + emits: mergeEmitsOptions, // objects methods: mergeObjectOptions, computed: mergeObjectOptions, @@ -1150,6 +1154,37 @@ function mergeObjectOptions(to: Object | undefined, from: Object | undefined) { return to ? extend(extend(Object.create(null), to), from) : from } +function mergeEmitsOptions( + to: EmitsOptions | undefined, + from: EmitsOptions | undefined +) { + const result: ObjectEmitsOptions = {} + if (to) { + if (isArray(to) && isArray(from)) { + const set = new Set(to) + from.forEach(key => set.add(key)) + return [...set] + } + if (isArray(from)) { + from.reduce((result, key) => { + result[key] = null + return result + }, result) + } + if (isArray(to)) { + to.reduce((result, key) => { + result[key] = null + return result + }, result) + } else { + return extend(result, to) + } + return result + } else { + return from + } +} + function mergeWatchOptions( to: ComponentWatchOptions | undefined, from: ComponentWatchOptions | undefined From a233367763d2362c76ec29788385bf8d0387ad43 Mon Sep 17 00:00:00 2001 From: himself65 Date: Sat, 8 Apr 2023 15:04:38 -0500 Subject: [PATCH 2/9] fix: type --- packages/runtime-core/src/componentOptions.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index c54045b166a..3dc3fb0391f 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -51,7 +51,8 @@ import { import { ComponentObjectPropsOptions, ExtractPropTypes, - ExtractDefaultPropTypes + ExtractDefaultPropTypes, + ComponentPropsOptions } from './componentProps' import { EmitsOptions, @@ -1073,8 +1074,8 @@ export function mergeOptions( export const internalOptionMergeStrats: Record = { data: mergeDataFn, - props: mergeObjectOptions, // TODO - emits: mergeEmitsOptions, + props: mergeEmitsOrPropsOptions, + emits: mergeEmitsOrPropsOptions, // objects methods: mergeObjectOptions, computed: mergeObjectOptions, @@ -1154,9 +1155,17 @@ function mergeObjectOptions(to: Object | undefined, from: Object | undefined) { return to ? extend(extend(Object.create(null), to), from) : from } -function mergeEmitsOptions( +function mergeEmitsOrPropsOptions( to: EmitsOptions | undefined, from: EmitsOptions | undefined +): EmitsOptions | undefined +function mergeEmitsOrPropsOptions( + to: ComponentPropsOptions | undefined, + from: ComponentPropsOptions | undefined +): ComponentPropsOptions | undefined +function mergeEmitsOrPropsOptions( + to: Record | string[] | undefined, + from: Record | string[] | undefined ) { const result: ObjectEmitsOptions = {} if (to) { From b7a1e2c43500faefc5c32e06e50d849e6fb007a8 Mon Sep 17 00:00:00 2001 From: himself65 Date: Sat, 8 Apr 2023 15:07:02 -0500 Subject: [PATCH 3/9] fix: code --- packages/runtime-core/src/componentOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 3dc3fb0391f..ad635e27577 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -1188,7 +1188,7 @@ function mergeEmitsOrPropsOptions( } else { return extend(result, to) } - return result + return extend(result, from) } else { return from } From 8ce53e74ce390fa20d397034eabb965b010caa49 Mon Sep 17 00:00:00 2001 From: himself65 Date: Sat, 8 Apr 2023 15:10:18 -0500 Subject: [PATCH 4/9] fix: code --- packages/runtime-core/src/componentOptions.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index ad635e27577..69f3d9e7f9b 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -1188,7 +1188,11 @@ function mergeEmitsOrPropsOptions( } else { return extend(result, to) } - return extend(result, from) + if (!isArray(from)) { + return extend(result, from) + } else { + return result + } } else { return from } From 9a3cd3e82838aae4b1fd8d2a8268e60a90294cab Mon Sep 17 00:00:00 2001 From: himself65 Date: Sat, 8 Apr 2023 15:32:12 -0500 Subject: [PATCH 5/9] test: add test case --- .../__tests__/componentEmits.spec.ts | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/__tests__/componentEmits.spec.ts b/packages/runtime-core/__tests__/componentEmits.spec.ts index 0e5f1e3983d..0db2242df4a 100644 --- a/packages/runtime-core/__tests__/componentEmits.spec.ts +++ b/packages/runtime-core/__tests__/componentEmits.spec.ts @@ -1,14 +1,15 @@ // Note: emits and listener fallthrough is tested in // ./rendererAttrsFallthrough.spec.ts. -import { vi } from 'vitest' +import { expect, vi } from 'vitest' import { render, defineComponent, h, nodeOps, toHandlers, - nextTick + nextTick, + ComponentPublicInstance } from '@vue/runtime-test' import { isEmitListener } from '../src/componentEmits' @@ -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) + }) }) From 6347fce7753b2be5ab8fd0c4f444bddc6c5b5db2 Mon Sep 17 00:00:00 2001 From: himself65 Date: Sat, 8 Apr 2023 15:51:06 -0500 Subject: [PATCH 6/9] fix: code --- packages/runtime-core/src/apiSetupHelpers.ts | 10 ++++-- packages/runtime-core/src/componentOptions.ts | 35 +++++-------------- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/packages/runtime-core/src/apiSetupHelpers.ts b/packages/runtime-core/src/apiSetupHelpers.ts index e0fe434210f..ddc5477fb8f 100644 --- a/packages/runtime-core/src/apiSetupHelpers.ts +++ b/packages/runtime-core/src/apiSetupHelpers.ts @@ -396,10 +396,16 @@ function getContext(): SetupContext { return i.setupContext || (i.setupContext = createSetupContext(i)) } -function normalizePropsOrEmits(props: ComponentPropsOptions | EmitsOptions) { +/** + * @internal + */ +export function normalizePropsOrEmits( + props: ComponentPropsOptions | EmitsOptions, + defaultValue: any = {} +) { return isArray(props) ? props.reduce( - (normalized, p) => ((normalized[p] = {}), normalized), + (normalized, p) => ((normalized[p] = defaultValue), normalized), {} as ComponentObjectPropsOptions | ObjectEmitsOptions ) : props diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 69f3d9e7f9b..aeddc3d37ea 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -54,11 +54,7 @@ import { ExtractDefaultPropTypes, ComponentPropsOptions } from './componentProps' -import { - EmitsOptions, - EmitsToProps, - ObjectEmitsOptions -} from './componentEmits' +import { EmitsOptions, EmitsToProps } from './componentEmits' import { Directive } from './directives' import { CreateComponentPublicInstance, @@ -80,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. @@ -1164,35 +1161,19 @@ function mergeEmitsOrPropsOptions( from: ComponentPropsOptions | undefined ): ComponentPropsOptions | undefined function mergeEmitsOrPropsOptions( - to: Record | string[] | undefined, - from: Record | string[] | undefined + to: ComponentPropsOptions | EmitsOptions | undefined, + from: ComponentPropsOptions | EmitsOptions | undefined ) { - const result: ObjectEmitsOptions = {} if (to) { if (isArray(to) && isArray(from)) { const set = new Set(to) from.forEach(key => set.add(key)) return [...set] } - if (isArray(from)) { - from.reduce((result, key) => { - result[key] = null - return result - }, result) - } - if (isArray(to)) { - to.reduce((result, key) => { - result[key] = null - return result - }, result) - } else { - return extend(result, to) - } - if (!isArray(from)) { - return extend(result, from) - } else { - return result - } + return extend( + normalizePropsOrEmits(to), + normalizePropsOrEmits(from ?? {}, null) + ) } else { return from } From e38e73cf34d6cd3996778b0d65761f5bfe96f2ee Mon Sep 17 00:00:00 2001 From: himself65 Date: Sat, 8 Apr 2023 15:57:05 -0500 Subject: [PATCH 7/9] fix: code --- packages/runtime-core/src/apiSetupHelpers.ts | 6 ++++-- packages/runtime-core/src/componentOptions.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/src/apiSetupHelpers.ts b/packages/runtime-core/src/apiSetupHelpers.ts index ddc5477fb8f..3a8d31fccfc 100644 --- a/packages/runtime-core/src/apiSetupHelpers.ts +++ b/packages/runtime-core/src/apiSetupHelpers.ts @@ -401,11 +401,13 @@ function getContext(): SetupContext { */ export function normalizePropsOrEmits( props: ComponentPropsOptions | EmitsOptions, - defaultValue: any = {} + defaultValueCreator = () => ({} as any) ) { return isArray(props) ? props.reduce( - (normalized, p) => ((normalized[p] = defaultValue), normalized), + (normalized, p) => ( + (normalized[p] = defaultValueCreator()), normalized + ), {} as ComponentObjectPropsOptions | ObjectEmitsOptions ) : props diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index aeddc3d37ea..9088468e012 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -1172,7 +1172,7 @@ function mergeEmitsOrPropsOptions( } return extend( normalizePropsOrEmits(to), - normalizePropsOrEmits(from ?? {}, null) + normalizePropsOrEmits(from ?? {}, () => null) ) } else { return from From 001364385747cf6231e7d2d852ce06fd65b770db Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 10 Apr 2023 14:47:30 +0800 Subject: [PATCH 8/9] refactor: small tweaks --- packages/runtime-core/__tests__/componentEmits.spec.ts | 2 +- packages/runtime-core/src/apiSetupHelpers.ts | 7 ++----- packages/runtime-core/src/componentOptions.ts | 5 +++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/runtime-core/__tests__/componentEmits.spec.ts b/packages/runtime-core/__tests__/componentEmits.spec.ts index 0db2242df4a..d8774b078b6 100644 --- a/packages/runtime-core/__tests__/componentEmits.spec.ts +++ b/packages/runtime-core/__tests__/componentEmits.spec.ts @@ -1,7 +1,7 @@ // Note: emits and listener fallthrough is tested in // ./rendererAttrsFallthrough.spec.ts. -import { expect, vi } from 'vitest' +import { vi } from 'vitest' import { render, defineComponent, diff --git a/packages/runtime-core/src/apiSetupHelpers.ts b/packages/runtime-core/src/apiSetupHelpers.ts index 3a8d31fccfc..de7426ad325 100644 --- a/packages/runtime-core/src/apiSetupHelpers.ts +++ b/packages/runtime-core/src/apiSetupHelpers.ts @@ -400,14 +400,11 @@ function getContext(): SetupContext { * @internal */ export function normalizePropsOrEmits( - props: ComponentPropsOptions | EmitsOptions, - defaultValueCreator = () => ({} as any) + props: ComponentPropsOptions | EmitsOptions ) { return isArray(props) ? props.reduce( - (normalized, p) => ( - (normalized[p] = defaultValueCreator()), normalized - ), + (normalized, p) => ((normalized[p] = null), normalized), {} as ComponentObjectPropsOptions | ObjectEmitsOptions ) : props diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 9088468e012..5ef858e0bb8 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -1149,7 +1149,7 @@ function mergeAsArray(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( @@ -1171,8 +1171,9 @@ function mergeEmitsOrPropsOptions( return [...set] } return extend( + Object.create(null), normalizePropsOrEmits(to), - normalizePropsOrEmits(from ?? {}, () => null) + normalizePropsOrEmits(from ?? {}) ) } else { return from From 2447f40a45850ef7ccf3f0e5138d6e1bb06d041a Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 10 Apr 2023 15:01:54 +0800 Subject: [PATCH 9/9] refactor: simplify array merge --- packages/runtime-core/src/componentOptions.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 5ef858e0bb8..bba5ade9ad4 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -1166,9 +1166,7 @@ function mergeEmitsOrPropsOptions( ) { if (to) { if (isArray(to) && isArray(from)) { - const set = new Set(to) - from.forEach(key => set.add(key)) - return [...set] + return [...new Set([...to, ...from])] } return extend( Object.create(null),