Spaces:
Runtime error
Runtime error
| 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 { | |
| /* istanbul ignore if */ | |
| 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"), // also used in JTD errors | |
| 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]) | |
| } | |