| import type {AnySchema, SchemaMap} from "../types" |
| import type {SchemaCxt} from "../compile" |
| import type {KeywordCxt} from "../compile/validate" |
| import {CodeGen, _, and, or, not, nil, strConcat, getProperty, Code, Name} from "../compile/codegen" |
| import {alwaysValidSchema, Type} from "../compile/util" |
| import N from "../compile/names" |
| import {useFunc} from "../compile/util" |
| export function checkReportMissingProp(cxt: KeywordCxt, prop: string): void { |
| const {gen, data, it} = cxt |
| gen.if(noPropertyInData(gen, data, prop, it.opts.ownProperties), () => { |
| cxt.setParams({missingProperty: _`${prop}`}, true) |
| cxt.error() |
| }) |
| } |
| |
| export function checkMissingProp( |
| {gen, data, it: {opts}}: KeywordCxt, |
| properties: string[], |
| missing: Name |
| ): Code { |
| return or( |
| ...properties.map((prop) => |
| and(noPropertyInData(gen, data, prop, opts.ownProperties), _`${missing} = ${prop}`) |
| ) |
| ) |
| } |
| |
| export function reportMissingProp(cxt: KeywordCxt, missing: Name): void { |
| cxt.setParams({missingProperty: missing}, true) |
| cxt.error() |
| } |
| |
| export function hasPropFunc(gen: CodeGen): Name { |
| return gen.scopeValue("func", { |
| // eslint-disable-next-line @typescript-eslint/unbound-method |
| ref: Object.prototype.hasOwnProperty, |
| code: _`Object.prototype.hasOwnProperty`, |
| }) |
| } |
| |
| export function isOwnProperty(gen: CodeGen, data: Name, property: Name | string): Code { |
| return _`${hasPropFunc(gen)}.call(${data}, ${property})` |
| } |
| |
| export function propertyInData( |
| gen: CodeGen, |
| data: Name, |
| property: Name | string, |
| ownProperties?: boolean |
| ): Code { |
| const cond = _`${data}${getProperty(property)} !== undefined` |
| return ownProperties ? _`${cond} && ${isOwnProperty(gen, data, property)}` : cond |
| } |
| |
| export function noPropertyInData( |
| gen: CodeGen, |
| data: Name, |
| property: Name | string, |
| ownProperties?: boolean |
| ): Code { |
| const cond = _`${data}${getProperty(property)} === undefined` |
| return ownProperties ? or(cond, not(isOwnProperty(gen, data, property))) : cond |
| } |
| |
| export function allSchemaProperties(schemaMap?: SchemaMap): string[] { |
| return schemaMap ? Object.keys(schemaMap).filter((p) => p !== "__proto__") : [] |
| } |
| |
| export function schemaProperties(it: SchemaCxt, schemaMap: SchemaMap): string[] { |
| return allSchemaProperties(schemaMap).filter( |
| (p) => !alwaysValidSchema(it, schemaMap[p] as AnySchema) |
| ) |
| } |
| |
| export function callValidateCode( |
| {schemaCode, data, it: {gen, topSchemaRef, schemaPath, errorPath}, it}: KeywordCxt, |
| func: Code, |
| context: Code, |
| passSchema?: boolean |
| ): Code { |
| const dataAndSchema = passSchema ? _`${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data |
| const valCxt: [Name, Code | number][] = [ |
| [N.instancePath, strConcat(N.instancePath, errorPath)], |
| [N.parentData, it.parentData], |
| [N.parentDataProperty, it.parentDataProperty], |
| [N.rootData, N.rootData], |
| ] |
| if (it.opts.dynamicRef) valCxt.push([N.dynamicAnchors, N.dynamicAnchors]) |
| const args = _`${dataAndSchema}, ${gen.object(...valCxt)}` |
| return context !== nil ? _`${func}.call(${context}, ${args})` : _`${func}(${args})` |
| } |
| |
| const newRegExp = _`new RegExp` |
| |
| export function usePattern({gen, it: {opts}}: KeywordCxt, pattern: string): Name { |
| const u = opts.unicodeRegExp ? "u" : "" |
| const {regExp} = opts.code |
| const rx = regExp(pattern, u) |
| |
| return gen.scopeValue("pattern", { |
| key: rx.toString(), |
| ref: rx, |
| code: _`${regExp.code === "new RegExp" ? newRegExp : useFunc(gen, regExp)}(${pattern}, ${u})`, |
| }) |
| } |
| |
| export function validateArray(cxt: KeywordCxt): Name { |
| const {gen, data, keyword, it} = cxt |
| const valid = gen.name("valid") |
| if (it.allErrors) { |
| const validArr = gen.let("valid", true) |
| validateItems(() => gen.assign(validArr, false)) |
| return validArr |
| } |
| gen.var(valid, true) |
| validateItems(() => gen.break()) |
| return valid |
| |
| function validateItems(notValid: () => void): void { |
| const len = gen.const("len", _`${data}.length`) |
| gen.forRange("i", 0, len, (i) => { |
| cxt.subschema( |
| { |
| keyword, |
| dataProp: i, |
| dataPropType: Type.Num, |
| }, |
| valid |
| ) |
| gen.if(not(valid), notValid) |
| }) |
| } |
| } |
| |
| export function validateUnion(cxt: KeywordCxt): void { |
| const {gen, schema, keyword, it} = cxt |
| /* istanbul ignore if */ |
| if (!Array.isArray(schema)) throw new Error("ajv implementation error") |
| const alwaysValid = schema.some((sch: AnySchema) => alwaysValidSchema(it, sch)) |
| if (alwaysValid && !it.opts.unevaluated) return |
| |
| const valid = gen.let("valid", false) |
| const schValid = gen.name("_valid") |
| |
| gen.block(() => |
| schema.forEach((_sch: AnySchema, i: number) => { |
| const schCxt = cxt.subschema( |
| { |
| keyword, |
| schemaProp: i, |
| compositeRule: true, |
| }, |
| schValid |
| ) |
| gen.assign(valid, _`${valid} || ${schValid}`) |
| const merged = cxt.mergeValidEvaluated(schCxt, schValid) |
| // can short-circuit if `unevaluatedProperties/Items` not supported (opts.unevaluated !== true) |
| // or if all properties and items were evaluated (it.props === true && it.items === true) |
| if (!merged) gen.if(not(valid)) |
| }) |
| ) |
| |
| cxt.result( |
| valid, |
| () => cxt.reset(), |
| () => cxt.error(true) |
| ) |
| } |