ajv icon indicating copy to clipboard operation
ajv copied to clipboard

Support different ajv options for each schema

Open qurafi opened this issue 1 year ago • 2 comments

What version of Ajv you are you using? 8.12.0

What problem do you want to solve? Currently is not possible to assign different compilation options to each specific schema. For example turning type coercing or allErrors.

I'm building a Vite/rollup plugin to compile json schemas using Ajv and I want to allow users to define different options on each schema on the same Ajv instance(so references can work)

What do you think is the correct solution to problem? I could think of two solutions for this but all has their own drawbacks and IMO this will not work well in my usecase

  • I could set ajv.options on each compile call and assign the old options back. This will works with refs but all the refs will have the same options(even if the referenced schema has different options)
  • Creating a new ajv instance with different options but this will not work completely with refs and I will have to sync the different instances

Will you be able to implement it? Of course I will help if it's necessary

qurafi avatar May 09 '23 04:05 qurafi

So taking a peak on how to implement this. I noticed that every compilation happens in https://github.com/ajv-validator/ajv/blob/45583fde112f80c06ba6ad5583b744ef22d0640a/lib/compile/index.ts#L111

I do a little experiment by overwriting this function module:


const compile_index = require("ajv/dist/compile/index");
const instance_opts = new WeakMap();
const schema_opts = new WeakMap();

const compileSchema = compile_index.compileSchema;

compile_index.compileSchema = function (sch) {
    const opts = schema_opts.get(sch.schema);
    if (opts) {
        if (!instance_opts.has(this)) {
            instance_opts.set(this, this.opts);
        }

        this.opts = { ...this.opts, ...opts };

        try {
            return compileSchema.call(this, sch);
        } finally {
            this.opts = instance_opts.get(this);
        }
    }

    return compileSchema.call(this, sch);
};

So any user could add different options to schema_opts

/** @param {import("ajv").Options} opts */
function addWithOptions(schema, opts) {
    opts && schema_opts.set(schema, opts);
    return ajv.addSchema(schema);
}

I tested this on multiple schemas including references. It worked well but there's some points about this:

  • This modify the module directly but it does not change the function interface but once changing or moving this function, it will eventually breaks so it's very unsafe approach for live server but could work with build tools like in my case.
  • I didn't test it on esm yet
  • This does not work with inlineRefs option(probably because ajv inline the referenced schema with the original?)
  • Finally, this is just a temporarily solution until we have a native way in the core

qurafi avatar May 09 '23 08:05 qurafi

This would be very useful

errodrigues avatar Apr 19 '24 00:04 errodrigues