eslint-plugin-json-schema-validator
Version:
ESLint plugin that validates data using JSON Schema Validator.
210 lines (209 loc) • 7.05 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.compile = void 0;
const url_1 = require("url");
const ajv_1 = __importDefault(require("./ajv"));
const limit_number_1 = require("./ajv-custom/limit-number");
const schema_1 = require("./schema");
const ajv = new ajv_1.default({
allErrors: true,
verbose: true,
validateSchema: false,
logger: false,
strict: false,
});
ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json"));
ajv.removeKeyword("id");
limit_number_1.applyLimitNumberKeywords(ajv);
function unescapeFragment(str) {
return unescapeJsonPointer(decodeURIComponent(str));
}
function unescapeJsonPointer(str) {
return str.replace(/~1/g, "/").replace(/~0/g, "~");
}
function compile(schema, schemaPath, context) {
return schemaToValidator(schema, schemaPath, context);
}
exports.compile = compile;
function schemaToValidator(schema, schemaPath, context) {
let validateSchema;
while (true) {
try {
validateSchema = ajv.compile(schema);
}
catch (e) {
if (resolveError(e, schemaPath, schema, context)) {
continue;
}
throw e;
}
break;
}
return (data) => {
if (validateSchema(data)) {
return [];
}
return validateSchema.errors.map(errorToValidateError);
};
}
function resolveError(error, baseSchemaPath, baseSchema, context) {
if (error.missingRef) {
let schemaPath = "";
let schemaId = "";
if (error.missingRef.startsWith("http://") ||
error.missingRef.startsWith("https://")) {
const uri = new url_1.URL(error.missingRef);
uri.hash = "";
schemaPath = uri.toString();
schemaId = schemaPath;
}
else {
const ref = error.missingRef;
const baseUri = new url_1.URL(baseSchema.$id || baseSchemaPath);
baseUri.hash = "";
const slashIndex = baseUri.pathname.lastIndexOf("/");
if (slashIndex >= 0) {
baseUri.pathname = baseUri.pathname.slice(0, slashIndex + 1);
}
const uri = new url_1.URL(`${baseUri.toString()}${ref}`);
uri.hash = "";
schemaPath = uri.toString();
schemaId = ref.split("#")[0];
}
if (schemaPath) {
const refSchema = schema_1.loadSchema(schemaPath, context);
if (refSchema) {
while (true) {
try {
ajv.addSchema(refSchema, schemaId);
}
catch (e) {
if (resolveError(e, schemaPath, refSchema, context)) {
continue;
}
throw e;
}
break;
}
return true;
}
}
}
return false;
}
function errorToValidateError(errorObject) {
const error = errorObject;
const dataPath = error.dataPath.startsWith("/")
? error.dataPath.slice(1)
: error.dataPath;
const path = dataPath
? dataPath.split("/").map(unescapeFragment)
: [];
if (error.keyword === "additionalProperties") {
path.push(error.params.additionalProperty);
return {
message: `Unexpected property ${joinPath(path)}`,
path,
};
}
if (error.keyword === "propertyNames") {
return {
message: `${joinPath(path)} property name ${JSON.stringify(error.params.propertyName)} is invalid.`,
path: [...path, error.params.propertyName],
};
}
if (error.keyword === "uniqueItems") {
const baseMessage = `should NOT have duplicate items (items ## ${error.params.j} and ${error.params.i} are identical)`;
return {
message: `${joinPath(path)} ${baseMessage}.`,
path: [...path, String(error.params.i)],
};
}
let baseMessage;
if (error.keyword === "enum") {
baseMessage = `should be equal to ${joinEnums(error.params.allowedValues)}`;
}
else if (error.keyword === "const") {
baseMessage = `should be equal to ${JSON.stringify(error.params.allowedValue)}`;
}
else if (error.keyword === "not") {
const schema = error.schema;
const schemaKeys = Object.keys(schema);
if (schemaKeys.length === 1 && schemaKeys[0] === "type") {
baseMessage = `should NOT be ${schema.type}`;
}
else if (schemaKeys.length === 1 && schemaKeys[0] === "enum") {
baseMessage = `should NOT be equal to ${joinEnums(schema.enum)}`;
}
else {
baseMessage = `should NOT be valid of define schema`;
}
}
else if (error.keyword === "type" ||
error.keyword === "oneOf" ||
error.keyword === "anyOf" ||
error.keyword === "minItems" ||
error.keyword === "maxItems" ||
error.keyword === "additionalItems" ||
error.keyword === "contains" ||
error.keyword === "required" ||
error.keyword === "maxProperties" ||
error.keyword === "minProperties" ||
error.keyword === "dependencies" ||
error.keyword === "pattern" ||
error.keyword === "maxLength" ||
error.keyword === "minLength" ||
error.keyword === "format" ||
error.keyword === "maximum" ||
error.keyword === "minimum" ||
error.keyword === "exclusiveMaximum" ||
error.keyword === "exclusiveMinimum" ||
error.keyword === "multipleOf" ||
error.keyword === "if") {
baseMessage = error.message;
}
else {
baseMessage = error.message;
}
if (error.propertyName) {
return {
message: `${joinPath(path)} property name ${JSON.stringify(error.propertyName)} ${baseMessage}.`,
path: [...path, error.propertyName],
};
}
return {
message: `${joinPath(path)} ${baseMessage}.`,
path,
};
function joinEnums(enums) {
const list = enums.map((v) => JSON.stringify(v));
const last = list.pop();
if (list.length) {
return `${list.join(", ")} or ${last}`;
}
return last;
}
function joinPath(paths) {
if (!paths.length) {
return "Root";
}
let result = "";
for (const p of paths) {
if (/^[a-z_$][\w$]*$/iu.test(p)) {
if (result) {
result += `.${p}`;
}
else {
result = p;
}
}
else {
result += `[${/^\d+$/iu.test(p) ? p : JSON.stringify(p)}]`;
}
}
return `"${result}"`;
}
}