Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Notable changes to this project are documented in this file. The format is based
Breaking changes:

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

Bugfixes:

Expand Down
1 change: 1 addition & 0 deletions src/Node/Process.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const setEnvImpl = (key, val) => {
export const unsetEnvImpl = (key) => {
delete process.env[key];
};
export const loadEnvFileImpl = (path) => process.loadEnvFile(path);
export const execArgv = () => process.execArgv.slice();
export const execPath = () => process.execPath;
export const exit = () => process.exit();
Expand Down
16 changes: 16 additions & 0 deletions src/Node/Process.purs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ module Node.Process
, lookupEnv
, setEnv
, unsetEnv
, loadEnvFile
, loadEnvFile'
, execArgv
, execPath
, exit
Expand Down Expand Up @@ -332,6 +334,20 @@ unsetEnv key = runEffectFn1 unsetEnvImpl key

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

-- | Load the `.env` file in the current working directory into `process.env`.
-- | Variables already present in the environment are not overwritten.
-- | Throws if the file does not exist. Lines that cannot be parsed are skipped.
loadEnvFile :: Effect Unit
loadEnvFile = runEffectFn1 loadEnvFileImpl ".env"

-- | Load the given `.env`-formatted file into `process.env`.
-- | Variables already present in the environment are not overwritten.
-- | Throws if the file does not exist. Lines that cannot be parsed are skipped.
loadEnvFile' :: String -> Effect Unit
loadEnvFile' path = runEffectFn1 loadEnvFileImpl path

foreign import loadEnvFileImpl :: EffectFn1 String Unit

-- | Get a copy of the Node-specific options passed to the `node` executable.
foreign import execArgv :: Effect (Array String)

Expand Down
50 changes: 46 additions & 4 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import Prelude

import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Exception (throw)
import Effect.Exception (catchException, throw)
import Foreign.Object as Object
import Node.Process as Process

main :: Effect Unit
main = do
testEnvMutation
testLoadEnvFile

testEnvMutation :: Effect Unit
testEnvMutation = do
let key = "PURESCRIPT_NODE_PROCESS_TEST"
original <- Process.lookupEnv key
Process.setEnv key "first"
Expand All @@ -18,11 +23,48 @@ main = do
current <- Process.lookupEnv key
Process.unsetEnv key
removed <- Process.lookupEnv key
case original of
Nothing -> Process.unsetEnv key
Just value -> Process.setEnv key value
restoreEnv key original
if
Object.lookup key snapshot == Just "first"
&& current == Just "second"
&& removed == Nothing then pure unit
else throw "Environment lookup, snapshot, or removal failed"

testLoadEnvFile :: Effect Unit
testLoadEnvFile = do
let loadedKey = "PURESCRIPT_NODE_PROCESS_ENV_FILE_LOADED"
let existingKey = "PURESCRIPT_NODE_PROCESS_ENV_FILE_EXISTING"
originalLoaded <- Process.lookupEnv loadedKey
originalExisting <- Process.lookupEnv existingKey
originalCwd <- Process.cwd

-- Explicit path: loads new variables, keeps existing ones.
Process.unsetEnv loadedKey
Process.setEnv existingKey "already-set"
Process.loadEnvFile' "test/fixtures/.env"
loadedByPath <- Process.lookupEnv loadedKey
kept <- Process.lookupEnv existingKey

-- Default path: resolves `.env` against the current working directory.
Process.unsetEnv loadedKey
Process.chdir "test/fixtures"
Process.loadEnvFile
Process.chdir originalCwd
loadedByDefault <- Process.lookupEnv loadedKey

-- Missing file throws.
missingThrew <- Process.loadEnvFile' "test/fixtures/missing.env" $> false # catchException \_ -> pure true

restoreEnv loadedKey originalLoaded
restoreEnv existingKey originalExisting
if
loadedByPath == Just "from-file"
&& kept == Just "already-set"
&& loadedByDefault == Just "from-file"
&& missingThrew then pure unit
else throw "loadEnvFile did not load the file, overwrote an existing variable, or did not throw for a missing file"

restoreEnv :: String -> Maybe String -> Effect Unit
restoreEnv key = case _ of
Nothing -> Process.unsetEnv key
Just value -> Process.setEnv key value
2 changes: 2 additions & 0 deletions test/fixtures/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PURESCRIPT_NODE_PROCESS_ENV_FILE_LOADED=from-file
PURESCRIPT_NODE_PROCESS_ENV_FILE_EXISTING=should-not-win
Loading