Skip to content

Créer une commande ​

La feature command sert à écrire des CLI qui restent simples à appeler et solides à maintenir. Vous décrivez ce que la commande accepte, la librairie lit les arguments du process, valide les entrées, génère le help et appelle votre handler avec des valeurs déjà typées.

Principe ​

Une commande est composée de trois éléments:

  • des options, comme --port 3000 ou --verbose;
  • des sujets, c'est-à-dire soit des arguments positionnels après les options, soit des sous-commandes;
  • un handler, appelé uniquement quand le parsing est valide.

Les options et les arguments s'appuient sur les DataParser de @duplojs/utils. C'est ce qui permet de transformer une entrée CLI, toujours textuelle au départ, en valeur métier typée.

Une première commande ​

ts
import { 
SC
} from "@duplojs/server-utils";
import {
DP
} from "@duplojs/utils";
await
SC
.
exec
(
{
description
: "Search text in a file",
options
: [
SC
.
createBooleanOption
(
"ignore-case", {
aliases
: ["i"],
description
: "Ignore letter case",
}, ), ],
subjects
: [
SC
.
createArgument
("pattern",
DP
.
string
()),
SC
.
createArgument
("filePath",
DP
.
string
()),
], }, ({
options
,
args
: {
pattern
,
filePath
} }) => {
const
sensitivity
=
options
["ignore-case"] ? "case-insensitive" : "case-sensitive";
console
.
log
(`search "${
pattern
}" in ${
filePath
} (${
sensitivity
})`);
}, );

Ici, exec crée la commande racine. La liste subjects déclare deux arguments positionnels (pattern et filePath), puis les valeurs parsées sont exposées dans args.pattern et args.filePath.

grep-like
$ grep-like "TODO" ./src/index.ts --ignore-case search "TODO" in ./src/index.ts (case-insensitive)

Ajouter des options ​

createBooleanOption crée un drapeau. Il vaut true si l'option est présente, false sinon.

createOption parse une valeur unique. Avec required: true, le handler reçoit toujours une valeur.

createArrayOption parse une liste de valeurs et peut imposer un minimum, un maximum ou un séparateur.

ts
import { 
SC
} from "@duplojs/server-utils";
import {
DP
} from "@duplojs/utils";
await
SC
.
exec
(
{
options
: [
SC
.
createOption
(
"type",
DP
.
literal
(["file", "directory"]),
{
required
: true,
aliases
: ["t"],
description
: "Expected resource type",
}, ),
SC
.
createArrayOption
(
"name",
DP
.
string
(),
{
separator
: ",",
description
: "Accepted resource names",
}, ), ], }, ({
options
}) => {
console
.
log
(
options
.
type
);
console
.
log
(
options
.
name
?? []);
}, );

Le type de options.type devient "file" | "directory". Si la valeur reçue ne correspond pas au parser, la commande n'appelle pas le handler.

Comprendre les erreurs ​

Quand le parsing échoue, exec affiche une erreur interprétée puis sort avec le code 1. L'erreur indique le chemin de commande, l'option/l'argument fautif, la valeur reçue et ce qui était attendu.

find-like
$ find-like --type socketCommand failedCOMMAND: rootOPTION: --type✖ expected file | directory but received "socket"

Ce comportement est utile pour les outils internes: vous pouvez garder des contrats stricts sans écrire vous-même les messages de diagnostic.

Sous-commandes ​

Pour une CLI avec plusieurs actions, créez des commandes avec SC.create, puis passez-les comme subjects d'une commande parente.

ts
import { 
SC
} from "@duplojs/server-utils";
import {
DP
} from "@duplojs/utils";
const
installCommand
=
SC
.
create
(
"install", {
description
: "Install a package",
options
: [
SC
.
createBooleanOption
(
"yes", {
aliases
: ["y"],
description
: "Answer yes to prompts",
}, ), ],
subjects
: [
SC
.
createArgument
("packageName",
DP
.
string
())],
}, ({
options
,
args
: {
packageName
} }) => {
console
.
log
(`install ${
packageName
}${
options
.
yes
? " without prompt" : ""}`);
}, ); await
SC
.
exec
(
{
description
: "Package manager",
subjects
: [
installCommand
],
}, () => {
console
.
log
("select a package command");
}, );
apt-like
$ apt-like install typescript --yes install typescript without prompt

Chaque sous-commande peut avoir sa propre description, ses options, ses arguments de sujet et son handler. Le help suit l'arbre de commandes.

Help généré ​

--help et -h sont automatiquement disponibles. Le rendu est construit depuis les descriptions, les alias, les options et les sujets déclarés.

apt-like
$ apt-like --helpCOMMAND: rootDESCRIPTION:Package managerCOMMAND: installDESCRIPTION:Install a packageOPTIONS:- yes: -y, --yesAnswer yes to promptsARGUMENTS: <packageName>- packageName: string

Il est donc important de renseigner les descriptions des commandes et des options: elles deviennent la documentation embarquée de votre outil.

Version légère avec execOptions ​

Quand vous écrivez un script qui n'a pas de sous-commandes ni d'argument positionnel, execOptions suffit. Il parse seulement les options du process et retourne un objet typé.

ts
import { 
SC
} from "@duplojs/server-utils";
import {
DP
} from "@duplojs/utils";
const
options
= await
SC
.
execOptions
(
SC
.
createOption
(
"port",
DP
.
coerce
.
number
(),
{
required
: true,
description
: "HTTP port",
}, ),
SC
.
createBooleanOption
(
"reload", {
aliases
: ["r"],
description
: "Restart on file change",
}, ), ); if (
options
.
reload
) {
console
.
log
(`server listens on ${
options
.
port
} with reload`);
}
server-script
$ server-script --port 3000 --reload server listens on 3000 with reload

Utilisez execOptions pour les scripts minimaux. Passez à exec dès que vous avez des arguments positionnels, des sous-commandes ou une vraie expérience CLI à exposer.

Diffusé sous licence MIT.