massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / table / node_modules / ajv / lib / compile / validate / keyword.ts
1 import type {KeywordCxt} from "."
2 import type {
3   AnySchema,
4   SchemaValidateFunction,
5   AnyValidateFunction,
6   AddedKeywordDefinition,
7   MacroKeywordDefinition,
8   FuncKeywordDefinition,
9 } from "../../types"
10 import type {SchemaObjCxt} from ".."
11 import {_, nil, not, stringify, Code, Name, CodeGen} from "../codegen"
12 import N from "../names"
13 import type {JSONType} from "../rules"
14 import {callValidateCode} from "../../vocabularies/code"
15 import {extendErrors} from "../errors"
16
17 type KeywordCompilationResult = AnySchema | SchemaValidateFunction | AnyValidateFunction
18
19 export 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
39 export 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
97 function modifyData(cxt: KeywordCxt): void {
98   const {gen, data, it} = cxt
99   gen.if(it.parentData, () => gen.assign(data, _`${it.parentData}[${it.parentDataProperty}]`))
100 }
101
102 function 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
116 function checkAsyncKeyword({schemaEnv}: SchemaObjCxt, def: FuncKeywordDefinition): void {
117   if (def.async && !schemaEnv.$async) throw new Error("async keyword in sync schema")
118 }
119
120 function 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
128 export 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
146 export 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 }