mirror of https://github.com/jkjoy/sunpeiwen.git
113 lines
3.3 KiB
TypeScript
113 lines
3.3 KiB
TypeScript
|
import type {
|
||
|
CodeKeywordDefinition,
|
||
|
ErrorObject,
|
||
|
KeywordErrorDefinition,
|
||
|
SchemaMap,
|
||
|
AnySchema,
|
||
|
} from "../../types"
|
||
|
import type KeywordCxt from "../../compile/context"
|
||
|
import {_, str} from "../../compile/codegen"
|
||
|
import {alwaysValidSchema} from "../../compile/util"
|
||
|
import {checkReportMissingProp, checkMissingProp, reportMissingProp, propertyInData} from "../code"
|
||
|
|
||
|
export type PropertyDependencies = {[K in string]?: string[]}
|
||
|
|
||
|
export interface DependenciesErrorParams {
|
||
|
property: string
|
||
|
missingProperty: string
|
||
|
depsCount: number
|
||
|
deps: string // TODO change to string[]
|
||
|
}
|
||
|
|
||
|
type SchemaDependencies = SchemaMap
|
||
|
|
||
|
export type DependenciesError = ErrorObject<
|
||
|
"dependencies",
|
||
|
DependenciesErrorParams,
|
||
|
{[K in string]?: string[] | AnySchema}
|
||
|
>
|
||
|
|
||
|
export const error: KeywordErrorDefinition = {
|
||
|
message: ({params: {property, depsCount, deps}}) => {
|
||
|
const property_ies = depsCount === 1 ? "property" : "properties"
|
||
|
return str`should have ${property_ies} ${deps} when property ${property} is present`
|
||
|
},
|
||
|
params: ({params: {property, depsCount, deps, missingProperty}}) =>
|
||
|
_`{property: ${property},
|
||
|
missingProperty: ${missingProperty},
|
||
|
depsCount: ${depsCount},
|
||
|
deps: ${deps}}`, // TODO change to reference
|
||
|
}
|
||
|
|
||
|
const def: CodeKeywordDefinition = {
|
||
|
keyword: "dependencies",
|
||
|
type: "object",
|
||
|
schemaType: "object",
|
||
|
error,
|
||
|
code(cxt: KeywordCxt) {
|
||
|
const [propDeps, schDeps] = splitDependencies(cxt)
|
||
|
validatePropertyDeps(cxt, propDeps)
|
||
|
validateSchemaDeps(cxt, schDeps)
|
||
|
},
|
||
|
}
|
||
|
|
||
|
function splitDependencies({schema}: KeywordCxt): [PropertyDependencies, SchemaDependencies] {
|
||
|
const propertyDeps: PropertyDependencies = {}
|
||
|
const schemaDeps: SchemaDependencies = {}
|
||
|
for (const key in schema) {
|
||
|
if (key === "__proto__") continue
|
||
|
const deps = Array.isArray(schema[key]) ? propertyDeps : schemaDeps
|
||
|
deps[key] = schema[key]
|
||
|
}
|
||
|
return [propertyDeps, schemaDeps]
|
||
|
}
|
||
|
|
||
|
export function validatePropertyDeps(
|
||
|
cxt: KeywordCxt,
|
||
|
propertyDeps: {[K in string]?: string[]} = cxt.schema
|
||
|
): void {
|
||
|
const {gen, data, it} = cxt
|
||
|
if (Object.keys(propertyDeps).length === 0) return
|
||
|
const missing = gen.let("missing")
|
||
|
for (const prop in propertyDeps) {
|
||
|
const deps = propertyDeps[prop] as string[]
|
||
|
if (deps.length === 0) continue
|
||
|
const hasProperty = propertyInData(gen, data, prop, it.opts.ownProperties)
|
||
|
cxt.setParams({
|
||
|
property: prop,
|
||
|
depsCount: deps.length,
|
||
|
deps: deps.join(", "),
|
||
|
})
|
||
|
if (it.allErrors) {
|
||
|
gen.if(hasProperty, () => {
|
||
|
for (const depProp of deps) {
|
||
|
checkReportMissingProp(cxt, depProp)
|
||
|
}
|
||
|
})
|
||
|
} else {
|
||
|
gen.if(_`${hasProperty} && (${checkMissingProp(cxt, deps, missing)})`)
|
||
|
reportMissingProp(cxt, missing)
|
||
|
gen.else()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export function validateSchemaDeps(cxt: KeywordCxt, schemaDeps: SchemaMap = cxt.schema): void {
|
||
|
const {gen, data, keyword, it} = cxt
|
||
|
const valid = gen.name("valid")
|
||
|
for (const prop in schemaDeps) {
|
||
|
if (alwaysValidSchema(it, schemaDeps[prop] as AnySchema)) continue
|
||
|
gen.if(
|
||
|
propertyInData(gen, data, prop, it.opts.ownProperties),
|
||
|
() => {
|
||
|
const schCxt = cxt.subschema({keyword, schemaProp: prop}, valid)
|
||
|
cxt.mergeValidEvaluated(schCxt, valid)
|
||
|
},
|
||
|
() => gen.var(valid, true) // TODO var
|
||
|
)
|
||
|
cxt.ok(valid)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export default def
|