-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
2,601 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { isArray } from '../utils' | ||
import { VaeLocale, VaeLocaleMessage } from './VaeLocale' | ||
import { VaeSchema, VaeSchemaOf } from './VaeSchema' | ||
|
||
export type VaeArraySchemaElementOf<T> = T extends Array<infer X> | ||
? VaeSchemaOf<X> | ||
: never | ||
|
||
export class VaeArraySchema<T extends any[] = any[]> extends VaeSchema<T> { | ||
constructor( | ||
element?: VaeArraySchemaElementOf<T>, | ||
message: VaeLocaleMessage = VaeLocale.array.type, | ||
) { | ||
super({ | ||
type: 'array', | ||
}) | ||
|
||
this.check({ | ||
fn: isArray, | ||
message: message, | ||
}) | ||
|
||
if (element) { | ||
this.element(element) | ||
} | ||
} | ||
|
||
element(element: VaeArraySchemaElementOf<T>) { | ||
return this.check({ | ||
fn: element, | ||
message: '', | ||
tag: 'element', | ||
}) | ||
} | ||
|
||
nonempty(message: VaeLocaleMessage = VaeLocale.array.nonempty) { | ||
return this.check({ | ||
fn: v => v.length > 0, | ||
message: message, | ||
}) | ||
} | ||
|
||
min(value: number, message: VaeLocaleMessage = VaeLocale.array.min) { | ||
return this.check({ | ||
fn: v => v.length >= value, | ||
message: message, | ||
messageParams: { | ||
min: value, | ||
}, | ||
}) | ||
} | ||
|
||
max(value: number, message: VaeLocaleMessage = VaeLocale.array.max) { | ||
return this.check({ | ||
fn: v => v.length <= value, | ||
message: message, | ||
messageParams: { | ||
max: value, | ||
}, | ||
}) | ||
} | ||
|
||
length(value: number, message: VaeLocaleMessage = VaeLocale.array.length) { | ||
return this.check({ | ||
fn: v => v.length === value, | ||
message: message, | ||
messageParams: { | ||
length: value, | ||
}, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { isBoolean } from '../utils' | ||
import { VaeLocale, VaeLocaleMessage } from './VaeLocale' | ||
import { VaeSchema } from './VaeSchema' | ||
|
||
export class VaeBooleanSchema< | ||
T extends boolean = boolean, | ||
> extends VaeSchema<T> { | ||
constructor(message: VaeLocaleMessage = VaeLocale.boolean.type) { | ||
super({ | ||
type: 'boolean', | ||
}) | ||
this.transform(Boolean as any).check({ | ||
fn: isBoolean, | ||
message: message, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { VaeIssue } from './VaeIssue' | ||
import { VaeSchemaPath } from './VaeSchema' | ||
|
||
export class VaeContext { | ||
oldPath: VaeSchemaPath = [] | ||
|
||
path: VaeSchemaPath = [] | ||
|
||
issues: VaeIssue[] = [] | ||
|
||
setPath = (path: VaeSchemaPath) => { | ||
this.oldPath = this.path.slice() | ||
this.path = path | ||
} | ||
|
||
restorePath = () => { | ||
this.path = this.oldPath | ||
} | ||
|
||
addIssue(issue: VaeIssue) { | ||
this.issues.push(issue) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { anyToDate, isDate, isValid } from '../date' | ||
import { VaeLocale, VaeLocaleMessage } from './VaeLocale' | ||
import { VaeSchema } from './VaeSchema' | ||
|
||
export class VaeDateSchema<T extends Date = Date> extends VaeSchema<T> { | ||
constructor(message: VaeLocaleMessage = VaeLocale.date.type) { | ||
super({ | ||
type: 'date', | ||
}) | ||
this.transform(v => anyToDate(v) as any).check({ | ||
fn: v => isDate(v) && isValid(v), | ||
message: message, | ||
}) | ||
} | ||
|
||
min( | ||
value: Date | string | number, | ||
message: VaeLocaleMessage = VaeLocale.date.min, | ||
) { | ||
const minDate = anyToDate(value) | ||
const minTime = minDate.getTime() | ||
return this.check({ | ||
fn: v => minTime <= v.getTime(), | ||
message: message, | ||
messageParams: { | ||
min: minDate, | ||
}, | ||
}) | ||
} | ||
|
||
max( | ||
value: Date | string | number, | ||
message: VaeLocaleMessage = VaeLocale.date.max, | ||
) { | ||
const maxDate = anyToDate(value) | ||
const maxTime = maxDate.getTime() | ||
return this.check({ | ||
fn: v => maxTime >= v.getTime(), | ||
message: message, | ||
messageParams: { | ||
max: maxDate, | ||
}, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { VaeIssue } from './VaeIssue' | ||
|
||
export interface VaeErrorIssue { | ||
path: Array<string | number> | ||
message: string | ||
} | ||
|
||
export class VaeError extends Error { | ||
name = 'VaeError' | ||
|
||
constructor(public issues: VaeIssue[]) { | ||
super() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { VaeSchemaPath } from './VaeSchema' | ||
|
||
export type VaeIssue = { | ||
path: VaeSchemaPath | ||
message: string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { formatDate } from '../date' | ||
import { VaeSchemaPath } from './VaeSchema' | ||
|
||
export type VaeLocaleMessagePayload = { | ||
label?: string | ||
path: VaeSchemaPath | ||
value: any | ||
params: Record<string, any> | ||
} | ||
|
||
export type VaeLocaleMessage = | ||
| string | ||
| ((payload: VaeLocaleMessagePayload) => string) | ||
|
||
export type VaeLocaleShape = { | ||
base: Record<'required' | 'enum', VaeLocaleMessage> | ||
string: Record< | ||
| 'type' | ||
| 'nonempty' | ||
| 'min' | ||
| 'max' | ||
| 'length' | ||
| 'email' | ||
| 'url' | ||
| 'regex' | ||
| 'includes' | ||
| 'startsWith' | ||
| 'endsWith' | ||
| 'phoneNumber' | ||
| 'idCardNumber', | ||
VaeLocaleMessage | ||
> | ||
object: Record<'type', VaeLocaleMessage> | ||
number: Record< | ||
| 'type' | ||
| 'min' | ||
| 'max' | ||
| 'lessThan' | ||
| 'moreThan' | ||
| 'integer' | ||
| 'positive' | ||
| 'nonpositive' | ||
| 'negative' | ||
| 'nonnegative' | ||
| 'positiveInteger', | ||
VaeLocaleMessage | ||
> | ||
boolean: Record<'type', VaeLocaleMessage> | ||
array: Record< | ||
'type' | 'nonempty' | 'min' | 'max' | 'length', | ||
VaeLocaleMessage | ||
> | ||
date: Record<'type' | 'min' | 'max', VaeLocaleMessage> | ||
} | ||
|
||
export class VaeLocaleBuilder { | ||
static zhCN(options: { | ||
getLabel: (payload: VaeLocaleMessagePayload) => string | ||
}): VaeLocaleShape { | ||
return { | ||
base: { | ||
required: payload => `${options.getLabel(payload)}应必填`, | ||
enum: payload => | ||
`${options.getLabel( | ||
payload, | ||
)}应是下列值之一:${payload.params.enum.join(',')}`, | ||
}, | ||
|
||
string: { | ||
type: payload => `${options.getLabel(payload)}应是字符串类型`, | ||
nonempty: payload => `${options.getLabel(payload)}应非空`, | ||
min: payload => | ||
`${options.getLabel(payload)}应至少包含${payload.params.min}位字符`, | ||
max: payload => | ||
`${options.getLabel(payload)}应最多包含${payload.params.max}位字符`, | ||
length: payload => | ||
`${options.getLabel(payload)}应仅包含${payload.params.length}位字符`, | ||
email: payload => `${options.getLabel(payload)}应是一个合法的邮箱`, | ||
url: payload => `${options.getLabel(payload)}应是一个合法的网址`, | ||
regex: payload => | ||
`${options.getLabel(payload)}应满足正则表达式${payload.params.regex}`, | ||
includes: payload => | ||
`${options.getLabel(payload)}应包含字符串${payload.params.includes}`, | ||
startsWith: payload => | ||
`${options.getLabel(payload)}应以字符串${ | ||
payload.params.startsWith | ||
}开头`, | ||
endsWith: payload => | ||
`${options.getLabel(payload)}应以字符串${ | ||
payload.params.endsWith | ||
}结尾`, | ||
phoneNumber: payload => | ||
`${options.getLabel(payload)}应是一个合法的手机号码`, | ||
idCardNumber: payload => | ||
`${options.getLabel(payload)}应是一个合法的身份证号码`, | ||
}, | ||
|
||
object: { | ||
type: payload => `${options.getLabel(payload)}应是对象类型`, | ||
}, | ||
|
||
number: { | ||
type: payload => `${options.getLabel(payload)}应是数值类型`, | ||
min: payload => | ||
`${options.getLabel(payload)}应大于或等于${payload.params.min}`, | ||
max: payload => | ||
`${options.getLabel(payload)}应小于或等于${payload.params.max}`, | ||
lessThan: payload => | ||
`${options.getLabel(payload)}应小于${payload.params.lessThan}`, | ||
moreThan: payload => | ||
`${options.getLabel(payload)}应大于${payload.params.moreThan}`, | ||
integer: payload => `${options.getLabel(payload)}应是一个整数`, | ||
positive: payload => `${options.getLabel(payload)}应是一个正数`, | ||
nonpositive: payload => `${options.getLabel(payload)}应是一个非正数`, | ||
negative: payload => `${options.getLabel(payload)}应是一个负数`, | ||
nonnegative: payload => `${options.getLabel(payload)}应是一个非负数`, | ||
positiveInteger: payload => | ||
`${options.getLabel(payload)}应是一个正整数`, | ||
}, | ||
|
||
boolean: { | ||
type: payload => `${options.getLabel(payload)}应是布尔类型`, | ||
}, | ||
|
||
array: { | ||
type: payload => `${options.getLabel(payload)}应是数组类型`, | ||
nonempty: payload => `${options.getLabel(payload)}应非空`, | ||
min: payload => | ||
`${options.getLabel(payload)}应至少包含${payload.params.min}个元素`, | ||
max: payload => | ||
`${options.getLabel(payload)}应最多包含${payload.params.max}个元素`, | ||
length: payload => | ||
`${options.getLabel(payload)}应仅包含${payload.params.max}个元素`, | ||
}, | ||
|
||
date: { | ||
type: payload => `${options.getLabel(payload)}应是日期类型`, | ||
min: payload => | ||
`${options.getLabel(payload)}应大于或等于${formatDate( | ||
payload.params.min, | ||
'yyyy-mm-dd hh:ii:ss', | ||
)}`, | ||
max: payload => | ||
`${options.getLabel(payload)}应小于或等于${formatDate( | ||
payload.params.max, | ||
'yyyy-mm-dd hh:ii:ss', | ||
)}`, | ||
}, | ||
} | ||
} | ||
} | ||
|
||
export const VaeLocale = { | ||
...VaeLocaleBuilder.zhCN({ | ||
getLabel: payload => payload.label || payload.path.join('.') || '.', | ||
}), | ||
$set: (locale: VaeLocaleShape) => { | ||
Object.assign(VaeLocale, locale) | ||
}, | ||
} |
Oops, something went wrong.