ajv-cli
ajv-cli copied to clipboard
Must you write code to get support for custom keywords/formats? [workaround available]
Since this is a cli I was hoping I could just reference a keywords.txt file or formats.txt file, but it doesn't appear that's an option.
I'll try try to follow an example from the ajv-keywords repo.
Perhaps I could even tie together a text file option once I've figured this out a little bit more.
UPDATE: 2021/05/05 For those with a similar question, I've written an npm package to address this (although not with a lot of flexibility). The details are below in this comment
You can pass module(s) that define custom keywords/formats. The modules should export a function that accepts Ajv instance as a parameter. The file name should start with ".", it will be resolved relative to the current folder. The package name can also be passed - it will be used in require as is.
Can you point me to the definition of this function?
I'm looking at this file as an example: https://github.com/ajv-validator/ajv-keywords/blob/master/src/index.ts
It appears to take an instance of Ajv as well as optional keyword(s) and returns the instance of Ajv. So the way to indicate an issue is by throwing an error.
Is that correct? I guess I'll give it a try
Here's my first attempt:
function customKeywords(ajv, keyword) {
console.log(`checking ${keyword}`);
if (keyword === 'myKeyword') {
return ajv;
}
throw new Error(`Unknown keyword ${keyword}`);
}
module.exports = customKeywords;
I've called this file mykeywords.js and .mykeywords.js because the docs insist it should have to start with a period.
I attempted to invoke and get an error
> ajv compile -c mykeywords myschema.schema.json
module mykeywords.js is invalid; it should export function
error: Cannot find module 'mykeywords.js'
Require stack:
- /Users/cwelchmi/.nvm/versions/node/v12.18.3/lib/node_modules/ajv-cli/dist/commands/ajv.js
- /Users/cwelchmi/.nvm/versions/node/v12.18.3/lib/node_modules/ajv-cli/dist/commands/compile.js
- /Users/cwelchmi/.nvm/versions/node/v12.18.3/lib/node_modules/ajv-cli/dist/commands/index.js
- /Users/cwelchmi/.nvm/versions/node/v12.18.3/lib/node_modules/ajv-cli/dist/index.js
I tried invoking with and without the .js extension with the same result.
So I don't even know if my code is even close to correct, because I need to determine how to actually invoke it properly
The help subsystem adds this additional blurb (not exactly in the README):
-c module(s) should export a function that accepts Ajv instance as parameter (file path should start with ".", otherwise used as require package) .json extension can be omitted (but should be used in globs)
So it looks like the reference to . means current directory (not file name, as quoted above). But the last sentence mentions .json extensions which is odd because I've never heard of code using a .json extension.
I'm looking at https://github.com/ajv-validator/ajv-cli/issues/86 for some additional help. Could you add a few words about you named your example and how you use -c to reference it?
Ok, by stepping thru the code what appears to be meant is that you need to give a full path, and for local files this includes a ./ prefix. In other words, in my example, ./mykeywords.js
Looks like what needs to be done is just use the addKeyword method.
Here's my simple example that adds one keyword:
function customKeywords(ajv, keyword) {
ajv.addKeyword("mykeyword");
}
module.exports = customKeywords;
Here's a short little module that can be reused to read keywords from a file. I plan to write a similar module for formats.
const fs = require("fs")
function customKeywords(ajv) {
const fileContents = fs.readFileSync("keywords.txt", {encoding: "utf8"})
const keywords = fileContents.split(/\s+/)
keywords.forEach((keyword) => {
if (keyword) {
ajv.addKeyword(keyword)
}
})
}
module.exports = customKeywords
Now it would be nice to be able to customize the location and file name of the keywords file, but this will meet my needs for now. "Publishing" here in case others wanted help with this.
To use this module
- Create a file called
keywords.txtwith your keywords separated by white space. - Copy this module (named
customKeywords.js) andkeywords.txtto a directory - Invoke
ajvfrom the same directory like this
> ajv compile -s myschema.schema.json -c ./customKeywords.js
I created a new npm module as a hack. https://www.npmjs.com/package/ajv-cli-custom
It's not great because it doesn't allow you to specify the name of the file. For now it's hard coded to look in the current directory for customs.json.
installation
> npm install -g ajv-cli-custom
Create customs.json file with your keywords and formats
Create this file in the location you are running ajv-cli from. For now it must be called customs.json:
{
"keywords": ["keyword1", "keyword2", /* ... */ ],
"formats": ["format1", "format2", /* ... */]
}
use
> ajv compile -s myschema.schema.json -c ajv-cli-custom
If this team is open to it, I wouldn't mind trying a PR to build this functionality into ajv-cli itself. Basically, introduce a new command line option to specify a json file with custom keywords and/or formats.
@michaelgwelch ajv-cli-custom looks like a great start for custom formats :)
I just want to add, for people like me, who can use the formats defined in https://github.com/ajv-validator/ajv-formats#formats, that they can easily be added with the following two steps:
npm i -D ajv-formats- Add
-c ajv-formatsto your CLI command forajx.
Example:
ajv test -s my.schema.json -d my.json --spec=draft2019 -c ajv-formats --valid --all-errors --errors=text
If you're just looking to support some custom formats, there's actually fairly little code required, as per the example on user-defined formats.
function customFormats(ajv) {
ajv.addFormat("identifier", /^a-z\$_[a-zA-Z$_0-9]*$/)
return ajv
}
module.exports = customFormats
Then put this in a file like formats.js and pass it to the CLI with the -c option, like -c "./formats.js"
Custom validation (going beyond regex) is just a matter of passing a validation block instead.
function customFormats(ajv) {
ajv.addFormat(
"byte",
{
type: "number",
validate: (x) => x >= 0 && x <= 255 && x % 1 == 0,
}
)
return ajv
}
module.exports = customFormats
I would suggest adding this example to the CLI project documentation, as a quick recipe to extend the tool.
@MartinDevi Sure I discovered that and wrote a little npm package to do this for others (as noted above. (It doesn't do any validation, just supports the use of user defined names for custom keywords and formats). Would still love to see my package replaced with a command line option built-in. The issue isn't really how little code is involved. It's how to distribute it to all of your colleagues that could benefit as well. My package helps a little but even I forget how to use it after a few months go by. :smile: I've had to go back and find it on npmjs/github and reread my own documentation.
One thing my package could benefit from is a way for a user to specify the location (and name) of the file containing their custom format and keyword names. Right now it's hard-coded to the current directly (less than ideal) and the file name customs.json