@schema-render/core-react
Version:
Through a set of simple JSON Schema, efficiently build a set of forms.
262 lines (261 loc) • 8.53 kB
JavaScript
import { EValidationStatus } from "../constants";
import { find, get } from "./tinyLodash";
import { templateCompiled } from "./base";
import { isFunction } from "./checking";
import { stringifyPath } from "./misc";
import { matchRenderer } from "./renderer";
import { isValidFunctionResult, isValidRules, normalizeRules, validateRules } from "./schemaRulesValidator";
import { performStatementWithPath } from "./statement";
import traverseSchema from "./traverseSchema";
export async function validateRootSchema({ rootSchema, rootValue, disabled, readonly, locale, renderers, rendererStorage, validators, userCtx }) {
const items = [];
traverseSchema(rootSchema, {
$$: {
enter ({ schema, path }) {
// 执行 hidden 语法
const shouldHidden = performStatementWithPath({
statement: schema.hidden,
rootValue,
path,
userCtx
});
if (shouldHidden) {
return;
}
const renderer = matchRenderer(renderers, schema.renderType);
if (!renderer) {
return;
}
const handler = async ()=>{
const value = get(rootValue, path);
const validatorParams = makeOpenValidatorParams({
schema,
value,
path,
disabled,
readonly,
rootValue,
locale,
userCtx
});
try {
const { status, message, extra } = await validateFormItem({
schema,
rootValue,
userCtx,
path,
locale,
value,
globalValidators: validators,
rendererValidator: renderer.validator,
rendererValidatorParams: validatorParams
});
if (status !== EValidationStatus.success) {
return {
path,
value,
status,
message,
extra
};
}
} catch (err) {
return {
path,
value,
status: EValidationStatus.error,
message: `[CatchError]: ${err === null || err === void 0 ? void 0 : err.message}`
};
}
};
items.push(handler());
}
},
$$Object: {
enter ({ traverse }) {
traverse();
}
}
}, {
clone: false
});
const errorList = [];
const warningList = [];
// 并发校验
const emittingList = await Promise.all(items).then((res)=>{
return res.filter((item)=>{
if ((item === null || item === void 0 ? void 0 : item.status) === EValidationStatus.error) {
// 错误列表
errorList.push(item);
} else if ((item === null || item === void 0 ? void 0 : item.status) === EValidationStatus.warning) {
// 警告列表
warningList.push(item);
}
// 过滤校验通过的返回值,即 undefined
return !!item;
});
});
// 通知渲染器展示错误或警告信息
emittingList.forEach(({ status, path, message, extra })=>{
const renderer = rendererStorage[stringifyPath(path)];
renderer === null || renderer === void 0 ? void 0 : renderer.setValidatorState({
status,
message,
extra
});
});
return {
hasError: !!errorList.length,
hasWarning: !!warningList.length,
errorList,
warningList
};
}
export async function validateFormItem({ schema, rootValue, userCtx, path, locale, value, globalValidators, rendererValidator, rendererValidatorParams }) {
// 将 schema.required 添加到 rules 规则
const rules = composeSchemaRules({
schema,
rootValue,
path,
locale,
userCtx
});
const commonValidateRulesParams = {
value,
globalValidators,
globalValidatorParams: rendererValidatorParams,
locale: locale.validation
};
// 优先校验:通用 schema rules 校验
// 如果成功,需要继续执行渲染器绑定的校验
if (isValidRules(rules)) {
const result = await validateRules({
...commonValidateRulesParams,
rules
});
if (result.status !== EValidationStatus.success) {
return result;
}
}
// 渲染器内置校验(rules 规则)
if (isValidRules(rendererValidator)) {
const result = await validateRules({
...commonValidateRulesParams,
rules: rendererValidator
});
return result;
}
// 渲染器内置校验(函数规则)
if (isFunction(rendererValidator)) {
const fnResult = await rendererValidator({
...rendererValidatorParams,
value
});
// 渲染器内置校验(返回值 rules 规则)
if (isValidRules(fnResult)) {
const result = await validateRules({
...commonValidateRulesParams,
rules: fnResult
});
return result;
}
// 渲染器内置校验(返回值 SchemaRender 规则)
if (isValidFunctionResult(fnResult)) {
return {
status: fnResult.status,
message: fnResult.message,
extra: fnResult.extra
};
}
}
return {
status: EValidationStatus.success
};
}
/**
* 将 schema.required 添加到 rules 规则
*/ function composeSchemaRules({ schema, rootValue, path, locale, userCtx }) {
const rules = normalizeRules(schema.rules);
const isRequired = performStatementWithPath({
statement: schema.required,
rootValue,
path,
userCtx
});
if (isRequired && !find(schema.rules, {
required: true
})) {
rules.unshift({
required: true,
message: templateCompiled(locale.validation.required, {
label: schema.title
})
});
}
return rules;
}
/**
* 获取 Schema required 的值,由以下两个协议确定
* - schema.required 的值
* - schema.rules 内 required 的值
*/ function calcRequiredValue({ schema, rootValue, path, userCtx }) {
const isRequired = performStatementWithPath({
statement: schema.required,
rootValue,
path,
userCtx
});
if (isRequired) {
return true;
}
if (find(normalizeRules(schema.rules), {
required: true
})) {
return true;
}
return false;
}
export function makeOpenValidatorParams({ schema, value, path, disabled, readonly, rootValue, locale, userCtx }) {
// 优先使用全局禁用态、只读态
let innerDisabled = !!disabled;
let innerReadonly = !!readonly;
// 已禁用的状态下,不需要执行语句,提升性能
if (!innerDisabled) {
innerDisabled = performStatementWithPath({
statement: schema.disabled,
rootValue,
path,
userCtx
});
}
// 只读态处理
if (!innerReadonly) {
innerReadonly = performStatementWithPath({
statement: schema.readonly,
rootValue,
path,
userCtx
});
}
// 是否必填
const required = calcRequiredValue({
schema,
path,
rootValue,
userCtx
});
return {
schema,
value,
path: [
...path
],
sPath: stringifyPath(path),
required,
disabled: innerDisabled,
readonly: innerReadonly,
rootValue,
locale,
userCtx
};
}