artesa
v0.4.3
Published
*Artesà* is a NodeJS class-oriented complete solution for creating command line interfaces. It's strongly influenced by [Laravel artisan CLI](https://laravel.com/docs/8.x/artisan#introduction)
Downloads
2
Readme
Artesa (Artesà)
Artesà is a NodeJS class-oriented complete solution for creating command line interfaces. It's strongly influenced by Laravel artisan CLI
Note! This project is early WIP. Documentation is WIP as well.
Features
- Class oriented extendable commands.
- Dynamic autogenerated CLI help feedback and Command help feedback.
- Expressively argument/option parser.
- Input/Output utilities (colors, warnings, etc...)
Getting Started
Install artesa
as a dependency:
npm install artesa
Then, create the CLI entrypoint:
// cli.ts
import { CLI } from 'artesa'
CLI.execute({
//Routes
}).run(process.argv.slice(2))
.then(r => r)
.catch(e => console.error(e));
In artesa, CLI works like a router. You provide a list of "paths" and which commands
must be called for each path.
We'll see how to define routes
later on the next section.
When we call to run()
, CLI will run the suitable command based on the input provided.
Usually for CLIs, process.argv
is used as input. In case you use process.argv
, be sure to remove the first
two elements as they refer to the node
and script path.
For the time being, we can try it calling CLI without any command (it will print the help):
node cli
Registering Commands
A command is a class which contains the logic to perform a specific action in the CLI. Let's see an example:
import { Command } from 'artesa';
import { ArgumentBag, OptionBag } from 'artesa';
export class SampleCommand extends Command {
protected async handle(args: ArgumentBag, options: OptionBag): Promise<number> {
this.io.success(`Command have been executed`);
return 0;
}
}
A command must implement the handle()
method where stays the action logic.
This is a quite simple command which just prints out a message in a green-boxed style (we'll see output helpers later).
However, commands can be as complex as the action it is implementing requires. Notice handle()
returns a Promise
thus
you can perform async
calls.
Registering the command
Once we have a command created, we must register it into the CLI we defined before. As we explained previously, CLI works like a router. Commands are registered with a "path" to call them. Let's modify our entrypoint to register our command:
As we want to call our command with node cli sample
, this will be how CLI entrypoint should look like:
//cli.ts
import { CLI } from 'artesa'
import { SampleCommand } from './commands/sample.command'
CLI.execute({
'sample': SampleCommand
}).run(process.argv.slice(2))
.then(r => r)
.catch(e => console.error(e));
That's it. Now SampleCommand.handle()
will be called with:
node cli sample
You can list all command available calling to the CLI without any path or with the option -h|--help
:
node cli --help
node cli
The command path
cannot contain whitespaces. You can use other kind of separators (for instance, :
: sample:one
)
or use nested commands which is explained in the next section.
Nested commands
Sometimes commands are tightly related. For instance, if we have a Car
model in our project, we might need
create
, update
and destroy
a Car
. In this scenario, would be really nice to be able to create command paths like:
cli cars create
cli cars update
cli cars destroy
This can be done using nested commands in our route list. First, create the commands which performs the actions as explained in the above section:
export class CreateCarCommand extends Command {...}
export class UpdateCarCommand extends Command {...}
export class DestroyCarCommand extends Command {...}
Then, let's register them in our entrypoint as nested commands:
//cli.ts
import { CLI } from 'artesa'
import { SampleCommand } from './commands/sample.command';
import { CreateCarCommand } from './commands/cars/create.command'
import { UpdateCarCommand } from './commands/cars/update.command'
import { DestroyCarCommand } from './commands/cars/destroy.command'
CLI.execute({
'sample': SampleCommand,
'cars': {
'create': CreateCarCommand,
'update': UpdateCarCommand,
'destroy': DestroyCarCommand
},
}).run(process.argv.slice(2))
.then(r => r)
.catch(e => console.error(e));
That's it, if we call to the CLI help we'll see our nested commands:
node cli --help // or just node cli
node cli cars --help // or just node cars
Command arguments
TODO
Command options
TODO
Command output utilities
TODO