1 import type {KeywordErrorCxt, KeywordErrorDefinition} from "../types"
2 import type {SchemaCxt} from "./index"
3 import {CodeGen, _, str, strConcat, Code, Name} from "./codegen"
4 import {SafeExpr} from "./codegen/code"
5 import {getErrorPath, Type} from "./util"
6 import N from "./names"
8 export const keywordError: KeywordErrorDefinition = {
9 message: ({keyword}) => str`must pass "${keyword}" keyword validation`,
12 export const keyword$DataError: KeywordErrorDefinition = {
13 message: ({keyword, schemaType}) =>
15 ? str`"${keyword}" keyword must be ${schemaType} ($data)`
16 : str`"${keyword}" keyword is invalid ($data)`,
19 export interface ErrorPaths {
22 parentSchema?: boolean
25 export function reportError(
27 error: KeywordErrorDefinition = keywordError,
28 errorPaths?: ErrorPaths,
29 overrideAllErrors?: boolean
32 const {gen, compositeRule, allErrors} = it
33 const errObj = errorObjectCode(cxt, error, errorPaths)
34 if (overrideAllErrors ?? (compositeRule || allErrors)) {
37 returnErrors(it, _`[${errObj}]`)
41 export function reportExtraError(
43 error: KeywordErrorDefinition = keywordError,
44 errorPaths?: ErrorPaths
47 const {gen, compositeRule, allErrors} = it
48 const errObj = errorObjectCode(cxt, error, errorPaths)
50 if (!(compositeRule || allErrors)) {
51 returnErrors(it, N.vErrors)
55 export function resetErrorsCount(gen: CodeGen, errsCount: Name): void {
56 gen.assign(N.errors, errsCount)
57 gen.if(_`${N.vErrors} !== null`, () =>
60 () => gen.assign(_`${N.vErrors}.length`, errsCount),
61 () => gen.assign(N.vErrors, null)
66 export function extendErrors({
73 }: KeywordErrorCxt): void {
74 /* istanbul ignore if */
75 if (errsCount === undefined) throw new Error("ajv implementation error")
76 const err = gen.name("err")
77 gen.forRange("i", errsCount, N.errors, (i) => {
78 gen.const(err, _`${N.vErrors}[${i}]`)
79 gen.if(_`${err}.instancePath === undefined`, () =>
80 gen.assign(_`${err}.instancePath`, strConcat(N.instancePath, it.errorPath))
82 gen.assign(_`${err}.schemaPath`, str`${it.errSchemaPath}/${keyword}`)
83 if (it.opts.verbose) {
84 gen.assign(_`${err}.schema`, schemaValue)
85 gen.assign(_`${err}.data`, data)
90 function addError(gen: CodeGen, errObj: Code): void {
91 const err = gen.const("err", errObj)
93 _`${N.vErrors} === null`,
94 () => gen.assign(N.vErrors, _`[${err}]`),
95 _`${N.vErrors}.push(${err})`
97 gen.code(_`${N.errors}++`)
100 function returnErrors(it: SchemaCxt, errs: Code): void {
101 const {gen, validateName, schemaEnv} = it
102 if (schemaEnv.$async) {
103 gen.throw(_`new ${it.ValidationError as Name}(${errs})`)
105 gen.assign(_`${validateName}.errors`, errs)
111 keyword: new Name("keyword"),
112 schemaPath: new Name("schemaPath"), // also used in JTD errors
113 params: new Name("params"),
114 propertyName: new Name("propertyName"),
115 message: new Name("message"),
116 schema: new Name("schema"),
117 parentSchema: new Name("parentSchema"),
120 function errorObjectCode(
121 cxt: KeywordErrorCxt,
122 error: KeywordErrorDefinition,
123 errorPaths?: ErrorPaths
125 const {createErrors} = cxt.it
126 if (createErrors === false) return _`{}`
127 return errorObject(cxt, error, errorPaths)
130 function errorObject(
131 cxt: KeywordErrorCxt,
132 error: KeywordErrorDefinition,
133 errorPaths: ErrorPaths = {}
135 const {gen, it} = cxt
136 const keyValues: [Name, SafeExpr | string][] = [
137 errorInstancePath(it, errorPaths),
138 errorSchemaPath(cxt, errorPaths),
140 extraErrorProps(cxt, error, keyValues)
141 return gen.object(...keyValues)
144 function errorInstancePath({errorPath}: SchemaCxt, {instancePath}: ErrorPaths): [Name, Code] {
145 const instPath = instancePath
146 ? str`${errorPath}${getErrorPath(instancePath, Type.Str)}`
148 return [N.instancePath, strConcat(N.instancePath, instPath)]
151 function errorSchemaPath(
152 {keyword, it: {errSchemaPath}}: KeywordErrorCxt,
153 {schemaPath, parentSchema}: ErrorPaths
154 ): [Name, string | Code] {
155 let schPath = parentSchema ? errSchemaPath : str`${errSchemaPath}/${keyword}`
157 schPath = str`${schPath}${getErrorPath(schemaPath, Type.Str)}`
159 return [E.schemaPath, schPath]
162 function extraErrorProps(
163 cxt: KeywordErrorCxt,
164 {params, message}: KeywordErrorDefinition,
165 keyValues: [Name, SafeExpr | string][]
167 const {keyword, data, schemaValue, it} = cxt
168 const {opts, propertyName, topSchemaRef, schemaPath} = it
170 [E.keyword, keyword],
171 [E.params, typeof params == "function" ? params(cxt) : params || _`{}`]
174 keyValues.push([E.message, typeof message == "function" ? message(cxt) : message])
178 [E.schema, schemaValue],
179 [E.parentSchema, _`${topSchemaRef}${schemaPath}`],
183 if (propertyName) keyValues.push([E.propertyName, propertyName])