You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							113 lines
						
					
					
						
							3.3 KiB
						
					
					
				
			
		
		
	
	
							113 lines
						
					
					
						
							3.3 KiB
						
					
					
				| import type {
 | |
|   CodeKeywordDefinition,
 | |
|   ErrorObject,
 | |
|   KeywordErrorDefinition,
 | |
|   SchemaMap,
 | |
|   AnySchema,
 | |
| } from "../../types"
 | |
| import type {KeywordCxt} from "../../compile/validate"
 | |
| 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`must 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
 |