Skip to content

Commit efc4800

Browse files
feat: provide .env API (#1034)
closes #975 * chore: lint test * feat: provide support for .env files injects via API * chore: lint * chore: update test --------- Co-authored-by: Anton Golub <antongolub@antongolub.com>
1 parent ae83b4b commit efc4800

File tree

7 files changed

+78
-29
lines changed

7 files changed

+78
-29
lines changed

docs/v7/api.md

+14
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,17 @@ The [yaml](https://www.npmjs.com/package/yaml) package.
204204
```js
205205
console.log(YAML.parse('foo: bar').foo)
206206
```
207+
208+
209+
## loadDotenv
210+
211+
Read env files and collects it into environment variables.
212+
213+
```js
214+
const env = loadDotenv(env1, env2)
215+
console.log((await $({ env })`echo $FOO`).stdout)
216+
---
217+
const env = loadDotenv(env1)
218+
$.env = env
219+
console.log((await $`echo $FOO`).stdout)
220+
```

src/goods.ts

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
isStringLiteral,
2222
parseBool,
2323
parseDuration,
24+
readEnvFromFile,
2425
toCamelCase,
2526
} from './util.js'
2627
import {
@@ -217,3 +218,10 @@ export async function spinner<T>(
217218
}
218219
})
219220
}
221+
222+
/**
223+
*
224+
* Read env files and collects it into environment variables
225+
*/
226+
export const loadDotenv = (...files: string[]): NodeJS.ProcessEnv =>
227+
files.reduce<NodeJS.ProcessEnv>((m, f) => readEnvFromFile(f, m), {})

test/cli.test.js

+2-15
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,8 @@ import { test, describe, before, after } from 'node:test'
1717
import { fileURLToPath } from 'node:url'
1818
import net from 'node:net'
1919
import getPort from 'get-port'
20-
import {
21-
argv,
22-
importPath,
23-
injectGlobalRequire,
24-
isMain,
25-
main,
26-
normalizeExt,
27-
runScript,
28-
printUsage,
29-
scriptFromStdin,
30-
scriptFromHttp,
31-
transformMarkdown,
32-
writeAndImport,
33-
} from '../build/cli.js'
34-
import { $, path, fs, tmpfile, tmpdir } from '../build/index.js'
20+
import { $, path, tmpfile, tmpdir, fs } from '../build/index.js'
21+
import { isMain, normalizeExt, transformMarkdown } from '../build/cli.js'
3522

3623
const __filename = fileURLToPath(import.meta.url)
3724
const spawn = $.spawn

test/core.test.js

+4-8
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,20 @@ import {
2626
resolveDefaults,
2727
cd,
2828
syncProcessCwd,
29-
log,
30-
kill,
31-
defaults,
3229
within,
3330
usePowerShell,
3431
usePwsh,
3532
useBash,
3633
} from '../build/core.js'
3734
import {
35+
tempfile,
3836
fs,
39-
nothrow,
40-
quiet,
37+
quote,
38+
quotePowerShell,
4139
sleep,
42-
tempfile,
43-
tempdir,
40+
quiet,
4441
which,
4542
} from '../build/index.js'
46-
import { quote, quotePowerShell } from '../build/util.js'
4743

4844
describe('core', () => {
4945
describe('resolveDefaults()', () => {

test/export.test.js

+1
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ describe('index', () => {
331331
assert.equal(typeof index.globby.isGitIgnored, 'function', 'index.globby.isGitIgnored')
332332
assert.equal(typeof index.globby.isGitIgnoredSync, 'function', 'index.globby.isGitIgnoredSync')
333333
assert.equal(typeof index.kill, 'function', 'index.kill')
334+
assert.equal(typeof index.loadDotenv, 'function', 'index.loadDotenv')
334335
assert.equal(typeof index.log, 'function', 'index.log')
335336
assert.equal(typeof index.minimist, 'function', 'index.minimist')
336337
assert.equal(typeof index.nothrow, 'function', 'index.nothrow')

test/goods.test.js

+44-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
// limitations under the License.
1414

1515
import assert from 'node:assert'
16-
import { test, describe } from 'node:test'
17-
import { $, chalk } from '../build/index.js'
18-
import { echo, sleep, parseArgv } from '../build/goods.js'
16+
import { test, describe, after } from 'node:test'
17+
import { $, chalk, fs, tempfile } from '../build/index.js'
18+
import { echo, sleep, parseArgv, loadDotenv } from '../build/goods.js'
1919

2020
describe('goods', () => {
2121
function zx(script) {
@@ -173,4 +173,45 @@ describe('goods', () => {
173173
}
174174
)
175175
})
176+
177+
describe('loadDotenv()', () => {
178+
const env1 = tempfile(
179+
'.env',
180+
`FOO=BAR
181+
BAR=FOO+`
182+
)
183+
const env2 = tempfile('.env.default', `BAR2=FOO2`)
184+
185+
after(() => {
186+
fs.remove(env1)
187+
fs.remove(env2)
188+
})
189+
190+
test('handles multiple dotenv files', async () => {
191+
const env = loadDotenv(env1, env2)
192+
193+
assert.equal((await $({ env })`echo $FOO`).stdout, 'BAR\n')
194+
assert.equal((await $({ env })`echo $BAR`).stdout, 'FOO+\n')
195+
assert.equal((await $({ env })`echo $BAR2`).stdout, 'FOO2\n')
196+
})
197+
198+
test('handles replace evn', async () => {
199+
const env = loadDotenv(env1)
200+
$.env = env
201+
assert.equal((await $`echo $FOO`).stdout, 'BAR\n')
202+
assert.equal((await $`echo $BAR`).stdout, 'FOO+\n')
203+
$.env = process.env
204+
})
205+
206+
test('handle error', async () => {
207+
try {
208+
loadDotenv('./.env')
209+
210+
assert.throw()
211+
} catch (e) {
212+
assert.equal(e.code, 'ENOENT')
213+
assert.equal(e.errno, -2)
214+
}
215+
})
216+
})
176217
})

test/util.test.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
import assert from 'node:assert'
1616
import fs from 'node:fs'
17-
import { test, describe } from 'node:test'
17+
import { test, describe, after } from 'node:test'
18+
import { fs as fsCore } from '../build/index.js'
1819
import {
1920
formatCmd,
2021
isString,
@@ -164,16 +165,17 @@ e.g. a private SSH key
164165
})
165166

166167
describe('readEnvFromFile()', () => {
168+
const file = tempfile('.env', 'ENV=value1\nENV2=value24')
169+
after(() => fsCore.remove(file))
170+
167171
test('handles correct proccess.env', () => {
168-
const file = tempfile('.env', 'ENV=value1\nENV2=value24')
169172
const env = readEnvFromFile(file)
170173
assert.equal(env.ENV, 'value1')
171174
assert.equal(env.ENV2, 'value24')
172175
assert.ok(env.NODE_VERSION !== '')
173176
})
174177

175178
test('handles correct some env', () => {
176-
const file = tempfile('.env', 'ENV=value1\nENV2=value24')
177179
const env = readEnvFromFile(file, { version: '1.0.0', name: 'zx' })
178180
assert.equal(env.ENV, 'value1')
179181
assert.equal(env.ENV2, 'value24')

0 commit comments

Comments
 (0)