ditro
v1.2.0
Published
An OOP CLI package that will ease your CLI production.
Downloads
1
Readme
Ditro
An OOP CLI package that will ease your CLI production.
This package was mainly inspired by Akairo.
Table Of Content
Introduction
Create CLI applications with ease like you never did before. Using similar structure to the famous known discord.js framework, Akairo.
The aim of this package is to make prompting and argument collecting easier for the developer.
It also gives the developer access to other commands in different scopes. This means you can make commands that use sub-commands, or have your own custom help command.
The package also makes use of event emitters, so you might use them for statistics purposes, or replying to the CLI user. It really opens different paths to the developer.
Getting Started
Setup
First install the package using:
$ npm i ditro
# or
$ yarn add ditro
You need to setup your project structure like this:
Project/
package.json
src/
index.js
cli/
Cli.js
commands/
listeners/
Inside package.json
, add a main
field, with the value of src/index.js
.
You can also add a bin
object specifying the name of the cli as the key, and path to main file as the value.
"main": "src/index.js",
"bin": {
"mycli": "src/index.js"
}
Note:
mycli
will be used as the cli bin name. It can be ran after doingnpm link
as "mycli ...args
". You can rename it to your desire.
Creating CLI
Inside src/cli/Cli.js
, you have to import DitroCli
from ditro
, and create a class extending DitroClass
, supplying valid options, then exporting it.
You will create placeholder methods to use later on in the "Getting Started" guide.
const { DitroCli } = require('ditro');
const pkg = require('../../package.json');
class MyCli extends DitroCli {
constructor() {
super('My Cli Name', { pkg });
}
_init() {
// placeholder method
}
start() {
this._init();
// placeholder method
}
}
module.exports = MyCli;
Inside src/index.js
you will create an instance of your custom CLI, and start it.
const MyCli = require('./cli/Cli');
const cli = new MyCli();
cli.start();
Creating Commands
First you have to create a CommandHandler
instance (imported from ditro
), supplying valid options and attaching it to the cli
as the property MyCli#commandHandler
.
CommandHandler
expects two arguments, a DitroCli instance and valid options.
You can choose which commands to load or load everything in the directory you specify. We are going to stick with the latter for now.
In the MyCli#start
method, we will call CommandHandler#run
which parses the user input and the corresponding command.
const { DitroCli, CommandHandler } = require('ditro');
const { join } = require('path');
const pkg = require('../../package.json');
class MyCli extends DitroCli {
constructor() {
super('My Cli Name', { pkg });
this.commandHandler = new CommandHandler(this, {
directory: join(__dirname, '..', 'commands/')
});
}
_init() {
this.commandHandler.loadAll();
}
start() {
this._init();
this.commandHandler.run();
}
}
module.exports = MyCli;
You can now create a very basic command. Create a new file src/commands/greet.js
. Import Command
from ditro
, create a class extending Command
and export it.
This command will prompt the user for their name and greet them.
Currently, there are three ways for collecting input from a user.
- args - Uses ArgsAndFlags (due to change in future). This parses arguments passed when running the cli, for example,
node start greet GamesProSeif
, here,GamesProSeif
is an argument. - flags - Also uses ArgsAndFlags (due to change in future). This parses flags passed when running the cli, for example,
node start adduser --age 15 --is-male
, here,age
is a flag with the value of15
andis-male
is a flag with value oftrue
. - prompt - Uses Inquirer, which is a very helpful package for prompting users. Their README has its documentation and relative information.
You can pass any of args
, flags
or prompt
in the command options (second parameter when calling super), as an array of input data, each respective to their relative packages ArgsAndFlags for args
and flags
, Inquirer for prompt
.
In this example we will use prompt
.
The super call expects two arguments, first is the
id
of the command, next is theCommandOptions
.
const { Command } = require('ditro');
class GreetCommand extends Command {
constructor() {
super('greet', {
aliases: ['greet', 'g'],
description: 'Greets a user.',
prompt: [
{
name: 'username',
message: 'What is your name?'
}
]
});
}
exec(data) {
console.log(`Hello ${data.prompt.username}`);
}
}
module.exports = GreetCommand;
Explaining the options:
aliases
- Array of strings. This is what is used to run the command. Theid
is for storing purposes, calling theid
as a command will not run it.description
- You can pass anything in this option, it will always be stored inCommand#description
. You set it as a string, or set it as an object containing multiple values. I generally do the latter, passing three properties,content
;usage
;examples
.- prompt - Array of Inquirer questions.
The Command#exec
method will be called passing it a CommandExecData
object.
Now you should be able to run the command, node . greet
.
Creating Listeners
First you have to create a ListenerHandler
instance (imported from ditro
), supplying valid options and attaching it to the cli
as the property MyCli#listenerHandler
.
ListenerHandler
expects two arguments, a DitroCli instance and valid options.
You can choose which listeners to load or load everything in the directory you specify. We are going to stick with the latter again.
You will need to call ListenerHandler#setEmitter
supplying valid EventEmitter
instances in the format
{
emittername: anActualEventEmitter
}
You can use emittername
later inside listeners as Listener#emitter
.
const { DitroCli, CommandHandler } = require('ditro');
const { join } = require('path');
const pkg = require('../../package.json');
class MyCli extends DitroCli {
constructor() {
super('My Cli Name', { pkg });
this.listenerHandler = new ListenerHandler(this, {
directory: join(__dirname, '..', 'listeners/')
});
this.commandHandler = new CommandHandler(this, {
directory: join(__dirname, '..', 'commands/')
});
}
_init() {
this.listenerHandler.setEmitters({
commandHandler: this.commandhandler
});
this.listenerHandler.loadAll();
this.commandHandler.loadAll();
}
start() {
this._init();
this.commandHandler.run();
}
}
module.exports = MyCli;
You can now head to creating your first listener. Create a new file src/listeners/end.js
. Import Listener
from ditro
, create a class extending Listener
and export it.
This listener will listen to the event Cli#end
which gets emitted when the cli finishes processing, and is about to exit.
The super call expects two arguments, first is the
id
of the listener, next is theListenerOptions
.
const { Listener } = require('ditro');
class EndListener extends Listener {
constructor() {
super('end', {
emitter: 'cli', // the event emitter
event: 'end', // event name
type: 'on' // either "on or "once" (default: "on")
});
}
// parameters here are ones passed in EventEmitter.on('event' (...args));
exec(successful) {
console.log('Processing ended - successful:', successful);
}
}
module.exports = EndListener;
successful
is a boolean which indicates whether a command was ran and was successful. If you try running the cli, when it finishes processing, the Cli#end
event will get emitted.
You can listen to other commands too. Events:
- Cli
- end
- desc: emitted when cli finishes processing and is about to exit.
- params: (successful: boolean)
- end
- CommandHandler
- commandStarted
- desc: emitted immediately before a command starts.
- params: (command: Command, argv: string[], data: CommandExecData)
- commandFinished
- desc: emitted immediately after a command finishes successfully.
- params: (command: Command, argv: string[], data: CommandExecData)
- error
- desc: emitted when the
Command#exec
method throws an uncatched error. - params: (error: Error, command: Command, argv: string[], data: CommandExecData)
- desc: emitted when the
- promptStarted
- desc: emitted immediately before the user is prompted for data.
- params: (command: Command, argv: string[], promptData: Question[])
- promptFinished
- desc: emitted immediately after user answers all prompt questions.
- params: (command: Command, argv: string[], promptData: Question[])
- invalidCommand
- desc: emitted when no command is found (no alias for it).
- params: (argv: string[])
- commandStarted
Credits
This package is authored and maintained with <3 by GamesProSeif