You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							185 lines
						
					
					
						
							5.1 KiB
						
					
					
				
			
		
		
	
	
							185 lines
						
					
					
						
							5.1 KiB
						
					
					
				| 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])
 | |
| }
 |