Skip to content

Commit b1bb033

Browse files
authored
Add loadEnvFile (#47)
1 parent 1058e44 commit b1bb033

5 files changed

Lines changed: 66 additions & 4 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Notable changes to this project are documented in this file. The format is based
77
Breaking changes:
88

99
New features:
10+
- Add FFI for `process.loadEnvFile` as `loadEnvFile` and `loadEnvFile'` (#47 by @m-rinaldi)
1011

1112
Bugfixes:
1213

‎src/Node/Process.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const setEnvImpl = (key, val) => {
2424
export const unsetEnvImpl = (key) => {
2525
delete process.env[key];
2626
};
27+
export const loadEnvFileImpl = (path) => process.loadEnvFile(path);
2728
export const execArgv = () => process.execArgv.slice();
2829
export const execPath = () => process.execPath;
2930
export const exit = () => process.exit();

‎src/Node/Process.purs‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ module Node.Process
3232
, lookupEnv
3333
, setEnv
3434
, unsetEnv
35+
, loadEnvFile
36+
, loadEnvFile'
3537
, execArgv
3638
, execPath
3739
, exit
@@ -332,6 +334,20 @@ unsetEnv key = runEffectFn1 unsetEnvImpl key
332334

333335
foreign import unsetEnvImpl :: EffectFn1 (String) (Unit)
334336

337+
-- | Load the `.env` file in the current working directory into `process.env`.
338+
-- | Variables already present in the environment are not overwritten.
339+
-- | Throws if the file does not exist. Lines that cannot be parsed are skipped.
340+
loadEnvFile :: Effect Unit
341+
loadEnvFile = runEffectFn1 loadEnvFileImpl ".env"
342+
343+
-- | Load the given `.env`-formatted file into `process.env`.
344+
-- | Variables already present in the environment are not overwritten.
345+
-- | Throws if the file does not exist. Lines that cannot be parsed are skipped.
346+
loadEnvFile' :: String -> Effect Unit
347+
loadEnvFile' path = runEffectFn1 loadEnvFileImpl path
348+
349+
foreign import loadEnvFileImpl :: EffectFn1 String Unit
350+
335351
-- | Get a copy of the Node-specific options passed to the `node` executable.
336352
foreign import execArgv :: Effect (Array String)
337353

‎test/Main.purs‎

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@ import Prelude
44

55
import Data.Maybe (Maybe(..))
66
import Effect (Effect)
7-
import Effect.Exception (throw)
7+
import Effect.Exception (catchException, throw)
88
import Foreign.Object as Object
99
import Node.Process as Process
1010

1111
main :: Effect Unit
1212
main = do
13+
testEnvMutation
14+
testLoadEnvFile
15+
16+
testEnvMutation :: Effect Unit
17+
testEnvMutation = do
1318
let key = "PURESCRIPT_NODE_PROCESS_TEST"
1419
original <- Process.lookupEnv key
1520
Process.setEnv key "first"
@@ -18,11 +23,48 @@ main = do
1823
current <- Process.lookupEnv key
1924
Process.unsetEnv key
2025
removed <- Process.lookupEnv key
21-
case original of
22-
Nothing -> Process.unsetEnv key
23-
Just value -> Process.setEnv key value
26+
restoreEnv key original
2427
if
2528
Object.lookup key snapshot == Just "first"
2629
&& current == Just "second"
2730
&& removed == Nothing then pure unit
2831
else throw "Environment lookup, snapshot, or removal failed"
32+
33+
testLoadEnvFile :: Effect Unit
34+
testLoadEnvFile = do
35+
let loadedKey = "PURESCRIPT_NODE_PROCESS_ENV_FILE_LOADED"
36+
let existingKey = "PURESCRIPT_NODE_PROCESS_ENV_FILE_EXISTING"
37+
originalLoaded <- Process.lookupEnv loadedKey
38+
originalExisting <- Process.lookupEnv existingKey
39+
originalCwd <- Process.cwd
40+
41+
-- Explicit path: loads new variables, keeps existing ones.
42+
Process.unsetEnv loadedKey
43+
Process.setEnv existingKey "already-set"
44+
Process.loadEnvFile' "test/fixtures/.env"
45+
loadedByPath <- Process.lookupEnv loadedKey
46+
kept <- Process.lookupEnv existingKey
47+
48+
-- Default path: resolves `.env` against the current working directory.
49+
Process.unsetEnv loadedKey
50+
Process.chdir "test/fixtures"
51+
Process.loadEnvFile
52+
Process.chdir originalCwd
53+
loadedByDefault <- Process.lookupEnv loadedKey
54+
55+
-- Missing file throws.
56+
missingThrew <- Process.loadEnvFile' "test/fixtures/missing.env" $> false # catchException \_ -> pure true
57+
58+
restoreEnv loadedKey originalLoaded
59+
restoreEnv existingKey originalExisting
60+
if
61+
loadedByPath == Just "from-file"
62+
&& kept == Just "already-set"
63+
&& loadedByDefault == Just "from-file"
64+
&& missingThrew then pure unit
65+
else throw "loadEnvFile did not load the file, overwrote an existing variable, or did not throw for a missing file"
66+
67+
restoreEnv :: String -> Maybe String -> Effect Unit
68+
restoreEnv key = case _ of
69+
Nothing -> Process.unsetEnv key
70+
Just value -> Process.setEnv key value

‎test/fixtures/.env‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
PURESCRIPT_NODE_PROCESS_ENV_FILE_LOADED=from-file
2+
PURESCRIPT_NODE_PROCESS_ENV_FILE_EXISTING=should-not-win

0 commit comments

Comments
 (0)