diff --git a/lib/main.d.ts b/lib/main.d.ts index af84746f..990b8cb0 100644 --- a/lib/main.d.ts +++ b/lib/main.d.ts @@ -1,6 +1,6 @@ // TypeScript Version: 3.0 /// -import type { URL } from 'node:url'; +import type { URL } from 'url'; export interface DotenvParseOutput { [name: string]: string; @@ -24,10 +24,12 @@ export interface DotenvConfigOptions { * Default: `path.resolve(process.cwd(), '.env')` * * Specify a custom path if your file containing environment variables is located elsewhere. + * Can also be an array of strings, specifying multiple paths. * * example: `require('dotenv').config({ path: '/custom/path/to/.env' })` + * example: `require('dotenv').config({ path: ['/path/to/first.env', '/path/to/second.env'] })` */ - path?: string | URL; + path?: string | string[] | URL; /** * Default: `utf8` diff --git a/lib/main.js b/lib/main.js index 84f81a1e..39d06714 100644 --- a/lib/main.js +++ b/lib/main.js @@ -150,14 +150,27 @@ function _instructions (result, dotenvKey) { } function _vaultPath (options) { - let dotenvPath = path.resolve(process.cwd(), '.env') + let possibleVaultPath = null if (options && options.path && options.path.length > 0) { - dotenvPath = options.path + if (Array.isArray(options.path)) { + for (const filepath of options.path) { + if (fs.existsSync(filepath)) { + possibleVaultPath = filepath.endsWith('.vault') ? filepath : `${filepath}.vault` + } + } + } else { + possibleVaultPath = options.path.endsWith('.vault') ? options.path : `${options.path}.vault` + } + } else { + possibleVaultPath = path.resolve(process.cwd(), '.env.vault') + } + + if (fs.existsSync(possibleVaultPath)) { + return possibleVaultPath } - // Locate .env.vault - return dotenvPath.endsWith('.vault') ? dotenvPath : `${dotenvPath}.vault` + return null } function _resolveHome (envPath) { @@ -224,7 +237,7 @@ function config (options) { } // dotenvKey exists but .env.vault file does not exist - if (!fs.existsSync(vaultPath)) { + if (!vaultPath) { _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`) return DotenvModule.configDotenv(options) diff --git a/tests/test-config-vault.js b/tests/test-config-vault.js index 160ffa58..31136158 100644 --- a/tests/test-config-vault.js +++ b/tests/test-config-vault.js @@ -25,6 +25,15 @@ t.afterEach(() => { } }) +t.test('logs when no path is set', ct => { + ct.plan(1) + + logStub = sinon.stub(console, 'log') + + dotenv.config() + ct.ok(logStub.called) +}) + t.test('logs', ct => { ct.plan(1) @@ -43,7 +52,7 @@ t.test('logs when testPath calls to .env.vault directly (interpret what the user ct.ok(logStub.called) }) -t.test('warns if DOTENV_KEY exists but .env.vault does not', ct => { +t.test('warns if DOTENV_KEY exists but .env.vault does not exist', ct => { ct.plan(1) logStub = sinon.stub(console, 'log') @@ -56,6 +65,19 @@ t.test('warns if DOTENV_KEY exists but .env.vault does not', ct => { ct.end() }) +t.test('warns if DOTENV_KEY exists but .env.vault does not exist (set as array)', ct => { + ct.plan(1) + + logStub = sinon.stub(console, 'log') + + const existsSync = sinon.stub(fs, 'existsSync').returns(false) // make .env.vault not exist + dotenv.config({ path: [testPath] }) + ct.ok(logStub.called) + existsSync.restore() + + ct.end() +}) + t.test('returns parsed object', ct => { ct.plan(1) @@ -65,6 +87,24 @@ t.test('returns parsed object', ct => { ct.end() }) +t.test('returns parsed object (set path as array)', ct => { + ct.plan(1) + + const env = dotenv.config({ path: [testPath] }) + ct.same(env.parsed, { ALPHA: 'zeta' }) + + ct.end() +}) + +t.test('returns parsed object (set path as array with .vault extension)', ct => { + ct.plan(1) + + const env = dotenv.config({ path: [`${testPath}.vault`] }) + ct.same(env.parsed, { ALPHA: 'zeta' }) + + ct.end() +}) + t.test('throws not found if .env.vault is empty', ct => { ct.plan(1) diff --git a/tests/test-config.js b/tests/test-config.js index b664ac1c..ea771ad8 100644 --- a/tests/test-config.js +++ b/tests/test-config.js @@ -30,6 +30,15 @@ t.test('takes string for path option', ct => { ct.equal(readFileSyncStub.args[0][0], testPath) }) +t.test('takes string for path option', ct => { + ct.plan(1) + + const testPath = 'tests/.env' + dotenv.config({ path: testPath }) + + ct.equal(readFileSyncStub.args[0][0], testPath) +}) + t.test('takes array for path option', ct => { ct.plan(1)