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)
|
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 |
|
82 |
|
83 | gen.if(not(valid), () => gen.break())
|
84 | }
|
85 | })
|
86 | })
|
87 | }
|
88 | },
|
89 | }
|
90 |
|
91 | export default def
|