diff --git a/lib/core.ts b/lib/core.ts index 3686ffe76..f7dd4fdee 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -117,6 +117,7 @@ export interface CurrentOptions { loadSchema?: (uri: string) => Promise // options to modify validated data: removeAdditional?: boolean | "all" | "failing" + removeUnevaluated?: boolean useDefaults?: boolean | "empty" coerceTypes?: boolean | "array" // advanced options: diff --git a/lib/vocabularies/unevaluated/unevaluatedProperties.ts b/lib/vocabularies/unevaluated/unevaluatedProperties.ts index 0e6868fa3..fec2f9171 100644 --- a/lib/vocabularies/unevaluated/unevaluatedProperties.ts +++ b/lib/vocabularies/unevaluated/unevaluatedProperties.ts @@ -29,7 +29,7 @@ const def: CodeKeywordDefinition = { const {gen, schema, data, errsCount, it} = cxt /* istanbul ignore if */ if (!errsCount) throw new Error("ajv implementation error") - const {allErrors, props} = it + const {allErrors, props, opts} = it if (props instanceof Name) { gen.if(_`${props} !== true`, () => gen.forIn("key", data, (key: Name) => @@ -66,6 +66,10 @@ const def: CodeKeywordDefinition = { ) if (!allErrors) gen.if(not(valid), () => gen.break()) } + + if (opts.removeUnevaluated) { + gen.code(_`delete ${data}[${key}]`) + } } function unevaluatedDynamic(evaluatedProps: Name, key: Name): Code { diff --git a/package.json b/package.json index c277d94d6..3b16f8b7f 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "prettier:write": "prettier --write \"./**/*.{json,yaml,js,ts}\"", "prettier:check": "prettier --list-different \"./**/*.{json,yaml,js,ts}\"", "test-spec": "cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register \"spec/**/*.spec.{ts,js}\" -R dot", + "myTest": "cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register \"spec/options/removeUnevaluated.spec.ts\"", "test-codegen": "nyc cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register 'spec/codegen.spec.ts' -R spec", "test-debug": "npm run test-spec -- --inspect-brk", "test-cov": "nyc npm run test-spec", @@ -85,7 +86,7 @@ "eslint-config-prettier": "^7.0.0", "fast-uri": "^1.0.0", "glob": "^7.0.0", - "husky": "^7.0.1", + "husky": "^8.0.3", "if-node-version": "^1.0.0", "jimp": "^0.16.1", "js-beautify": "^1.7.3", diff --git a/spec/options/removeUnevaluated.spec.ts b/spec/options/removeUnevaluated.spec.ts new file mode 100644 index 000000000..753b3e010 --- /dev/null +++ b/spec/options/removeUnevaluated.spec.ts @@ -0,0 +1,134 @@ +import _Ajv from "../ajv2019" +import chai from "../chai" +chai.should() + +describe("removeAdditional option", () => { + it("should remove unevaluated properties", () => { + const ajv = new _Ajv({removeUnevaluated: true, unevaluated: true}) + + ajv.addSchema({ + $id: "//test/fooBar", + type: "object", + properties: {foo: {type: "string"}, bar: {type: "string"}}, + unevaluatedProperties: true, + }) + + const object = { + foo: "foo", + bar: "bar", + baz: "baz-to-be-removed", + } + + ajv.validate("//test/fooBar", object).should.equal(true) + object.should.have.property("foo") + object.should.have.property("bar") + object.should.not.have.property("baz") + }) + + it("should remove redundant properties in oneOf situations", () => { + const ajv = new _Ajv({removeUnevaluated: true, unevaluated: true}) + ajv.addSchema({ + $id: "//test/fooBar", + type: "object", + oneOf: [ + { + required: ["a", "b"], + properties: { + a: {type: "number"}, + b: {type: "number"}, + }, + }, + { + properties: { + c: {type: "number"}, + d: {type: "number"}, + }, + }, + ], + unevaluatedProperties: true, + }) + const object = { + b: 2, + c: 3, + d: 4, + } + ajv.validate("//test/fooBar", object).should.equal(true) + object.should.not.have.property("b") + object.should.have.property("c") + object.should.have.property("d") + }) + + it("should remove redundant properties in anyOf situations, for the first alternative", () => { + const ajv = new _Ajv({removeUnevaluated: true, unevaluated: true}) + ajv.addSchema({ + $id: "//test/fooBar", + type: "object", + anyOf: [ + { + required: ["a", "b"], + properties: { + a: {type: "number"}, + b: {type: "number"}, + }, + unevaluatedProperties: true + }, + { + required: ["c", "d"], + properties: { + c: {type: "number"}, + d: {type: "number"}, + }, + }, + ], + unevaluatedProperties: true, + }) + const object = { + a: 1, + b: 2, + c: 3, + d: 4, + } + ajv.validate("//test/fooBar", object).should.equal(true) + object.should.have.property("a") + object.should.have.property("b") + object.should.not.have.property("c") + object.should.not.have.property("d") + }) + + it("should remove redundant properties in anyOf situations, for subsequent alternatives", () => { + const ajv = new _Ajv({removeUnevaluated: true, unevaluated: true}) + ajv.addSchema({ + $id: "//test/fooBar", + type: "object", + anyOf: [ + { + required: ["a", "b"], + properties: { + a: {type: "number"}, + b: {type: "number"}, + }, + unevaluatedProperties: true + }, + { + required: ["c", "d"], + properties: { + c: {type: "number"}, + d: {type: "number"}, + }, + }, + ], + unevaluatedProperties: true, + }) + const object = { + b: 2, + c: 3, + d: 4, + } + ajv.validate("//test/fooBar", object).should.equal(true) + object.should.not.have.property("a") + object.should.not.have.property("b") + object.should.have.property("c") + object.should.have.property("d") + }) + +})