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
			| 
											3 years ago
										 | 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]) | ||
|  | } |