The documentation for Joi extensions is disappointingly lacklustre for such a useful feature. Fortunately a lot of Joi's core is written using extensions so a lot can be learned from looking at the source.
If I were to write your rule as an extension it'd be like this:
const customJoi = Joi.extend(joi => ({
type: 'string',
base: joi.string(),
messages: {
'string.snakeAlpha': '{{#label}} must be snake case'
},
rules: {
snakeAlpha: {
validate(value, helpers)
{
if (!/^[a-z]+(_[a-z]+)*$/.test(value))
{
return helpers.error('string.snakeAlpha', { value });
}
return value;
}
}
}
}));
Which can be used like:
customJoi.object().keys({
foo: customJoi.string().snakeAlpha()
});
UPDATE
Whether this is the correct way of working with dependant extensions, I'm not sure, but this is how I typically handle them...
I first define my extensions in an array ensuring dependant extensions are defined first. Then I'll iterate through the array re-using the previous customJoi
instance so the next extension includes those defined before it. A simple working example will probably explain better than I can put into words!
(I've also simplified the extensions to be more inline with how you're used to using them)
const Joi = require('joi');
let customJoi = Joi;
const extensions = [
joi => ({
type: 'snakeAlpha',
base: joi.string().regex(/^[a-z]+(_[a-z]+)*$/)
}),
// this instance of 'joi' will include 'snakeAlpha'
joi => ({
type: 'kebabAlpha',
base: joi.string().regex(/^[a-z]+(-[a-z]+)*$/)
}),
// this instance of 'joi' will include 'snakeAlpha' and 'kebabAlpha'
joi => ({
type: 'arrayKebabAlpha',
base: joi.array().items(joi.kebabAlpha())
})
];
extensions.forEach(extension =>
customJoi = customJoi.extend(extension));
customJoi.assert([ 'hello-world' ], customJoi.arrayKebabAlpha());
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…