1 | import type {AnySchema, SchemaMap} from "../types"
|
---|
2 | import type {SchemaCxt} from "../compile"
|
---|
3 | import type {KeywordCxt} from "../compile/validate"
|
---|
4 | import {CodeGen, _, and, or, not, nil, strConcat, getProperty, Code, Name} from "../compile/codegen"
|
---|
5 | import {alwaysValidSchema, Type} from "../compile/util"
|
---|
6 | import N from "../compile/names"
|
---|
7 |
|
---|
8 | export function checkReportMissingProp(cxt: KeywordCxt, prop: string): void {
|
---|
9 | const {gen, data, it} = cxt
|
---|
10 | gen.if(noPropertyInData(gen, data, prop, it.opts.ownProperties), () => {
|
---|
11 | cxt.setParams({missingProperty: _`${prop}`}, true)
|
---|
12 | cxt.error()
|
---|
13 | })
|
---|
14 | }
|
---|
15 |
|
---|
16 | export function checkMissingProp(
|
---|
17 | {gen, data, it: {opts}}: KeywordCxt,
|
---|
18 | properties: string[],
|
---|
19 | missing: Name
|
---|
20 | ): Code {
|
---|
21 | return or(
|
---|
22 | ...properties.map((prop) =>
|
---|
23 | and(noPropertyInData(gen, data, prop, opts.ownProperties), _`${missing} = ${prop}`)
|
---|
24 | )
|
---|
25 | )
|
---|
26 | }
|
---|
27 |
|
---|
28 | export function reportMissingProp(cxt: KeywordCxt, missing: Name): void {
|
---|
29 | cxt.setParams({missingProperty: missing}, true)
|
---|
30 | cxt.error()
|
---|
31 | }
|
---|
32 |
|
---|
33 | export function hasPropFunc(gen: CodeGen): Name {
|
---|
34 | return gen.scopeValue("func", {
|
---|
35 | // eslint-disable-next-line @typescript-eslint/unbound-method
|
---|
36 | ref: Object.prototype.hasOwnProperty,
|
---|
37 | code: _`Object.prototype.hasOwnProperty`,
|
---|
38 | })
|
---|
39 | }
|
---|
40 |
|
---|
41 | export function isOwnProperty(gen: CodeGen, data: Name, property: Name | string): Code {
|
---|
42 | return _`${hasPropFunc(gen)}.call(${data}, ${property})`
|
---|
43 | }
|
---|
44 |
|
---|
45 | export function propertyInData(
|
---|
46 | gen: CodeGen,
|
---|
47 | data: Name,
|
---|
48 | property: Name | string,
|
---|
49 | ownProperties?: boolean
|
---|
50 | ): Code {
|
---|
51 | const cond = _`${data}${getProperty(property)} !== undefined`
|
---|
52 | return ownProperties ? _`${cond} && ${isOwnProperty(gen, data, property)}` : cond
|
---|
53 | }
|
---|
54 |
|
---|
55 | export function noPropertyInData(
|
---|
56 | gen: CodeGen,
|
---|
57 | data: Name,
|
---|
58 | property: Name | string,
|
---|
59 | ownProperties?: boolean
|
---|
60 | ): Code {
|
---|
61 | const cond = _`${data}${getProperty(property)} === undefined`
|
---|
62 | return ownProperties ? or(cond, not(isOwnProperty(gen, data, property))) : cond
|
---|
63 | }
|
---|
64 |
|
---|
65 | export function allSchemaProperties(schemaMap?: SchemaMap): string[] {
|
---|
66 | return schemaMap ? Object.keys(schemaMap).filter((p) => p !== "__proto__") : []
|
---|
67 | }
|
---|
68 |
|
---|
69 | export function schemaProperties(it: SchemaCxt, schemaMap: SchemaMap): string[] {
|
---|
70 | return allSchemaProperties(schemaMap).filter(
|
---|
71 | (p) => !alwaysValidSchema(it, schemaMap[p] as AnySchema)
|
---|
72 | )
|
---|
73 | }
|
---|
74 |
|
---|
75 | export function callValidateCode(
|
---|
76 | {schemaCode, data, it: {gen, topSchemaRef, schemaPath, errorPath}, it}: KeywordCxt,
|
---|
77 | func: Code,
|
---|
78 | context: Code,
|
---|
79 | passSchema?: boolean
|
---|
80 | ): Code {
|
---|
81 | const dataAndSchema = passSchema ? _`${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data
|
---|
82 | const valCxt: [Name, Code | number][] = [
|
---|
83 | [N.instancePath, strConcat(N.instancePath, errorPath)],
|
---|
84 | [N.parentData, it.parentData],
|
---|
85 | [N.parentDataProperty, it.parentDataProperty],
|
---|
86 | [N.rootData, N.rootData],
|
---|
87 | ]
|
---|
88 | if (it.opts.dynamicRef) valCxt.push([N.dynamicAnchors, N.dynamicAnchors])
|
---|
89 | const args = _`${dataAndSchema}, ${gen.object(...valCxt)}`
|
---|
90 | return context !== nil ? _`${func}.call(${context}, ${args})` : _`${func}(${args})`
|
---|
91 | }
|
---|
92 |
|
---|
93 | export function usePattern({gen, it: {opts}}: KeywordCxt, pattern: string): Name {
|
---|
94 | const u = opts.unicodeRegExp ? "u" : ""
|
---|
95 | return gen.scopeValue("pattern", {
|
---|
96 | key: pattern,
|
---|
97 | ref: new RegExp(pattern, u),
|
---|
98 | code: _`new RegExp(${pattern}, ${u})`,
|
---|
99 | })
|
---|
100 | }
|
---|
101 |
|
---|
102 | export function validateArray(cxt: KeywordCxt): Name {
|
---|
103 | const {gen, data, keyword, it} = cxt
|
---|
104 | const valid = gen.name("valid")
|
---|
105 | if (it.allErrors) {
|
---|
106 | const validArr = gen.let("valid", true)
|
---|
107 | validateItems(() => gen.assign(validArr, false))
|
---|
108 | return validArr
|
---|
109 | }
|
---|
110 | gen.var(valid, true)
|
---|
111 | validateItems(() => gen.break())
|
---|
112 | return valid
|
---|
113 |
|
---|
114 | function validateItems(notValid: () => void): void {
|
---|
115 | const len = gen.const("len", _`${data}.length`)
|
---|
116 | gen.forRange("i", 0, len, (i) => {
|
---|
117 | cxt.subschema(
|
---|
118 | {
|
---|
119 | keyword,
|
---|
120 | dataProp: i,
|
---|
121 | dataPropType: Type.Num,
|
---|
122 | },
|
---|
123 | valid
|
---|
124 | )
|
---|
125 | gen.if(not(valid), notValid)
|
---|
126 | })
|
---|
127 | }
|
---|
128 | }
|
---|
129 |
|
---|
130 | export function validateUnion(cxt: KeywordCxt): void {
|
---|
131 | const {gen, schema, keyword, it} = cxt
|
---|
132 | /* istanbul ignore if */
|
---|
133 | if (!Array.isArray(schema)) throw new Error("ajv implementation error")
|
---|
134 | const alwaysValid = schema.some((sch: AnySchema) => alwaysValidSchema(it, sch))
|
---|
135 | if (alwaysValid && !it.opts.unevaluated) return
|
---|
136 |
|
---|
137 | const valid = gen.let("valid", false)
|
---|
138 | const schValid = gen.name("_valid")
|
---|
139 |
|
---|
140 | gen.block(() =>
|
---|
141 | schema.forEach((_sch: AnySchema, i: number) => {
|
---|
142 | const schCxt = cxt.subschema(
|
---|
143 | {
|
---|
144 | keyword,
|
---|
145 | schemaProp: i,
|
---|
146 | compositeRule: true,
|
---|
147 | },
|
---|
148 | schValid
|
---|
149 | )
|
---|
150 | gen.assign(valid, _`${valid} || ${schValid}`)
|
---|
151 | const merged = cxt.mergeValidEvaluated(schCxt, schValid)
|
---|
152 | // can short-circuit if `unevaluatedProperties/Items` not supported (opts.unevaluated !== true)
|
---|
153 | // or if all properties and items were evaluated (it.props === true && it.items === true)
|
---|
154 | if (!merged) gen.if(not(valid))
|
---|
155 | })
|
---|
156 | )
|
---|
157 |
|
---|
158 | cxt.result(
|
---|
159 | valid,
|
---|
160 | () => cxt.reset(),
|
---|
161 | () => cxt.error(true)
|
---|
162 | )
|
---|
163 | }
|
---|