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