Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,24 @@ console.log(config)
// output: { PORT: 3000 }
```

It is possible to enhance the default ajv instance providing the `customOptions` function parameter.
It is possible to enhance the default ajv instance providing the `customOptions` as a function or object parameter.

When `customOptions` is an object, the provided ajv options override the default ones:

```js
const config = envSchema({
schema: schema,
data: data,
dotenv: true,
ajv: {
customOptions: {
coerceTypes: true
}
}
})
```

When `customOptions` is a function, it must return the updated ajv instance.
This example shows how to use the `format` keyword in your schemas.

```js
Expand All @@ -100,8 +117,6 @@ const config = envSchema({
})
```

Note that it is mandatory to return the ajv instance.

### Order of configuration loading

The order of precedence for configuration data is as follows, from least
Expand Down
20 changes: 12 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,27 +91,31 @@ function envSchema (_opts) {
}

function chooseAjvInstance (defaultInstance, ajvOpts) {
if (!ajvOpts) {
return defaultInstance
} else if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'function') {
const ajv = ajvOpts.customOptions(getDefaultInstance())
if (ajvOpts instanceof Ajv) {
return ajvOpts
}
let ajv = defaultInstance
if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'function') {
ajv = ajvOpts.customOptions(getDefaultInstance())
if (!(ajv instanceof Ajv)) {
throw new TypeError('customOptions function must return an instance of Ajv')
}
return ajv
} else if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'object') {
ajv = getDefaultInstance(ajvOpts.customOptions)
}
return ajvOpts
return ajv
}

function getDefaultInstance () {
function getDefaultInstance (overrideOpts = {}) {
return new Ajv({
allErrors: true,
removeAdditional: true,
useDefaults: true,
coerceTypes: true,
allowUnionTypes: true,
addUsedSchema: false,
keywords: [separator]
keywords: [separator],
...overrideOpts
})
}

Expand Down
47 changes: 38 additions & 9 deletions test/custom-ajv.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ const strictValidator = new Ajv({
})

test('ajv enhancement', async t => {
t.plan(2)
const testCase = {
t.plan(3)
const testCaseFn = {
schema: {
type: 'object',
required: ['MONGODB_URL'],
Expand All @@ -316,25 +316,41 @@ test('ajv enhancement', async t => {
MONGODB_URL: 'mongodb://localhost/pippo'
}
}
const testCaseObj = {
schema: {
type: 'object',
required: ['PORT'],
properties: {
PORT: {
type: 'string',
}
}
},
data: [{ PORT: 3333 }],
isOk: true,
confExpected: {
PORT: '3333'
}
}

await t.test('return', async t => {
await t.test('customOptions fn return', async t => {
const options = {
schema: testCase.schema,
data: testCase.data,
schema: testCaseFn.schema,
data: testCaseFn.data,
ajv: {
customOptions (ajvInstance) {
require('ajv-formats')(ajvInstance)
return ajvInstance
}
}
}
makeTest(t, options, testCase.isOk, testCase.confExpected)
makeTest(t, options, testCaseFn.isOk, testCaseFn.confExpected)
})

await t.test('no return', async t => {
await t.test('customOptions fn no return', async t => {
const options = {
schema: testCase.schema,
data: testCase.data,
schema: testCaseFn.schema,
data: testCaseFn.data,
ajv: {
customOptions (_ajvInstance) {
// do nothing
Expand All @@ -343,4 +359,17 @@ test('ajv enhancement', async t => {
}
makeTest(t, options, false, undefined, 'customOptions function must return an instance of Ajv')
})

await t.test('customOptions object override', async t => {
const options = {
schema: testCaseObj.schema,
data: testCaseObj.data,
ajv: {
customOptions: {
coerceTypes: true,
}
}
}
makeTest(t, options, testCaseObj.isOk, testCaseObj.confExpected)
})
})