You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					130 lines
				
				3.3 KiB
			
		
		
			
		
	
	
					130 lines
				
				3.3 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const Ajv = require('ajv') | ||
|  | 
 | ||
|  | const separator = { | ||
|  |   keyword: 'separator', | ||
|  |   type: 'string', | ||
|  |   metaSchema: { | ||
|  |     type: 'string', | ||
|  |     description: 'value separator' | ||
|  |   }, | ||
|  |   modifying: true, | ||
|  |   valid: true, | ||
|  |   errors: false, | ||
|  |   compile: (schema) => (data, dataPath, parentData, parentDataProperty) => { | ||
|  |     // In some cases parentData and parentDataProperty will be undefined.
 | ||
|  |     // We need to fall back to the dataPath object to provide those values.
 | ||
|  |     if (parentData && parentDataProperty) { | ||
|  |       parentData[parentDataProperty] = data === '' ? [] : data.split(schema) | ||
|  |     } else { | ||
|  |       const { | ||
|  |         parentData: pData, | ||
|  |         parentDataProperty: pDataProperty | ||
|  |       } = dataPath | ||
|  |       pData[pDataProperty] = data === '' ? [] : data.split(schema) | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | const optsSchema = { | ||
|  |   type: 'object', | ||
|  |   required: ['schema'], | ||
|  |   properties: { | ||
|  |     schema: { type: 'object', additionalProperties: true }, | ||
|  |     data: { | ||
|  |       oneOf: [ | ||
|  |         { type: 'array', items: { type: 'object' }, minItems: 1 }, | ||
|  |         { type: 'object' } | ||
|  |       ], | ||
|  |       default: {} | ||
|  |     }, | ||
|  |     env: { type: 'boolean', default: true }, | ||
|  |     dotenv: { type: ['boolean', 'object'], default: false }, | ||
|  |     expandEnv: { type: ['boolean'], default: false }, | ||
|  |     ajv: { type: 'object', additionalProperties: true } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | const sharedAjvInstance = getDefaultInstance() | ||
|  | 
 | ||
|  | const optsSchemaValidator = sharedAjvInstance.compile(optsSchema) | ||
|  | 
 | ||
|  | function loadAndValidateEnvironment (_opts) { | ||
|  |   const opts = Object.assign({}, _opts) | ||
|  | 
 | ||
|  |   if (opts.schema && opts.schema[Symbol.for('fluent-schema-object')]) { | ||
|  |     opts.schema = opts.schema.valueOf() | ||
|  |   } | ||
|  | 
 | ||
|  |   const isOptionValid = optsSchemaValidator(opts) | ||
|  |   if (!isOptionValid) { | ||
|  |     const error = new Error(sharedAjvInstance.errorsText(optsSchemaValidator.errors, { dataVar: 'opts' })) | ||
|  |     error.errors = optsSchemaValidator.errors | ||
|  |     throw error | ||
|  |   } | ||
|  | 
 | ||
|  |   const schema = opts.schema | ||
|  |   schema.additionalProperties = false | ||
|  | 
 | ||
|  |   let data = opts.data | ||
|  |   if (!Array.isArray(opts.data)) { | ||
|  |     data = [data] | ||
|  |   } | ||
|  | 
 | ||
|  |   if (opts.dotenv) { | ||
|  |     require('dotenv').config(Object.assign({}, opts.dotenv)) | ||
|  |   } | ||
|  | 
 | ||
|  |   if (opts.env) { | ||
|  |     if (opts.expandEnv) { | ||
|  |       require('dotenv-expand').expand({ parsed: process.env }) | ||
|  |     } | ||
|  |     data.unshift(process.env) | ||
|  |   } | ||
|  | 
 | ||
|  |   const merge = {} | ||
|  |   data.forEach(d => Object.assign(merge, d)) | ||
|  | 
 | ||
|  |   const ajv = chooseAjvInstance(sharedAjvInstance, opts.ajv) | ||
|  | 
 | ||
|  |   const valid = ajv.validate(schema, merge) | ||
|  |   if (!valid) { | ||
|  |     const error = new Error(ajv.errorsText(ajv.errors, { dataVar: 'env' })) | ||
|  |     error.errors = ajv.errors | ||
|  |     throw error | ||
|  |   } | ||
|  | 
 | ||
|  |   return merge | ||
|  | } | ||
|  | 
 | ||
|  | function chooseAjvInstance (defaultInstance, ajvOpts) { | ||
|  |   if (!ajvOpts) { | ||
|  |     return defaultInstance | ||
|  |   } else if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'function') { | ||
|  |     const ajv = ajvOpts.customOptions(getDefaultInstance()) | ||
|  |     if (!(ajv instanceof Ajv)) { | ||
|  |       throw new Error('customOptions function must return an instance of Ajv') | ||
|  |     } | ||
|  |     return ajv | ||
|  |   } | ||
|  |   return ajvOpts | ||
|  | } | ||
|  | 
 | ||
|  | function getDefaultInstance () { | ||
|  |   return new Ajv({ | ||
|  |     allErrors: true, | ||
|  |     removeAdditional: true, | ||
|  |     useDefaults: true, | ||
|  |     coerceTypes: true, | ||
|  |     allowUnionTypes: true, | ||
|  |     addUsedSchema: false, | ||
|  |     keywords: [separator] | ||
|  |   }) | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = loadAndValidateEnvironment | ||
|  | module.exports.default = loadAndValidateEnvironment | ||
|  | module.exports.envSchema = loadAndValidateEnvironment | ||
|  | module.exports.envSchema.keywords = { separator } |