Commands
Command is simply an object with name
and run
function property. Whenever you run the command in terminal, it executes the run
function with the context data. Here's the some of the features of command:
- Execute synchronous and asynchronous code
- Run multi-level sub commands
- Run another command programmatically
- Parse and validate arguments and flags
- Provide tools and utils through context
- Allow middlewares to update the context data
Overview
First, let's see how a command looks like,
const lesy = require('@lesy/compiler');
const commands= [{ name: "hello", run: () => console.log("Hello Buddy!")}],
lesy({commands}).parse();
If you have generated the project from Lesy CLI, you might have an index
file where the commands are exported from there. Your code wight look like this,
// src/index.(js|ts)
const commands = [`${__dirname}/commands`];
const middlewares = [...];
export { commands, middlewares };
// src/commands/hello.(js|ts)
export default {
name: "hello",
run: () => console.log("Hello Buddy!"),
}
In the below examples lets assume the code has index
file where all commands are exported.
Creating a command
Command can be simple Object
, Function
or Class
.
Command Object
{
name: "hello",
run: () => console.log("Hello Buddy!")
}
Command Function
function hello(cmd) {
cmd.name = "hello";
cmd.run = () => console.log("Hello Buddy!");
}
Command Class
class Hello {
name = "hello";
run() {
console.log("Hello Buddy!");
}
}
Based on the compexity of your project you can choose one of these types. For simplicity, we will use object type throughout this guide.
Adding commands
You can directly add the commands in the lesy options as mentioned in the overview section, or you can provide a file path or a directory for lookup.
File path
// src/commands/welcome.(js|ts)
export default {
name: "welcome",
run: () => console.log("Hi!"),
};
Add the file path in index file.
const commands = [`${__dirname}/commands/welcome.(js|ts)`];
export { commands };
Commands directory
You can also refer the commands directly path. this will be usefull if you have many nested commands.
const commands = [`${__dirname}/commands`];
export { commands };
If you want to exclude one or more files, you can simply add
_
prefix to the file name. For example,/commands/_greeting.js
Properties
The only required property for a command is run
. Everything else is optional.
const GreetCommand = {
name:'greet',
description:'Prints greeting message',
aliases:['hello','welcome'],
args:{ name:{} },
flags:{
gender:{
aliases:['g']
}
},
additionalInfo:'',
main:'',
usage:''
group:'commands',
isVisible:true,
run:(ctx)=>console.log(`hello ${ctx.args.name}`)
}
name: String
It refers the name of the command. It should be unique and short. Hyphen -
is allowed.
By default lesy looks for the command named default
if no args supplied in the input. If you would like to run a different command as a default command, set it in the config.
const lesy = require('@lesy/compiler');
const options={
commands:[...],
config:{ defaultCommand:"hello" }
};
lesy(options).parse();
description: String
This is a command description used by help and other plugins.
run(context):void
It is a function which will be executed when this command is triggered. You can also use async/await
for asynchronous calls. Check run context section for more info.
aliases: Array<String>
Command can have multiple aliases. Please make sure you are not using the same alias in the another command.
{
name:"hello",
aliases:["hi","yo"],
run:()=>{}
}
/*
./bin/cmd hello
./bin/cmd hi
./bin/cmd yo
*/
args: Object
Arguments can be defined by a simple object with arg name as property key name. And the value will be the validation schema. Currently, lesy comes with required
, size
and exact
validators. You can check out validator for creating a new rules and schema.
{
name:"hello",
args:{
username:{
required:true,
requiredError:"Please provide username",
},
age:{}
},
run:({args})=>{
console.log(`arg values are ${args}`)
}
}
/*
./bin/cmd hello john 28 // GOOD
./bin/cmd hello john // OK
./bin/cmd hello // THROWS ERROR
*/
flags: Object
Flags are very similar to args. But flags have aliases and type in additional.
{
name:"hello",
flags:{
title:{
type:"string",
required:true
},
isMorning:{}
},
run:({flags})=>{
console.log(`flag values are ${flags}`)
}
}
/*
./bin/cmd hello --title=miss --is-morning // GOOD
./bin/cmd hello --title=mr // OK
./bin/cmd hello // THROWS ERROR
*/
additionalInfo: String
This extra text will be used by other plugins and middlewares. Originally, use case is for showing info about the command in help or pilot command
{
name:"delete",
additionalInfo:"Deletes all files. Run with caution!",
run:()=>{}
}
main: String
When you have sub commands, main
should refer to parent command name. Learn more about sub commands.
{
main:"generate",
name:"component",
run:()=>{}
}
/*
./bin/cmd generate component
*/
usage: String
Sample usage code is shown in the help command.
group: String
If you have many commands you can group them and show it in the help.
Run context
The run
method provides various data about the command and app.
{
name:"hello",
run:(ctx)=>{
// ctx.args - resolved args
// ctx.flags - resolved flags
// ctx.config - returns configuration set in index and by other plugins
// ctx.feature - object of features from app and plugins
// ctx.root - root file path
// ctx.utils - returns color() and spinner() util
// ctx.argv - return raw input argv
// ctx.id - command ID
// ctx.request - object of dynamic actions
}
}
request: Object
request
object can be used to run command dynamically or fetch other command info
{
name:"hello",
run:({request})=>{
request.runCommand(["hello","John"]);
request.getCommandById(1);
request.getCommandByName("Hello");
request.getCommands();
}
}
utils: Object
todo
Sub commands
Commands can be nested multiple levels. If you want to create a sub-command, just add the parent name in main
property. Other properties of the command remains same for sub commands. You can place the sub commands files anywhere, as lesy doesnt iterate through the directories but with the name.
/* PARENT COMMAND */
// src/commands/generate.js
{
name:"generate",
aliases:["g"],
run:()=>{}
}
/* SUB COMMANDS */
// src/commands/generate/component.js
{
main:"generate",
name:"component",
aliases:["c"],
args:{
componentName:{
required:true
}
},
run:()=>{}
}
// src/commands/generate/util.js
{
main:"generate",
name:"util",
flags:{
global:{}
},
run:()=>{}
}
./bin/cmd generate component <componentName>
./bin/cmd g c <componentName>
./bin/cmd generate
./bin/cmd g util --global
Dont use parent command aliases in the
main
property. Always mention the parent command'sname
value inmain
.