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.
		
		
		
		
		
			
		
			
				
					
					
						
							119 lines
						
					
					
						
							3.7 KiB
						
					
					
				
			
		
		
	
	
							119 lines
						
					
					
						
							3.7 KiB
						
					
					
				| import type {
 | |
|   CodeKeywordDefinition,
 | |
|   AddedKeywordDefinition,
 | |
|   ErrorObject,
 | |
|   KeywordErrorDefinition,
 | |
|   AnySchema,
 | |
| } from "../../types"
 | |
| import {allSchemaProperties, usePattern, isOwnProperty} from "../code"
 | |
| import {_, nil, or, not, Code, Name} from "../../compile/codegen"
 | |
| import N from "../../compile/names"
 | |
| import type {SubschemaArgs} from "../../compile/validate/subschema"
 | |
| import {alwaysValidSchema, schemaRefOrVal, Type} from "../../compile/util"
 | |
| 
 | |
| export type AdditionalPropertiesError = ErrorObject<
 | |
|   "additionalProperties",
 | |
|   {additionalProperty: string},
 | |
|   AnySchema
 | |
| >
 | |
| 
 | |
| const error: KeywordErrorDefinition = {
 | |
|   message: "must NOT have additional properties",
 | |
|   params: ({params}) => _`{additionalProperty: ${params.additionalProperty}}`,
 | |
| }
 | |
| 
 | |
| const def: CodeKeywordDefinition & AddedKeywordDefinition = {
 | |
|   keyword: "additionalProperties",
 | |
|   type: ["object"],
 | |
|   schemaType: ["boolean", "object"],
 | |
|   allowUndefined: true,
 | |
|   trackErrors: true,
 | |
|   error,
 | |
|   code(cxt) {
 | |
|     const {gen, schema, parentSchema, data, errsCount, it} = cxt
 | |
|     /* istanbul ignore if */
 | |
|     if (!errsCount) throw new Error("ajv implementation error")
 | |
|     const {allErrors, opts} = it
 | |
|     it.props = true
 | |
|     if (opts.removeAdditional !== "all" && alwaysValidSchema(it, schema)) return
 | |
|     const props = allSchemaProperties(parentSchema.properties)
 | |
|     const patProps = allSchemaProperties(parentSchema.patternProperties)
 | |
|     checkAdditionalProperties()
 | |
|     cxt.ok(_`${errsCount} === ${N.errors}`)
 | |
| 
 | |
|     function checkAdditionalProperties(): void {
 | |
|       gen.forIn("key", data, (key: Name) => {
 | |
|         if (!props.length && !patProps.length) additionalPropertyCode(key)
 | |
|         else gen.if(isAdditional(key), () => additionalPropertyCode(key))
 | |
|       })
 | |
|     }
 | |
| 
 | |
|     function isAdditional(key: Name): Code {
 | |
|       let definedProp: Code
 | |
|       if (props.length > 8) {
 | |
|         // TODO maybe an option instead of hard-coded 8?
 | |
|         const propsSchema = schemaRefOrVal(it, parentSchema.properties, "properties")
 | |
|         definedProp = isOwnProperty(gen, propsSchema as Code, key)
 | |
|       } else if (props.length) {
 | |
|         definedProp = or(...props.map((p) => _`${key} === ${p}`))
 | |
|       } else {
 | |
|         definedProp = nil
 | |
|       }
 | |
|       if (patProps.length) {
 | |
|         definedProp = or(definedProp, ...patProps.map((p) => _`${usePattern(cxt, p)}.test(${key})`))
 | |
|       }
 | |
|       return not(definedProp)
 | |
|     }
 | |
| 
 | |
|     function deleteAdditional(key: Name): void {
 | |
|       gen.code(_`delete ${data}[${key}]`)
 | |
|     }
 | |
| 
 | |
|     function additionalPropertyCode(key: Name): void {
 | |
|       if (opts.removeAdditional === "all" || (opts.removeAdditional && schema === false)) {
 | |
|         deleteAdditional(key)
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       if (schema === false) {
 | |
|         cxt.setParams({additionalProperty: key})
 | |
|         cxt.error()
 | |
|         if (!allErrors) gen.break()
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       if (typeof schema == "object" && !alwaysValidSchema(it, schema)) {
 | |
|         const valid = gen.name("valid")
 | |
|         if (opts.removeAdditional === "failing") {
 | |
|           applyAdditionalSchema(key, valid, false)
 | |
|           gen.if(not(valid), () => {
 | |
|             cxt.reset()
 | |
|             deleteAdditional(key)
 | |
|           })
 | |
|         } else {
 | |
|           applyAdditionalSchema(key, valid)
 | |
|           if (!allErrors) gen.if(not(valid), () => gen.break())
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     function applyAdditionalSchema(key: Name, valid: Name, errors?: false): void {
 | |
|       const subschema: SubschemaArgs = {
 | |
|         keyword: "additionalProperties",
 | |
|         dataProp: key,
 | |
|         dataPropType: Type.Str,
 | |
|       }
 | |
|       if (errors === false) {
 | |
|         Object.assign(subschema, {
 | |
|           compositeRule: true,
 | |
|           createErrors: false,
 | |
|           allErrors: false,
 | |
|         })
 | |
|       }
 | |
|       cxt.subschema(subschema, valid)
 | |
|     }
 | |
|   },
 | |
| }
 | |
| 
 | |
| export default def
 |