1 | import type {CodeKeywordDefinition} from "../../types"
|
---|
2 | import type {KeywordCxt} from "../../compile/validate"
|
---|
3 | import {allSchemaProperties, usePattern} from "../code"
|
---|
4 | import {_, not, Name} from "../../compile/codegen"
|
---|
5 | import {alwaysValidSchema, checkStrictMode} from "../../compile/util"
|
---|
6 | import {evaluatedPropsToName, Type} from "../../compile/util"
|
---|
7 | import {AnySchema} from "../../types"
|
---|
8 |
|
---|
9 | const def: CodeKeywordDefinition = {
|
---|
10 | keyword: "patternProperties",
|
---|
11 | type: "object",
|
---|
12 | schemaType: "object",
|
---|
13 | code(cxt: KeywordCxt) {
|
---|
14 | const {gen, schema, data, parentSchema, it} = cxt
|
---|
15 | const {opts} = it
|
---|
16 | const patterns = allSchemaProperties(schema)
|
---|
17 | const alwaysValidPatterns = patterns.filter((p) =>
|
---|
18 | alwaysValidSchema(it, schema[p] as AnySchema)
|
---|
19 | )
|
---|
20 |
|
---|
21 | if (
|
---|
22 | patterns.length === 0 ||
|
---|
23 | (alwaysValidPatterns.length === patterns.length &&
|
---|
24 | (!it.opts.unevaluated || it.props === true))
|
---|
25 | ) {
|
---|
26 | return
|
---|
27 | }
|
---|
28 |
|
---|
29 | const checkProperties =
|
---|
30 | opts.strictSchema && !opts.allowMatchingProperties && parentSchema.properties
|
---|
31 | const valid = gen.name("valid")
|
---|
32 | if (it.props !== true && !(it.props instanceof Name)) {
|
---|
33 | it.props = evaluatedPropsToName(gen, it.props)
|
---|
34 | }
|
---|
35 | const {props} = it
|
---|
36 | validatePatternProperties()
|
---|
37 |
|
---|
38 | function validatePatternProperties(): void {
|
---|
39 | for (const pat of patterns) {
|
---|
40 | if (checkProperties) checkMatchingProperties(pat)
|
---|
41 | if (it.allErrors) {
|
---|
42 | validateProperties(pat)
|
---|
43 | } else {
|
---|
44 | gen.var(valid, true) // TODO var
|
---|
45 | validateProperties(pat)
|
---|
46 | gen.if(valid)
|
---|
47 | }
|
---|
48 | }
|
---|
49 | }
|
---|
50 |
|
---|
51 | function checkMatchingProperties(pat: string): void {
|
---|
52 | for (const prop in checkProperties) {
|
---|
53 | if (new RegExp(pat).test(prop)) {
|
---|
54 | checkStrictMode(
|
---|
55 | it,
|
---|
56 | `property ${prop} matches pattern ${pat} (use allowMatchingProperties)`
|
---|
57 | )
|
---|
58 | }
|
---|
59 | }
|
---|
60 | }
|
---|
61 |
|
---|
62 | function validateProperties(pat: string): void {
|
---|
63 | gen.forIn("key", data, (key) => {
|
---|
64 | gen.if(_`${usePattern(cxt, pat)}.test(${key})`, () => {
|
---|
65 | const alwaysValid = alwaysValidPatterns.includes(pat)
|
---|
66 | if (!alwaysValid) {
|
---|
67 | cxt.subschema(
|
---|
68 | {
|
---|
69 | keyword: "patternProperties",
|
---|
70 | schemaProp: pat,
|
---|
71 | dataProp: key,
|
---|
72 | dataPropType: Type.Str,
|
---|
73 | },
|
---|
74 | valid
|
---|
75 | )
|
---|
76 | }
|
---|
77 |
|
---|
78 | if (it.opts.unevaluated && props !== true) {
|
---|
79 | gen.assign(_`${props}[${key}]`, true)
|
---|
80 | } else if (!alwaysValid && !it.allErrors) {
|
---|
81 | // can short-circuit if `unevaluatedProperties` is not supported (opts.next === false)
|
---|
82 | // or if all properties were evaluated (props === true)
|
---|
83 | gen.if(not(valid), () => gen.break())
|
---|
84 | }
|
---|
85 | })
|
---|
86 | })
|
---|
87 | }
|
---|
88 | },
|
---|
89 | }
|
---|
90 |
|
---|
91 | export default def
|
---|