source: trip-planner-front/node_modules/ajv/lib/compile/validate/keyword.ts

Last change on this file was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 5.2 KB
Line 
1import type {KeywordCxt} from "."
2import type {
3 AnySchema,
4 SchemaValidateFunction,
5 AnyValidateFunction,
6 AddedKeywordDefinition,
7 MacroKeywordDefinition,
8 FuncKeywordDefinition,
9} from "../../types"
10import type {SchemaObjCxt} from ".."
11import {_, nil, not, stringify, Code, Name, CodeGen} from "../codegen"
12import N from "../names"
13import type {JSONType} from "../rules"
14import {callValidateCode} from "../../vocabularies/code"
15import {extendErrors} from "../errors"
16
17type KeywordCompilationResult = AnySchema | SchemaValidateFunction | AnyValidateFunction
18
19export function macroKeywordCode(cxt: KeywordCxt, def: MacroKeywordDefinition): void {
20 const {gen, keyword, schema, parentSchema, it} = cxt
21 const macroSchema = def.macro.call(it.self, schema, parentSchema, it)
22 const schemaRef = useKeyword(gen, keyword, macroSchema)
23 if (it.opts.validateSchema !== false) it.self.validateSchema(macroSchema, true)
24
25 const valid = gen.name("valid")
26 cxt.subschema(
27 {
28 schema: macroSchema,
29 schemaPath: nil,
30 errSchemaPath: `${it.errSchemaPath}/${keyword}`,
31 topSchemaRef: schemaRef,
32 compositeRule: true,
33 },
34 valid
35 )
36 cxt.pass(valid, () => cxt.error(true))
37}
38
39export function funcKeywordCode(cxt: KeywordCxt, def: FuncKeywordDefinition): void {
40 const {gen, keyword, schema, parentSchema, $data, it} = cxt
41 checkAsyncKeyword(it, def)
42 const validate =
43 !$data && def.compile ? def.compile.call(it.self, schema, parentSchema, it) : def.validate
44 const validateRef = useKeyword(gen, keyword, validate)
45 const valid = gen.let("valid")
46 cxt.block$data(valid, validateKeyword)
47 cxt.ok(def.valid ?? valid)
48
49 function validateKeyword(): void {
50 if (def.errors === false) {
51 assignValid()
52 if (def.modifying) modifyData(cxt)
53 reportErrs(() => cxt.error())
54 } else {
55 const ruleErrs = def.async ? validateAsync() : validateSync()
56 if (def.modifying) modifyData(cxt)
57 reportErrs(() => addErrs(cxt, ruleErrs))
58 }
59 }
60
61 function validateAsync(): Name {
62 const ruleErrs = gen.let("ruleErrs", null)
63 gen.try(
64 () => assignValid(_`await `),
65 (e) =>
66 gen.assign(valid, false).if(
67 _`${e} instanceof ${it.ValidationError as Name}`,
68 () => gen.assign(ruleErrs, _`${e}.errors`),
69 () => gen.throw(e)
70 )
71 )
72 return ruleErrs
73 }
74
75 function validateSync(): Code {
76 const validateErrs = _`${validateRef}.errors`
77 gen.assign(validateErrs, null)
78 assignValid(nil)
79 return validateErrs
80 }
81
82 function assignValid(_await: Code = def.async ? _`await ` : nil): void {
83 const passCxt = it.opts.passContext ? N.this : N.self
84 const passSchema = !(("compile" in def && !$data) || def.schema === false)
85 gen.assign(
86 valid,
87 _`${_await}${callValidateCode(cxt, validateRef, passCxt, passSchema)}`,
88 def.modifying
89 )
90 }
91
92 function reportErrs(errors: () => void): void {
93 gen.if(not(def.valid ?? valid), errors)
94 }
95}
96
97function modifyData(cxt: KeywordCxt): void {
98 const {gen, data, it} = cxt
99 gen.if(it.parentData, () => gen.assign(data, _`${it.parentData}[${it.parentDataProperty}]`))
100}
101
102function addErrs(cxt: KeywordCxt, errs: Code): void {
103 const {gen} = cxt
104 gen.if(
105 _`Array.isArray(${errs})`,
106 () => {
107 gen
108 .assign(N.vErrors, _`${N.vErrors} === null ? ${errs} : ${N.vErrors}.concat(${errs})`)
109 .assign(N.errors, _`${N.vErrors}.length`)
110 extendErrors(cxt)
111 },
112 () => cxt.error()
113 )
114}
115
116function checkAsyncKeyword({schemaEnv}: SchemaObjCxt, def: FuncKeywordDefinition): void {
117 if (def.async && !schemaEnv.$async) throw new Error("async keyword in sync schema")
118}
119
120function useKeyword(gen: CodeGen, keyword: string, result?: KeywordCompilationResult): Name {
121 if (result === undefined) throw new Error(`keyword "${keyword}" failed to compile`)
122 return gen.scopeValue(
123 "keyword",
124 typeof result == "function" ? {ref: result} : {ref: result, code: stringify(result)}
125 )
126}
127
128export function validSchemaType(
129 schema: unknown,
130 schemaType: JSONType[],
131 allowUndefined = false
132): boolean {
133 // TODO add tests
134 return (
135 !schemaType.length ||
136 schemaType.some((st) =>
137 st === "array"
138 ? Array.isArray(schema)
139 : st === "object"
140 ? schema && typeof schema == "object" && !Array.isArray(schema)
141 : typeof schema == st || (allowUndefined && typeof schema == "undefined")
142 )
143 )
144}
145
146export function validateKeywordUsage(
147 {schema, opts, self, errSchemaPath}: SchemaObjCxt,
148 def: AddedKeywordDefinition,
149 keyword: string
150): void {
151 /* istanbul ignore if */
152 if (Array.isArray(def.keyword) ? !def.keyword.includes(keyword) : def.keyword !== keyword) {
153 throw new Error("ajv implementation error")
154 }
155
156 const deps = def.dependencies
157 if (deps?.some((kwd) => !Object.prototype.hasOwnProperty.call(schema, kwd))) {
158 throw new Error(`parent schema must have dependencies of ${keyword}: ${deps.join(",")}`)
159 }
160
161 if (def.validateSchema) {
162 const valid = def.validateSchema(schema[keyword])
163 if (!valid) {
164 const msg =
165 `keyword "${keyword}" value is invalid at path "${errSchemaPath}": ` +
166 self.errorsText(def.validateSchema.errors)
167 if (opts.validateSchema === "log") self.logger.error(msg)
168 else throw new Error(msg)
169 }
170 }
171}
Note: See TracBrowser for help on using the repository browser.