diff --git a/README.md b/README.md index 9397b9d..992c169 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,16 @@ positional arguments: * `environment` - An object containing your env vars (eg. `process.env`) * `validators` - An object that specifies the format of required vars. * `options` - An (optional) object, which supports the following keys: - * `strict` - If true, the output of `cleanEnv` will *only* contain the env - vars that were specified in the `validators` argument. + * `strict` - (default: `false`) If true, the output of `cleanEnv` will *only* + contain the env vars that were specified in the `validators` argument. * `reporter` - Pass in a function to override the default error handling and console output. See `lib/reporter.js` for the default implementation. * `transformer` - A function used to transform the cleaned environment object before it is returned from `cleanEnv` + * `dotEnvPath` - (default: `'.env'`) Path to the file that is parsed by + [dotenv](https://github.com/motdotla/dotenv) to + optionally load more env vars at runtime. Pass `null` if you want + to skip `dotenv` processing entirely and only load from `process.env`. By default, `cleanEnv()` will log an error message and exit if any required env vars are missing or invalid. diff --git a/index.js b/index.js index b23339f..91d7f49 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +const fs = require('fs') const dotenv = require('dotenv') const { EnvError, EnvMissingError, makeValidator, bool, num, str, json, url, email } = require('./lib/validators') @@ -36,8 +37,17 @@ function formatSpecDescription(spec) { return `${spec.desc}${egText}${docsText}` || '' } -function extendWithDotEnv(inputEnv) { - const { parsed } = dotenv.config() +// Extend an env var object with the values parsed from a ".env" +// file, whose path is given by the second argument. +function extendWithDotEnv(inputEnv, dotEnvPath = '.env') { + let dotEnvBuffer = null + try { + dotEnvBuffer = fs.readFileSync(dotEnvPath) + } catch (err) { + if (err.code === 'ENOENT') return inputEnv + throw err + } + const parsed = dotenv.parse(dotEnvBuffer) return extend(parsed, inputEnv) } @@ -46,7 +56,9 @@ function cleanEnv(inputEnv, specs = {}, options = {}) { let defaultNodeEnv = '' const errors = {} const shouldLoadDotEnv = (options.loadDotEnv !== false) - const env = shouldLoadDotEnv ? extendWithDotEnv(inputEnv) : inputEnv + const env = (options.dotEnvPath !== null) + ? extendWithDotEnv(inputEnv, options.dotEnvPath) + : inputEnv const varKeys = Object.keys(specs) // If validation for NODE_ENV isn't specified, use the default validation: diff --git a/tests/test_dotenv.js b/tests/test_dotenv.js index be5042f..e6c29d3 100644 --- a/tests/test_dotenv.js +++ b/tests/test_dotenv.js @@ -26,7 +26,16 @@ test('.env test in strict mode', () => { assert.deepEqual(env, { MYNUM: 4 }) }) -test('can opt out of dotenv with loadDotEnv=false', () => { - const env = cleanEnv({ FOO: 'bar' }, {}, { loadDotEnv: false }) +test('can opt out of dotenv with dotEnvPath=null', () => { + const env = cleanEnv({ FOO: 'bar' }, {}, { dotEnvPath: null }) assert.deepEqual(env, { FOO: 'bar' }) }) + +test('can use a custom .env file name', () => { + const path = '.custom-env' + fs.writeFileSync(path, 'CUSTOM=hi') + + const env = cleanEnv({ FOO: 'bar' }, {}, { dotEnvPath: path }) + assert.deepEqual(env, { FOO: 'bar', CUSTOM: 'hi' }) + fs.unlinkSync(path) +})