From 5a2093a08da74d19a3a35a69b9788c9f16390ce8 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Fri, 21 Jul 2023 06:21:33 -0700 Subject: [PATCH 1/2] Add changelog entry --- CHANGELOG.md | 1 + src/Node/Process.js | 55 ++++++++++------------ src/Node/Process.purs | 103 ++++++++++++++++++++---------------------- 3 files changed, 73 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79d6240..ac22e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Other improvements: - Bumped CI's node version to `lts/*` (#37 by @JordanMartinez) - Updated CI `actions/checkout` and `actions/setup-nodee` to `v3` (#37 by @JordanMartinez) - Format codebase & enforce formatting in CI via purs-tidy (#37 by @JordanMartinez) +- Use uncurried FFI (#38 by @JordanMartinez) ## [v10.0.0](https://github.com/purescript-node/purescript-node-process/releases/tag/v10.0.0) - 2022-04-29 diff --git a/src/Node/Process.js b/src/Node/Process.js index 11b9594..0f8b4ce 100644 --- a/src/Node/Process.js +++ b/src/Node/Process.js @@ -1,5 +1,4 @@ import process from "process"; -export { process }; export function onBeforeExit(callback) { return () => { @@ -37,34 +36,28 @@ export function onSignalImpl(signal) { }; } -export function chdir(dir) { - return () => { - process.chdir(dir); - }; -} +export const nextTickImpl = (cb) => process.nextTick(cb); +export const argv = () => process.argv.slice(); +export const execArgv = () => process.execArgv.slice(); +export const execPath = () => process.execPath +export const chdirImpl = (dir) => process.chdir(dir); +export const cwd = () => process.cwd; +export const getEnv = () => Object.assign({}, process.env); +export const unsafeGetEnv = () => process.env; +export const setEnvImpl = (key, val) => { + process.env[key] = val; +}; +export const unsetEnvImpl = (key) => { + delete process.env[key]; +}; +export const pid = process.pid; +export const platformStr = process.platform; +export const exitImpl = (code) => process.exit(code); +export const stdin = process.stdin; +export const stdout = process.stdout; +export const stderr = process.stderr; +export const stdinIsTTY = process.stdinIsTTY; +export const stdoutIsTTY = process.stdoutIsTTY; +export const stderrIsTTY = process.stderrIsTTY; +export const version = process.version; -export function setEnv(var_) { - return val => () => { - process.env[var_] = val; - }; -} - -export function unsetEnv(var_) { - return () => { - delete process.env[var_]; - }; -} - -export function exit(code) { - return () => { - process.exit(code); - }; -} - -export function copyArray(xs) { - return () => xs.slice(); -} - -export function copyObject(o) { - return () => Object.assign({}, o); -} diff --git a/src/Node/Process.purs b/src/Node/Process.purs index 286fc93..991c2cb 100644 --- a/src/Node/Process.purs +++ b/src/Node/Process.purs @@ -35,17 +35,11 @@ import Data.Posix.Signal (Signal) import Data.Posix.Signal as Signal import Effect (Effect) import Effect.Exception (Error) +import Effect.Uncurried (EffectFn1, EffectFn2, runEffectFn1, runEffectFn2) import Foreign.Object as FO import Node.Platform (Platform) import Node.Platform as Platform import Node.Stream (Readable, Writable) -import Unsafe.Coerce (unsafeCoerce) - --- YOLO -foreign import process :: forall props. { | props } - -mkEffect :: forall a. (Unit -> a) -> Effect a -mkEffect = unsafeCoerce -- | Register a callback to be performed when the event loop empties, and -- | Node.js is about to exit. Asynchronous calls can be made in the callback, @@ -86,102 +80,101 @@ onSignal sig = onSignalImpl (Signal.toString sig) -- | Register a callback to run as soon as the current event loop runs to -- | completion. nextTick :: Effect Unit -> Effect Unit -nextTick callback = mkEffect \_ -> process.nextTick callback +nextTick cb = runEffectFn1 nextTickImpl cb + +foreign import nextTickImpl :: EffectFn1 (Effect Unit) (Unit) --- | Get an array containing the command line arguments. -argv :: Effect (Array String) -argv = copyArray process.argv +-- | Get a copy of the array containing the command line arguments. +foreign import argv :: Effect (Array String) --- | Node-specific options passed to the `node` executable. -execArgv :: Effect (Array String) -execArgv = copyArray process.execArgv +-- | Get a copy of the Node-specific options passed to the `node` executable. +foreign import execArgv :: Effect (Array String) -- | The absolute pathname of the `node` executable that started the -- | process. -execPath :: Effect String -execPath = mkEffect \_ -> process.execPath +foreign import execPath :: Effect (String) -- | Change the current working directory of the process. If the current -- | directory could not be changed, an exception will be thrown. -foreign import chdir :: String -> Effect Unit +chdir :: String -> Effect Unit +chdir dir = runEffectFn1 chdirImpl dir + +foreign import chdirImpl :: EffectFn1 (String) (Unit) -- | Get the current working directory of the process. -cwd :: Effect String -cwd = process.cwd +foreign import cwd :: Effect (String) -- | Get a copy of the current environment. -getEnv :: Effect (FO.Object String) -getEnv = copyObject process.env +-- | If you only want to look up a value without paying +-- | for the overhead of the copy, use `lookupEnv`. +foreign import getEnv :: Effect (FO.Object String) + +-- | Get the current environment object without copying it. +-- | Any mutations to the returned object +-- | or any mutations via `unsetEnv` and `setEnv` +-- | will affect all values that were obtained +-- | via this function. +-- | Thus, this is an internal function that is +-- | not exported. +foreign import unsafeGetEnv :: Effect (FO.Object String) -- | Lookup a particular environment variable. lookupEnv :: String -> Effect (Maybe String) -lookupEnv k = lookupMutableObject k process.env +lookupEnv k = map (FO.lookup k) $ unsafeGetEnv -- | Set an environment variable. -foreign import setEnv :: String -> String -> Effect Unit +setEnv :: String -> String -> Effect Unit +setEnv key value = runEffectFn2 setEnvImpl key value + +foreign import setEnvImpl :: EffectFn2 (String) (String) (Unit) -- | Delete an environment variable. -- | Use case: to hide secret environment variable from child processes. -foreign import unsetEnv :: String -> Effect Unit +unsetEnv :: String -> Effect Unit +unsetEnv key = runEffectFn1 unsetEnvImpl key + +foreign import unsetEnvImpl :: EffectFn1 (String) (Unit) -pid :: Pid -pid = process.pid +foreign import pid :: Pid platform :: Maybe Platform platform = Platform.fromString platformStr -platformStr :: String -platformStr = process.platform +foreign import platformStr :: String -- | Cause the process to exit with the supplied integer code. An exit code -- | of 0 is normally considered successful, and anything else is considered a -- | failure. -foreign import exit :: forall a. Int -> Effect a +exit :: forall a. Int -> Effect a +exit code = runEffectFn1 exitImpl code + +foreign import exitImpl :: forall a. EffectFn1 (Int) (a) -- | The standard input stream. Note that this stream will never emit an `end` -- | event, so any handlers attached via `onEnd` will never be called. -stdin :: Readable () -stdin = process.stdin +foreign import stdin :: Readable () -- | The standard output stream. Note that this stream cannot be closed; calling -- | `end` will result in an exception being thrown. -stdout :: Writable () -stdout = process.stdout +foreign import stdout :: Writable () -- | The standard error stream. Note that this stream cannot be closed; calling -- | `end` will result in an exception being thrown. -stderr :: Writable () -stderr = process.stderr +foreign import stderr :: Writable () -- | Check whether the standard input stream appears to be attached to a TTY. -- | It is a good idea to check this before processing the input data from stdin. -stdinIsTTY :: Boolean -stdinIsTTY = process.stdin.isTTY +foreign import stdinIsTTY :: Boolean -- | Check whether the standard output stream appears to be attached to a TTY. -- | It is a good idea to check this before printing ANSI codes to stdout -- | (e.g. for coloured text in the terminal). -stdoutIsTTY :: Boolean -stdoutIsTTY = process.stdout.isTTY +foreign import stdoutIsTTY :: Boolean -- | Check whether the standard error stream appears to be attached to a TTY. -- | It is a good idea to check this before printing ANSI codes to stderr -- | (e.g. for coloured text in the terminal). -stderrIsTTY :: Boolean -stderrIsTTY = process.stderr.isTTY +foreign import stderrIsTTY :: Boolean -- | Get the Node.js version. -version :: String -version = process.version - --- Utils - -foreign import data MutableArray :: Type -> Type -foreign import data MutableObject :: Type -> Type - -foreign import copyArray :: forall a. MutableArray a -> Effect (Array a) -foreign import copyObject :: forall a. MutableObject a -> Effect (FO.Object a) - -lookupMutableObject :: forall a. String -> MutableObject a -> Effect (Maybe a) -lookupMutableObject k o = - mkEffect \_ -> FO.lookup k (unsafeCoerce o) +foreign import version :: String From 774ef9a35deca631a8d3a35e24fd3ce770f34a28 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Fri, 21 Jul 2023 06:24:02 -0700 Subject: [PATCH 2/2] Fix missing semicolon --- src/Node/Process.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Process.js b/src/Node/Process.js index 0f8b4ce..0d5babf 100644 --- a/src/Node/Process.js +++ b/src/Node/Process.js @@ -39,7 +39,7 @@ export function onSignalImpl(signal) { export const nextTickImpl = (cb) => process.nextTick(cb); export const argv = () => process.argv.slice(); export const execArgv = () => process.execArgv.slice(); -export const execPath = () => process.execPath +export const execPath = () => process.execPath; export const chdirImpl = (dir) => process.chdir(dir); export const cwd = () => process.cwd; export const getEnv = () => Object.assign({}, process.env);