import type {CodeKeywordDefinition} from "../../types" import type {KeywordCxt} from "../../compile/validate" import {allSchemaProperties, usePattern} from "../code" import {_, not, Name} from "../../compile/codegen" import {alwaysValidSchema, checkStrictMode} from "../../compile/util" import {evaluatedPropsToName, Type} from "../../compile/util" import {AnySchema} from "../../types" const def: CodeKeywordDefinition = { keyword: "patternProperties", type: "object", schemaType: "object", code(cxt: KeywordCxt) { const {gen, schema, data, parentSchema, it} = cxt const {opts} = it const patterns = allSchemaProperties(schema) const alwaysValidPatterns = patterns.filter((p) => alwaysValidSchema(it, schema[p] as AnySchema) ) if ( patterns.length === 0 || (alwaysValidPatterns.length === patterns.length && (!it.opts.unevaluated || it.props === true)) ) { return } const checkProperties = opts.strictSchema && !opts.allowMatchingProperties && parentSchema.properties const valid = gen.name("valid") if (it.props !== true && !(it.props instanceof Name)) { it.props = evaluatedPropsToName(gen, it.props) } const {props} = it validatePatternProperties() function validatePatternProperties(): void { for (const pat of patterns) { if (checkProperties) checkMatchingProperties(pat) if (it.allErrors) { validateProperties(pat) } else { gen.var(valid, true) // TODO var validateProperties(pat) gen.if(valid) } } } function checkMatchingProperties(pat: string): void { for (const prop in checkProperties) { if (new RegExp(pat).test(prop)) { checkStrictMode( it, `property ${prop} matches pattern ${pat} (use allowMatchingProperties)` ) } } } function validateProperties(pat: string): void { gen.forIn("key", data, (key) => { gen.if(_`${usePattern(cxt, pat)}.test(${key})`, () => { const alwaysValid = alwaysValidPatterns.includes(pat) if (!alwaysValid) { cxt.subschema( { keyword: "patternProperties", schemaProp: pat, dataProp: key, dataPropType: Type.Str, }, valid ) } if (it.opts.unevaluated && props !== true) { gen.assign(_`${props}[${key}]`, true) } else if (!alwaysValid && !it.allErrors) { // can short-circuit if `unevaluatedProperties` is not supported (opts.next === false) // or if all properties were evaluated (props === true) gen.if(not(valid), () => gen.break()) } }) }) } }, } export default def