subschema.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import type {AnySchema} from "../../types"
  2. import type {SchemaObjCxt} from ".."
  3. import {_, str, getProperty, Code, Name} from "../codegen"
  4. import {escapeFragment, getErrorPath, Type} from "../util"
  5. import type {JSONType} from "../rules"
  6. export interface SubschemaContext {
  7. // TODO use Optional? align with SchemCxt property types
  8. schema: AnySchema
  9. schemaPath: Code
  10. errSchemaPath: string
  11. topSchemaRef?: Code
  12. errorPath?: Code
  13. dataLevel?: number
  14. dataTypes?: JSONType[]
  15. data?: Name
  16. parentData?: Name
  17. parentDataProperty?: Code | number
  18. dataNames?: Name[]
  19. dataPathArr?: (Code | number)[]
  20. propertyName?: Name
  21. jtdDiscriminator?: string
  22. jtdMetadata?: boolean
  23. compositeRule?: true
  24. createErrors?: boolean
  25. allErrors?: boolean
  26. }
  27. export type SubschemaArgs = Partial<{
  28. keyword: string
  29. schemaProp: string | number
  30. schema: AnySchema
  31. schemaPath: Code
  32. errSchemaPath: string
  33. topSchemaRef: Code
  34. data: Name | Code
  35. dataProp: Code | string | number
  36. dataTypes: JSONType[]
  37. definedProperties: Set<string>
  38. propertyName: Name
  39. dataPropType: Type
  40. jtdDiscriminator: string
  41. jtdMetadata: boolean
  42. compositeRule: true
  43. createErrors: boolean
  44. allErrors: boolean
  45. }>
  46. export function getSubschema(
  47. it: SchemaObjCxt,
  48. {keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef}: SubschemaArgs
  49. ): SubschemaContext {
  50. if (keyword !== undefined && schema !== undefined) {
  51. throw new Error('both "keyword" and "schema" passed, only one allowed')
  52. }
  53. if (keyword !== undefined) {
  54. const sch = it.schema[keyword]
  55. return schemaProp === undefined
  56. ? {
  57. schema: sch,
  58. schemaPath: _`${it.schemaPath}${getProperty(keyword)}`,
  59. errSchemaPath: `${it.errSchemaPath}/${keyword}`,
  60. }
  61. : {
  62. schema: sch[schemaProp],
  63. schemaPath: _`${it.schemaPath}${getProperty(keyword)}${getProperty(schemaProp)}`,
  64. errSchemaPath: `${it.errSchemaPath}/${keyword}/${escapeFragment(schemaProp)}`,
  65. }
  66. }
  67. if (schema !== undefined) {
  68. if (schemaPath === undefined || errSchemaPath === undefined || topSchemaRef === undefined) {
  69. throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"')
  70. }
  71. return {
  72. schema,
  73. schemaPath,
  74. topSchemaRef,
  75. errSchemaPath,
  76. }
  77. }
  78. throw new Error('either "keyword" or "schema" must be passed')
  79. }
  80. export function extendSubschemaData(
  81. subschema: SubschemaContext,
  82. it: SchemaObjCxt,
  83. {dataProp, dataPropType: dpType, data, dataTypes, propertyName}: SubschemaArgs
  84. ): void {
  85. if (data !== undefined && dataProp !== undefined) {
  86. throw new Error('both "data" and "dataProp" passed, only one allowed')
  87. }
  88. const {gen} = it
  89. if (dataProp !== undefined) {
  90. const {errorPath, dataPathArr, opts} = it
  91. const nextData = gen.let("data", _`${it.data}${getProperty(dataProp)}`, true)
  92. dataContextProps(nextData)
  93. subschema.errorPath = str`${errorPath}${getErrorPath(dataProp, dpType, opts.jsPropertySyntax)}`
  94. subschema.parentDataProperty = _`${dataProp}`
  95. subschema.dataPathArr = [...dataPathArr, subschema.parentDataProperty]
  96. }
  97. if (data !== undefined) {
  98. const nextData = data instanceof Name ? data : gen.let("data", data, true) // replaceable if used once?
  99. dataContextProps(nextData)
  100. if (propertyName !== undefined) subschema.propertyName = propertyName
  101. // TODO something is possibly wrong here with not changing parentDataProperty and not appending dataPathArr
  102. }
  103. if (dataTypes) subschema.dataTypes = dataTypes
  104. function dataContextProps(_nextData: Name): void {
  105. subschema.data = _nextData
  106. subschema.dataLevel = it.dataLevel + 1
  107. subschema.dataTypes = []
  108. it.definedProperties = new Set<string>()
  109. subschema.parentData = it.data
  110. subschema.dataNames = [...it.dataNames, _nextData]
  111. }
  112. }
  113. export function extendSubschemaMode(
  114. subschema: SubschemaContext,
  115. {jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors}: SubschemaArgs
  116. ): void {
  117. if (compositeRule !== undefined) subschema.compositeRule = compositeRule
  118. if (createErrors !== undefined) subschema.createErrors = createErrors
  119. if (allErrors !== undefined) subschema.allErrors = allErrors
  120. subschema.jtdDiscriminator = jtdDiscriminator // not inherited
  121. subschema.jtdMetadata = jtdMetadata // not inherited
  122. }