From 40b323278111bda90f0465de496b1bb5c8bce7c3 Mon Sep 17 00:00:00 2001 From: Nandor Kraszlan Date: Thu, 18 Feb 2021 21:23:11 +0000 Subject: [PATCH 1/4] feat(props): provide props to validation Added all props to the prop validator as a second argument https://github.com/vuejs/vue-next/issues/3254 --- .../__tests__/componentProps.spec.ts | 22 +++++++++++++++++++ packages/runtime-core/src/componentProps.ts | 6 +++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index 863b12d0699..ca30dd8232c 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -278,6 +278,28 @@ describe('component props', () => { expect(root.innerHTML).toBe('
2
') }) + test('validator arguments', async () => { + const mockFn = jest.fn((...args: any[]) => true) + const Comp = defineComponent({ + props: { + foo: { + type: Number, + validator: (value, props) => mockFn(value, props) + }, + bar: { + type: Number + } + }, + template: `
` + }) + + // Note this one is using the main Vue render so it can compile template + // on the fly + const root = document.createElement('div') + domRender(h(Comp, { foo: 1, bar: 2 }), root) + expect(mockFn).toHaveBeenCalledWith(1, { foo: 1, bar: 2 }) + }) + test('warn props mutation', () => { let instance: ComponentInternalInstance let setupProps: any diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 924f40a7384..e634314af76 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -56,7 +56,7 @@ export interface PropOptions { type?: PropType | true | null required?: boolean default?: D | DefaultFactory | null | undefined | object - validator?(value: unknown): boolean + validator?(value: unknown, props: Data): boolean } export type PropType = PropConstructor | PropConstructor[] @@ -575,6 +575,7 @@ function validateProps( key, resolvedValues[key], opt, + resolvedValues !hasOwn(rawProps, key) && !hasOwn(rawProps, hyphenate(key)) ) } @@ -587,6 +588,7 @@ function validateProp( name: string, value: unknown, prop: PropOptions, + props: Data, isAbsent: boolean ) { const { type, required, validator } = prop @@ -616,7 +618,7 @@ function validateProp( } } // custom validator - if (validator && !validator(value)) { + if (validator && !validator(value, props)) { warn('Invalid prop: custom validator check failed for prop "' + name + '".') } } From a7b5f1cad1fe976e02745eb92424be30cdb80782 Mon Sep 17 00:00:00 2001 From: Nandor Kraszlan Date: Sat, 20 Feb 2021 16:18:16 +0000 Subject: [PATCH 2/4] feat(props): configured prop argument to readonly --- .../__tests__/componentProps.spec.ts | 62 ++++++++++++++----- packages/runtime-core/src/componentProps.ts | 5 +- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index ca30dd8232c..fbe274d2c60 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -278,26 +278,54 @@ describe('component props', () => { expect(root.innerHTML).toBe('
2
') }) - test('validator arguments', async () => { - const mockFn = jest.fn((...args: any[]) => true) - const Comp = defineComponent({ - props: { - foo: { - type: Number, - validator: (value, props) => mockFn(value, props) + describe('validator', () => { + test('validator should be called with two arguments', async () => { + const mockFn = jest.fn((...args: any[]) => true) + const Comp = defineComponent({ + props: { + foo: { + type: Number, + validator: (value, props) => mockFn(value, props) + }, + bar: { + type: Number + } }, - bar: { - type: Number - } - }, - template: `
` + template: `
` + }) + + // Note this one is using the main Vue render so it can compile template + // on the fly + const root = document.createElement('div') + domRender(h(Comp, { foo: 1, bar: 2 }), root) + expect(mockFn).toHaveBeenCalledWith(1, { foo: 1, bar: 2 }) }) - // Note this one is using the main Vue render so it can compile template - // on the fly - const root = document.createElement('div') - domRender(h(Comp, { foo: 1, bar: 2 }), root) - expect(mockFn).toHaveBeenCalledWith(1, { foo: 1, bar: 2 }) + test('validator should not be able to mutate other props', async () => { + const mockFn = jest.fn((...args: any[]) => true) + const Comp = defineComponent({ + props: { + foo: { + type: Number, + validator: (value, props) => !!(props.bar = 1) + }, + bar: { + type: Number, + validator: value => mockFn(value) + } + }, + template: `
` + }) + + // Note this one is using the main Vue render so it can compile template + // on the fly + const root = document.createElement('div') + domRender(h(Comp, { foo: 1, bar: 2 }), root) + expect( + `Set operation on key "bar" failed: target is readonly.` + ).toHaveBeenWarnedLast() + expect(mockFn).toHaveBeenCalledWith(2) + }) }) test('warn props mutation', () => { diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index e634314af76..c7568227ae3 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -2,7 +2,8 @@ import { toRaw, shallowReactive, trigger, - TriggerOpTypes + TriggerOpTypes, + shallowReadonly } from '@vue/reactivity' import { EMPTY_OBJ, @@ -575,7 +576,7 @@ function validateProps( key, resolvedValues[key], opt, - resolvedValues + shallowReadonly(resolvedValues), !hasOwn(rawProps, key) && !hasOwn(rawProps, hyphenate(key)) ) } From a32f7f7a990b36ba3bd8fef35333e30bc104cd32 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 26 Oct 2022 04:02:58 -0400 Subject: [PATCH 3/4] Update componentProps.ts --- packages/runtime-core/src/componentProps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index c7568227ae3..061c4476eb2 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -576,7 +576,7 @@ function validateProps( key, resolvedValues[key], opt, - shallowReadonly(resolvedValues), + __DEV__ ? shallowReadonly(resolvedValues) : resolvedValues, !hasOwn(rawProps, key) && !hasOwn(rawProps, hyphenate(key)) ) } From b2d42d64b06b04c0b2315832dc986327b0e44ce5 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 5 Dec 2023 17:12:27 +0800 Subject: [PATCH 4/4] jest -> vi --- packages/runtime-core/__tests__/componentProps.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index ee1af47804b..3ac22ae660b 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -284,7 +284,7 @@ describe('component props', () => { describe('validator', () => { test('validator should be called with two arguments', async () => { - const mockFn = jest.fn((...args: any[]) => true) + const mockFn = vi.fn((...args: any[]) => true) const Comp = defineComponent({ props: { foo: { @@ -306,7 +306,7 @@ describe('component props', () => { }) test('validator should not be able to mutate other props', async () => { - const mockFn = jest.fn((...args: any[]) => true) + const mockFn = vi.fn((...args: any[]) => true) const Comp = defineComponent({ props: { foo: {