run-in-cwd
Run CLI commands with Node.
Table Of Contents
Get Started
- Install:$ npm install run-in-cwd
- Require:const createCwd =
- Get a
Cwd
Instance:const projectDir = - Run a command:// run to completionprojectDir// run with more controlprojectDir
WARNING: If you let your users pass in the command and/or any of its arguments - make sure they are safe.
Default Instance
run-in-cwd
exports a default instance ready to use if your target folder is in which the current process runs in. Meaning, what process.cwd()
returns.
const cwd = ; cwd
It's the same instance you would get if you run createCwd('./')
or even createCwd()
NOTE: For context's sake, do not extract cwd methods:
// DO NOT:const runCmd =
Instance API
.runCmd( cmd, [args, [options] ] )
Executes a command and returns a completion promise (command exits).
Similar to Node's child_process.exec
and child_process.execFile
(see docs).
Don't forget to handle errors with promise.catch()
or a try-catch
wrapper.
Arguments:
- cmd (Required) - A command string (e.g.
'npm'
) - args - An array of the command's arguments (e.g.
['-flag', 'key=value']
). Could also be a string or a number for a single argument.
NOTE:
cmd
string could also include the arguments e.g..runCmd('npm install')
- options - spawn options object. See Node's docs.
Additional option:maxCacheSize
- Limit the command cache in MegaBytes. The limit is the total output for bothstdout
&stderr
streams. Default value is10
. An exception is thrown when max size is exceeded.cwd // 20 MB limit
Returns: A promise for a command results object.
A result object has the following properties:
exitCode
- Number - The command exit code.isOk
- Boolean -true
if command exit code is 0.false
otherwise.stdout
- String - The commands'sstdout
output string (utf-8 encoded).stderr
- String - The commands'sstderr
output string (utf-8 encoded).output
- String - Both streams' output string (utf-8 encoded).stdoutLines
- Array - The commands'sstdout
output, split to lines.stderrLines
- Array -The commands'sstderr
output, split to lines.outputLines
- Array - Both streams' output, split to lines.
Examples:
Promise style:
cwd ;
Async-Await style:
async { try const isOk stdoutLines = await projectDir // ... catch err // handle exception... };
!isOk
, stderr
and error
:
The difference between A CLI command creates a process that has two output channels it utilize to communicate with its parent process: one for the command's standard output (stdout
), and one for its errors (stderr
).
Every command also finishes with an exit code. The consensus is exit code 0
for success (a kind of an HTTP "200 OK" status code) and non-zero otherwise. Different commands use those channels and exit codes differently, even "incorrectly" as there is no standard other than subjective common sense...
So when a command fails it will probably send some details through its stderr
channel and exit with a non-zero exit code but it will be completed. No errors will be thrown in the Node process that executed the command.
An exception
is thrown when there was a problem with the command execution in our environment. For example when the command itself is not found (e.g. a typo like ggit
).
.spawn( cmd, [args, [options] ] )
Runs a command and returns a child process.
It is highly recommended to handle errors:
childProc
Arguments:
- cmd (Required) - A command string (e.g.
'npm'
) - args - An array of the command's arguments (e.g.
['-flag', 'cache=500']
). Could also be a string or a number for a single argument.
NOTE:
cmd
string could also include the arguments e.g..spawn('npm install')
- options - spawn options object. See Node's docs.
Returns: <child_process>
Examples:
Calling .spawn()
is only the first part of spawning a process. We then need to handle the returned process's lifecycle events.
First, spawn a child process:
const childProc = cwd
<child_process>
.spawn
's return object is Node's native ChildProcess with additional events.
-
### Event:
'line'
Is triggered for each line of bothstdout
andstderr
streams. Text is split to lines by newline characters. -
### Event:
'line/out'
Same asline
event, but forstdout
only. -
### Event:
'line/err'
Same asline
event, but forstderr
only.
const cwd = ;const childProc = cwd let isClean = false; childProc childProc
.runShellCmd( cmd, [args, [options] ] )
.spawnShell( cmd, [args, [options] ] )
Those are the shelled versions of .runcCmd()
and .spawn()
, respectivly. Meaning, the option {shell: true}
is used. Use the shelled versions when you need to chain commands, redirect I/O, file globbing patterns and other shell behvior.
For example:
cwd
.parentProcess
Returns a cwd instance that redirects all of its commands' I/O to its parent process. It utilizes Node's {stdio:inherit}
option. Read more.
cwdparentProcess
Is the equivalent of:
cwd
NOTE: All
cwd
instances (including the default instance) have the.parentProcess
prop.
When the parent process is Node's global process
object, it usually means write output to the terminal screen and read input from the keyboard. In this case the above code acts just like running git status
from the terminal yourself.
What is CWD?
CWD
stands for: Current Working Directory.
When working with a Command Line Interface (CLI) you execute commands from within a certain directory, your code
folder, for example:
# Windows C:\path\code\> # Linux ~/path/code $
This basic concept of running commands from within a certain folder was the main trigger to create run-in-cwd
.
run-in-cwd
vs. Node's child_process
Command arguments
With Node, when you want to run a simple command with arguments like: git status
you would normally either (1) pass a command string and an arguments array (even for a single argument) or (2) add the {shell: true}
option.
const childProc = childProcchildProc
run-in-cwd
takes care of the arguments for you
const cwd = cwd
And when you really need a shell:
cwd
Setting the working directory
Both ways use process.cwd()
as the default working directory for running commands.
When you need to run commands on the same directory but it's not your Current Working Directory, with Node, you will find yourself repetitively using the "cwd" option.
Node's child process:
const childProc = childProcchildProcchildProc
With run-in-cwd
you only do it once:
const createCwd = const cwd = cwdcwdcwd
Data Events
To read a command's output we need to listen to the command streams 'data'
events.
Data events emit chunks:
cpstdout
Data chunks, besides being buffers, as random pieces of data cannot gurantee you get a whole word or a full sentence.
run-in-cwd
provides some higher-level events that emit text (UTF-8 strings), split into lines.
So the equivalent of the native way:
// Native Nodeconst childProc = const cp = childProc; cpstdoutcpstderr
would be:
// run-in-cwdconst cwd = const cp = cwd; cpcp // emits for bothcp