UNPKG

5.27 kBPlain TextView Raw
1import type {KeywordErrorCxt, KeywordErrorDefinition} from "../types"
2import type {SchemaCxt} from "./index"
3import {CodeGen, _, str, strConcat, Code, Name} from "./codegen"
4import {SafeExpr} from "./codegen/code"
5import {getErrorPath, Type} from "./util"
6import N from "./names"
7
8export const keywordError: KeywordErrorDefinition = {
9 message: ({keyword}) => str`must pass "${keyword}" keyword validation`,
10}
11
12export const keyword$DataError: KeywordErrorDefinition = {
13 message: ({keyword, schemaType}) =>
14 schemaType
15 ? str`"${keyword}" keyword must be ${schemaType} ($data)`
16 : str`"${keyword}" keyword is invalid ($data)`,
17}
18
19export interface ErrorPaths {
20 instancePath?: Code
21 schemaPath?: string
22 parentSchema?: boolean
23}
24
25export function reportError(
26 cxt: KeywordErrorCxt,
27 error: KeywordErrorDefinition = keywordError,
28 errorPaths?: ErrorPaths,
29 overrideAllErrors?: boolean
30): void {
31 const {it} = cxt
32 const {gen, compositeRule, allErrors} = it
33 const errObj = errorObjectCode(cxt, error, errorPaths)
34 if (overrideAllErrors ?? (compositeRule || allErrors)) {
35 addError(gen, errObj)
36 } else {
37 returnErrors(it, _`[${errObj}]`)
38 }
39}
40
41export function reportExtraError(
42 cxt: KeywordErrorCxt,
43 error: KeywordErrorDefinition = keywordError,
44 errorPaths?: ErrorPaths
45): void {
46 const {it} = cxt
47 const {gen, compositeRule, allErrors} = it
48 const errObj = errorObjectCode(cxt, error, errorPaths)
49 addError(gen, errObj)
50 if (!(compositeRule || allErrors)) {
51 returnErrors(it, N.vErrors)
52 }
53}
54
55export function resetErrorsCount(gen: CodeGen, errsCount: Name): void {
56 gen.assign(N.errors, errsCount)
57 gen.if(_`${N.vErrors} !== null`, () =>
58 gen.if(
59 errsCount,
60 () => gen.assign(_`${N.vErrors}.length`, errsCount),
61 () => gen.assign(N.vErrors, null)
62 )
63 )
64}
65
66export function extendErrors({
67 gen,
68 keyword,
69 schemaValue,
70 data,
71 errsCount,
72 it,
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))
81 )
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)
86 }
87 })
88}
89
90function addError(gen: CodeGen, errObj: Code): void {
91 const err = gen.const("err", errObj)
92 gen.if(
93 _`${N.vErrors} === null`,
94 () => gen.assign(N.vErrors, _`[${err}]`),
95 _`${N.vErrors}.push(${err})`
96 )
97 gen.code(_`${N.errors}++`)
98}
99
100function 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})`)
104 } else {
105 gen.assign(_`${validateName}.errors`, errs)
106 gen.return(false)
107 }
108}
109
110const E = {
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"),
118}
119
120function errorObjectCode(
121 cxt: KeywordErrorCxt,
122 error: KeywordErrorDefinition,
123 errorPaths?: ErrorPaths
124): Code {
125 const {createErrors} = cxt.it
126 if (createErrors === false) return _`{}`
127 return errorObject(cxt, error, errorPaths)
128}
129
130function errorObject(
131 cxt: KeywordErrorCxt,
132 error: KeywordErrorDefinition,
133 errorPaths: ErrorPaths = {}
134): Code {
135 const {gen, it} = cxt
136 const keyValues: [Name, SafeExpr | string][] = [
137 errorInstancePath(it, errorPaths),
138 errorSchemaPath(cxt, errorPaths),
139 ]
140 extraErrorProps(cxt, error, keyValues)
141 return gen.object(...keyValues)
142}
143
144function errorInstancePath({errorPath}: SchemaCxt, {instancePath}: ErrorPaths): [Name, Code] {
145 const instPath = instancePath
146 ? str`${errorPath}${getErrorPath(instancePath, Type.Str)}`
147 : errorPath
148 return [N.instancePath, strConcat(N.instancePath, instPath)]
149}
150
151function errorSchemaPath(
152 {keyword, it: {errSchemaPath}}: KeywordErrorCxt,
153 {schemaPath, parentSchema}: ErrorPaths
154): [Name, string | Code] {
155 let schPath = parentSchema ? errSchemaPath : str`${errSchemaPath}/${keyword}`
156 if (schemaPath) {
157 schPath = str`${schPath}${getErrorPath(schemaPath, Type.Str)}`
158 }
159 return [E.schemaPath, schPath]
160}
161
162function extraErrorProps(
163 cxt: KeywordErrorCxt,
164 {params, message}: KeywordErrorDefinition,
165 keyValues: [Name, SafeExpr | string][]
166): void {
167 const {keyword, data, schemaValue, it} = cxt
168 const {opts, propertyName, topSchemaRef, schemaPath} = it
169 keyValues.push(
170 [E.keyword, keyword],
171 [E.params, typeof params == "function" ? params(cxt) : params || _`{}`]
172 )
173 if (opts.messages) {
174 keyValues.push([E.message, typeof message == "function" ? message(cxt) : message])
175 }
176 if (opts.verbose) {
177 keyValues.push(
178 [E.schema, schemaValue],
179 [E.parentSchema, _`${topSchemaRef}${schemaPath}`],
180 [N.data, data]
181 )
182 }
183 if (propertyName) keyValues.push([E.propertyName, propertyName])
184}