1.0.0 • Published 2 years ago

ajv-extends v1.0.0

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

ajv-extends

Ajv 延伸扩展。Ajv 是基于 json-schema / JTD 对数据进行验证的一个辅助库。其功能是能满足大部分的数据验证的场景的,但这个实际运用却很生草。

首先,Ajv 将初始化和错误处理(多语言),进行了分离,使得实际调用的代码异常臃肿。

如果你需要在项目环境中引用诸如 ajv-formats ajv-i18n ,那你的代码可能会是如下:

import Ajv from 'ajv';
import addFormats from 'ajv-formats';
import localize_zh from 'ajv-i18n/localize/zh';

const ajv = new Ajv();

addFormats(ajv);
ajv.addKeyword({
  keyword : 'someKeyword',
  validate: () => true,
});
// 这里可能还有很多你自己项目里的扩展


// 到使用:
const schema = {
  type      : 'object',
  properties: {
    // ....
  },
  required  : [
    // ...
  ]
}
const validate = ajv.compile(schema);

const data = {};
const valid = validate(data);

if (!valid) {
  // 将错误信息本地化
  localize_zh(validate.errors);
}

其次,如果你项目中需要使用 react-hook-form,以及 @hookform/resolverreact-hook-form 出品的包含市面常用的 js validator 的 resolver),就会发现,@hookform/resolver/ajvResolver 是在其内部闭环的,你无法在这过程中加入其他的 ajv 扩展或者多语言本地化的处理。

可以参考这个文件:@hookform/resolver/ajv.ts

再次,ajv-i18n 的汉化,仍有需要不完善的地方,大体可以用,但是总需要在实际项目进行拦截和优化。

最后的最后,往往当我们在实际项目中添加一种 format / keyword,可能希望他能被更简单的在下一个项目重用起来,而一种 format / keyword 往往是和 localize 紧密结合的,所以需要有一个更简易的环境去使用 ajv,这也是创建这个项目的目的。

Ajv 延伸,意在由你自己决定如何扩展延伸,项目本身只提供一个简易的粘合作用,将 Ajv 的一些常用方法重新调整调用的方式,以便于人类理解使用。

主要内容

ajv.ts

该部分主要提供了一个全局简易创建和使用 Ajv 实例的快捷方式。

import { createAjv, ajv } from 'ajv-extends';

createAjv 创建一个全局统一的创建 Ajv 实例的方法

import addFormats from 'ajv-formats';
import localize_zh from 'ajv-i18n/localize/zh';

// 全局的 ajv 实例创建句柄
// 这样,通过 ajv 创建的所有 ajv 实例,都会自动套用 use / useLocalize 所添加的扩展和本地化
export const ajv = createAjv()
  // 加入扩展
  .use((ajvInstance, { useLocalize }) => {
    // 每次创建 ajv 实例的时候会执行
    addFormats(ajvInstance);
    // 其他扩展
    ajvInstance.addKeyword({
      keyword : 'someKeyword',
      error: {
        message: () => 'xxx',
        // => 需要将这个 keyword 的 params 进行传递,才能在本地化的时候得到这个 keyword 相应的 params
        params: (cxt) => cxt.params,
      },
      validate: () => true,
    });

    useLocalize((errors) => {
      if (!errors || !errors.length) return;
      errors.forEach(err => {
        if (err.keyword === 'someKeyword') {
          err.message = 'xxx' + err.params;
        }
      })
    });
  })
  // 本地化
  // 只在验证时发生错误时执行
  .useLocalize(localize_zh)
  // 创建 ajv 实例时候的默认 options
  .setOptions({ allErrors: true })
;

// 创建一个 ajv 实例
const localAjv = ajv();

// 1. 创建一个 ajv 实例,
// 2. 并 compile schema,
// 3. 然后执行 validate
// 默认模式
const { valid, errors } = ajv.validate(data, schema);

// throw error 模式
try {
  ajv.validate(data, schema, true);
} catch (err) {
  // TS 将所有 err 变为 unknown 类型,所以需要自行 detect
  if (ajv.isValidationError(err)) {
    console.log(err.errors);
  }
}

ajv 为默认创建的一个全局单例 export const ajv = createAjv() ,主要用于给一些小项目直接使用(比如有些时候真的连单独创建一个 ajv 的文件都懒的时候)。但不推荐这样用(不使用他,可以不导出这个 ajv)。

schema.ts

该部分主要为快捷创建 schema 的方法封装,目前只提供 object 类型的快捷定义

import { schemaObject } from 'ajv-extends';

schemaObject({
  key: { type: 'string' },
}, ['key'])

// 支持指定泛型
// 解除了 ajv 默认的 properties 锁定的设定,即:
// 1. 强制限定 Type.field 的类型,必须和 properties 的类型强关联(实际应用中,验证器往往总是要包含更多情况)
// 2. 如果指定 Type.field 为 ? (即可能为 undefined),会强制要求对应的 properties 声明中增加 nullable
// 3. required 声明也必须限定在 Type 字段内。
// 以上三方面的限制皆去除,并且能自动根据 Type 进行代码提示。
type Type = {
  field: string,
}
schemaObject<Type>(
  // properties 声明
  {
    field: { type: 'string' },
  },
  // required 声明
  ['field'],
  // additionalProperties
  true,
  // 其他 schema 属性
  {
    patternProperties: {
      
    },
    // ....
  }
)