Skip to content

Commit

Permalink
feat(vae): 支持 runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Aug 18, 2023
1 parent 583e87b commit 079c1ba
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 18 deletions.
55 changes: 37 additions & 18 deletions src/vae/VaeSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type VaeSchemaType =
| 'boolean'
| 'array'

export type VaeSchemaOptions<T> = {
export type VaeSchemaOptions<T, S> = {
type: VaeSchemaType
label?: string
default?: T | (() => T)
Expand All @@ -50,6 +50,7 @@ export type VaeSchemaOptions<T> = {
stringTrim?: boolean
stringEmptyable?: boolean
processors?: Array<VaeSchemaCheckPayload<T> | VaeSchemaTransformPayload<T>>
runtime?: VaeSchemaRuntimeFn<T, S>
}

export type VaeSchemaPath = Array<string | number>
Expand All @@ -64,6 +65,8 @@ export type VaeSchemaCheckPayload<T> = {

export type VaeSchemaTransformPayload<T> = (value: T) => T

export type VaeSchemaRuntimeFn<T, S> = (payload: { value: T; schema: S }) => any

export type VaeSchemaParseOptions = {
/**
* 上下文,内部使用
Expand Down Expand Up @@ -137,9 +140,9 @@ export type VaeSchemaOf<T0, T = RequiredDeep<T0>> =
VaeObjectSchema<T>

export abstract class VaeSchema<T extends any = any> {
protected _options!: RequiredBy<VaeSchemaOptions<T>, 'processors'>
protected _options!: RequiredBy<VaeSchemaOptions<T, any>, 'processors'>

constructor(options: VaeSchemaOptions<T>) {
constructor(options: VaeSchemaOptions<T, any>) {
this._options = {
...options,
processors: options.processors || [],
Expand Down Expand Up @@ -229,6 +232,11 @@ export abstract class VaeSchema<T extends any = any> {
})
}

runtime(fn: VaeSchemaRuntimeFn<T, this>) {
this._options.runtime = fn
return this
}

clone() {
// https://stackoverflow.com/a/44782052
const newSchema: this = assign(
Expand Down Expand Up @@ -337,18 +345,29 @@ export abstract class VaeSchema<T extends any = any> {
}
}

const processors = this._options.processors.slice()
// 运行时
// eslint-disable-next-line @typescript-eslint/no-this-alias
let schema = this
if (this._options.runtime) {
schema = this.clone()
this._options.runtime({
value: data,
schema: schema,
})
}

const processors = schema._options.processors.slice()

// 必填规则始终前置
if (this._options.required) {
if (schema._options.required) {
processors.unshift({
fn: () => !dataIsNil,
message: this._options.requiredMessage!,
message: schema._options.requiredMessage!,
})
}

// 对于数组,将 element 的验证移到最后
if (this._options.type === 'array') {
if (schema._options.type === 'array') {
moveToBottom(
processors,
processors.findIndex(
Expand All @@ -370,7 +389,7 @@ export abstract class VaeSchema<T extends any = any> {
const fullPath = [...curPath, ...path]
if (fn instanceof VaeSchema) {
const pathData = path.length ? get(data, path) : data
if (this._options.type === 'array' && tag === 'element') {
if (schema._options.type === 'array' && tag === 'element') {
if (pathData) {
for (let j = 0; j < (pathData as any[]).length; j++) {
const item = (pathData as any[])[j]
Expand Down Expand Up @@ -411,17 +430,17 @@ export abstract class VaeSchema<T extends any = any> {
if (options.cast) {
data = dataIsNil
? data
: this._options.type === 'string'
: schema._options.type === 'string'
? String(data)
: this._options.type === 'number'
: schema._options.type === 'number'
? Number(data)
: this._options.type === 'boolean'
: schema._options.type === 'boolean'
? Boolean(data)
: this._options.type === 'date'
: schema._options.type === 'date'
? new Date(data as any)
: this._options.type === 'array'
: schema._options.type === 'array'
? toArray(data)
: this._options.type === 'object'
: schema._options.type === 'object'
? toPlainObject(data)
: null
} else {
Expand All @@ -433,7 +452,7 @@ export abstract class VaeSchema<T extends any = any> {
path: fullPath,
params: messageParams || {},
value: data,
label: this._options.label,
label: schema._options.label,
})
: message,
})
Expand All @@ -460,9 +479,9 @@ export abstract class VaeSchema<T extends any = any> {
success: true,
data:
!options.preserveUnknownKeys &&
this._options.type === 'object' &&
this._options.objectKeys?.length
? (pick(data, this._options.objectKeys) as any)
schema._options.type === 'object' &&
schema._options.objectKeys?.length
? (pick(data, schema._options.objectKeys) as any)
: data,
}
}
Expand Down
44 changes: 44 additions & 0 deletions src/vae/__snapshots__/vae.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,50 @@ Object {
}
`;

exports[`vae runtime 1`] = `
Object {
"data": Object {
"name": "ffff",
"status": true,
},
"success": true,
}
`;

exports[`vae runtime 2`] = `
Object {
"data": Object {
"name": "ffff",
"status": false,
},
"success": true,
}
`;

exports[`vae runtime 3`] = `
Object {
"issues": Array [
Object {
"message": "name应必填",
"path": Array [
"name",
],
},
],
"success": false,
}
`;

exports[`vae runtime 4`] = `
Object {
"data": Object {
"name": undefined,
"status": false,
},
"success": true,
}
`;

exports[`vae string 1`] = `
Object {
"data": undefined,
Expand Down
41 changes: 41 additions & 0 deletions src/vae/vae.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,4 +678,45 @@ describe('vae', () => {
expect(v.number().max(3).parse(4)).toMatchSnapshot()
expect(v.number().max(3).max(8).parse(4)).toMatchSnapshot()
})

test('runtime', () => {
const schema = v
.object({
status: v.boolean().required(),
name: v.string().required(),
})
.runtime(({ value, schema }) => {
if (value.status === false) {
schema.reach('name').optional()
}
})
expect(
schema.parse({
status: true,
name: 'ffff',
}),
).toMatchSnapshot()
expect(
schema.parse({
status: false,
name: 'ffff',
}),
).toMatchSnapshot()
expect(
schema.parse(
// @ts-expect-error
{
status: true,
},
),
).toMatchSnapshot()
expect(
schema.parse(
// @ts-expect-error
{
status: false,
},
),
).toMatchSnapshot()
})
})

0 comments on commit 079c1ba

Please sign in to comment.