Skip to content

Commit 053c178

Browse files
feat: support customOptions as object (#215)
1 parent eb2c542 commit 053c178

File tree

3 files changed

+68
-20
lines changed

3 files changed

+68
-20
lines changed

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,24 @@ console.log(config)
8383
// output: { PORT: 3000 }
8484
```
8585

86-
It is possible to enhance the default ajv instance providing the `customOptions` function parameter.
86+
It is possible to enhance the default ajv instance providing the `customOptions` as a function or object parameter.
87+
88+
When `customOptions` is an object, the provided ajv options override the default ones:
89+
90+
```js
91+
const config = envSchema({
92+
schema: schema,
93+
data: data,
94+
dotenv: true,
95+
ajv: {
96+
customOptions: {
97+
coerceTypes: true
98+
}
99+
}
100+
})
101+
```
102+
103+
When `customOptions` is a function, it must return the updated ajv instance.
87104
This example shows how to use the `format` keyword in your schemas.
88105

89106
```js
@@ -100,8 +117,6 @@ const config = envSchema({
100117
})
101118
```
102119

103-
Note that it is mandatory to return the ajv instance.
104-
105120
### Order of configuration loading
106121

107122
The order of precedence for configuration data is as follows, from least

index.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,27 +91,31 @@ function envSchema (_opts) {
9191
}
9292

9393
function chooseAjvInstance (defaultInstance, ajvOpts) {
94-
if (!ajvOpts) {
95-
return defaultInstance
96-
} else if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'function') {
97-
const ajv = ajvOpts.customOptions(getDefaultInstance())
94+
if (ajvOpts instanceof Ajv) {
95+
return ajvOpts
96+
}
97+
let ajv = defaultInstance
98+
if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'function') {
99+
ajv = ajvOpts.customOptions(getDefaultInstance())
98100
if (!(ajv instanceof Ajv)) {
99101
throw new TypeError('customOptions function must return an instance of Ajv')
100102
}
101-
return ajv
103+
} else if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'object') {
104+
ajv = getDefaultInstance(ajvOpts.customOptions)
102105
}
103-
return ajvOpts
106+
return ajv
104107
}
105108

106-
function getDefaultInstance () {
109+
function getDefaultInstance (overrideOpts = {}) {
107110
return new Ajv({
108111
allErrors: true,
109112
removeAdditional: true,
110113
useDefaults: true,
111114
coerceTypes: true,
112115
allowUnionTypes: true,
113116
addUsedSchema: false,
114-
keywords: [separator]
117+
keywords: [separator],
118+
...overrideOpts
115119
})
116120
}
117121

test/custom-ajv.test.js

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,8 @@ const strictValidator = new Ajv({
298298
})
299299

300300
test('ajv enhancement', async t => {
301-
t.plan(2)
302-
const testCase = {
301+
t.plan(3)
302+
const testCaseFn = {
303303
schema: {
304304
type: 'object',
305305
required: ['MONGODB_URL'],
@@ -316,25 +316,41 @@ test('ajv enhancement', async t => {
316316
MONGODB_URL: 'mongodb://localhost/pippo'
317317
}
318318
}
319+
const testCaseObj = {
320+
schema: {
321+
type: 'object',
322+
required: ['PORT'],
323+
properties: {
324+
PORT: {
325+
type: 'string',
326+
}
327+
}
328+
},
329+
data: [{ PORT: 3333 }],
330+
isOk: true,
331+
confExpected: {
332+
PORT: '3333'
333+
}
334+
}
319335

320-
await t.test('return', async t => {
336+
await t.test('customOptions fn return', async t => {
321337
const options = {
322-
schema: testCase.schema,
323-
data: testCase.data,
338+
schema: testCaseFn.schema,
339+
data: testCaseFn.data,
324340
ajv: {
325341
customOptions (ajvInstance) {
326342
require('ajv-formats')(ajvInstance)
327343
return ajvInstance
328344
}
329345
}
330346
}
331-
makeTest(t, options, testCase.isOk, testCase.confExpected)
347+
makeTest(t, options, testCaseFn.isOk, testCaseFn.confExpected)
332348
})
333349

334-
await t.test('no return', async t => {
350+
await t.test('customOptions fn no return', async t => {
335351
const options = {
336-
schema: testCase.schema,
337-
data: testCase.data,
352+
schema: testCaseFn.schema,
353+
data: testCaseFn.data,
338354
ajv: {
339355
customOptions (_ajvInstance) {
340356
// do nothing
@@ -343,4 +359,17 @@ test('ajv enhancement', async t => {
343359
}
344360
makeTest(t, options, false, undefined, 'customOptions function must return an instance of Ajv')
345361
})
362+
363+
await t.test('customOptions object override', async t => {
364+
const options = {
365+
schema: testCaseObj.schema,
366+
data: testCaseObj.data,
367+
ajv: {
368+
customOptions: {
369+
coerceTypes: true,
370+
}
371+
}
372+
}
373+
makeTest(t, options, testCaseObj.isOk, testCaseObj.confExpected)
374+
})
346375
})

0 commit comments

Comments
 (0)