CLI
Design of our CLI
The CLI is what you use to connect your applications to keyshade to utilize the configurations. Apart from that, you can also use the CLI to manage your configurations, workspace, and other things. In this document, we will cover how we have developed our CLI, talk about the infrastructure, and how you can start contributing to it.
Stack
We have a very simple stack for our CLI:
NodeJS as the base
Commander as the CLI framework
Clack for interactive prompts
Structure
We are following the principles of OOP to design a clean and extendible CLI. The current architecture allows us to easily build upon the existing system to add new features.
Folder structure
commands: Contains the command files that are used by the CLI
util: Contains utility functions that are used across the CLI
types: Contains the types that are used across the CLI
The BaseCommand
class
BaseCommand
classThe BaseCommand
class is the parent class for all the commands that are used by the CLI. It contains common methods that are used by all the commands. You can find it in src/commands/base.command.ts
. The objects of the subclasses created are primarily tied up in the COMMANDS
variable in index.ts
.
Methods
It is an abstract class with only two abstract methods:
getName
: This method returns the name of the commandgetDescription
: This method returns the description of the command
The rest of the commands are optional and can be overridden as per the requirement.
Creating a new command
Whenever you try to develop a command, you would need to take the following steps:
Create a new file in the
commands
folder with this name:<command-name>.command.ts
Create a new class in that file with this format:
export class <CommandName>Command extends BaseCommand
Implement the abstract methods
getName
andgetDescription
in the classNext up, decide on the other fields that you would like to override from the
BaseCommand
class. It is highly recommended to go through theBaseCommand
class to understand the methods that you can override.If you think you will need to create any more subcommands, create a folder with the name of the command in the
commands
folder and follow the same steps as above. Once done, you would need to tie up those commands by overriding thegetSubCommands
function in the parent command.Once you have finished working on the commands, you would need to add the command to the
COMMANDS
variable inindex.ts
.
The action
function
action
functionThis function is at the heart of the CLI. Any and every action performed by the commands are done inside this function. It is recommended to keep the action
function as clean as possible and move the logic to other private
functions if needed.
The action function accepts just a single argument with the type CommandActionData
:
The arguments and options used by the command are passed to the action
function. You can access them using data.options
and data.args
.
Consider this command: ks profile update main --name "New Name"
. In this command, main
is the argument and --name "New Name"
is the option. You can access the argument using data.args[0]
and the option using data.options.name
. The code implementation will look something like this:
Best practices
Please ensure that you don't stash too much functionality inside the
action
. Use self-explanatory private function names and move the logic to those functions.We use
fetch
for making HTTP requests. Please ensure that you use the HTTP client fromsrc/http
to make requests.Whenever you are creating any HTTP request, please add the API request to the
src/http/<command>.ts
file. This will help in maintaining the codebase and will make it easier for others to understand the code.
Last updated