-
Notifications
You must be signed in to change notification settings - Fork 15
Research: CLI third party libs
The CLI sits in between the user input and the node process. It's main purpose is to interact with the user or the CI pipeline.
A couple of main reasons for a CLI are:
- process user input
- validate input
- transform input
- prompt information
- execute logic
This document aims to list a view third party libraries that can be used to create the functionality.
Problems to solve:
- commands
- options (like module run -n --force)
- a dynamically generated help menu based on your arguments,
- bash-completion shortcuts for commands and options
. Package. | Deps | Docs. | Support | NPM installs | Bundle Size | GitHub Stars | OS Support |
---|---|---|---|---|---|---|---|
yargs | 7 | good | recently active | 10M+ weekly | 110.9kB | ~7k. | all |
commander.js | 0 | good | recently active | 15M+ weekly | 32.5kB | ~20k. | all |
yargs helps you build interactive command line tools by parsing arguments and generating an elegant user interface.
commander a similar majored alternative to yargs.
Summary:
Flexibility & Features:
While both libraries offer a robust set of features for command-line parsing, yargs tends to be more feature-rich and offers a more flexible API. It is often praised for its detailed help output and advanced parsing capabilities.
Nx uses yargs under the hood and had a re evaluation of the CLI wrapped a year ago and stayed with yargs.
A clear problem with yarg's is the testing and error messages.
Problems to solve:
- color output
- weight output
. Package. | Deps | Docs. | Support | NPM installs | Bundle Size | GitHub Stars | OS Support |
---|---|---|---|---|---|---|---|
chalk | 0 | good | ??? | 20M+ weekly | 5.6kB | ~15k. | all |
kleur | 0 | good | ??? | 2M+ weekly | 1.9kB | ~1k. | all |
chalk.
Uses supports-color to detect whether the current environment supports color, making it very adaptive to different systems and terminals. It also has a graceful fallback for not supported features in different OS.
kleur
As a lightweight alternative, kleur does handle coloring across platforms, but may not have the extensive environment detection that chalk has due to its minimalist approach.
Summary
kleur is juner (maybe more modern stack), but imo the OS support and gracefulness to not existing features wins.
@TODO update for https://github.com/jaywcjlove/colors-cli
As this is essential and not too hard to do I suggest to go with a custom implementation.
/**
* Executes an asynchronous process and returns a `Promise`.
* It provides a set of callbacks for progress handling (`next`, `error`, `complete`).
**/
export function executeProcess(cfg: ProcessConfig): Promise<string> {
const {observer} = cfg;
let {next, error, complete} = observer || {};
const nextCb = next || ((v: string) => void 0);
const errorCb = error || ((v: any) => void 0);
const completeCb = complete || (() => void 0);
return new Promise((resolve, reject) => {
const process = spawn(cfg.command, cfg.args);
let output = '';
process.stdout.on('data', (data) => {
output += data.toString();
nextCb(data.toString());
});
process.stderr.on('data', (data) => {
output += data.toString();
nextCb(data.toString());
});
process.on('error', (error) => {
output += error.toString();
errorCb(error);
reject(error);
});
process.on('close', (code) => {
if (code === 0) {
completeCb(output);
resolve(output);
} else {
const error = new Error(`Process exited with code ${code}`);
errorCb(error);
reject(error);
}
});
});
}
const blocking = await executeProcess({
command: 'node',
args: ['./execute-multiple-aidits.js']
})
.catchError(() => console.log('execution failed'));
const promise = executeProcess({
command: 'node',
args: ['./execute-multiple-aidits.js'],
{next: (stdout) => console.log(stdout), error: () => console.log('execution failed')}
});
As a parser and type generator we use zod.
A downside of zod is, it has hard to read error messages.
Example of the issue:
Error message:
ZodError: [
{
"code": "invalid_type",
"expected": "number",
"received": "nan",
"path": [
"parallel"
],
"message": "Expected number, received nan"
}
]
A better readable error message could be generated with zod-error.
import { generateErrorMessage, ErrorMessageOptions } from 'zod-error';
import { z } from 'zod';
enum Color {
Red = 'Red',
Blue = 'Blue',
}
const options: ErrorMessageOptions = {
delimiter: {
error: ' 🔥 ',
},
transform: ({ errorMessage, index }) => `Error #${index + 1}: ${errorMessage}`,
};
const schema = z.object({
color: z.nativeEnum(Color),
shape: z.string(),
size: z.number().gt(0),
});
const data = {
color: 'Green',
size: -1,
};
const result = schema.safeParse(data);
if (!result.success) {
const errorMessage = generateErrorMessage(result.error.issues, options);
throw new Error(errorMessage);
}
Problems to solve:
- e2e-test CLI
- provide dryRun features
List VFS libraries for nodeJs we looked at:
As we want to keep our architecture mostly un-influenced by the VFS we prefere to patch the filesystem, not provide a replacement. vinyl, unionfs, memory-fs are not patching so we exclude them.
. Package. | Deps | Docs. | Support | NPM installs | Bundle Size | GitHub Stars | OS Support |
---|---|---|---|---|---|---|---|
fs-monkey | lots | todo | ??? | 12M+ weekly | 8.4kB | ~100 | all |
mock-fs | lots | todo | ??? | 651k+ weekly | 27.1kB | ~900 | all |
Testing in vitest:
https://github.com/flowup/quality-metrics-cli/commit/3aa29409a45536e258d05ea3e37d693a3c403fe5