| import type {KeywordErrorCxt, KeywordErrorDefinition} from "../types"
|
| import type {SchemaCxt} from "./index"
|
| import {CodeGen, _, str, strConcat, Code, Name} from "./codegen"
|
| import {SafeExpr} from "./codegen/code"
|
| import {getErrorPath, Type} from "./util"
|
| import N from "./names"
|
|
|
| export const keywordError: KeywordErrorDefinition = {
|
| message: ({keyword}) => str`must pass "${keyword}" keyword validation`,
|
| }
|
|
|
| export const keyword$DataError: KeywordErrorDefinition = {
|
| message: ({keyword, schemaType}) =>
|
| schemaType
|
| ? str`"${keyword}" keyword must be ${schemaType} ($data)`
|
| : str`"${keyword}" keyword is invalid ($data)`,
|
| }
|
|
|
| export interface ErrorPaths {
|
| instancePath?: Code
|
| schemaPath?: string
|
| parentSchema?: boolean
|
| }
|
|
|
| export function reportError(
|
| cxt: KeywordErrorCxt,
|
| error: KeywordErrorDefinition = keywordError,
|
| errorPaths?: ErrorPaths,
|
| overrideAllErrors?: boolean
|
| ): void {
|
| const {it} = cxt
|
| const {gen, compositeRule, allErrors} = it
|
| const errObj = errorObjectCode(cxt, error, errorPaths)
|
| if (overrideAllErrors ?? (compositeRule || allErrors)) {
|
| addError(gen, errObj)
|
| } else {
|
| returnErrors(it, _`[${errObj}]`)
|
| }
|
| }
|
|
|
| export function reportExtraError(
|
| cxt: KeywordErrorCxt,
|
| error: KeywordErrorDefinition = keywordError,
|
| errorPaths?: ErrorPaths
|
| ): void {
|
| const {it} = cxt
|
| const {gen, compositeRule, allErrors} = it
|
| const errObj = errorObjectCode(cxt, error, errorPaths)
|
| addError(gen, errObj)
|
| if (!(compositeRule || allErrors)) {
|
| returnErrors(it, N.vErrors)
|
| }
|
| }
|
|
|
| export function resetErrorsCount(gen: CodeGen, errsCount: Name): void {
|
| gen.assign(N.errors, errsCount)
|
| gen.if(_`${N.vErrors} !== null`, () =>
|
| gen.if(
|
| errsCount,
|
| () => gen.assign(_`${N.vErrors}.length`, errsCount),
|
| () => gen.assign(N.vErrors, null)
|
| )
|
| )
|
| }
|
|
|
| export function extendErrors({
|
| gen,
|
| keyword,
|
| schemaValue,
|
| data,
|
| errsCount,
|
| it,
|
| }: KeywordErrorCxt): void {
|
|
|
| if (errsCount === undefined) throw new Error("ajv implementation error")
|
| const err = gen.name("err")
|
| gen.forRange("i", errsCount, N.errors, (i) => {
|
| gen.const(err, _`${N.vErrors}[${i}]`)
|
| gen.if(_`${err}.instancePath === undefined`, () =>
|
| gen.assign(_`${err}.instancePath`, strConcat(N.instancePath, it.errorPath))
|
| )
|
| gen.assign(_`${err}.schemaPath`, str`${it.errSchemaPath}/${keyword}`)
|
| if (it.opts.verbose) {
|
| gen.assign(_`${err}.schema`, schemaValue)
|
| gen.assign(_`${err}.data`, data)
|
| }
|
| })
|
| }
|
|
|
| function addError(gen: CodeGen, errObj: Code): void {
|
| const err = gen.const("err", errObj)
|
| gen.if(
|
| _`${N.vErrors} === null`,
|
| () => gen.assign(N.vErrors, _`[${err}]`),
|
| _`${N.vErrors}.push(${err})`
|
| )
|
| gen.code(_`${N.errors}++`)
|
| }
|
|
|
| function returnErrors(it: SchemaCxt, errs: Code): void {
|
| const {gen, validateName, schemaEnv} = it
|
| if (schemaEnv.$async) {
|
| gen.throw(_`new ${it.ValidationError as Name}(${errs})`)
|
| } else {
|
| gen.assign(_`${validateName}.errors`, errs)
|
| gen.return(false)
|
| }
|
| }
|
|
|
| const E = {
|
| keyword: new Name("keyword"),
|
| schemaPath: new Name("schemaPath"),
|
| params: new Name("params"),
|
| propertyName: new Name("propertyName"),
|
| message: new Name("message"),
|
| schema: new Name("schema"),
|
| parentSchema: new Name("parentSchema"),
|
| }
|
|
|
| function errorObjectCode(
|
| cxt: KeywordErrorCxt,
|
| error: KeywordErrorDefinition,
|
| errorPaths?: ErrorPaths
|
| ): Code {
|
| const {createErrors} = cxt.it
|
| if (createErrors === false) return _`{}`
|
| return errorObject(cxt, error, errorPaths)
|
| }
|
|
|
| function errorObject(
|
| cxt: KeywordErrorCxt,
|
| error: KeywordErrorDefinition,
|
| errorPaths: ErrorPaths = {}
|
| ): Code {
|
| const {gen, it} = cxt
|
| const keyValues: [Name, SafeExpr | string][] = [
|
| errorInstancePath(it, errorPaths),
|
| errorSchemaPath(cxt, errorPaths),
|
| ]
|
| extraErrorProps(cxt, error, keyValues)
|
| return gen.object(...keyValues)
|
| }
|
|
|
| function errorInstancePath({errorPath}: SchemaCxt, {instancePath}: ErrorPaths): [Name, Code] {
|
| const instPath = instancePath
|
| ? str`${errorPath}${getErrorPath(instancePath, Type.Str)}`
|
| : errorPath
|
| return [N.instancePath, strConcat(N.instancePath, instPath)]
|
| }
|
|
|
| function errorSchemaPath(
|
| {keyword, it: {errSchemaPath}}: KeywordErrorCxt,
|
| {schemaPath, parentSchema}: ErrorPaths
|
| ): [Name, string | Code] {
|
| let schPath = parentSchema ? errSchemaPath : str`${errSchemaPath}/${keyword}`
|
| if (schemaPath) {
|
| schPath = str`${schPath}${getErrorPath(schemaPath, Type.Str)}`
|
| }
|
| return [E.schemaPath, schPath]
|
| }
|
|
|
| function extraErrorProps(
|
| cxt: KeywordErrorCxt,
|
| {params, message}: KeywordErrorDefinition,
|
| keyValues: [Name, SafeExpr | string][]
|
| ): void {
|
| const {keyword, data, schemaValue, it} = cxt
|
| const {opts, propertyName, topSchemaRef, schemaPath} = it
|
| keyValues.push(
|
| [E.keyword, keyword],
|
| [E.params, typeof params == "function" ? params(cxt) : params || _`{}`]
|
| )
|
| if (opts.messages) {
|
| keyValues.push([E.message, typeof message == "function" ? message(cxt) : message])
|
| }
|
| if (opts.verbose) {
|
| keyValues.push(
|
| [E.schema, schemaValue],
|
| [E.parentSchema, _`${topSchemaRef}${schemaPath}`],
|
| [N.data, data]
|
| )
|
| }
|
| if (propertyName) keyValues.push([E.propertyName, propertyName])
|
| }
|
|
|