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])
							 | 
						||
| 
								 | 
							
								}
							 |