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) {