Spaces:
Running
Running
import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" | |
import type {KeywordCxt} from "../../compile/validate" | |
import { | |
checkReportMissingProp, | |
checkMissingProp, | |
reportMissingProp, | |
propertyInData, | |
noPropertyInData, | |
} from "../code" | |
import {_, str, nil, not, Name, Code} from "../../compile/codegen" | |
import {checkStrictMode} from "../../compile/util" | |
export type RequiredError = ErrorObject< | |
"required", | |
{missingProperty: string}, | |
string[] | {$data: string} | |
> | |
const error: KeywordErrorDefinition = { | |
message: ({params: {missingProperty}}) => str`must have required property '${missingProperty}'`, | |
params: ({params: {missingProperty}}) => _`{missingProperty: ${missingProperty}}`, | |
} | |
const def: CodeKeywordDefinition = { | |
keyword: "required", | |
type: "object", | |
schemaType: "array", | |
$data: true, | |
error, | |
code(cxt: KeywordCxt) { | |
const {gen, schema, schemaCode, data, $data, it} = cxt | |
const {opts} = it | |
if (!$data && schema.length === 0) return | |
const useLoop = schema.length >= opts.loopRequired | |
if (it.allErrors) allErrorsMode() | |
else exitOnErrorMode() | |
if (opts.strictRequired) { | |
const props = cxt.parentSchema.properties | |
const {definedProperties} = cxt.it | |
for (const requiredKey of schema) { | |
if (props?.[requiredKey] === undefined && !definedProperties.has(requiredKey)) { | |
const schemaPath = it.schemaEnv.baseId + it.errSchemaPath | |
const msg = `required property "${requiredKey}" is not defined at "${schemaPath}" (strictRequired)` | |
checkStrictMode(it, msg, it.opts.strictRequired) | |
} | |
} | |
} | |
function allErrorsMode(): void { | |
if (useLoop || $data) { | |
cxt.block$data(nil, loopAllRequired) | |
} else { | |
for (const prop of schema) { | |
checkReportMissingProp(cxt, prop) | |
} | |
} | |
} | |
function exitOnErrorMode(): void { | |
const missing = gen.let("missing") | |
if (useLoop || $data) { | |
const valid = gen.let("valid", true) | |
cxt.block$data(valid, () => loopUntilMissing(missing, valid)) | |
cxt.ok(valid) | |
} else { | |
gen.if(checkMissingProp(cxt, schema, missing)) | |
reportMissingProp(cxt, missing) | |
gen.else() | |
} | |
} | |
function loopAllRequired(): void { | |
gen.forOf("prop", schemaCode as Code, (prop) => { | |
cxt.setParams({missingProperty: prop}) | |
gen.if(noPropertyInData(gen, data, prop, opts.ownProperties), () => cxt.error()) | |
}) | |
} | |
function loopUntilMissing(missing: Name, valid: Name): void { | |
cxt.setParams({missingProperty: missing}) | |
gen.forOf( | |
missing, | |
schemaCode as Code, | |
() => { | |
gen.assign(valid, propertyInData(gen, data, missing, opts.ownProperties)) | |
gen.if(not(valid), () => { | |
cxt.error() | |
gen.break() | |
}) | |
}, | |
nil | |
) | |
} | |
}, | |
} | |
export default def | |