iterate.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import type {SchemaObjCxt} from ".."
  2. import type {JSONType, Rule, RuleGroup} from "../rules"
  3. import {shouldUseGroup, shouldUseRule} from "./applicability"
  4. import {checkDataType, reportTypeError} from "./dataType"
  5. import {assignDefaults} from "./defaults"
  6. import {keywordCode} from "./keyword"
  7. import {schemaHasRulesButRef} from "../util"
  8. import {checkStrictMode} from "."
  9. import {_, Name} from "../codegen"
  10. import N from "../names"
  11. export function schemaKeywords(
  12. it: SchemaObjCxt,
  13. types: JSONType[],
  14. typeErrors: boolean,
  15. errsCount?: Name
  16. ): void {
  17. const {gen, schema, data, allErrors, opts, self} = it
  18. const {RULES} = self
  19. if (schema.$ref && (opts.ignoreKeywordsWithRef || !schemaHasRulesButRef(schema, RULES))) {
  20. gen.block(() => keywordCode(it, "$ref", (RULES.all.$ref as Rule).definition)) // TODO typecast
  21. return
  22. }
  23. checkStrictTypes(it, types)
  24. gen.block(() => {
  25. for (const group of RULES.rules) groupKeywords(group)
  26. groupKeywords(RULES.post)
  27. })
  28. function groupKeywords(group: RuleGroup): void {
  29. if (!shouldUseGroup(schema, group)) return
  30. if (group.type) {
  31. gen.if(checkDataType(group.type, data, opts.strict))
  32. iterateKeywords(it, group)
  33. if (types.length === 1 && types[0] === group.type && typeErrors) {
  34. gen.else()
  35. reportTypeError(it)
  36. }
  37. gen.endIf()
  38. } else {
  39. iterateKeywords(it, group)
  40. }
  41. // TODO make it "ok" call?
  42. if (!allErrors) gen.if(_`${N.errors} === ${errsCount || 0}`)
  43. }
  44. }
  45. function iterateKeywords(it: SchemaObjCxt, group: RuleGroup): void {
  46. const {
  47. gen,
  48. schema,
  49. opts: {useDefaults},
  50. } = it
  51. if (useDefaults) assignDefaults(it, group.type)
  52. gen.block(() => {
  53. for (const rule of group.rules) {
  54. if (shouldUseRule(schema, rule)) {
  55. keywordCode(it, rule.keyword, rule.definition, group.type)
  56. }
  57. }
  58. })
  59. }
  60. function checkStrictTypes(it: SchemaObjCxt, types: JSONType[]): void {
  61. if (it.schemaEnv.meta || !it.opts.strictTypes) return
  62. checkContextTypes(it, types)
  63. if (!it.opts.allowUnionTypes) checkMultipleTypes(it, types)
  64. checkKeywordTypes(it, it.dataTypes)
  65. }
  66. function checkContextTypes(it: SchemaObjCxt, types: JSONType[]): void {
  67. if (!types.length) return
  68. if (!it.dataTypes.length) {
  69. it.dataTypes = types
  70. return
  71. }
  72. types.forEach((t) => {
  73. if (!includesType(it.dataTypes, t)) {
  74. strictTypesError(it, `type "${t}" not allowed by context "${it.dataTypes.join(",")}"`)
  75. }
  76. })
  77. it.dataTypes = it.dataTypes.filter((t) => includesType(types, t))
  78. }
  79. function checkMultipleTypes(it: SchemaObjCxt, ts: JSONType[]): void {
  80. if (ts.length > 1 && !(ts.length === 2 && ts.includes("null"))) {
  81. strictTypesError(it, "use allowUnionTypes to allow union type keyword")
  82. }
  83. }
  84. function checkKeywordTypes(it: SchemaObjCxt, ts: JSONType[]): void {
  85. const rules = it.self.RULES.all
  86. for (const keyword in rules) {
  87. const rule = rules[keyword]
  88. if (typeof rule == "object" && shouldUseRule(it.schema, rule)) {
  89. const {type} = rule.definition
  90. if (type.length && !type.some((t) => hasApplicableType(ts, t))) {
  91. strictTypesError(it, `missing type "${type.join(",")}" for keyword "${keyword}"`)
  92. }
  93. }
  94. }
  95. }
  96. function hasApplicableType(schTs: JSONType[], kwdT: JSONType): boolean {
  97. return schTs.includes(kwdT) || (kwdT === "number" && schTs.includes("integer"))
  98. }
  99. function includesType(ts: JSONType[], t: JSONType): boolean {
  100. return ts.includes(t) || (t === "integer" && ts.includes("number"))
  101. }
  102. function strictTypesError(it: SchemaObjCxt, msg: string): void {
  103. const schemaPath = it.schemaEnv.baseId + it.errSchemaPath
  104. msg += ` at "${schemaPath}" (strictTypes)`
  105. checkStrictMode(it, msg, it.opts.strictTypes)
  106. }