An asynchronous readline interface for Node.js.
Instantiates a readline Interface
(RLI) with the following additional features:
-
rl.spawnAsyncProcess()
- Spawns a new child process within the RLI to asynchronously run a given command. Leaves the event loop unblocked but with the appearance of running synchronously. I.e., the user cannot enter input (e.g., commands) during the process, but can terminate the process with^C
and return to the RLI. In contrast, Node's default RLI blocks the event loop, requiring the user to externally kill the entire RLI process. -
rl.addCommands()
- Registers commands for the RLI to parse and execute. Automatically implementstab
autocompletion for the command names. -
rl.onLine()
- Assigns a function to invoke when the user hitsreturn
orenter
, and the input is not a registered command. -
Automatically implements the following commands:
-
.help
- Displays a detailed usage screen of the registered commands, and is automatically invoked upon receiving unrecognized commands. -
.repl
- Enters the Node.js REPL. -
.history
- Displays all previous lines of input.
-
-
Automatically removes older history lines that duplicate new ones.
-
Listens for
^C
(SIGINT
) in the input stream and prompts the user to confirm before exiting the RLI.
npm install dannynemer/readline-async
// Instantiate a readline `Interface`.
var rl = require('readline-async')
// Register commands, executed via `.command`.
rl.addCommands({
name: 'test',
description: 'Run \'myTest.js\' as terminable asynchronous child process.',
func: function () {
rl.spawnAsyncProcess('node', [ './myTest.js' ])
}
}, {
name: 'echo',
argNames: [ '<string>' ],
description: 'Write <string> to the standard output.',
func: function (string) {
console.log(string || '')
}
})
// Listen for when the user hits `return` and the input is not a registered command.
rl.onLine(function (line) {
console.log('Thank you for your input:', line)
})
RLI when ran from command line (with autocompletion and auto-implemented commands):
rl.prototype.spawnAsyncProcess(command, args, [stdio=[ 'ignore', process.stdout, process.stderr ]], [callback])
Spawns a new process within the readline Interface
(RLI) to asynchronously run command
with args
.
Runs the child process within the user's shell (i.e., $SHELL
), enabling args
to contain pipes (e.g., test | pbcopy
), IO redirection (e.g., test > file
), globs (e.g., *.js
), et cetera.
Executes command
as an asynchronous child process, leaving the event loop unblocked, but with the appearance of running synchronously. I.e., the user can not enter input (e.g., commands) during the process, but can terminate the process with ^C
and return to the RLI. In contrast, Node's default RLI blocks the event loop, requiring processes to complete before accepting any input; i.e., the user must externally kill the entire RLI process.
Temporarily disables the RLI's stdio
(input and output) while the child is processing, except for ^C
(SIGINT
) to terminate the child. Restores the RLI stdio
when the child exits or terminates.
command
(string): The command to run as a child process.args
(string[]): The command line arguments forcommand
.[stdio=[ 'ignore', process.stdout, process.stderr ]]
(string|*[]): The optional child process'sstdio
configuration.[callback]
(Function): The optional function to execute after the child process exits and before returning control to the RLI.
(ChildProcess): Returns the spawned ChildProcess
.
rl.addCommands({
name: 'test',
description: 'Run \'myTest.js\' as terminable asynchronous child process.',
func: function () {
rl.spawnAsyncProcess('node', [ './myTest.js' ])
}
})
❯ .test
...executing stuff in 'myTest.js'...
...
→ user sends `^C` from command line
Error: Child process terminated due to receipt of signal SIGINT
❯ .test | grep "Danny" | pbcopy
...executing 'myTest.js' and piping output...
Registers commands
for the RLI to parse and execute. Automatically implements <tab>
autocompletion for the command names.
Commands are executed in the RLI with a leading period followed by the command name: .command
. Commands are passed all arguments that follow the command name.
[commands]
(...Object): The commands the RLI will parse and execute.command.name
(string): The name that invokescommand.func
when input to RLI in the form.name
.[command.argNames]
(string[]): The optional argument names displayed in the RLI usage screen.command.description
(string): The description displayed in the RLI usage screen.command.func
(Function): The function the RLI executes.
rl.addCommands({
name: 'echo',
argNames: [ '<string>' ],
description: 'Write <string> to the standard output.',
func: function (string) {
console.log(string || '')
}
}, {
name: 'exit',
description: 'Terminate RLI.',
func: function () {
rl.close()
}
})
RLI ran from command line (with autocompletion and auto-implemented commands):
❯ <tab>
.test .echo .exit .repl .history .help
❯ . → .ec<tab> → .echo → .echo hello
hello
❯ .foo
Commands
.echo <string> Write <string> to the standard output.
.exit Terminate RLI.
.repl Enter the Node.js REPL.
.history Print the RLI history.
.help Print this screen.
Unrecognized command: .foo
Assigns an event handler to invoke when the user hits return
or enter
and the input is not a registered command (set by rl.addCommands()
).
func
(Function): The event handler invoked per RLI input that is not a registered command. Passed the input line as the only argument.
// Listen for when the user hits `return` and the input is not a registered command.
rl.onLine(function (line) {
console.log('Thank you for your input:', line)
})