├── .babelrc
├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── WIKI
├── demo
├── index.array.js
├── index.html
└── index.js
├── dist
└── slime-validator.umd.js
├── docs
├── apis.md
└── index.md
├── index.d.ts
├── index.js
├── package.json
├── packages
├── enum.js
├── is_email.js
├── is_url.js
├── max_length.js
├── max_num.js
├── min_length.js
├── min_num.js
├── not_empty.js
├── regexp.js
└── required.js
├── test
├── default.test.js
├── plugin.test.js
├── rule.test.js
├── schema.test.js
└── sign.rule.test.js
├── utils.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env",
5 | {
6 | "targets": {
7 | "browsers": [
8 | "> 1%",
9 | "last 2 versions",
10 | "Android >= 3.2",
11 | "Firefox >= 20",
12 | "iOS 7"
13 | ]
14 | }
15 | }
16 | ]
17 | ],
18 | "plugins": []
19 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 | .DS_Store
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 10
4 | - 12
5 | - node
6 | email:
7 | after_success:
8 | env:
9 | NODE_ENV: JEST
10 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution Guideline
2 |
3 | Thanks for considering to contribute this project. All issues and pull requests are highly appreciated.
4 |
5 | ## Pull Requests
6 |
7 | Before sending pull request to this project, please read and follow guidelines below.
8 |
9 | 1. Test: Make sure to test your code.
10 |
11 | Add device mode, API version, related log, screenshots and other related information in your pull request if possible.
12 |
13 | NOTE: We assume all your contribution can be licensed under the [Apache License 2.0](LICENSE).
14 |
15 | ## Issues
16 |
17 | We love clearly described issues. :)
18 |
19 | Following information can help us to resolve the issue faster.
20 |
21 | * Device mode and hardware information.
22 | * API version.
23 | * Logs.
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 |
3 | Version 2.0, January 2004
4 |
5 | http://www.apache.org/licenses/
6 |
7 |
8 |
9 |
10 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
11 |
12 |
13 |
14 |
15 | 1. Definitions.
16 |
17 |
18 |
19 |
20 | "License" shall mean the terms and conditions for use, reproduction,
21 |
22 | and distribution as defined by Sections 1 through 9 of this document.
23 |
24 |
25 |
26 |
27 | "Licensor" shall mean the copyright owner or entity authorized by
28 |
29 | the copyright owner that is granting the License.
30 |
31 |
32 |
33 |
34 | "Legal Entity" shall mean the union of the acting entity and all
35 |
36 | other entities that control, are controlled by, or are under common
37 |
38 | control with that entity. For the purposes of this definition,
39 |
40 | "control" means (i) the power, direct or indirect, to cause the
41 |
42 | direction or management of such entity, whether by contract or
43 |
44 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
45 |
46 | outstanding shares, or (iii) beneficial ownership of such entity.
47 |
48 |
49 |
50 |
51 | "You" (or "Your") shall mean an individual or Legal Entity
52 |
53 | exercising permissions granted by this License.
54 |
55 |
56 |
57 |
58 | "Source" form shall mean the preferred form for making modifications,
59 |
60 | including but not limited to software source code, documentation
61 |
62 | source, and configuration files.
63 |
64 |
65 |
66 |
67 | "Object" form shall mean any form resulting from mechanical
68 |
69 | transformation or translation of a Source form, including but
70 |
71 | not limited to compiled object code, generated documentation,
72 |
73 | and conversions to other media types.
74 |
75 |
76 |
77 |
78 | "Work" shall mean the work of authorship, whether in Source or
79 |
80 | Object form, made available under the License, as indicated by a
81 |
82 | copyright notice that is included in or attached to the work
83 |
84 | (an example is provided in the Appendix below).
85 |
86 |
87 |
88 |
89 | "Derivative Works" shall mean any work, whether in Source or Object
90 |
91 | form, that is based on (or derived from) the Work and for which the
92 |
93 | editorial revisions, annotations, elaborations, or other modifications
94 |
95 | represent, as a whole, an original work of authorship. For the purposes
96 |
97 | of this License, Derivative Works shall not include works that remain
98 |
99 | separable from, or merely link (or bind by name) to the interfaces of,
100 |
101 | the Work and Derivative Works thereof.
102 |
103 |
104 |
105 |
106 | "Contribution" shall mean any work of authorship, including
107 |
108 | the original version of the Work and any modifications or additions
109 |
110 | to that Work or Derivative Works thereof, that is intentionally
111 |
112 | submitted to Licensor for inclusion in the Work by the copyright owner
113 |
114 | or by an individual or Legal Entity authorized to submit on behalf of
115 |
116 | the copyright owner. For the purposes of this definition, "submitted"
117 |
118 | means any form of electronic, verbal, or written communication sent
119 |
120 | to the Licensor or its representatives, including but not limited to
121 |
122 | communication on electronic mailing lists, source code control systems,
123 |
124 | and issue tracking systems that are managed by, or on behalf of, the
125 |
126 | Licensor for the purpose of discussing and improving the Work, but
127 |
128 | excluding communication that is conspicuously marked or otherwise
129 |
130 | designated in writing by the copyright owner as "Not a Contribution."
131 |
132 |
133 |
134 |
135 | "Contributor" shall mean Licensor and any individual or Legal Entity
136 |
137 | on behalf of whom a Contribution has been received by Licensor and
138 |
139 | subsequently incorporated within the Work.
140 |
141 |
142 |
143 |
144 | 2. Grant of Copyright License. Subject to the terms and conditions of
145 |
146 | this License, each Contributor hereby grants to You a perpetual,
147 |
148 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
149 |
150 | copyright license to reproduce, prepare Derivative Works of,
151 |
152 | publicly display, publicly perform, sublicense, and distribute the
153 |
154 | Work and such Derivative Works in Source or Object form.
155 |
156 |
157 |
158 |
159 | 3. Grant of Patent License. Subject to the terms and conditions of
160 |
161 | this License, each Contributor hereby grants to You a perpetual,
162 |
163 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
164 |
165 | (except as stated in this section) patent license to make, have made,
166 |
167 | use, offer to sell, sell, import, and otherwise transfer the Work,
168 |
169 | where such license applies only to those patent claims licensable
170 |
171 | by such Contributor that are necessarily infringed by their
172 |
173 | Contribution(s) alone or by combination of their Contribution(s)
174 |
175 | with the Work to which such Contribution(s) was submitted. If You
176 |
177 | institute patent litigation against any entity (including a
178 |
179 | cross-claim or counterclaim in a lawsuit) alleging that the Work
180 |
181 | or a Contribution incorporated within the Work constitutes direct
182 |
183 | or contributory patent infringement, then any patent licenses
184 |
185 | granted to You under this License for that Work shall terminate
186 |
187 | as of the date such litigation is filed.
188 |
189 |
190 |
191 |
192 | 4. Redistribution. You may reproduce and distribute copies of the
193 |
194 | Work or Derivative Works thereof in any medium, with or without
195 |
196 | modifications, and in Source or Object form, provided that You
197 |
198 | meet the following conditions:
199 |
200 |
201 |
202 |
203 | (a) You must give any other recipients of the Work or
204 |
205 | Derivative Works a copy of this License; and
206 |
207 |
208 |
209 |
210 | (b) You must cause any modified files to carry prominent notices
211 |
212 | stating that You changed the files; and
213 |
214 |
215 |
216 |
217 | (c) You must retain, in the Source form of any Derivative Works
218 |
219 | that You distribute, all copyright, patent, trademark, and
220 |
221 | attribution notices from the Source form of the Work,
222 |
223 | excluding those notices that do not pertain to any part of
224 |
225 | the Derivative Works; and
226 |
227 |
228 |
229 |
230 | (d) If the Work includes a "NOTICE" text file as part of its
231 |
232 | distribution, then any Derivative Works that You distribute must
233 |
234 | include a readable copy of the attribution notices contained
235 |
236 | within such NOTICE file, excluding those notices that do not
237 |
238 | pertain to any part of the Derivative Works, in at least one
239 |
240 | of the following places: within a NOTICE text file distributed
241 |
242 | as part of the Derivative Works; within the Source form or
243 |
244 | documentation, if provided along with the Derivative Works; or,
245 |
246 | within a display generated by the Derivative Works, if and
247 |
248 | wherever such third-party notices normally appear. The contents
249 |
250 | of the NOTICE file are for informational purposes only and
251 |
252 | do not modify the License. You may add Your own attribution
253 |
254 | notices within Derivative Works that You distribute, alongside
255 |
256 | or as an addendum to the NOTICE text from the Work, provided
257 |
258 | that such additional attribution notices cannot be construed
259 |
260 | as modifying the License.
261 |
262 |
263 |
264 |
265 | You may add Your own copyright statement to Your modifications and
266 |
267 | may provide additional or different license terms and conditions
268 |
269 | for use, reproduction, or distribution of Your modifications, or
270 |
271 | for any such Derivative Works as a whole, provided Your use,
272 |
273 | reproduction, and distribution of the Work otherwise complies with
274 |
275 | the conditions stated in this License.
276 |
277 |
278 |
279 |
280 | 5. Submission of Contributions. Unless You explicitly state otherwise,
281 |
282 | any Contribution intentionally submitted for inclusion in the Work
283 |
284 | by You to the Licensor shall be under the terms and conditions of
285 |
286 | this License, without any additional terms or conditions.
287 |
288 | Notwithstanding the above, nothing herein shall supersede or modify
289 |
290 | the terms of any separate license agreement you may have executed
291 |
292 | with Licensor regarding such Contributions.
293 |
294 |
295 |
296 |
297 | 6. Trademarks. This License does not grant permission to use the trade
298 |
299 | names, trademarks, service marks, or product names of the Licensor,
300 |
301 | except as required for reasonable and customary use in describing the
302 |
303 | origin of the Work and reproducing the content of the NOTICE file.
304 |
305 |
306 |
307 |
308 | 7. Disclaimer of Warranty. Unless required by applicable law or
309 |
310 | agreed to in writing, Licensor provides the Work (and each
311 |
312 | Contributor provides its Contributions) on an "AS IS" BASIS,
313 |
314 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
315 |
316 | implied, including, without limitation, any warranties or conditions
317 |
318 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
319 |
320 | PARTICULAR PURPOSE. You are solely responsible for determining the
321 |
322 | appropriateness of using or redistributing the Work and assume any
323 |
324 | risks associated with Your exercise of permissions under this License.
325 |
326 |
327 |
328 |
329 | 8. Limitation of Liability. In no event and under no legal theory,
330 |
331 | whether in tort (including negligence), contract, or otherwise,
332 |
333 | unless required by applicable law (such as deliberate and grossly
334 |
335 | negligent acts) or agreed to in writing, shall any Contributor be
336 |
337 | liable to You for damages, including any direct, indirect, special,
338 |
339 | incidental, or consequential damages of any character arising as a
340 |
341 | result of this License or out of the use or inability to use the
342 |
343 | Work (including but not limited to damages for loss of goodwill,
344 |
345 | work stoppage, computer failure or malfunction, or any and all
346 |
347 | other commercial damages or losses), even if such Contributor
348 |
349 | has been advised of the possibility of such damages.
350 |
351 |
352 |
353 |
354 | 9. Accepting Warranty or Additional Liability. While redistributing
355 |
356 | the Work or Derivative Works thereof, You may choose to offer,
357 |
358 | and charge a fee for, acceptance of support, warranty, indemnity,
359 |
360 | or other liability obligations and/or rights consistent with this
361 |
362 | License. However, in accepting such obligations, You may act only
363 |
364 | on Your own behalf and on Your sole responsibility, not on behalf
365 |
366 | of any other Contributor, and only if You agree to indemnify,
367 |
368 | defend, and hold each Contributor harmless for any liability
369 |
370 | incurred by, or claims asserted against, such Contributor by reason
371 |
372 | of your accepting any such warranty or additional liability.
373 |
374 |
375 |
376 |
377 | END OF TERMS AND CONDITIONS
378 |
379 |
380 |
381 |
382 | APPENDIX: How to apply the Apache License to your work.
383 |
384 |
385 |
386 |
387 | To apply the Apache License to your work, attach the following
388 |
389 | boilerplate notice, with the fields enclosed by brackets "{}"
390 |
391 | replaced with your own identifying information. (Don't include
392 |
393 | the brackets!) The text should be enclosed in the appropriate
394 |
395 | comment syntax for the file format. We also recommend that a
396 |
397 | file or class name and description of purpose be included on the
398 |
399 | same "printed page" as the copyright notice for easier
400 |
401 | identification within third-party archives.
402 |
403 |
404 |
405 |
406 | Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved.
407 |
408 |
409 |
410 |
411 | Licensed under the Apache License, Version 2.0 (the "License");
412 |
413 | you may not use this file except in compliance with the License.
414 |
415 | You may obtain a copy of the License at
416 |
417 |
418 |
419 |
420 | http://www.apache.org/licenses/LICENSE-2.0
421 |
422 |
423 |
424 |
425 | Unless required by applicable law or agreed to in writing, software
426 |
427 | distributed under the License is distributed on an "AS IS" BASIS,
428 |
429 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
430 |
431 | See the License for the specific language governing permissions and
432 |
433 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![NPM version][npm-image]][npm-url]
2 | [![NPM quality][quality-image]][quality-url]
3 | [![npm download][download-image]][download-url]
4 | [![build status][travis-image]][travis-url]
5 |
6 | [travis-image]: https://travis-ci.org/didi/slime-validator.svg?branch=master&status=passed
7 | [travis-url]: https://travis-ci.org/didi/slime-validator
8 | [download-image]: https://img.shields.io/npm/dm/slime-validator.svg?style=flat-square
9 | [download-url]: https://npmjs.org/package/slime-validator
10 | [npm-image]: https://img.shields.io/npm/v/slime-validator.svg?style=flat-square
11 | [npm-url]: https://npmjs.org/package/slime-validator
12 | [quality-image]: http://npm.packagequality.com/shield/slime-validator.svg?style=flat-square
13 | [quality-url]: http://packagequality.com/#?package=slime-validator
14 | [codecov-image]: https://img.shields.io/codecov/c/github/didi/slime-validator.svg?style=flat-square
15 | [codecov-url]: https://codecov.io/gh/didi/slime-validator
16 |
17 | # slime-validator
18 | `slime-validator` is a JavaScript library of validation based on `Plugin` system and make data validation be easy.
19 |
20 | # Description
21 | `slime-validator` make data validation with less code, save more than 20% code than others.
22 |
23 | Plugin system let you make your own validate rules very easy and complex validate rule too. Both on Browser and Nodejs.
24 |
25 | # Installation
26 |
27 | ### Using npm
28 | ```sh
29 | $ npm install slime-validator -save
30 | ```
31 |
32 | ### Browser
33 | ```js
34 |
35 | ```
36 |
37 | # Basic Usage
38 |
39 | ### For es module
40 | ```js
41 | import Validator from 'slime-validator'
42 |
43 | // One
44 | const V1 = new Validator({ Required:true })
45 | console.log(V1.validate('Hello world')) // Output: null
46 | console.log(V1.validate()) // Output: Input is required
47 |
48 | // Two
49 | console.log(Validator.validate({ Required:true }, 'Hello world')) // Output: null
50 | console.log(Validator.validate({ Required:true }, null)) // Output: Input is required
51 | ```
52 |
53 | ### For CDN
54 | ```html
55 |
56 |
57 |
66 |
67 |
68 |
69 |
70 | ```
71 |
72 | # Custom Validation Rule
73 | ```js
74 | Validator.usePlugin({
75 | tagName: 'IsNotRequired',
76 | message(field, value, opts) {
77 | return `${field} Check failed`
78 | },
79 | validate(field, value, opts) {
80 | return false
81 | }
82 | }, true) // true means to replace the exist rule with the same name.
83 |
84 | const V7 = new Validator({
85 | field: { IsNotRequired: true }
86 | })
87 | console.log(V7.validate({
88 | field: "Something"
89 | })) // Output:{ field: 'field Check failed' }
90 | ```
91 |
92 | For more information you can read [Document](docs/index.md)
93 |
94 | # Who use?
95 |
96 |

97 |

98 |
99 |
100 |
--------------------------------------------------------------------------------
/WIKI:
--------------------------------------------------------------------------------
1 | # 更加简单易用的JS校验库
2 |
3 | `slime-validator`是一个结合目前主流校验库能力,集各家所长,并提供更加简单易用的校验能力。相比其他库可以节约校验相关代码量`20%`左右,在复杂参数校验的情况下甚至能达到`30%`以上`(这里是单纯的指代码字母总数量对比)`。并且支持字段`联动校验`,因此你也可以通过`slime-validator`更加复杂的校验功能,这是其它校验库很少支持的能力。尤其是`validatorjs`,往往需要自己重新封装。
4 |
5 | * 关于字段`联动校验`可以参看文末的例子。
6 |
7 | # 如何使用
8 | `slime-validator`可以应用于后端,也可以应用于前端,但是最初设计是针对后端设计的,因为前端绝大多数情况下都是直接使用`antd`或者`elementUI`,而它们都内置校验库,所以没有太大必要使用`slime-validator`。
9 |
10 | ## 安装
11 |
12 | ```js
13 | $ npm i slime-validator -save
14 | ```
15 |
16 | 当然也提供了通过`script`标签直接引入的方式,但是由于没有发布`CDN`。需要自行解决引入源的问题。
17 |
18 | ## 如何使用
19 | `slime-validator`有两种校验方法,一种是每次创建一个`Validator`对象。这样可以重复使用这个对象直接对参数进行预期规则的校验。另一种是通过`Validator.validate`函数,通过传入校验规则和待校验对象进行校验。和其他校验库用法类似。
20 |
21 | ```js
22 | import Validator from 'slime-validator'
23 |
24 | // 用法一
25 | const V1 = new Validator({ Required:true })
26 | console.log(V1.validate('Hello world')) // 输出: null
27 | console.log(V1.validate()) // 输出: Input is required
28 |
29 | // 用法二
30 | console.log(Validator.validate({ Required:true }, 'Hello world')) // 输出: null
31 | console.log(Validator.validate({ Required:true }, null)) // 输出: Input is required
32 | ```
33 |
34 | ## 如何校验一个对象参数
35 | 校验对象,是校验非常普遍的一个需求场景。`slime-validator`自然而然也必须支持这个能力。
36 |
37 | ```js
38 | import Validator from 'slime-validator'
39 |
40 | // 校验一个对象
41 | const V2 = new Validator({
42 | username: { Required: true, MinLength: 3 }, // 用户名必填,且不小于3个字符
43 | age: { Required: true, MinNum: 18 }, // 年龄必填,且需要大于18岁
44 | address: { MaxLength: 10 } // 地址选填,最大不超过10个字
45 | })
46 | console.log(V2.validate({
47 | username: 'ryouaki',
48 | age: 17
49 | })) // 输出:{ age: 'Min value of age is 18' }
50 | ```
51 | * 也可以直接使用`Validator.validate`来校验。
52 |
53 | ## 如何校验一个嵌套对象
54 | 在复杂的业务场景,我们需要校验的对象会非常复杂,这个时候,对于像`validatorjs`就要额外封装才能满足需要,而`async-validator`一样需要用一些额外字段去识别这是什么类型的待校验对象。
55 |
56 | 但是在`slime-validator`中,只需要一个`$fields`字段就完成了所以功能,在内部会自动识别对象还是数据还是对象加数组,因此开发者可以少书写很多代码。校验的对象越复杂,收益越明显。
57 |
58 | ```js
59 | // 校验一个嵌套对象
60 | const V3 = new Validator({
61 | person: {
62 | $fields: {
63 | height: { Required: true },
64 | age: { Required: true },
65 | weight: { Required: true }
66 | }
67 | }
68 | })
69 | console.log(V3.validate({
70 | person: {
71 | height: 175,
72 | age: 18
73 | }
74 | })) // 输出:{"person":{"weight":"weight is required"}}
75 |
76 | // 校验一个嵌套数组
77 | const V4 = new Validator({
78 | persons: {
79 | MinLength: 2,
80 | $fields: {
81 | name: { Required: true }
82 | }
83 | }
84 | })
85 | console.log(V4.validate({
86 | persons: []
87 | })) // 输出:{"persons":"Min length of persons is 2"}
88 | console.log(V4.validate({
89 | persons: [{},{name: "太君是我,别开枪"},{}] // 索引1的元素有name所以错误信息是null
90 | })) // 输出:{"persons":[{"name":"name is required"},null,{"name":"name is required"}]}
91 | ```
92 | * 也可以直接使用`Validator.validate`来校验。
93 |
94 | * `在这里,当待校验对象是一个数组的时候,返需要用填充null来弥补占位,而其它可以参考的库在这块支持相对较弱,所以,目前也没有一个比较好的Idea。`
95 |
96 | ## 如何自定义信息和校验规则
97 | 通常,虽然校验规则是一样的,但是提示信息可能不一样,或者在大部分情况下提示信息是一样的,但是校验规则可能略有不同。这个时候我们可以通过传入`message`和`validate`两个参数来针对性修改。
98 |
99 | ```js
100 | // 使用自定义消息覆盖规则默认消息
101 | const V5 = new Validator({
102 | field: {Required: true, message: '我是消息'}
103 | })5
104 | console.log(V5.validate({
105 | })) // 输出:{ field: '我是消息' }
106 |
107 | // 使用自定义规则覆盖默认规则
108 | const V6 = new Validator({
109 | field: {Required: true, validate(field, value, opts) {
110 | return false // 永远校验失败
111 | }}
112 | })
113 | console.log(V6.validate({
114 | field: "我有数据"
115 | })) // 输出:{ field: 'field is required' }
116 | ```
117 | * 也可以直接使用`Validator.validate`来校验。
118 |
119 | ## 关于自定义扩展
120 | 默认提供的校验规则往往很难满足所有业务场景,而且在具体的业务处理过程中,也希望有一些规则可以被反复的复用。因此`slime-validator`提供了插件的能力,一旦注册插件,即可在全局使用该规则。这就像`Vue`的`plugin`是一样的。只要`use`一个组件以后,在任何地方都可以直接使用了。
121 |
122 | ```js
123 | // 自定义校验规则插件
124 | Validator.usePlugin({
125 | tagName: 'IsNotRequired',
126 | message(field, value, opts) {
127 | // field为校验的字段名,value为当前字段的值,opts为附加信息,包括tagName,当前规则名,tagValue当前规则的值,root,校验对象,parent,当前校验值的上一级父对象。
128 | return `${field} 校验不通过`
129 | },
130 | validate(field, value, opts) {
131 | return false
132 | }
133 | }, true) // 通过第二个参数传入true可以覆盖目前系统中已经注册的同名校验规则。
134 |
135 | const V7 = new Validator({
136 | field: { IsNotRequired: true }
137 | })
138 | console.log(V7.validate({
139 | field: "我有数据"
140 | })) // 输出:{ field: 'field 校验不通过' }
141 | ```
142 | * 也可以直接使用`Validator.validate`来校验。
143 |
144 | ## 联动校验
145 | 根据不同的国家,选择不同的类型进行联动校验。
146 | ```js
147 | // type_rules.js
148 | import Validator from 'slime-validator';
149 |
150 | Validator.usePlugin({
151 | tagName: "CheckType", // 注册一个CheckType校验规则
152 | message(field, value, opts) {
153 | const { tagValue, parent } = opts; // 通过parent 我们可以很容易拿到依赖的父级对象。
154 | return `当前值为 ${value} 有效值为 ${tagValue[parent.country]}`
155 | },
156 | validate(field, value, opts) {
157 | const { tagValue, parent } = opts;
158 | return tagValue[parent.country].indexOf(value) > -1
159 | }
160 | })
161 |
162 | // TypeController.js
163 | const rules = {
164 | country: {
165 | Required: true,
166 | Enum: ["CN", "USA", "JP"], message: "国家内容类型不正确" // 国家字段只能是"CN", "USA", "JP"其中之一。
167 | },
168 | type: { Required: true, CheckType: {
169 | CN: [1, 3],
170 | USA: [1, 2, 3],
171 | JP: [2, 3]
172 | }}
173 | }
174 |
175 | const V = new Validator(rules);
176 |
177 | // CN
178 | console.log("CN", JSON.stringify(Validator.validate(rules, {
179 | country: "CN",
180 | type: 1
181 | })))
182 | console.log("CN", JSON.stringify(V.validate({
183 | country: "CN",
184 | type: 2
185 | })))
186 |
187 | // USA
188 | console.log("USA", JSON.stringify(Validator.validate(rules, {
189 | country: "USA",
190 | type: 1
191 | })))
192 | console.log("USA", JSON.stringify(V.validate({
193 | country: "USA",
194 | type: 2
195 | })))
196 |
197 | // JP
198 | console.log("JP", JSON.stringify(Validator.validate(rules, {
199 | country: "JP",
200 | type: 1
201 | })))
202 | console.log("JP", JSON.stringify(V.validate({
203 | country: "JP",
204 | type: 2
205 | })))
206 | ```
207 |
208 | * 如果使用`new Validator`的方式代码量会更少,所以还是推荐使用对象的方式使用。
209 | ```js
210 | $ CN null
211 | $ CN {"type":"当前值为 2 有效值为 1,3"}
212 | $ USA null
213 | $ USA null
214 | $ JP {"type":"当前值为 1 有效值为 2,3"}
215 | $ JP null
216 | ```
217 |
218 | ## 更多的内置校验规则
219 | ### Required 必填
220 | 进行必填校验,值为`true`或者`false`。
221 |
222 | ```js
223 | { Required: true }
224 | ```
225 |
226 | ### Enum 枚举校验
227 | 校验值是否为枚举范围内的值,值为包含基础数据类型的数组,内部是通过值比较实现的。
228 |
229 | ```js
230 | { Enum: [0, 1, 2, 3] }
231 | ```
232 |
233 | ### NotEmpty 非空校验
234 | 判断待校验对象是否为空,支持字符串,数组,对象的校验,值为`true`或者`false`。
235 |
236 | ```js
237 | { NotEmpty: true }
238 | ```
239 |
240 | ### MaxLength 和 MinLength
241 | 最大长度和最小长度校验,支持数组,字符串,值为数字。
242 |
243 | ```js
244 | { MinLength: 1, MaxLength: 10 }
245 | ```
246 |
247 | ### MaxNum 和 MinNum
248 | 最大数值和最小数值校验,值为数字。
249 |
250 | ```js
251 | { MinLength: 1, MaxLength: 10 }
252 | ```
253 |
254 | ### RegExp 正则校验
255 | 正则匹配校验,值为正则对象或者正则字符串。
256 |
257 | ```js
258 | { RegExp: /^a/g }
259 | { RegExp: '^a' }
260 | ```
261 |
262 | ### IsEmail 校验
263 | email校验,值为email地址字符串。
264 |
265 | ```js
266 | {
267 | field: { IsEmail: true }
268 | }
269 | ```
270 | ### IsURL 校验
271 | http|https 校验,值为url地址字符串。
272 |
273 | ```js
274 | {field: [{ IsURL: true }]}
275 | ```
276 |
--------------------------------------------------------------------------------
/demo/index.array.js:
--------------------------------------------------------------------------------
1 | const Validator = require('./../index');
2 |
3 | let ret = null
4 | try {
5 | const v = new Validator({
6 | field: {
7 | $fields: {
8 | field1: { Required: true },
9 | field2: [{ Required: true }, { $fields: [{ Enum: [1, 2, 3] }] }]
10 | }
11 | }
12 | })
13 | ret = v.validate({ field: [{ field1: null }, { field2: [4, 1, 9] }] })
14 | } finally {
15 | console.log(JSON.stringify(ret))
16 | }
17 |
18 | try {
19 | const v = new Validator({
20 | field: {
21 | $fields: { Required: true, MinNum: 200 }
22 | }
23 | })
24 | ret = v.validate({ field: [1, 2000, 2] })
25 | } finally {
26 | console.log(JSON.stringify(ret))
27 | }
28 |
29 | // 校验一个嵌套数组
30 | const V4 = new Validator({
31 | persons: {
32 | MinLength: 2,
33 | $fields: {
34 | name: { Required: true }
35 | }
36 | }
37 | })
38 | console.log(JSON.stringify(V4.validate({
39 | persons: []
40 | }))) // 输出:{"persons":"Min length of persons is 2"}
41 | console.log(JSON.stringify(V4.validate({
42 | persons: [{},{name: "太君是我,别开枪"},{}]
43 | }))) // 输出:{"persons":{"0":{"name":"name is required"},{"1":{"name":"name is required"}}}
44 |
45 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/demo/index.js:
--------------------------------------------------------------------------------
1 | const Validator = require('./../index.js');
2 |
3 | Validator.usePlugin({
4 | tagName: "AwardType",
5 | message(field, value, opts) {
6 | const { tagValue, parent } = opts;
7 | return `该类型奖励只允许在指定国家开放, 当前值为 ${value} 有效值为 ${tagValue[parent.country]}`
8 | },
9 | validate(field, value, opts) {
10 | const { tagValue, parent } = opts;
11 | return tagValue[parent.country].indexOf(value) > -1
12 | }
13 | })
14 |
15 | const rules = {
16 | country: { Required: true, Enum: ["BR", "MX", "JP"], message: "国家内容不合法" },
17 | award_type: { Required: true, AwardType: {
18 | BR: [1, 3],
19 | MX: [1, 2, 3],
20 | JP: [2, 3]
21 | }}
22 | }
23 |
24 | const V = new Validator(rules);
25 |
26 | // BR
27 | console.log("BR", JSON.stringify(Validator.validate(rules, {
28 | country: "BR",
29 | award_type: 1
30 | })))
31 | console.log("BR", JSON.stringify(V.validate({
32 | country: "BR",
33 | award_type: 2
34 | })))
35 |
36 | // MX
37 | console.log("MX", JSON.stringify(Validator.validate(rules, {
38 | country: "MX",
39 | award_type: 1
40 | })))
41 | console.log("MX", JSON.stringify(V.validate({
42 | country: "MX",
43 | award_type: 2
44 | })))
45 |
46 | // JP
47 | console.log("JP", JSON.stringify(Validator.validate(rules, {
48 | country: "JP",
49 | award_type: 1
50 | })))
51 | console.log("JP", JSON.stringify(V.validate({
52 | country: "JP",
53 | award_type: 2
54 | })))
--------------------------------------------------------------------------------
/dist/slime-validator.umd.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SlimeValidator=t():e.SlimeValidator=t()}(self,(function(){return e={45:function(e,t,n){var r=["message","validate","$fields"];function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){for(var n=0;n=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}function u(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var c=n(712),s=c.isArray,l=c.isObject,f=c.isString,g=c.isFunction,v={},d="Input";function m(){return!0}function p(e,t,n,a){var i=t,c=e;l(t)||(i=u({},d,i),c=u({},d,c));for(var b=Object.keys(c),h=b.length,y=null,x=0;x0&&(S=!0,N=u({},j,U))}else if(s(O)){for(var C=[],D=O.length,_=0;_0&&(S=!0),C.push(l(O[_])||null===B?B:B[j])}S&&(N=u({},j,C))}else console.warn("$fields for ".concat(j," will be ignored"))}S&&(j===d?y=N[j]:null===y?y=u({},j,N[j]):y[j]=N[j])}return y}var b=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(a(this,e),!l(t)&&!s(t))throw new Error("Schema must be an Object or Array");this.schema=t}var t,n;return t=e,(n=[{key:"validate",value:function(e){return p(this.schema,e,e,e)}}])&&i(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}();b.validate=function(e,t){if(!l(e)&&!s(e))throw new Error("Schema must be an Object or Array");return p(e,t,t,t)},b.usePlugin=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(!l(e))throw new Error("Parameter must be Object ant can not be empty");var n=e.tagName,r=e.message,a=e.validate;if(!f(n)||n.length<=0)throw new Error("tagName must be String and not empty.");if(!g(r)||!g(a))throw new Error("message and validate must be Function.");if(!t&&void 0!==v[n])throw new Error("Plugin [ ".concat(n," ] is existed"));void 0!==v[n]&&console.warn("Plugin [ ".concat(n," ] is existed and will be replaced")),v[n]={message:r,validate:a}},b.usePlugin(n(566)),b.usePlugin(n(82)),b.usePlugin(n(178)),b.usePlugin(n(680)),b.usePlugin(n(434)),b.usePlugin(n(563)),b.usePlugin(n(556)),b.usePlugin(n(466)),b.usePlugin(n(832)),b.usePlugin(n(153)),e.exports=b},178:function(e,t,n){var r=n(712).isArray;e.exports={tagName:"Enum",message:function(e,t,n){return"".concat(e," with value ").concat(t," is incorrect")},validate:function(e,t,n){var a=n.tagValue;return!t||!r(a)||a.indexOf(t)>-1}}},832:function(e,t,n){var r=n(712).isString,a=/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;e.exports={tagName:"IsEmail",message:function(e,t,n){return"".concat(e," is not a email")},validate:function(e,t,n){return!n.tagValue||!t||!!r(t)&&!!t.match(a)}}},153:function(e,t,n){var r=n(712).isString;e.exports={tagName:"IsURL",message:function(e,t,n){return"".concat(e," is not a url")},validate:function(e,t,n){var a=n.tagValue,i=n.allowDataUrl,o=void 0!==i&&i,u=n.allowLocal,c=void 0!==u&&u,s=n.schemes,l=void 0===s?["http","https"]:s;if(!a||!t)return!0;if(!r(t))return!1;var f="^(?:(?:"+l.join("|")+")://)(?:\\S+(?::\\S*)?@)?(?:",g="(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))";return c?g+="?":f+="(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})",f+="(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*"+g+")(?::\\d{2,5})?(?:[/?#]\\S*)?$",o&&(f="(?:"+f+")|(?:^data:(?:\\w+\\/[-+.\\w]+(?:;[\\w=]+)*)?(?:;base64)?,[A-Za-z0-9-_.!~\\*'();\\/?:@&=+$,%]*$)"),!!t.match(f)}}},680:function(e,t,n){var r=n(712),a=r.isArray,i=r.isString;e.exports={tagName:"MaxLength",message:function(e,t,n){var r=n.tagValue,a=void 0===r?0:r;return"Max length of ".concat(e," is ").concat(a)},validate:function(e,t,n){var r=n.tagValue,o=void 0===r?0:r;return!t||!!(a(t)&&t.length<=o)||!!(i(t)&&t.length<=o)}}},563:function(e,t,n){var r=n(712).isNumber;e.exports={tagName:"MaxNum",message:function(e,t,n){var r=n.tagValue,a=void 0===r?0:r;return"Max value of ".concat(e," is ").concat(a)},validate:function(e,t,n){var a=n.tagValue,i=void 0===a?0:a;return!t||!!(r(t)&&t<=i)}}},434:function(e,t,n){var r=n(712),a=r.isArray,i=r.isString;e.exports={tagName:"MinLength",message:function(e,t,n){var r=n.tagValue,a=void 0===r?0:r;return"Min length of ".concat(e," is ").concat(a)},validate:function(e,t,n){var r=n.tagValue,o=void 0===r?0:r;return!t&&""!==t||!!(a(t)&&t.length>=o)||!!(i(t)&&t.length>=o)}}},556:function(e,t,n){var r=n(712).isNumber;e.exports={tagName:"MinNum",message:function(e,t,n){var r=n.tagValue,a=void 0===r?0:r;return"Min value of ".concat(e," is ").concat(a)},validate:function(e,t,n){var a=n.tagValue,i=void 0===a?0:a;return!t||!!(r(t)&&t>=i)}}},82:function(e,t,n){var r=n(712),a=r.isArray,i=r.isString,o=r.isObject;e.exports={tagName:"NotEmpty",message:function(e,t,n){return"".concat(e," is empty")},validate:function(e,t,n){var r=n.tagValue;return!(void 0!==r&&r&&null!=t&&!(a(t)&&t.length>0)&&!(i(t)&&t.length>0)&&!(o(t)&&Object.keys(t).length>0))}}},466:function(e,t,n){var r=n(712),a=r.isString,i=r.isRegExp;e.exports={tagName:"RegExp",message:function(e,t,n){return"Test ".concat(e," is failed")},validate:function(e,t,n){var r=n.tagValue;return a(r)&&(r=new RegExp(r)),!i(r)||r.test(t)}}},566:function(e){e.exports={tagName:"Required",message:function(e,t,n){return"".concat(e," is required")},validate:function(e,t,n){var r=n.tagValue;return!(void 0!==r&&r)||null!=t}}},712:function(e){e.exports={isArray:function(e){return Array.isArray(e)},isObject:function(e){return"[object Object]"===Object.prototype.toString.call(e)},isString:function(e){return"[object String]"===Object.prototype.toString.call(e)},isFunction:function(e){return"[object Function]"===Object.prototype.toString.call(e)},isNumber:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&!isNaN(e)},isRegExp:function(e){return"[object RegExp]"===Object.prototype.toString.call(e)}}}},t={},function n(r){var a=t[r];if(void 0!==a)return a.exports;var i=t[r]={exports:{}};return e[r](i,i.exports,n),i.exports}(45);var e,t}));
--------------------------------------------------------------------------------
/docs/apis.md:
--------------------------------------------------------------------------------
1 | # Required 必填
2 | 进行必填校验,值为`true`或者`false`。
3 |
4 | ```js
5 | { Required: true }
6 | ```
7 |
8 | # Enum 枚举校验
9 | 校验值是否为枚举范围内的值,值为包含基础数据类型的数组,内部是通过值比较实现的。
10 |
11 | ```js
12 | { Enum: [0, 1, 2, 3] }
13 | ```
14 |
15 | # NotEmpty 非空校验
16 | 判断待校验对象是否为空,支持字符串,数组,对象的校验,值为`true`或者`false`。
17 |
18 | ```js
19 | { NotEmpty: true }
20 | ```
21 |
22 | # MaxLength 和 MinLength
23 | 最大长度和最小长度校验,支持数组,字符串,值为数字。
24 |
25 | ```js
26 | { MinLength: 1, MaxLength: 10 }
27 | ```
28 |
29 | # MaxNum 和 MinNum
30 | 最大数值和最小数值校验,值为数字。
31 |
32 | ```js
33 | { MinLength: 1, MaxLength: 10 }
34 | ```
35 |
36 | # RegExp 正则校验
37 | 正则匹配校验,值为正则对象或者正则字符串。
38 |
39 | ```js
40 | { RegExp: /^a/g }
41 | { RegExp: '^a' }
42 | ```
43 |
44 | # IsEmail 校验
45 | email校验,值为email地址字符串。
46 |
47 | ```js
48 | { field: { IsEmail: true } }
49 | ```
50 | # IsURL 校验
51 | http|https 校验,值为url地址字符串。
52 |
53 | ```js
54 | { field: [{ IsURL: true }] }
55 | ```
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # 更加简单易用的JS校验库
2 |
3 | `slime-validator`是一个结合目前主流校验库能力,集各家所长,并提供更加简单易用的校验能力。相比其他库可以节约校验相关代码量`20%`左右,在复杂参数校验的情况下甚至能达到`30%`以上`(这里是单纯的指代码字母总数量对比)`。并且支持字段`联动校验`,因此你也可以通过`slime-validator`更加复杂的校验功能,这是其它校验库很少支持的能力。尤其是`validatorjs`,往往需要自己重新封装。
4 |
5 | * 关于字段`联动校验`可以参看文末的例子。
6 |
7 | # 如何使用
8 | `slime-validator`可以应用于后端,也可以应用于前端,但是最初设计是针对后端设计的,因为前端绝大多数情况下都是直接使用`antd`或者`elementUI`,而它们都内置校验库,所以没有太大必要使用`slime-validator`。
9 |
10 | ## 安装
11 |
12 | ```js
13 | $ npm i slime-validator -save
14 | ```
15 |
16 | 当然也提供了通过`script`标签直接引入的方式,但是由于没有发布`CDN`。需要自行解决引入源的问题。
17 |
18 | ## 如何使用
19 | `slime-validator`有两种校验方法,一种是每次创建一个`Validator`对象。这样可以重复使用这个对象直接对参数进行预期规则的校验。另一种是通过`Validator.validate`函数,通过传入校验规则和待校验对象进行校验。和其他校验库用法类似。
20 |
21 | ```js
22 | import Validator from 'slime-validator'
23 |
24 | // 用法一
25 | const V1 = new Validator({ Required:true })
26 | console.log(V1.validate('Hello world')) // 输出: null
27 | console.log(V1.validate()) // 输出: Input is required
28 |
29 | // 用法二
30 | console.log(Validator.validate({ Required:true }, 'Hello world')) // 输出: null
31 | console.log(Validator.validate({ Required:true }, null)) // 输出: Input is required
32 | ```
33 |
34 | ## 如何校验一个对象参数
35 | 校验对象,是校验非常普遍的一个需求场景。`slime-validator`自然而然也必须支持这个能力。
36 |
37 | ```js
38 | import Validator from 'slime-validator'
39 |
40 | // 校验一个对象
41 | const V2 = new Validator({
42 | username: { Required: true, MinLength: 3 }, // 用户名必填,且不小于3个字符
43 | age: { Required: true, MinNum: 18 }, // 年龄必填,且需要大于18岁
44 | address: { MaxLength: 10 } // 地址选填,最大不超过10个字
45 | })
46 | console.log(V2.validate({
47 | username: 'ryouaki',
48 | age: 17
49 | })) // 输出:{ age: 'Min value of age is 18' }
50 | ```
51 | * 也可以直接使用`Validator.validate`来校验。
52 |
53 | ## 如何校验一个嵌套对象
54 | 在复杂的业务场景,我们需要校验的对象会非常复杂,这个时候,对于像`validatorjs`就要额外封装才能满足需要,而`async-validator`一样需要用一些额外字段去识别这是什么类型的待校验对象。
55 |
56 | 但是在`slime-validator`中,只需要一个`$fields`字段就完成了所以功能,在内部会自动识别对象还是数据还是对象加数组,因此开发者可以少书写很多代码。校验的对象越复杂,收益越明显。
57 |
58 | ```js
59 | // 校验一个嵌套对象
60 | const V3 = new Validator({
61 | person: {
62 | $fields: {
63 | height: { Required: true },
64 | age: { Required: true },
65 | weight: { Required: true }
66 | }
67 | }
68 | })
69 | console.log(V3.validate({
70 | person: {
71 | height: 175,
72 | age: 18
73 | }
74 | })) // 输出:{"person":{"weight":"weight is required"}}
75 |
76 | // 校验一个嵌套数组
77 | const V4 = new Validator({
78 | persons: {
79 | MinLength: 2,
80 | $fields: {
81 | name: { Required: true }
82 | }
83 | }
84 | })
85 | console.log(V4.validate({
86 | persons: []
87 | })) // 输出:{"persons":"Min length of persons is 2"}
88 | console.log(V4.validate({
89 | persons: [{},{name: "太君是我,别开枪"},{}] // 索引1的元素有name所以错误信息是null
90 | })) // 输出:{"persons":[{"name":"name is required"},null,{"name":"name is required"}]}
91 | ```
92 | * 也可以直接使用`Validator.validate`来校验。
93 |
94 | * `在这里,当待校验对象是一个数组的时候,返需要用填充null来弥补占位,而其它可以参考的库在这块支持相对较弱,所以,目前也没有一个比较好的Idea。`
95 |
96 | ## 如何自定义信息和校验规则
97 | 通常,虽然校验规则是一样的,但是提示信息可能不一样,或者在大部分情况下提示信息是一样的,但是校验规则可能略有不同。这个时候我们可以通过传入`message`和`validate`两个参数来针对性修改。
98 |
99 | ```js
100 | // 使用自定义消息覆盖规则默认消息
101 | const V5 = new Validator({
102 | field: {Required: true, message: '我是消息'}
103 | })5
104 | console.log(V5.validate({
105 | })) // 输出:{ field: '我是消息' }
106 |
107 | // 使用自定义规则覆盖默认规则
108 | const V6 = new Validator({
109 | field: {Required: true, validate(field, value, opts) {
110 | return false // 永远校验失败
111 | }}
112 | })
113 | console.log(V6.validate({
114 | field: "我有数据"
115 | })) // 输出:{ field: 'field is required' }
116 | ```
117 | * 也可以直接使用`Validator.validate`来校验。
118 |
119 | ## 关于自定义扩展
120 | 默认提供的校验规则往往很难满足所有业务场景,而且在具体的业务处理过程中,也希望有一些规则可以被反复的复用。因此`slime-validator`提供了插件的能力,一旦注册插件,即可在全局使用该规则。这就像`Vue`的`plugin`是一样的。只要`use`一个组件以后,在任何地方都可以直接使用了。
121 |
122 | ```js
123 | // 自定义校验规则插件
124 | Validator.usePlugin({
125 | tagName: 'IsNotRequired',
126 | message(field, value, opts) {
127 | // field为校验的字段名,value为当前字段的值,opts为附加信息,包括tagName,当前规则名,tagValue当前规则的值,root,校验对象,parent,当前校验值的上一级父对象。
128 | return `${field} 校验不通过`
129 | },
130 | validate(field, value, opts) {
131 | return false
132 | }
133 | }, true) // 通过第二个参数传入true可以覆盖目前系统中已经注册的同名校验规则。
134 |
135 | const V7 = new Validator({
136 | field: { IsNotRequired: true }
137 | })
138 | console.log(V7.validate({
139 | field: "我有数据"
140 | })) // 输出:{ field: 'field 校验不通过' }
141 | ```
142 | * 也可以直接使用`Validator.validate`来校验。
143 |
144 | ## 联动校验
145 | 根据不同的国家,选择不同的类型进行联动校验。
146 | ```js
147 | // type_rules.js
148 | import Validator from 'slime-validator';
149 |
150 | Validator.usePlugin({
151 | tagName: "CheckType", // 注册一个CheckType校验规则
152 | message(field, value, opts) {
153 | const { tagValue, parent } = opts; // 通过parent 我们可以很容易拿到依赖的父级对象。
154 | return `当前值为 ${value} 有效值为 ${tagValue[parent.country]}`
155 | },
156 | validate(field, value, opts) {
157 | const { tagValue, parent } = opts;
158 | return tagValue[parent.country].indexOf(value) > -1
159 | }
160 | })
161 |
162 | // TypeController.js
163 | const rules = {
164 | country: {
165 | Required: true,
166 | Enum: ["CN", "USA", "JP"], message: "国家内容类型不正确" // 国家字段只能是"CN", "USA", "JP"其中之一。
167 | },
168 | type: { Required: true, CheckType: {
169 | CN: [1, 3],
170 | USA: [1, 2, 3],
171 | JP: [2, 3]
172 | }}
173 | }
174 |
175 | const V = new Validator(rules);
176 |
177 | // CN
178 | console.log("CN", JSON.stringify(Validator.validate(rules, {
179 | country: "CN",
180 | type: 1
181 | })))
182 | console.log("CN", JSON.stringify(V.validate({
183 | country: "CN",
184 | type: 2
185 | })))
186 |
187 | // USA
188 | console.log("USA", JSON.stringify(Validator.validate(rules, {
189 | country: "USA",
190 | type: 1
191 | })))
192 | console.log("USA", JSON.stringify(V.validate({
193 | country: "USA",
194 | type: 2
195 | })))
196 |
197 | // JP
198 | console.log("JP", JSON.stringify(Validator.validate(rules, {
199 | country: "JP",
200 | type: 1
201 | })))
202 | console.log("JP", JSON.stringify(V.validate({
203 | country: "JP",
204 | type: 2
205 | })))
206 | ```
207 |
208 | * 如果使用`new Validator`的方式代码量会更少,所以还是推荐使用对象的方式使用。
209 | ```js
210 | $ CN null
211 | $ CN {"type":"当前值为 2 有效值为 1,3"}
212 | $ USA null
213 | $ USA null
214 | $ JP {"type":"当前值为 1 有效值为 2,3"}
215 | $ JP null
216 | ```
217 |
218 | ## 更多的内置校验规则
219 | 目前已经内置了一部分校验规则可以参考这里[内置校验API文档](apis.md)
220 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | /** Declaration file generated by dts-gen */
2 |
3 | export = slime_validator;
4 |
5 | declare class slime_validator {
6 | constructor(...args: any[]);
7 |
8 | validate(...args: any[]): void;
9 |
10 | static usePlugin(plugin: any, replace: any): void;
11 |
12 | static validate(schema: any, target: any): any;
13 |
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const {
2 | isArray,
3 | isObject,
4 | isString,
5 | isFunction
6 | } = require('./utils');
7 |
8 | const __$$plugins = {};
9 | const __$$DEFAULT = "Input";
10 |
11 | function defaultValidate () {
12 | return true
13 | }
14 |
15 | function doValidate(schema, target, root, parent) {
16 | let _target = target;
17 | let _schema = schema;
18 |
19 | if (!isObject(target)) {
20 | _target = {
21 | [__$$DEFAULT]: _target
22 | }
23 |
24 | _schema = {
25 | [__$$DEFAULT]: _schema
26 | }
27 | }
28 |
29 | const fields = Object.keys(_schema);
30 | const fieldNum = fields.length;
31 | let errors = null;
32 | // 之所以不用forEach,是因为原始for循环的内存效率和执行效率都更优。在大流量场景下有一定性能收益。
33 | for (let i = 0; i < fieldNum; i++) {
34 | const field = fields[i];
35 | const rules = _schema[field];
36 | const value = _target[field];
37 | let hasError = false;
38 | let err = null;
39 | parent = target;
40 |
41 | let rs = isArray(rules) ? rules : [rules];
42 | const ruleNum = rs.length;
43 | for (let j = 0; j < ruleNum && !hasError; j++) {
44 | const rule = rs[j];
45 |
46 | let {
47 | message,
48 | validate,
49 | $fields, // 我赌你的校验对象中没有 $fields 这个字段。如果有,我叫你爸爸。
50 | ...tags
51 | } = rule;
52 |
53 | // 优先处理非嵌套结构校验规则。
54 | const tagNames = Object.keys(tags);
55 | const tagNums = tagNames.length;
56 | for (let n = 0; n < tagNums && !hasError; n++) {
57 | const tagName = tagNames[n];
58 | const plugin = __$$plugins[tagName];
59 |
60 | if (!plugin) { // 插件没有注册
61 | throw new Error(`Plugin with tagName = ${tagName} is not registered!`);
62 | }
63 |
64 | const ef = validate || plugin.validate || defaultValidate;
65 | const options = {
66 | tagName,
67 | tagValue: tags[tagName],
68 | root,
69 | parent
70 | }
71 | if (ef(field, value, options) === false) {
72 | let msg = '';
73 | if (message && isFunction(message)) {
74 | msg = message(field, value, options);
75 | } else if (message && isString(message)) {
76 | msg = message
77 | } else {
78 | msg = plugin.message(field, value, options)
79 | }
80 |
81 | hasError = true
82 | err = {
83 | [field]: msg
84 | }
85 | }
86 | }
87 |
88 | // 嵌套校验规则。需要是先通过其它单项规则后如果没有校验问题的情况下才进行。
89 | // 比如我是一个数组,要求数组内必须大于一个元素。此时。如果数组为空,就没必要进行嵌套校验了
90 | if ($fields &&!hasError) {
91 | if (isObject(value)) {
92 | const e = doValidate($fields, value, root, target);
93 | if (isObject(e) && Object.keys(e).length > 0) {
94 | hasError = true;
95 | err = {
96 | [field]: e
97 | }
98 | }
99 | } else if (isArray(value)) {
100 | const errs = [];
101 | const len = value.length;
102 |
103 | for (let k = 0; k < len; k++) {
104 | let e = isObject(value[k]) ?
105 | doValidate($fields, value[k], root, target)
106 | : doValidate({[field]: $fields}, {[field]: value[k]}, root, target);
107 | if (Object.keys(e || {}).length > 0) {
108 | hasError = true;
109 | }
110 | errs.push((isObject(value[k]) || e === null) ? e : e[field]);
111 | }
112 |
113 | if (hasError) {
114 | err = {
115 | [field]: errs
116 | }
117 | }
118 | } else {
119 | console.warn(`$fields for ${field} will be ignored`);
120 | }
121 | }
122 | }
123 | if (hasError) {
124 | if (field === __$$DEFAULT) {
125 | errors = err[field]
126 | } else {
127 | errors === null ? errors = {
128 | [field]: err[field]
129 | } : errors[field] = err[field]
130 | }
131 | }
132 | }
133 |
134 | return errors;
135 | }
136 |
137 | class Validator {
138 | constructor(schema = {}) {
139 | if (!isObject(schema) && !isArray(schema)) {
140 | throw new Error('Schema must be an Object or Array');
141 | }
142 | this.schema = schema;
143 | }
144 |
145 | validate(target) {
146 | return doValidate(this.schema, target, target, target);
147 | }
148 | }
149 |
150 | Validator.validate = function (schema, target) {
151 | if (!isObject(schema) && !isArray(schema)) {
152 | throw new Error('Schema must be an Object or Array');
153 | }
154 | return doValidate(schema, target, target, target);
155 | }
156 |
157 | Validator.usePlugin = function (plugin, replace = false) {
158 | if (!isObject(plugin)) {
159 | throw new Error('Parameter must be Object ant can not be empty')
160 | }
161 |
162 | const {
163 | tagName,
164 | message,
165 | validate
166 | } = plugin;
167 |
168 | if (!isString(tagName) || tagName.length <= 0) {
169 | throw new Error('tagName must be String and not empty.')
170 | }
171 |
172 | if (!isFunction(message) || !isFunction(validate)) {
173 | throw new Error('message and validate must be Function.')
174 | }
175 |
176 | if (!replace && __$$plugins[tagName] !== undefined) {
177 | throw new Error(`Plugin [ ${tagName} ] is existed`)
178 | }
179 |
180 | if (__$$plugins[tagName] !== undefined) {
181 | console.warn(`Plugin [ ${tagName} ] is existed and will be replaced`)
182 | }
183 |
184 | __$$plugins[tagName] = {
185 | message,
186 | validate
187 | }
188 | }
189 |
190 | Validator.usePlugin(require('./packages/required'));
191 | Validator.usePlugin(require('./packages/not_empty'));
192 | Validator.usePlugin(require('./packages/enum'));
193 | Validator.usePlugin(require('./packages/max_length'));
194 | Validator.usePlugin(require('./packages/min_length'));
195 | Validator.usePlugin(require('./packages/max_num'));
196 | Validator.usePlugin(require('./packages/min_num'));
197 | Validator.usePlugin(require('./packages/regexp'));
198 | Validator.usePlugin(require('./packages/is_email'));
199 | Validator.usePlugin(require('./packages/is_url'));
200 |
201 | module.exports = Validator;
202 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slime-validator",
3 | "version": "1.1.1",
4 | "description": "slime-validator is a JavaScript library of validation based on `Plugin` system and make data validation be easy.",
5 | "main": "index.js",
6 | "module": "dist/slime-validator.umd.js",
7 | "types": "index.d.ts",
8 | "scripts": {
9 | "test": "jest",
10 | "dev": "jest --watch",
11 | "build": "webpack --config webpack.config.js"
12 | },
13 | "keywords": [
14 | "validator",
15 | "validate",
16 | "validation",
17 | "slime-validator"
18 | ],
19 | "files": [
20 | "index.js",
21 | "utils.js",
22 | "package.json",
23 | "LICENSE",
24 | "README.md",
25 | "packages",
26 | "dist",
27 | "index.d.ts"
28 | ],
29 | "author": "ryouaki(46517115@qq.com)",
30 | "license": "Apache",
31 | "devDependencies": {
32 | "@babel/preset-env": "^7.13.12",
33 | "babel-loader": "^8.2.2",
34 | "jest": "^26.6.3",
35 | "webpack": "^5.30.0",
36 | "webpack-cli": "^4.6.0"
37 | },
38 | "engines": {
39 | "node": ">=10.0.0"
40 | },
41 | "bugs": {
42 | "url": "https://github.com/didi/slime-validator/issues"
43 | },
44 | "repository": {
45 | "type": "git",
46 | "url": "git+https://github.com/didi/slime-validator.git"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/enum.js:
--------------------------------------------------------------------------------
1 | const {
2 | isArray
3 | } = require('../utils')
4 |
5 | module.exports = {
6 | tagName: "Enum",
7 | message (field, value, opts) {
8 | return `${field} with value ${value} is incorrect`
9 | },
10 | validate (field, value, opts) {
11 | const { tagValue } = opts;
12 |
13 | if (!value) {
14 | return true;
15 | }
16 |
17 | if (!isArray(tagValue)) {
18 | return true; // 枚举值列表不对
19 | }
20 |
21 | return tagValue.indexOf(value) > -1
22 | }
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/packages/is_email.js:
--------------------------------------------------------------------------------
1 | const {
2 | isString
3 | } = require('../utils')
4 |
5 | const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
6 |
7 | module.exports = {
8 | tagName: "IsEmail",
9 | message (field, value, opts) {
10 | return `${field} is not a email`
11 | },
12 | validate (field, value, opts) {
13 | const { tagValue } = opts;
14 | if (!tagValue || !value) {
15 | return true;
16 | }
17 |
18 | if (isString(value)) {
19 | return !!value.match(reg)
20 | }
21 |
22 | return false
23 | }
24 | }
--------------------------------------------------------------------------------
/packages/is_url.js:
--------------------------------------------------------------------------------
1 | const {
2 | isString
3 | } = require('../utils')
4 |
5 | module.exports = {
6 | tagName: "IsURL",
7 | message (field, value, opts) {
8 | return `${field} is not a url`
9 | },
10 | validate (field, value, opts) {
11 | // 暂不支持 下面属性
12 | // allowDataUrl 是否是数据地址 base64
13 | // allowLocal 是否是本地url
14 | // schemes
15 | const { tagValue, allowDataUrl = false, allowLocal = false, schemes = ['http', 'https'] } = opts;
16 | if (!tagValue || !value) {
17 | return true;
18 | }
19 |
20 | if (!isString(value)) {
21 | return false;
22 | }
23 | // https://gist.github.com/dperini/729294
24 |
25 | let regex =
26 | "^" +
27 | // protocol identifier
28 | "(?:(?:" + schemes.join("|") + ")://)" +
29 | // user:pass authentication
30 | "(?:\\S+(?::\\S*)?@)?" +
31 | "(?:";
32 |
33 | let tld = "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))";
34 |
35 | if (allowLocal) {
36 | tld += "?";
37 | } else {
38 | regex +=
39 | // IP address exclusion
40 | // private & local networks
41 | "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
42 | "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
43 | "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})";
44 | }
45 |
46 | regex +=
47 | // IP address dotted notation octets
48 | // excludes loopback network 0.0.0.0
49 | // excludes reserved space >= 224.0.0.0
50 | // excludes network & broacast addresses
51 | // (first & last IP address of each class)
52 | "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
53 | "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
54 | "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
55 | "|" +
56 | // host name
57 | "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
58 | // domain name
59 | "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
60 | tld +
61 | ")" +
62 | // port number
63 | "(?::\\d{2,5})?" +
64 | // resource path
65 | "(?:[/?#]\\S*)?" +
66 | "$";
67 |
68 | if (allowDataUrl) {
69 | // RFC 2397
70 | let mediaType = "\\w+\\/[-+.\\w]+(?:;[\\w=]+)*";
71 | let urlChar = "[A-Za-z0-9-_.!~\\*'();\\/?:@&=+$,%]*";
72 | let dataUrl = "data:(?:"+mediaType+")?(?:;base64)?,"+urlChar;
73 | regex = "(?:"+regex+")|(?:^"+dataUrl+"$)";
74 | }
75 |
76 | return !!value.match(regex)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/packages/max_length.js:
--------------------------------------------------------------------------------
1 | const {
2 | isArray,
3 | isString
4 | } = require('../utils')
5 |
6 | module.exports = {
7 | tagName: "MaxLength",
8 | message (field, value, opts) {
9 | const { tagValue = 0 } = opts;
10 | return `Max length of ${field} is ${tagValue}`
11 | },
12 | validate (field, value, opts) {
13 | const { tagValue = 0 } = opts;
14 | if (!value) {
15 | return true;
16 | }
17 | // Array
18 | if (isArray(value) && value.length <= tagValue) {
19 | return true;
20 | }
21 | // String
22 | if (isString(value) && value.length <= tagValue) {
23 | return true;
24 | }
25 |
26 | return false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/max_num.js:
--------------------------------------------------------------------------------
1 | const {
2 | isNumber
3 | } = require('../utils')
4 |
5 | module.exports = {
6 | tagName: "MaxNum",
7 | message (field, value, opts) {
8 | const { tagValue = 0 } = opts;
9 | return `Max value of ${field} is ${tagValue}`
10 | },
11 | validate (field, value, opts) {
12 | const { tagValue = 0 } = opts;
13 | if (!value) {
14 | return true;
15 | }
16 | if (isNumber(value) && value <= tagValue) {
17 | return true;
18 | }
19 |
20 | return false;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/min_length.js:
--------------------------------------------------------------------------------
1 | const {
2 | isArray,
3 | isString
4 | } = require('../utils')
5 |
6 | module.exports = {
7 | tagName: "MinLength",
8 | message (field, value, opts) {
9 | const { tagValue = 0 } = opts;
10 | return `Min length of ${field} is ${tagValue}`
11 | },
12 | validate (field, value, opts) {
13 | const { tagValue = 0 } = opts;
14 | if (!value && value !== '') {
15 | return true;
16 | }
17 | // Array
18 | if (isArray(value) && value.length >= tagValue) {
19 | return true;
20 | }
21 | // String
22 | if (isString(value) && value.length >= tagValue) {
23 | return true;
24 | }
25 |
26 | return false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/min_num.js:
--------------------------------------------------------------------------------
1 | const {
2 | isNumber
3 | } = require('../utils')
4 |
5 | module.exports = {
6 | tagName: "MinNum",
7 | message (field, value, opts) {
8 | const { tagValue = 0 } = opts;
9 | return `Min value of ${field} is ${tagValue}`
10 | },
11 | validate (field, value, opts) {
12 | const { tagValue = 0 } = opts;
13 |
14 | if (!value) {
15 | return true;
16 | }
17 | if (isNumber(value) && value >= tagValue) {
18 | return true;
19 | }
20 |
21 | return false;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/not_empty.js:
--------------------------------------------------------------------------------
1 | const {
2 | isArray,
3 | isString,
4 | isObject
5 | } = require('../utils')
6 |
7 | module.exports = {
8 | tagName: "NotEmpty",
9 | message (field, value, opts) {
10 | return `${field} is empty`
11 | },
12 | validate (field, value, opts) {
13 | const { tagValue = false } = opts;
14 | if (!tagValue || value === null || value === undefined) {
15 | return true;
16 | }
17 | // Array is empty
18 | if (isArray(value) && value.length > 0) {
19 | return true;
20 | }
21 | // String is empty
22 | if (isString(value) && value.length > 0) {
23 | return true;
24 | }
25 | // Object is empty
26 | if (isObject(value) && Object.keys(value).length > 0) {
27 | return true;
28 | }
29 |
30 | return false;
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/packages/regexp.js:
--------------------------------------------------------------------------------
1 | const {
2 | isString,
3 | isRegExp
4 | } = require('../utils')
5 |
6 | module.exports = {
7 | tagName: "RegExp",
8 | message (field, value, opts) {
9 | return `Test ${field} is failed`
10 | },
11 | validate (field, value, opts) {
12 | const { tagValue } = opts;
13 | let reg = tagValue;
14 |
15 | if (isString(reg)) {
16 | reg = new RegExp(reg);
17 | }
18 |
19 | if (isRegExp(reg)) {
20 | return reg.test(value);
21 | }
22 |
23 | return true
24 | }
25 | }
--------------------------------------------------------------------------------
/packages/required.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tagName: "Required",
3 | message (field, value, opts) {
4 | return `${field} is required`
5 | },
6 | validate (field, value, opts) {
7 | const { tagValue = false } = opts;
8 | if (!tagValue) {
9 | return true;
10 | }
11 | if (value === null || value === undefined) {
12 | return false;
13 | }
14 | return true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/default.test.js:
--------------------------------------------------------------------------------
1 | const Validator = require('./../index');
2 |
3 | describe("默认Rule校验", () => {
4 | it("Required Success", () => {
5 | let ret = null
6 | try {
7 | const v = new Validator({
8 | field: [{ Required: true }]
9 | })
10 | ret = v.validate({field: "ok"})
11 | } finally {
12 | expect(JSON.stringify(ret))
13 | .toBe(JSON.stringify(null));
14 | }
15 |
16 | ret = Validator.validate({
17 | field: [{ Required: true }]
18 | }, {field: "ok"});
19 |
20 | expect(JSON.stringify(ret))
21 | .toBe(JSON.stringify(null));
22 | });
23 |
24 | it("Required Failed", () => {
25 | let ret = null
26 | try {
27 | const v = new Validator({
28 | field: [{ Required: true }]
29 | })
30 | ret = v.validate({})
31 | } finally {
32 | expect(JSON.stringify(ret))
33 | .toBe(JSON.stringify({
34 | "field": "field is required"
35 | }));
36 | }
37 |
38 | ret = Validator.validate({
39 | field: [{ Required: true }]
40 | }, {});
41 |
42 | expect(JSON.stringify(ret))
43 | .toBe(JSON.stringify({
44 | "field": "field is required"
45 | }));
46 | });
47 |
48 | it("Enum Success", () => {
49 | let ret = null
50 | try {
51 | const v = new Validator({
52 | field: [{ Enum: [1, 2, 3]}]
53 | })
54 | ret = v.validate({field: 1})
55 | } finally {
56 | expect(JSON.stringify(ret))
57 | .toBe(JSON.stringify(null));
58 | }
59 |
60 | ret = Validator.validate({
61 | field: [{ Enum: [1, 2, 3]}]
62 | }, {field: 1});
63 |
64 | expect(JSON.stringify(ret))
65 | .toBe(JSON.stringify(null));
66 | });
67 |
68 | it("Enum Failed", () => {
69 | let ret = null
70 | try {
71 | const v = new Validator({
72 | field: [{ Enum: [1, 2, 3]}]
73 | })
74 | ret = v.validate({field: 1111})
75 | } finally {
76 | expect(JSON.stringify(ret))
77 | .toBe(JSON.stringify({
78 | "field": "field with value 1111 is incorrect"
79 | }));
80 | }
81 |
82 | ret = Validator.validate({
83 | field: [{ Enum: [1, 2, 3]}]
84 | }, {field: 1111});
85 |
86 | expect(JSON.stringify(ret))
87 | .toBe(JSON.stringify({
88 | "field": "field with value 1111 is incorrect"
89 | }));
90 | });
91 |
92 | it("NotEmpty String Success", () => {
93 | let ret = null
94 | try {
95 | const v = new Validator({
96 | field: [{ NotEmpty: true }]
97 | })
98 | ret = v.validate({field: 'test'})
99 | } finally {
100 | expect(JSON.stringify(ret))
101 | .toBe(JSON.stringify(null));
102 | }
103 |
104 | ret = Validator.validate({
105 | field: [{ NotEmpty: true }]
106 | }, {field: 'test'});
107 |
108 | expect(JSON.stringify(ret))
109 | .toBe(JSON.stringify(null));
110 | });
111 |
112 | it("NotEmpty String Failed", () => {
113 | let ret = null
114 | try {
115 | const v = new Validator({
116 | field: [{ NotEmpty: true }]
117 | })
118 | ret = v.validate({field: ''})
119 | } finally {
120 | expect(JSON.stringify(ret))
121 | .toBe(JSON.stringify({"field":"field is empty"}));
122 | }
123 |
124 | ret = Validator.validate({
125 | field: [{ NotEmpty: true }]
126 | }, {field: ''});
127 |
128 | expect(JSON.stringify(ret))
129 | .toBe(JSON.stringify({"field":"field is empty"}));
130 | });
131 |
132 | it("NotEmpty Array Success", () => {
133 | let ret = null
134 | try {
135 | const v = new Validator({
136 | field: [{ NotEmpty: true }]
137 | })
138 | ret = v.validate({field: [1]})
139 | } finally {
140 | expect(JSON.stringify(ret))
141 | .toBe(JSON.stringify(null));
142 | }
143 |
144 | ret = Validator.validate({
145 | field: [{ NotEmpty: true }]
146 | }, {field: [1]});
147 |
148 | expect(JSON.stringify(ret))
149 | .toBe(JSON.stringify(null));
150 | });
151 |
152 | it("NotEmpty Array Failed", () => {
153 | let ret = null
154 | try {
155 | const v = new Validator({
156 | field: [{ NotEmpty: true }]
157 | })
158 | ret = v.validate({field: []})
159 | } finally {
160 | expect(JSON.stringify(ret))
161 | .toBe(JSON.stringify({"field":"field is empty"}));
162 | }
163 |
164 | ret = Validator.validate({
165 | field: [{ NotEmpty: true }]
166 | }, {field: []});
167 |
168 | expect(JSON.stringify(ret))
169 | .toBe(JSON.stringify({"field":"field is empty"}));
170 | });
171 |
172 | it("NotEmpty Object Success", () => {
173 | let ret = null
174 | try {
175 | const v = new Validator({
176 | field: [{ NotEmpty: true }]
177 | })
178 | ret = v.validate({field: {a : 1}})
179 | } finally {
180 | expect(JSON.stringify(ret))
181 | .toBe(JSON.stringify(null));
182 | }
183 |
184 | ret = Validator.validate({
185 | field: [{ NotEmpty: true }]
186 | }, {field: {a : 1}});
187 |
188 | expect(JSON.stringify(ret))
189 | .toBe(JSON.stringify(null));
190 | });
191 |
192 | it("NotEmpty Object Failed", () => {
193 | let ret = null
194 | try {
195 | const v = new Validator({
196 | field: [{ NotEmpty: true }]
197 | })
198 | ret = v.validate({field: {}})
199 | } finally {
200 | expect(JSON.stringify(ret))
201 | .toBe(JSON.stringify({"field":"field is empty"}));
202 | }
203 |
204 | ret = Validator.validate({
205 | field: [{ NotEmpty: true }]
206 | }, {field: {}});
207 |
208 | expect(JSON.stringify(ret))
209 | .toBe(JSON.stringify({"field":"field is empty"}));
210 | });
211 |
212 | it("MaxLength String Success", () => {
213 | let ret = null
214 | try {
215 | const v = new Validator({
216 | field: [{ MaxLength: 10 }]
217 | })
218 | ret = v.validate({field: '111'})
219 | } finally {
220 | expect(JSON.stringify(ret))
221 | .toBe(JSON.stringify(null));
222 | }
223 |
224 | ret = Validator.validate({
225 | field: [{ NotEmpty: true }]
226 | }, {field: '111'});
227 |
228 | expect(JSON.stringify(ret))
229 | .toBe(JSON.stringify(null));
230 | });
231 |
232 | it("MaxLength String Failed", () => {
233 | let ret = null
234 | try {
235 | const v = new Validator({
236 | field: [{ MaxLength: 10 }]
237 | })
238 | ret = v.validate({field: '01234567890'})
239 | } finally {
240 | expect(JSON.stringify(ret))
241 | .toBe(JSON.stringify({"field":"Max length of field is 10"}));
242 | }
243 |
244 | ret = Validator.validate({
245 | field: [{ MaxLength: 10 }]
246 | }, {field: '01234567890'});
247 |
248 | expect(JSON.stringify(ret))
249 | .toBe(JSON.stringify({"field":"Max length of field is 10"}));
250 | });
251 |
252 | it("MaxLength Array Success", () => {
253 | let ret = null
254 | try {
255 | const v = new Validator({
256 | field: [{ MaxLength: 10 }]
257 | })
258 | ret = v.validate({field: [1, 2, 3]})
259 | } finally {
260 | expect(JSON.stringify(ret))
261 | .toBe(JSON.stringify(null));
262 | }
263 |
264 | ret = Validator.validate({
265 | field: [{ MaxLength: 10 }]
266 | }, {field: [1, 2, 3]});
267 |
268 | expect(JSON.stringify(ret))
269 | .toBe(JSON.stringify(null));
270 | });
271 |
272 | it("MaxLength Array Failed", () => {
273 | let ret = null
274 | try {
275 | const v = new Validator({
276 | field: [{ MaxLength: 10 }]
277 | })
278 | ret = v.validate({field: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]})
279 | } finally {
280 | expect(JSON.stringify(ret))
281 | .toBe(JSON.stringify({"field":"Max length of field is 10"}));
282 | }
283 |
284 | ret = Validator.validate({
285 | field: [{ MaxLength: 10 }]
286 | }, {field: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]});
287 |
288 | expect(JSON.stringify(ret))
289 | .toBe(JSON.stringify({"field":"Max length of field is 10"}));
290 | });
291 |
292 | it("MinLength String Success", () => {
293 | let ret = null
294 | try {
295 | const v = new Validator({
296 | field: [{ MinLength: 10 }]
297 | })
298 | ret = v.validate({field: '01234567890'})
299 | } finally {
300 | expect(JSON.stringify(ret))
301 | .toBe(JSON.stringify(null));
302 | }
303 |
304 | ret = Validator.validate({
305 | field: [{ MinLength: 10 }]
306 | }, {field: '01234567890'});
307 |
308 | expect(JSON.stringify(ret))
309 | .toBe(JSON.stringify(null));
310 | });
311 |
312 | it("MinLength String Failed", () => {
313 | let ret = null
314 | try {
315 | const v = new Validator({
316 | field: [{ MinLength: 10 }]
317 | })
318 | ret = v.validate({field: '0'})
319 | } finally {
320 | expect(JSON.stringify(ret))
321 | .toBe(JSON.stringify({"field":"Min length of field is 10"}));
322 | }
323 |
324 | ret = Validator.validate({
325 | field: [{ MinLength: 10 }]
326 | }, {field: '0'});
327 |
328 | expect(JSON.stringify(ret))
329 | .toBe(JSON.stringify({"field":"Min length of field is 10"}));
330 | });
331 |
332 | it("MinLength Array Success", () => {
333 | let ret = null
334 | try {
335 | const v = new Validator({
336 | field: [{ MinLength: 10 }]
337 | })
338 | ret = v.validate({field: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]})
339 | } finally {
340 | expect(JSON.stringify(ret))
341 | .toBe(JSON.stringify(null));
342 | }
343 |
344 | ret = Validator.validate({
345 | field: [{ MinLength: 10 }]
346 | }, {field: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]});
347 |
348 | expect(JSON.stringify(ret))
349 | .toBe(JSON.stringify(null));
350 | });
351 |
352 | it("MinLength Array Failed", () => {
353 | let ret = null
354 | try {
355 | const v = new Validator({
356 | field: [{ MinLength: 10 }]
357 | })
358 | ret = v.validate({field: [1, 2, 3]})
359 | } finally {
360 | expect(JSON.stringify(ret))
361 | .toBe(JSON.stringify({"field":"Min length of field is 10"}));
362 | }
363 |
364 | ret = Validator.validate({
365 | field: [{ MinLength: 10 }]
366 | }, {field: [1, 2, 3, ]});
367 |
368 | expect(JSON.stringify(ret))
369 | .toBe(JSON.stringify({"field":"Min length of field is 10"}));
370 | });
371 |
372 | it("MaxNum Success", () => {
373 | let ret = null
374 | try {
375 | const v = new Validator({
376 | field: [{ MaxNum: 10 }]
377 | })
378 | ret = v.validate({field: 9})
379 | } finally {
380 | expect(JSON.stringify(ret))
381 | .toBe(JSON.stringify(null));
382 | }
383 |
384 | ret = Validator.validate({
385 | field: [{ MaxNum: 10 }]
386 | }, {field: 9});
387 |
388 | expect(JSON.stringify(ret))
389 | .toBe(JSON.stringify(null));
390 | });
391 |
392 | it("MaxNum Failed", () => {
393 | let ret = null
394 | try {
395 | const v = new Validator({
396 | field: [{ MaxNum: 10 }]
397 | })
398 | ret = v.validate({field: 11})
399 | } finally {
400 | expect(JSON.stringify(ret))
401 | .toBe(JSON.stringify({"field":"Max value of field is 10"}));
402 | }
403 |
404 | ret = Validator.validate({
405 | field: [{ MaxNum: 10 }]
406 | }, {field: 11});
407 |
408 | expect(JSON.stringify(ret))
409 | .toBe(JSON.stringify({"field":"Max value of field is 10"}));
410 | });
411 |
412 | it("MinNum Success", () => {
413 | let ret = null
414 | try {
415 | const v = new Validator({
416 | field: [{ MinNum: 10 }]
417 | })
418 | ret = v.validate({field: 19})
419 | } finally {
420 | expect(JSON.stringify(ret))
421 | .toBe(JSON.stringify(null));
422 | }
423 |
424 | ret = Validator.validate({
425 | field: [{ MinNum: 10 }]
426 | }, {field: 19});
427 |
428 | expect(JSON.stringify(ret))
429 | .toBe(JSON.stringify(null));
430 | });
431 |
432 | it("MinNum Failed", () => {
433 | let ret = null
434 | try {
435 | const v = new Validator({
436 | field: [{ MinNum: 10 }]
437 | })
438 | ret = v.validate({field: 6})
439 | } finally {
440 | expect(JSON.stringify(ret))
441 | .toBe(JSON.stringify({"field":"Min value of field is 10"}));
442 | }
443 |
444 | ret = Validator.validate({
445 | field: [{ MinNum: 10 }]
446 | }, {field: 6});
447 |
448 | expect(JSON.stringify(ret))
449 | .toBe(JSON.stringify({"field":"Min value of field is 10"}));
450 | });
451 |
452 | it("RegExp Success", () => {
453 | let ret = null
454 | try {
455 | const v = new Validator({
456 | field: [{ RegExp: /^a/g }]
457 | })
458 | ret = v.validate({field: 'atest'})
459 | } finally {
460 | expect(JSON.stringify(ret))
461 | .toBe(JSON.stringify(null));
462 | }
463 |
464 | ret = Validator.validate({
465 | field: [{ RegExp: /^a/g }]
466 | }, {field: 'atest'});
467 |
468 | expect(JSON.stringify(ret))
469 | .toBe(JSON.stringify(null));
470 | });
471 |
472 | it("RegExp Success", () => {
473 | let ret = null
474 | try {
475 | const v = new Validator({
476 | field: [{ RegExp: '^a' }]
477 | })
478 | ret = v.validate({field: 'atest'})
479 | } finally {
480 | expect(JSON.stringify(ret))
481 | .toBe(JSON.stringify(null));
482 | }
483 |
484 | ret = Validator.validate({
485 | field: [{ RegExp: '^a' }]
486 | }, {field: 'atest'});
487 |
488 | expect(JSON.stringify(ret))
489 | .toBe(JSON.stringify(null));
490 | });
491 |
492 | it("RegExp Failed", () => {
493 | let ret = null
494 | try {
495 | const v = new Validator({
496 | field: [{ RegExp: /^a/g }]
497 | })
498 | ret = v.validate({field: 'test'})
499 | } finally {
500 | expect(JSON.stringify(ret))
501 | .toBe(JSON.stringify({"field":"Test field is failed"}));
502 | }
503 |
504 | ret = Validator.validate({
505 | field: [{ RegExp: '^a' }]
506 | }, {field: 'test'});
507 |
508 | expect(JSON.stringify(ret))
509 | .toBe(JSON.stringify({"field":"Test field is failed"}));
510 | });
511 |
512 | it("IsEmail Success", () => {
513 | let ret = null
514 | try {
515 | const v = new Validator({
516 | field: { IsEmail: true }
517 | })
518 | ret = v.validate({field: 'benmo1602@gmail.com'})
519 | } finally {
520 | expect(JSON.stringify(ret)).toBe(JSON.stringify(null));
521 | }
522 | ret = Validator.validate({field: [{ IsEmail: true }]}, {field: 'benmo1602@gmail.com'});
523 |
524 | expect(JSON.stringify(ret))
525 | .toBe(JSON.stringify(null));
526 | });
527 |
528 | it("IsEmail Failed", () => {
529 | let ret = null
530 | try {
531 | const v = new Validator({
532 | field: { IsEmail: true }
533 | })
534 | ret = v.validate({field: 'benmo1602.com'})
535 | } finally {
536 | expect(JSON.stringify(ret)).toBe(JSON.stringify({field: 'field is not a email'}));
537 | }
538 | ret = Validator.validate({field: [{ IsEmail: true }]}, {field: 'benmo1602.com'});
539 |
540 | expect(JSON.stringify(ret)).toBe(JSON.stringify({field: 'field is not a email'}));
541 | });
542 |
543 | it("IsURL Success", () => {
544 | let ret = null
545 | try {
546 | const v = new Validator({
547 | field: { IsURL: true }
548 | })
549 | ret = v.validate({field: 'https://gist.github.com/dperini/729294'})
550 | } finally {
551 | expect(JSON.stringify(ret)).toBe(JSON.stringify(null));
552 | }
553 | ret = Validator.validate({field: [{ IsURL: true }]}, {field: 'https://gist.github.com/dperini/729294'});
554 |
555 | expect(JSON.stringify(ret))
556 | .toBe(JSON.stringify(null));
557 | });
558 |
559 | it("IsURL Failed", () => {
560 | let ret = null
561 | try {
562 | const v = new Validator({
563 | field: { IsURL: true }
564 | })
565 | ret = v.validate({field: 'gist.github.com/dperini/729294'})
566 | } finally {
567 | expect(JSON.stringify(ret)).toBe(JSON.stringify({field: 'field is not a url'}));
568 | }
569 | ret = Validator.validate({field: [{ IsURL: true }]}, {field: 'gist.github.com/dperini/729294'});
570 | expect(JSON.stringify(ret)).toBe(JSON.stringify({field: 'field is not a url'}));
571 | });
572 | })
573 |
--------------------------------------------------------------------------------
/test/plugin.test.js:
--------------------------------------------------------------------------------
1 | const Validator = require('./../index');
2 |
3 | describe("插件校验", () => {
4 | it("usePlugin校验,空参数", () => {
5 | let ret = null
6 | try {
7 | ret = Validator.usePlugin()
8 | } catch (err) {
9 | expect(err.message).toBe('Parameter must be Object ant can not be empty');
10 | } finally {
11 | expect(ret).toBe(null);
12 | }
13 | });
14 |
15 | it("usePlugin校验,非法参数 []", () => {
16 | let ret = null
17 | try {
18 | ret = Validator.usePlugin([])
19 | } catch (err) {
20 | expect(err.message).toBe('Parameter must be Object ant can not be empty');
21 | } finally {
22 | expect(ret).toBe(null);
23 | }
24 | });
25 |
26 | it("usePlugin校验,非法参数 Date", () => {
27 | let ret = null
28 | try {
29 | ret = Validator.usePlugin(new Date())
30 | } catch (err) {
31 | expect(err.message).toBe('Parameter must be Object ant can not be empty');
32 | } finally {
33 | expect(ret).toBe(null);
34 | }
35 | });
36 |
37 | it("usePlugin校验,非法参数 ''", () => {
38 | let ret = null
39 | try {
40 | ret = Validator.usePlugin("")
41 | } catch (err) {
42 | expect(err.message).toBe('Parameter must be Object ant can not be empty');
43 | } finally {
44 | expect(ret).toBe(null);
45 | }
46 | });
47 |
48 | it("usePlugin校验,非法参数 缺失tagName", () => {
49 | let ret = null
50 | try {
51 | ret = Validator.usePlugin({
52 |
53 | })
54 | } catch (err) {
55 | expect(err.message).toBe('tagName must be String and not empty.');
56 | } finally {
57 | expect(ret).toBe(null);
58 | }
59 | });
60 |
61 | it("usePlugin校验,非法参数 缺失message", () => {
62 | let ret = null
63 | try {
64 | ret = Validator.usePlugin({
65 | tagName: "jest"
66 | })
67 | } catch (err) {
68 | expect(err.message).toBe('message and validate must be Function.');
69 | } finally {
70 | expect(ret).toBe(null);
71 | }
72 | });
73 |
74 | it("usePlugin校验,非法参数 message", () => {
75 | let ret = null
76 | try {
77 | ret = Validator.usePlugin({
78 | tagName: "jest",
79 | message: ""
80 | })
81 | } catch (err) {
82 | expect(err.message).toBe('message and validate must be Function.');
83 | } finally {
84 | expect(ret).toBe(null);
85 | }
86 | });
87 |
88 | it("usePlugin校验,非法参数 缺失validate", () => {
89 | let ret = null
90 | try {
91 | ret = Validator.usePlugin({
92 | tagName: "jest",
93 | message() {}
94 | })
95 | } catch (err) {
96 | expect(err.message).toBe('message and validate must be Function.');
97 | } finally {
98 | expect(ret).toBe(null);
99 | }
100 | });
101 |
102 | it("usePlugin校验,非法参数 validate", () => {
103 | let ret = null
104 | try {
105 | ret = Validator.usePlugin({
106 | tagName: "jest",
107 | message() {},
108 | validate: ""
109 | })
110 | } catch (err) {
111 | expect(err.message).toBe('message and validate must be Function.');
112 | } finally {
113 | expect(ret).toBe(null);
114 | }
115 | });
116 |
117 | it("usePlugin校验,All right", () => {
118 | let ret = null
119 | try {
120 | ret = Validator.usePlugin({
121 | tagName: "jest",
122 | message() {},
123 | validate() {}
124 | })
125 | } finally {
126 | expect(ret).toBe(undefined);
127 | }
128 | });
129 | });
130 |
--------------------------------------------------------------------------------
/test/rule.test.js:
--------------------------------------------------------------------------------
1 | const Validator = require('./../index');
2 |
3 | describe("Rule校验", () => {
4 | it("tag没有注册", () => {
5 | let ret = null
6 | try {
7 | const v = new Validator({
8 | field: [{ required: true }]
9 | })
10 | ret = v.validate({})
11 | } catch (e) {
12 | expect(e.message)
13 | .toBe('Plugin with tagName = required is not registered!');
14 | }
15 | });
16 |
17 | it("一个rule中一个tag", () => {
18 | let ret = null
19 | try {
20 | const v = new Validator({
21 | field: [{ Required: true }]
22 | })
23 | ret = v.validate({})
24 | } finally {
25 | expect(JSON.stringify(ret))
26 | .toBe(JSON.stringify({
27 | "field": "field is required"
28 | }));
29 | }
30 | });
31 |
32 | it("单一值校验", () => {
33 | let ret = null
34 | try {
35 | const v = new Validator({ Enum: [true, false] })
36 | ret = v.validate(1)
37 | } finally {
38 | expect(JSON.stringify(ret))
39 | .toBe(JSON.stringify("Input with value 1 is incorrect"));
40 | }
41 | });
42 |
43 | it("一个rule中多个tag", () => {
44 | let ret1 = null
45 | let ret2 = null
46 | try {
47 | const v = new Validator({
48 | field: [{ Required: true, NotEmpty: true }]
49 | })
50 | ret1 = v.validate({})
51 | ret2 = v.validate({ field: '' })
52 | } finally {
53 | expect(JSON.stringify(ret1))
54 | .toBe(JSON.stringify({
55 | "field": "field is required"
56 | }));
57 | expect(JSON.stringify(ret2))
58 | .toBe(JSON.stringify({
59 | "field": "field is empty"
60 | }));
61 | }
62 | });
63 |
64 | it("多个rule中多个tag", () => {
65 | let ret1 = null
66 | let ret2 = null
67 | try {
68 | const v = new Validator({
69 | field: [{ Required: true }, { NotEmpty: true }]
70 | })
71 | ret1 = v.validate({})
72 | ret2 = v.validate({ field: '' })
73 | } finally {
74 | expect(JSON.stringify(ret1))
75 | .toBe(JSON.stringify({
76 | "field": "field is required"
77 | }));
78 | expect(JSON.stringify(ret2))
79 | .toBe(JSON.stringify({
80 | "field": "field is empty"
81 | }));
82 | }
83 | });
84 |
85 | it("一个rule中多个tag, 自定义覆盖消息", () => {
86 | let ret1 = null
87 | let ret2 = null
88 | try {
89 | const v = new Validator({
90 | field: [{ Required: true, NotEmpty: true, message: "覆盖消息" }]
91 | })
92 | ret1 = v.validate({})
93 | ret2 = v.validate({ field: '' })
94 | } finally {
95 | expect(JSON.stringify(ret1))
96 | .toBe(JSON.stringify({
97 | "field": "覆盖消息"
98 | }));
99 | expect(JSON.stringify(ret2))
100 | .toBe(JSON.stringify({
101 | "field": "覆盖消息"
102 | }));
103 | }
104 | });
105 |
106 | it("自定义规则覆盖消息", () => {
107 | let ret = null
108 | try {
109 | const v = new Validator({
110 | field: [{ Required: true, validate() { return false } }]
111 | })
112 | ret = v.validate({ field: '' })
113 | } finally {
114 | expect(JSON.stringify(ret))
115 | .toBe(JSON.stringify({
116 | "field": "field is required"
117 | }));
118 | }
119 | });
120 |
121 | it("嵌套对象,一层属性", () => {
122 | let ret = null
123 | try {
124 | const v = new Validator({
125 | field: [{
126 | Required: true, $fields: {
127 | field1: [{ Required: true }]
128 | }
129 | }]
130 | })
131 | ret = v.validate({ field: {} })
132 | } finally {
133 | expect(JSON.stringify(ret))
134 | .toBe(JSON.stringify({
135 | "field": { field1: 'field1 is required' }
136 | }));
137 | }
138 | });
139 |
140 | it("嵌套对象,一层属性多属性", () => {
141 | let ret = null
142 | try {
143 | const v = new Validator({
144 | field: [{
145 | Required: true, $fields: {
146 | field1: [{ Required: true }],
147 | field2: [{ Required: true }]
148 | }
149 | }]
150 | })
151 | ret = v.validate({ field: {} })
152 | } finally {
153 | expect(JSON.stringify(ret))
154 | .toBe(JSON.stringify({
155 | "field": {
156 | field1: 'field1 is required',
157 | field2: 'field2 is required'
158 | }
159 | }));
160 | }
161 | });
162 |
163 | it("嵌套对象,二层属性", () => {
164 | let ret = null
165 | try {
166 | const v = new Validator({
167 | field: [{
168 | Required: true, $fields: {
169 | field1: [{
170 | Required: true, $fields: {
171 | field2: [{ Required: true }]
172 | }
173 | }]
174 | }
175 | }]
176 | })
177 | ret = v.validate({ field: { field1: {} } })
178 | } finally {
179 | expect(JSON.stringify(ret))
180 | .toBe(JSON.stringify({
181 | "field": { field1: { field2: 'field2 is required' } }
182 | }));
183 | }
184 | });
185 |
186 | it("嵌套对象,二层属性多属性", () => {
187 | let ret = null
188 | try {
189 | const v = new Validator({
190 | field: [{
191 | Required: true, $fields: {
192 | field1: [{
193 | Required: true, $fields: {
194 | field2: [{ Required: true }]
195 | }
196 | }],
197 | field2: [{
198 | Required: true, $fields: {
199 | field2: [{ Required: true }]
200 | }
201 | }]
202 | }
203 | }]
204 | })
205 | ret = v.validate({ field: { field1: {} } })
206 | } finally {
207 | expect(JSON.stringify(ret))
208 | .toBe(JSON.stringify({
209 | "field": {
210 | field1: {
211 | field2: 'field2 is required'
212 | },
213 | field2: 'field2 is required'
214 | }
215 | }));
216 | }
217 | });
218 |
219 | it("嵌套数组 基本元素数组 多tag", () => {
220 | let ret = null
221 | try {
222 | const v = new Validator({
223 | field: [{ $fields: { Required: true, Enum: [1, 2, 3] } }]
224 | })
225 | ret = v.validate({ field: [4, 1, undefined] })
226 | } finally {
227 | expect(JSON.stringify(ret))
228 | .toBe(JSON.stringify({
229 | "field": ["field with value 4 is incorrect",null,"field is required"]
230 | }));
231 | }
232 | });
233 |
234 | it("嵌套数组 复合元素数组 数组内对象字段校验", () => {
235 | let ret = null
236 | try {
237 | const v = new Validator({
238 | field: [{
239 | $fields: {
240 | field1: [{ Required: true }]
241 | }
242 | }]
243 | })
244 | ret = v.validate({ field: [{ field1: null }] })
245 | } finally {
246 | expect(JSON.stringify(ret))
247 | .toBe(JSON.stringify({ "field": [{
248 | "field1": "field1 is required"
249 | }] }));
250 | }
251 | });
252 |
253 | it("嵌套数组 复合元素数组 数组内对象多字段校验", () => {
254 | let ret = null
255 | try {
256 | const v = new Validator({
257 | field: [{
258 | $fields: {
259 | field1: [{ Required: true }],
260 | field2: [{ Required: true }]
261 | }
262 | }]
263 | })
264 | ret = v.validate({ field: [{ field1: null }] })
265 | } finally {
266 | expect(JSON.stringify(ret))
267 | .toBe(JSON.stringify({
268 | "field": [{
269 | "field1": "field1 is required",
270 | "field2": "field2 is required"
271 | }]
272 | }));
273 | }
274 | });
275 |
276 | it("嵌套数组 复合元素数组 数组内多元素多字段校验", () => {
277 | let ret = null
278 | try {
279 | const v = new Validator({
280 | field: [{
281 | $fields: {
282 | field1: [{ Required: true }],
283 | field2: [{ Required: true }]
284 | }
285 | }]
286 | })
287 | ret = v.validate({ field: [{ field1: null }, { field2: "" }] })
288 | } finally {
289 | expect(JSON.stringify(ret))
290 | .toBe(JSON.stringify({
291 | "field": [{
292 | "field1": "field1 is required",
293 | "field2": "field2 is required"
294 | }, {
295 | "field1": "field1 is required"
296 | }]
297 | }));
298 | }
299 | });
300 |
301 | it("嵌套数组 复合元素数组 数组内对象,数组混合校验", () => {
302 | let ret = null
303 | try {
304 | const v = new Validator({
305 | field: [{
306 | $fields: {
307 | field1: [{ Required: true }],
308 | field2: [{ Required: true }, { $fields: [{ Enum: [1, 2, 3] }] }]
309 | }
310 | }]
311 | })
312 | ret = v.validate({ field: [{ field1: null }, { field2: [4] }] })
313 | } finally {
314 | expect(JSON.stringify(ret))
315 | .toBe(JSON.stringify({
316 | "field": [{
317 | "field1": "field1 is required",
318 | "field2": "field2 is required"
319 | }, {
320 | "field1": "field1 is required",
321 | "field2": ["field2 with value 4 is incorrect"]
322 | }]
323 | }));
324 | }
325 | });
326 |
327 | it("嵌套数组 复合元素数组 多层嵌套", () => {
328 | let ret = null
329 | try {
330 | const v = new Validator({
331 | field: [{
332 | $fields: {
333 | field1: [{ Required: true }],
334 | field2: [{ Required: true }, { $fields: [{ Enum: [1, 2, 3] }] }]
335 | }
336 | }]
337 | })
338 | ret = v.validate({ field: [{ field1: null }, { field2: [4] }] })
339 | } finally {
340 | expect(JSON.stringify(ret))
341 | .toBe(JSON.stringify({
342 | "field": [{
343 | "field1": "field1 is required",
344 | "field2": "field2 is required"
345 | }, {
346 | "field1": "field1 is required",
347 | "field2": ["field2 with value 4 is incorrect"]
348 | }]
349 | }));
350 | }
351 | });
352 | })
--------------------------------------------------------------------------------
/test/schema.test.js:
--------------------------------------------------------------------------------
1 | const Validator = require('../index');
2 |
3 | describe("Schema校验", () => {
4 | it("Validator构造器测试,空参数", () => {
5 | try {
6 | new Validator()
7 | } catch (err) {
8 | expect(err.message).toBe('Schema must be an Object or Array');
9 | }
10 | });
11 |
12 | it("Validator构造器测试,非法参数 null", () => {
13 | try {
14 | new Validator(null)
15 | } catch (err) {
16 | expect(err.message).toBe('Schema must be an Object or Array');
17 | }
18 | });
19 |
20 | it("Validator构造器测试,非法参数 ''", () => {
21 | try {
22 | new Validator('')
23 | } catch (err) {
24 | expect(err.message).toBe('Schema must be an Object or Array');
25 | }
26 | });
27 |
28 | it("Validator构造器测试,非法参数 Date", () => {
29 | try {
30 | new Validator(new Date())
31 | } catch (err) {
32 | expect(err.message).toBe('Schema must be an Object or Array');
33 | }
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/test/sign.rule.test.js:
--------------------------------------------------------------------------------
1 | const Validator = require('./../index');
2 |
3 | describe("Rule校验", () => {
4 | it("tag没有注册", () => {
5 | let ret = null
6 | try {
7 | const v = new Validator({
8 | field: { required: true }
9 | })
10 | ret = v.validate({})
11 | } catch (e) {
12 | expect(e.message)
13 | .toBe('Plugin with tagName = required is not registered!');
14 | }
15 | });
16 |
17 | it("一个rule中一个tag", () => {
18 | let ret = null
19 | try {
20 | const v = new Validator({
21 | field: { Required: true }
22 | })
23 | ret = v.validate({})
24 | } finally {
25 | expect(JSON.stringify(ret))
26 | .toBe(JSON.stringify({
27 | "field": "field is required"
28 | }));
29 | }
30 | });
31 |
32 | it("单一值校验", () => {
33 | let ret = null
34 | try {
35 | const v = new Validator({ Enum: [true, false] })
36 | ret = v.validate(1)
37 | } finally {
38 | expect(JSON.stringify(ret))
39 | .toBe(JSON.stringify("Input with value 1 is incorrect"));
40 | }
41 | });
42 |
43 | it("一个rule中多个tag", () => {
44 | let ret1 = null
45 | let ret2 = null
46 | try {
47 | const v = new Validator({
48 | field: { Required: true, NotEmpty: true }
49 | })
50 | ret1 = v.validate({})
51 | ret2 = v.validate({ field: '' })
52 | } finally {
53 | expect(JSON.stringify(ret1))
54 | .toBe(JSON.stringify({
55 | "field": "field is required"
56 | }));
57 | expect(JSON.stringify(ret2))
58 | .toBe(JSON.stringify({
59 | "field": "field is empty"
60 | }));
61 | }
62 | })
63 |
64 | it("一个rule中多个tag, 自定义覆盖消息", () => {
65 | let ret1 = null
66 | let ret2 = null
67 | try {
68 | const v = new Validator({
69 | field: { Required: true, NotEmpty: true, message: "覆盖消息" }
70 | })
71 | ret1 = v.validate({})
72 | ret2 = v.validate({ field: '' })
73 | } finally {
74 | expect(JSON.stringify(ret1))
75 | .toBe(JSON.stringify({
76 | "field": "覆盖消息"
77 | }));
78 | expect(JSON.stringify(ret2))
79 | .toBe(JSON.stringify({
80 | "field": "覆盖消息"
81 | }));
82 | }
83 | });
84 |
85 | it("自定义规则覆盖消息", () => {
86 | let ret = null
87 | try {
88 | const v = new Validator({
89 | field: { Required: true, validate() { return false } }
90 | })
91 | ret = v.validate({ field: '' })
92 | } finally {
93 | expect(JSON.stringify(ret))
94 | .toBe(JSON.stringify({
95 | "field": "field is required"
96 | }));
97 | }
98 | });
99 |
100 | it("嵌套对象,一层属性", () => {
101 | let ret = null
102 | try {
103 | const v = new Validator({
104 | field: [{
105 | Required: true, $fields: {
106 | field1: { Required: true }
107 | }
108 | }]
109 | })
110 | ret = v.validate({ field: {} })
111 | } finally {
112 | expect(JSON.stringify(ret))
113 | .toBe(JSON.stringify({
114 | "field": { field1: 'field1 is required' }
115 | }));
116 | }
117 | });
118 |
119 | it("嵌套对象,一层属性多属性", () => {
120 | let ret = null
121 | try {
122 | const v = new Validator({
123 | field: {
124 | Required: true, $fields: {
125 | field1: { Required: true },
126 | field2: { Required: true }
127 | }
128 | }
129 | })
130 | ret = v.validate({ field: {} })
131 | } finally {
132 | expect(JSON.stringify(ret))
133 | .toBe(JSON.stringify({
134 | "field": {
135 | field1: 'field1 is required',
136 | field2: 'field2 is required'
137 | }
138 | }));
139 | }
140 | });
141 |
142 | it("嵌套对象,二层属性", () => {
143 | let ret = null
144 | try {
145 | const v = new Validator({
146 | field: {
147 | Required: true, $fields: {
148 | field1: {
149 | Required: true, $fields: {
150 | field2: { Required: true }
151 | }
152 | }
153 | }
154 | }
155 | })
156 | ret = v.validate({ field: { field1: {} } })
157 | } finally {
158 | expect(JSON.stringify(ret))
159 | .toBe(JSON.stringify({
160 | "field": { field1: { field2: 'field2 is required' } }
161 | }));
162 | }
163 | });
164 |
165 | it("嵌套对象,二层属性多属性", () => {
166 | let ret = null
167 | try {
168 | const v = new Validator({
169 | field: {
170 | Required: true, $fields: {
171 | field1: {
172 | Required: true, $fields: {
173 | field2: { Required: true }
174 | }
175 | },
176 | field2: {
177 | Required: true, $fields: {
178 | field2: { Required: true }
179 | }
180 | }
181 | }
182 | }
183 | })
184 | ret = v.validate({ field: { field1: {} } })
185 | } finally {
186 | expect(JSON.stringify(ret))
187 | .toBe(JSON.stringify({
188 | "field": {
189 | field1: {
190 | field2: 'field2 is required'
191 | },
192 | field2: 'field2 is required'
193 | }
194 | }));
195 | }
196 | });
197 |
198 | it("嵌套数组 基本元素数组 多tag", () => {
199 | let ret = null
200 | try {
201 | const v = new Validator({
202 | field: { $fields: { Required: true, Enum: [1, 2, 3] } }
203 | })
204 | ret = v.validate({ field: [4, 1, undefined] })
205 | } finally {
206 | expect(JSON.stringify(ret))
207 | .toBe(JSON.stringify({
208 | "field": ["field with value 4 is incorrect", null, "field is required"]
209 | }));
210 | }
211 | });
212 |
213 | it("嵌套数组 复合元素数组 数组内对象字段校验", () => {
214 | let ret = null
215 | try {
216 | const v = new Validator({
217 | field: {
218 | $fields: {
219 | field1: { Required: true }
220 | }
221 | }
222 | })
223 | ret = v.validate({ field: [{ field1: null }] })
224 | } finally {
225 | expect(JSON.stringify(ret))
226 | .toBe(JSON.stringify({"field":[{"field1":"field1 is required"}]}));
227 | }
228 | });
229 |
230 | it("嵌套数组 复合元素数组 数组内对象多字段校验", () => {
231 | let ret = null
232 | try {
233 | const v = new Validator({
234 | field: {
235 | $fields: {
236 | field1: { Required: true },
237 | field2: { Required: true }
238 | }
239 | }
240 | })
241 | ret = v.validate({ field: [{ field1: null }] })
242 | } finally {
243 | expect(JSON.stringify(ret))
244 | .toBe(JSON.stringify({
245 | "field": [{"field1":"field1 is required","field2":"field2 is required"}]
246 | }));
247 | }
248 | });
249 |
250 | it("嵌套数组 复合元素数组 数组内多元素多字段校验", () => {
251 | let ret = null
252 | try {
253 | const v = new Validator({
254 | field: {
255 | $fields: {
256 | field1: { Required: true },
257 | field2: { Required: true }
258 | }
259 | }
260 | })
261 | ret = v.validate({ field: [{ field1: null }, { field2: "" }] })
262 | } finally {
263 | expect(JSON.stringify(ret))
264 | .toBe(JSON.stringify({
265 | "field": [{
266 | "field1": "field1 is required",
267 | "field2": "field2 is required"
268 | }, {
269 | "field1": "field1 is required"
270 | }]
271 | }));
272 | }
273 | });
274 |
275 | it("嵌套数组 复合元素数组 数组内对象,数组混合校验", () => {
276 | let ret = null
277 | try {
278 | const v = new Validator({
279 | field: {
280 | $fields: {
281 | field1: { Required: true },
282 | field2: [{ Required: true }, { $fields: [{ Enum: [1, 2, 3] }] }]
283 | }
284 | }
285 | })
286 | ret = v.validate({ field: [{ field1: null }, { field2: [4] }] })
287 | } finally {
288 | expect(JSON.stringify(ret))
289 | .toBe(JSON.stringify({
290 | "field": [{
291 | "field1": "field1 is required",
292 | "field2": "field2 is required"
293 | }, {
294 | "field1": "field1 is required",
295 | "field2": ["field2 with value 4 is incorrect"]
296 | }]
297 | }));
298 | }
299 | });
300 |
301 | it("嵌套数组 复合元素数组 多层嵌套", () => {
302 | let ret = null
303 | try {
304 | const v = new Validator({
305 | field: {
306 | $fields: {
307 | field1: { Required: true },
308 | field2: [{ Required: true }, { $fields: [{ Enum: [1, 2, 3] }] }]
309 | }
310 | }
311 | })
312 | ret = v.validate({ field: [{ field1: null }, { field2: [4] }] })
313 | } finally {
314 | expect(JSON.stringify(ret))
315 | .toBe(JSON.stringify({
316 | "field": [{
317 | "field1": "field1 is required",
318 | "field2": "field2 is required"
319 | }, {
320 | "field1": "field1 is required",
321 | "field2": ["field2 with value 4 is incorrect"]
322 | }]
323 | }));
324 | }
325 | });
326 |
327 | it("数组类型校验 返回错误信息格式校验 1", () => {
328 | let ret = null
329 | try {
330 | const v = new Validator({
331 | field: {
332 | $fields: {
333 | field1: { Required: true },
334 | field2: [{ Required: true }, { $fields: [{ Enum: [1, 2, 3] }] }]
335 | }
336 | }
337 | })
338 | ret = v.validate({ field: [{ field1: null }, { field2: [4, 1, 9] }] })
339 | } finally {
340 | expect(JSON.stringify(ret))
341 | .toBe(JSON.stringify({
342 | "field": [{
343 | "field1": "field1 is required",
344 | "field2": "field2 is required"
345 | }, {
346 | "field1": "field1 is required",
347 | "field2": ["field2 with value 4 is incorrect", null, "field2 with value 9 is incorrect"]
348 | }]
349 | }));
350 | }
351 | });
352 |
353 | it("数组类型校验 返回错误信息格式校验 2", () => {
354 | let ret = null
355 | try {
356 | const v = new Validator({
357 | field: {
358 | $fields: { Required: true, MinNum: 200 }
359 | }
360 | })
361 | ret = v.validate({ field: [1, 2000, 2] })
362 | } finally {
363 | expect(JSON.stringify(ret))
364 | .toBe(JSON.stringify({
365 | "field": ["Min value of field is 200", null, "Min value of field is 200"]
366 | }));
367 | }
368 | });
369 | })
--------------------------------------------------------------------------------
/utils.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | isArray(arr) {
3 | return Array.isArray(arr)
4 | },
5 | isObject(o) {
6 | return Object.prototype.toString.call(o) === "[object Object]"
7 | },
8 | isString(s) {
9 | return Object.prototype.toString.call(s) === "[object String]"
10 | },
11 | isFunction(f) {
12 | return Object.prototype.toString.call(f) === "[object Function]"
13 | },
14 | isNumber(n) {
15 | return Object.prototype.toString.call(n) === "[object Number]" && !isNaN(n)
16 | },
17 | isRegExp(r) {
18 | return Object.prototype.toString.call(r) === "[object RegExp]"
19 | }
20 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = (env) => {
4 | const plugins = [];
5 |
6 | return {
7 | entry: {
8 | validator: './index.js'
9 | },
10 | mode: 'production',
11 | output: {
12 | path: path.join(__dirname, 'dist'),
13 | filename: 'slime-validator.umd.js',
14 | libraryTarget: 'umd',
15 | library: 'SlimeValidator'
16 | },
17 | target: ['web', 'es5'],
18 | module: {
19 | rules: [
20 | {
21 | test: /\.js$/,
22 | loader: 'babel-loader',
23 | exclude: /node_modules/
24 | }
25 | ]
26 | },
27 | plugins: plugins
28 | }
29 | }
--------------------------------------------------------------------------------