UNPKG

json-schema-library

Version:

Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation

85 lines (75 loc) 3.26 kB
import copy from "fast-copy"; import { getRef } from "./keywords/$ref"; import { draft04 } from "./draft04"; import { draft06 } from "./draft06"; import { draft07 } from "./draft07"; import { draft2019 } from "./draft2019"; import { draft2020 } from "./draft2020"; import { pick } from "./utils/pick"; import { JsonSchema, Draft, BooleanSchema } from "./types"; import { TemplateOptions } from "./methods/getData"; import { SchemaNode, SchemaNodeMethods, addKeywords, isSchemaNode } from "./SchemaNode"; export type CompileOptions = { drafts?: Draft[]; remote?: SchemaNode; formatAssertion?: boolean | "meta-schema"; getDataDefaultOptions?: TemplateOptions; }; const defaultDrafts: Draft[] = [draft04, draft06, draft07, draft2019, draft2020]; function getDraft(drafts: Draft[], $schema: string) { return drafts.find((d) => new RegExp(d.$schemaRegEx).test($schema)) ?? drafts[drafts.length - 1]; } /** * With compileSchema we replace the schema and all sub-schemas with a schemaNode, * wrapping each schema with utilities and as much preevaluation is possible. Each * node will be reused for each task, but will create a compiledNode for bound data. */ export function compileSchema(schema: JsonSchema, options: CompileOptions = {}) { let formatAssertion = options.formatAssertion ?? true; const drafts = options.drafts ?? defaultDrafts; const draft = getDraft(drafts, schema?.$schema); const node: SchemaNode = { evaluationPath: "#", lastIdPointer: "#", schemaLocation: "#", dynamicId: "", reducers: [], resolvers: [], validators: [], schema, // @ts-expect-error self-reference add edlater context: { remotes: {}, dynamicAnchors: {}, ...(options.remote?.context ?? {}), anchors: {}, refs: {}, ...copy(pick(draft, "methods", "keywords", "version", "formats", "errors")), getDataDefaultOptions: options.getDataDefaultOptions, drafts }, ...SchemaNodeMethods }; node.context.rootNode = node; node.context.remotes[schema?.$id ?? "#"] = node; if (options.remote) { const metaSchema = getRef(node, node.schema.$schema); if (isSchemaNode(metaSchema) && metaSchema.schema.$vocabulary) { const vocabs = Object.keys(metaSchema.schema.$vocabulary); // const withAnnotations = vocabs.find((vocab) => vocab.includes("vocab/format-annotation")); const formatAssertionString = vocabs.find((vocab) => vocab.includes("vocab/format-assertion")); if (formatAssertion === "meta-schema") { formatAssertion = metaSchema.schema.$vocabulary[formatAssertionString] === true; } const validKeywords = Object.keys(metaSchema.getData({}, { addOptionalProps: true })); if (validKeywords.length > 0) { node.context.keywords = node.context.keywords.filter((f) => validKeywords.includes(f.keyword)); } } } if (formatAssertion === false) { node.context.keywords = node.context.keywords.filter((f) => f.keyword !== "format"); } addKeywords(node); return node; }