From bec7292bdbf231c11c96937ecbd8373b708ab14b Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Sat, 18 Jan 2025 23:22:37 +0800 Subject: [PATCH 1/2] feat: validator support return string --- .../__tests__/componentProps.spec.ts | 30 +++++++++++++++++++ packages/runtime-core/src/componentProps.ts | 13 ++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index b8eb0e47208..c1393c6233f 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -333,6 +333,36 @@ describe('component props', () => { }) }) + test('validator should allow custom warning messages', async () => { + let warnMsg = '' + vi.spyOn(console, 'warn').mockImplementation(msg => { + warnMsg = msg + }) + const Comp = defineComponent({ + props: { + foo: { + type: Number, + validator: (value, props) => { + if (!Number.isInteger(value)) { + return `Invalid prop: ${props.foo}. Expected an integer.` + } + return true + }, + }, + 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.1, bar: 2 }), root) + expect(warnMsg).toMatch(`Invalid prop: 1.1. Expected an integer.`) + }) + //#12011 test('replace camelize with hyphenate to handle props key', () => { const Comp = { diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 8baa7808665..b65a4c1d462 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, props: Data): boolean + validator?(value: unknown, props: Data): boolean | string /** * @internal */ @@ -705,8 +705,15 @@ function validateProp( } } // custom validator - if (validator && !validator(value, props)) { - warn('Invalid prop: custom validator check failed for prop "' + name + '".') + if (validator) { + const validatorResult = validator(value, props) + let msg = `'Invalid prop: custom validator check failed for prop "${name}".'` + if (typeof validatorResult === 'string') { + msg += ` Reason: ${validatorResult}.` + warn(msg) + } else if (!validatorResult) { + warn(msg) + } } } From 942328e4b533a0805e4befab1c5fbbeb396c2bda Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Sun, 19 Jan 2025 13:24:06 +0800 Subject: [PATCH 2/2] feat: rename extendValidator --- .../__tests__/componentProps.spec.ts | 17 +++++++----- packages/runtime-core/src/componentProps.ts | 26 +++++++++++-------- packages/runtime-core/src/warning.ts | 2 ++ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index c1393c6233f..eb739b8ef5e 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -333,7 +333,7 @@ describe('component props', () => { }) }) - test('validator should allow custom warning messages', async () => { + test('extendValidator custom warn message', async () => { let warnMsg = '' vi.spyOn(console, 'warn').mockImplementation(msg => { warnMsg = msg @@ -342,11 +342,16 @@ describe('component props', () => { props: { foo: { type: Number, - validator: (value, props) => { - if (!Number.isInteger(value)) { - return `Invalid prop: ${props.foo}. Expected an integer.` + extendValidator: (name, value, props, warn) => { + if (typeof value !== 'number') { + warn( + 'Invalid prop: custom validator check failed for prop "' + + name + + '".', + ) + } else if (!Number.isInteger(value)) { + warn(`Invalid prop: ${name}. Expected an integer.`) } - return true }, }, bar: { @@ -360,7 +365,7 @@ describe('component props', () => { // on the fly const root = document.createElement('div') domRender(h(Comp, { foo: 1.1, bar: 2 }), root) - expect(warnMsg).toMatch(`Invalid prop: 1.1. Expected an integer.`) + expect(warnMsg).toMatch(`Invalid prop: foo. Expected an integer.`) }) //#12011 diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index b65a4c1d462..8a6b082b766 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -25,6 +25,7 @@ import { toRawType, } from '@vue/shared' import { warn } from './warning' +import type { WarnFunction } from './warning' import { type ComponentInternalInstance, type ComponentOptions, @@ -56,7 +57,13 @@ export interface PropOptions { type?: PropType | true | null required?: boolean default?: D | DefaultFactory | null | undefined | object - validator?(value: unknown, props: Data): boolean | string + validator?(value: unknown, props: Data): boolean + extendValidator?: ( + name: string, + value: unknown, + props: Data, + warn: WarnFunction, + ) => unknown /** * @internal */ @@ -678,7 +685,7 @@ function validateProp( props: Data, isAbsent: boolean, ) { - const { type, required, validator, skipCheck } = prop + const { type, required, validator, skipCheck, extendValidator } = prop // required! if (required && isAbsent) { warn('Missing required prop: "' + name + '"') @@ -705,15 +712,12 @@ function validateProp( } } // custom validator - if (validator) { - const validatorResult = validator(value, props) - let msg = `'Invalid prop: custom validator check failed for prop "${name}".'` - if (typeof validatorResult === 'string') { - msg += ` Reason: ${validatorResult}.` - warn(msg) - } else if (!validatorResult) { - warn(msg) - } + if (extendValidator) { + extendValidator(name, value, props, warn) + return + } + if (validator && !validator(value, props)) { + warn('Invalid prop: custom validator check failed for prop "' + name + '".') } } diff --git a/packages/runtime-core/src/warning.ts b/packages/runtime-core/src/warning.ts index 788e9372154..7ff4fdd8041 100644 --- a/packages/runtime-core/src/warning.ts +++ b/packages/runtime-core/src/warning.ts @@ -78,6 +78,8 @@ export function warn(msg: string, ...args: any[]): void { isWarning = false } +export type WarnFunction = typeof warn + export function getComponentTrace(): ComponentTraceStack { let currentVNode: VNode | null = stack[stack.length - 1] if (!currentVNode) {