136 lines
4.3 KiB
TypeScript
136 lines
4.3 KiB
TypeScript
|
import type {AnySchema} from "../../types"
|
||
|
import type {SchemaObjCxt} from ".."
|
||
|
import {_, str, getProperty, Code, Name} from "../codegen"
|
||
|
import {escapeFragment, getErrorPath, Type} from "../util"
|
||
|
import type {JSONType} from "../rules"
|
||
|
|
||
|
export interface SubschemaContext {
|
||
|
// TODO use Optional? align with SchemCxt property types
|
||
|
schema: AnySchema
|
||
|
schemaPath: Code
|
||
|
errSchemaPath: string
|
||
|
topSchemaRef?: Code
|
||
|
errorPath?: Code
|
||
|
dataLevel?: number
|
||
|
dataTypes?: JSONType[]
|
||
|
data?: Name
|
||
|
parentData?: Name
|
||
|
parentDataProperty?: Code | number
|
||
|
dataNames?: Name[]
|
||
|
dataPathArr?: (Code | number)[]
|
||
|
propertyName?: Name
|
||
|
jtdDiscriminator?: string
|
||
|
jtdMetadata?: boolean
|
||
|
compositeRule?: true
|
||
|
createErrors?: boolean
|
||
|
allErrors?: boolean
|
||
|
}
|
||
|
|
||
|
export type SubschemaArgs = Partial<{
|
||
|
keyword: string
|
||
|
schemaProp: string | number
|
||
|
schema: AnySchema
|
||
|
schemaPath: Code
|
||
|
errSchemaPath: string
|
||
|
topSchemaRef: Code
|
||
|
data: Name | Code
|
||
|
dataProp: Code | string | number
|
||
|
dataTypes: JSONType[]
|
||
|
definedProperties: Set<string>
|
||
|
propertyName: Name
|
||
|
dataPropType: Type
|
||
|
jtdDiscriminator: string
|
||
|
jtdMetadata: boolean
|
||
|
compositeRule: true
|
||
|
createErrors: boolean
|
||
|
allErrors: boolean
|
||
|
}>
|
||
|
|
||
|
export function getSubschema(
|
||
|
it: SchemaObjCxt,
|
||
|
{keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef}: SubschemaArgs
|
||
|
): SubschemaContext {
|
||
|
if (keyword !== undefined && schema !== undefined) {
|
||
|
throw new Error('both "keyword" and "schema" passed, only one allowed')
|
||
|
}
|
||
|
|
||
|
if (keyword !== undefined) {
|
||
|
const sch = it.schema[keyword]
|
||
|
return schemaProp === undefined
|
||
|
? {
|
||
|
schema: sch,
|
||
|
schemaPath: _`${it.schemaPath}${getProperty(keyword)}`,
|
||
|
errSchemaPath: `${it.errSchemaPath}/${keyword}`,
|
||
|
}
|
||
|
: {
|
||
|
schema: sch[schemaProp],
|
||
|
schemaPath: _`${it.schemaPath}${getProperty(keyword)}${getProperty(schemaProp)}`,
|
||
|
errSchemaPath: `${it.errSchemaPath}/${keyword}/${escapeFragment(schemaProp)}`,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (schema !== undefined) {
|
||
|
if (schemaPath === undefined || errSchemaPath === undefined || topSchemaRef === undefined) {
|
||
|
throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"')
|
||
|
}
|
||
|
return {
|
||
|
schema,
|
||
|
schemaPath,
|
||
|
topSchemaRef,
|
||
|
errSchemaPath,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
throw new Error('either "keyword" or "schema" must be passed')
|
||
|
}
|
||
|
|
||
|
export function extendSubschemaData(
|
||
|
subschema: SubschemaContext,
|
||
|
it: SchemaObjCxt,
|
||
|
{dataProp, dataPropType: dpType, data, dataTypes, propertyName}: SubschemaArgs
|
||
|
): void {
|
||
|
if (data !== undefined && dataProp !== undefined) {
|
||
|
throw new Error('both "data" and "dataProp" passed, only one allowed')
|
||
|
}
|
||
|
|
||
|
const {gen} = it
|
||
|
|
||
|
if (dataProp !== undefined) {
|
||
|
const {errorPath, dataPathArr, opts} = it
|
||
|
const nextData = gen.let("data", _`${it.data}${getProperty(dataProp)}`, true)
|
||
|
dataContextProps(nextData)
|
||
|
subschema.errorPath = str`${errorPath}${getErrorPath(dataProp, dpType, opts.jsPropertySyntax)}`
|
||
|
subschema.parentDataProperty = _`${dataProp}`
|
||
|
subschema.dataPathArr = [...dataPathArr, subschema.parentDataProperty]
|
||
|
}
|
||
|
|
||
|
if (data !== undefined) {
|
||
|
const nextData = data instanceof Name ? data : gen.let("data", data, true) // replaceable if used once?
|
||
|
dataContextProps(nextData)
|
||
|
if (propertyName !== undefined) subschema.propertyName = propertyName
|
||
|
// TODO something is possibly wrong here with not changing parentDataProperty and not appending dataPathArr
|
||
|
}
|
||
|
|
||
|
if (dataTypes) subschema.dataTypes = dataTypes
|
||
|
|
||
|
function dataContextProps(_nextData: Name): void {
|
||
|
subschema.data = _nextData
|
||
|
subschema.dataLevel = it.dataLevel + 1
|
||
|
subschema.dataTypes = []
|
||
|
it.definedProperties = new Set<string>()
|
||
|
subschema.parentData = it.data
|
||
|
subschema.dataNames = [...it.dataNames, _nextData]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export function extendSubschemaMode(
|
||
|
subschema: SubschemaContext,
|
||
|
{jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors}: SubschemaArgs
|
||
|
): void {
|
||
|
if (compositeRule !== undefined) subschema.compositeRule = compositeRule
|
||
|
if (createErrors !== undefined) subschema.createErrors = createErrors
|
||
|
if (allErrors !== undefined) subschema.allErrors = allErrors
|
||
|
subschema.jtdDiscriminator = jtdDiscriminator // not inherited
|
||
|
subschema.jtdMetadata = jtdMetadata // not inherited
|
||
|
}
|