clite
v0.3.0
Published
A slight CLI framework
Downloads
5,161
Readme
clite
A CLI lite framework for your node CLI utilities. Provides basic routine functionality that you don't want to write yourself. Pronounced: slight.
Features
- version: echo's current
package.json
version, or if omitted (if you're using semantic-release) will echo the current branch & commit - help: simplifies loading and reading help files
- stdin: automatically detects bodies on stdin and passes it as an argument to your code
- update notification: uses update-notifier to automatically notify your users of an update to your CLI tool
- on exceptions, will echo the error and quit with a non-zero exit code
- automatically creates aliases for all your CLI commands and options using abbrev
- supports boolean flags, options and aliases using yargs
- promise based (note that promises are polyfilled in node < 4, using es6-promise)
- command modules are lazy loaded, which reduces boot and exit time in your CLI tool
Usage
After npm install --save clite
to your project, the simplest CLI script contains:
#!/usr/bin/env node
var clite = require('clite');
clite(require('./config'));
The config drives how your code is run from the CLI. Note that by default, clite expects your code to return a string (which will be echo'ed on STDOUT
) or throw an error that also echos the error.message
on STDERR
.
Debugging
To see more details on what clite is parsing and see any stacktraces inside of clite, use the DEBUG=clite
env value:
DEBUG=clite <your-demo-app>
Return objects in commands (and async use)
Your command modules are called inside of promises. The clite framework expects a string
to be returned out of the promises to be printed on STDOUT
.
If your command needs to make use of asnychonous programming, return a promise, and resolve the promise with a string. For example:
module.exports = function echoLater(args) {
return new Promise(resolve =>
setTimeout(() => resolve('All done'), 1000)
);
};
Config
The configuration is made up of the following properties:
- commands: a map of CLI arguments to JavaScript files
- booleans: an array of arguments you wish to accept as boolean values
- options: an array of arguments you wish to accept as strings (or numbers)
- alias: a map of alias keys (the CLI alias) and values (the real value)
- help: either a filename, or a map of keys and values to text filenames
- return: defaulted to false, returns all values, errors included, to user code (by default exceptions cause a
process.exit(1)
)
Important: all filenames (for help and commands) are relative to the root of your package.json file.
For commands
and help
, a special key of _
that is used if no argument is given (i.e. your user runs your CLI utility without any arguments).
A sample config can be seen in the example section below.
Commands
The commands are the mapping from the CLI argument to your scripts. An example commands
in the config could be:
module.exports = {
commands: {
'_': 'lib/index',
'publish': 'lib/publish',
'search': 'lib/search',
'new': 'lib/create-new-post'
},
// snip
Although clite uses promises, your code does not need to use them (but you can if chose to), however, if you throw
an error, this will echo to the terminal and exit(1)
the program. The commands modules receive three arguments:
args
: an object of the fully parsed CLI arguments, all command arguments are stored inargs._
as an array (note that the array only contains all the remaining args not matched to flags or commands)settings
: the configuration of clite (including defaults)body
: the body of text if content was piped viaSTDIN
For example, lib/create-new-post
could contain:
module.exports = (args, settings, body) => {
if (body) {
// create the post in the db
return new Post({
body,
title: args._[0]
}).save().then(r => `Successfully created ${r.id}`);
}
};
// called using `cat post.md | my-cli-tool new "Awesome Post Title"
This also assumes that your bin script is using .then(console.log)
to redirect responses to the terminal. Of course, you don't have to do that, you can handle printing to the terminal as you please.
Example
Directory structure:
clite-demo
├── cli
│ ├── config.js
│ └── index.js
├── help
│ ├── help.txt
│ └── setup.txt
├── lib
│ ├── search.js
│ ├── publish.js
│ ├── create-new-post.js
│ └── index.js
├── node_modules
│ └── clite <snip>
└── package.json
Snippet of package.json
:
{
"name": "clite-demo",
"main": "lib/index.js",
"dependencies": {
"clite": "^1.0.0"
},
"bin": {
"clite-demo": "cli/index.js"
}
}
Contents of cli/index.js
(which is linked to the bin file clite-demo
):
#!/usr/bin/env node
var clite = require('clite');
clite(require('./config')).then(console.log);
Contents of cli/config.js
:
module.exports = {
commands: {
'_': 'lib/index',
'publish': 'lib/publish',
'search': 'lib/search',
'new': 'lib/create-new-post'
},
option: [
'grep',
'count',
],
flag: [
'debug',
'json'
],
help: {
_: 'help/help.txt',
setup: 'help/setup.txt'
},
};
Important note: where _
is used, this is the fallback for if the user has not specified a value for a particular command. If the default is not found, clite will revert to loading ".
" (aka the index of package directory).
FAQTIMU
Frequently asked questions...that I made up:
I see undefined
at the end of the output
This is because clite is logging out your content, then your code is including a final log, such as:
clite(require('./config')).then(console.log);
To fix this, remove the final .then(console.log)
.
Can I get the original CLI arguments?
The original CLI args is on process.argv
, if you want everything that wasn't a boolean or an option, then this is in the command's arg.argv
- which is usually the same as arg._
except doesn't contain the path to node
, the script that ran the code and if a command was matched, that command.
Feedback, filing issues & pull requests
Please see the contributing for guidelines. All feedback is welcome ❤
License
MIT / http://rem.mit-license.org