
Développement d'applications en ligne de commande (CLI)
Apprenez à développer des outils et applications en ligne de commande (CLI) robustes et interactifs en utilisant Node.js et des bibliothèques populaires comme yargs, commander et inquirer.
Pourquoi développer des CLI avec Node.js ?
Les applications en ligne de commande (Command Line Interfaces - CLI) sont des programmes avec lesquels les utilisateurs interagissent via un terminal textuel, en tapant des commandes et des arguments. Loin d'être obsolètes, les CLI sont omniprésentes dans le monde du développement logiciel et de l'administration système. Elles sont utilisées pour automatiser des tâches, gérer des configurations, interagir avec des services, scaffolder des projets, et bien plus encore (pensez à `git`, `npm`, `docker`, `aws-cli`, etc.).
Node.js s'avère être une plateforme exceptionnellement bien adaptée au développement de CLI pour plusieurs raisons :
- Ecosystème Riche (npm) : Accès à une vaste collection de bibliothèques pour parser les arguments, gérer les interactions utilisateur, colorer la sortie, afficher des indicateurs de progression, et interagir avec le système de fichiers ou le réseau.
- Multiplateforme : Les scripts Node.js fonctionnent généralement de manière cohérente sur Windows, macOS et Linux, simplifiant la distribution.
- Performances : Node.js est rapide, en particulier pour les tâches liées aux I/O, ce qui est courant dans les CLI (lecture/écriture de fichiers, appels API).
- Familiarité : De nombreux développeurs web connaissent déjà JavaScript, ce qui abaisse la barrière d'entrée pour créer des outils CLI.
- Facilité de distribution : Les CLI Node.js peuvent être facilement packagées et distribuées via npm, permettant une installation simple avec `npm install -g mon-cli`.
Cette section explore les outils et techniques fondamentaux pour construire des CLI efficaces et conviviales avec Node.js.
Les bases : accès aux arguments et modules Node.js essentiels
Au niveau le plus fondamental, Node.js donne accès aux arguments passés en ligne de commande via l'objet global `process.argv`. C'est un tableau où :
- `process.argv[0]` est le chemin vers l'exécutable Node.js.
- `process.argv[1]` est le chemin vers le script en cours d'exécution.
- `process.argv[2]` et les suivants contiennent les arguments passés par l'utilisateur.
// simple_cli.js
console.log('Arguments reçus :', process.argv.slice(2));
// Exécution: node simple_cli.js argument1 --option=valeur -f
// Output: Arguments reçus : [ 'argument1', '--option=valeur', '-f' ]Parser manuellement `process.argv` devient rapidement fastidieux et source d'erreurs pour gérer les options, les commandes, la validation et l'aide. C'est pourquoi on utilise presque toujours des bibliothèques dédiées.
D'autres modules Node.js intégrés sont souvent utilisés dans les CLI :
- `fs` (File System) : Pour lire, écrire, et manipuler des fichiers et des répertoires.
- `path` : Pour travailler avec les chemins de fichiers de manière multiplateforme.
- `child_process` : Pour exécuter des commandes externes ou d'autres scripts.
- `os` : Pour obtenir des informations sur le système d'exploitation (par exemple, le répertoire personnel de l'utilisateur).
- `readline` : Pour créer des interfaces interactives en lisant l'entrée utilisateur ligne par ligne (bien que des bibliothèques comme `inquirer` soient souvent préférées pour des interactions plus riches).
Parser les arguments avec élégance : Yargs et Commander
Pour structurer une CLI et gérer facilement les arguments, les options et les commandes, deux bibliothèques se distinguent particulièrement : Yargs et Commander.js.
Yargs : C'est une bibliothèque très puissante et flexible, souvent appréciée pour son approche déclarative et son système de parsing avancé. Elle permet de :
- Définir des commandes et sous-commandes.
- Déclarer des options (flags) avec des alias, des types, des descriptions, des valeurs par défaut.
- Gérer la validation des arguments (obligatoires, type, etc.).
- Générer automatiquement des messages d'aide (`--help`) et de version (`--version`).
- Utiliser un système de middleware.
#!/usr/bin/env node
// yargs_cli.js
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
yargs(hideBin(process.argv))
.command('greet [name]', 'Salue quelqu\'un', (yargs) => {
// Config de la commande 'greet'
return yargs
.positional('name', {
describe: 'Le nom de la personne à saluer',
default: 'Monde'
})
.option('loud', {
alias: 'l',
type: 'boolean',
description: 'Crier la salutation'
});
}, (argv) => {
// Logique de la commande 'greet'
let greeting = `Bonjour, ${argv.name}!`;
if (argv.loud) {
greeting = greeting.toUpperCase();
}
console.log(greeting);
})
.demandCommand(1, 'Vous devez spécifier une commande.')
.help()
.alias('help', 'h')
.argv; // Important: déclenche le parsingCommander.js : C'est une autre bibliothèque très populaire, connue pour sa simplicité et son API inspirée de `git`. Elle offre des fonctionnalités similaires à Yargs (commandes, options, aide auto-générée) avec une syntaxe légèrement différente, souvent considérée comme plus concise pour les cas simples.
#!/usr/bin/env node
// commander_cli.js
const { Command } = require('commander');
const program = new Command();
program
.name('salutations-cli')
.description('CLI pour dire bonjour')
.version('1.0.0');
program
.command('greet')
.description('Salue quelqu\'un')
.argument('[name]', 'Le nom de la personne à saluer', 'Monde')
.option('-l, --loud', 'Crier la salutation')
.action((name, options) => {
let greeting = `Bonjour, ${name}!`;
if (options.loud) {
greeting = greeting.toUpperCase();
}
console.log(greeting);
});
program.parse(process.argv); // Déclenche le parsingLe choix entre Yargs et Commander dépend souvent des préférences personnelles et de la complexité de la CLI à construire. Les deux sont d'excellents choix.
Rendre les CLI interactives avec Inquirer.js
Parfois, une CLI a besoin de poser des questions à l'utilisateur pour obtenir des informations de manière interactive, plutôt que de tout attendre via les arguments. La bibliothèque Inquirer.js est la référence pour créer des interfaces de prompt en ligne de commande riches et conviviales.
Inquirer.js permet de poser différents types de questions :
- Input : Simple champ de texte.
- Number : Champ de texte acceptant uniquement des nombres.
- Confirm : Question oui/non.
- List : Sélection d'une option dans une liste (avec les flèches haut/bas).
- Rawlist : Liste similaire mais avec des index numériques.
- Expand : Liste avec des choix abrégés qui s'étendent.
- Checkbox : Sélection de plusieurs options dans une liste.
- Password : Saisie masquée pour les mots de passe.
- Editor : Ouvre l'éditeur de texte par défaut de l'utilisateur pour une saisie plus longue.
Son utilisation se fait via une méthode `prompt` qui prend un tableau de questions et retourne une Promesse résolue avec un objet contenant les réponses.
#!/usr/bin/env node
const inquirer = require('inquirer');
console.log('Bienvenue dans la configuration interactive !');
inquirer
.prompt([
{
type: 'input',
name: 'projectName',
message: 'Quel est le nom de votre projet ?',
default: 'mon-super-projet',
},
{
type: 'list',
name: 'framework',
message: 'Quel framework souhaitez-vous utiliser ?',
choices: ['React', 'Vue', 'Angular', 'Aucun'],
},
{
type: 'confirm',
name: 'useTypescript',
message: 'Utiliser TypeScript ?',
default: true,
}
])
.then((answers) => {
console.log('\n--- Configuration choisie ---');
console.log('Nom du projet:', answers.projectName);
console.log('Framework:', answers.framework);
console.log('TypeScript:', answers.useTypescript ? 'Oui' : 'Non');
// Ici, vous utiliseriez les réponses pour générer des fichiers, etc.
})
.catch((error) => {
if (error.isTtyError) {
// Erreur si le prompt ne peut pas être rendu dans l'environnement actuel
console.error("Impossible d'exécuter le prompt dans cet environnement de terminal.");
} else {
// Autre erreur
console.error('Une erreur est survenue:', error);
}
});Inquirer.js améliore considérablement l'expérience utilisateur pour les CLI nécessitant une configuration ou des choix multiples.
Améliorer l'expérience utilisateur : couleurs, spinners, barres de progression
Une bonne CLI n'est pas seulement fonctionnelle, elle est aussi agréable à utiliser. Plusieurs bibliothèques permettent d'améliorer l'aspect visuel et le feedback donné à l'utilisateur :
- Chalk : La bibliothèque standard pour ajouter facilement de la couleur et du style (gras, italique, souligné) au texte affiché dans le terminal. Essentiel pour différencier les messages d'information, d'avertissement et d'erreur.
- Ora : Permet d'afficher des spinners animés pour indiquer qu'une tâche de fond est en cours (par exemple, un téléchargement, une installation). Cela évite que l'utilisateur pense que la CLI a planté pendant une opération longue.
- cli-progress : Pour afficher des barres de progression textuelles, utiles pour suivre l'avancement d'une tâche longue et divisible (par exemple, traitement d'un grand nombre de fichiers).
- Boxen : Pour dessiner des boîtes autour de votre texte dans le terminal.
L'utilisation judicieuse de ces outils rend la CLI plus informative et professionnelle.
const chalk = require('chalk');
const ora = require('ora');
console.log(chalk.blue('Début du processus...'));
const spinner = ora('Téléchargement des dépendances').start();
setTimeout(() => {
spinner.succeed(chalk.green('Dépendances téléchargées !'));
console.log(chalk.yellow.bold('Attention: une configuration manuelle peut être nécessaire.'));
console.error(chalk.red('Erreur simulée lors de la post-installation.'));
}, 3000); // Simule une tâche de 3 secondesPackaging et distribution de votre CLI Node.js
Pour rendre votre CLI facilement installable et exécutable par d'autres (ou par vous-même sur différentes machines), vous devez configurer votre `package.json` :
- Le Shebang : Ajoutez la ligne `#!/usr/bin/env node` tout en haut de votre fichier de script principal. Ce 'shebang' indique au système d'exploitation qu'il doit exécuter ce fichier avec l'interpréteur Node.js trouvé dans l'environnement de l'utilisateur. N'oubliez pas de rendre votre fichier exécutable (`chmod +x votre_cli.js`).
- La propriété `bin` : Dans votre `package.json`, ajoutez une propriété `bin`. C'est un objet où la clé est le nom de la commande que les utilisateurs taperont dans leur terminal, et la valeur est le chemin vers votre fichier de script principal.
// package.json (extrait)
{
"name": "ma-super-cli",
"version": "1.0.0",
"description": "Une CLI Node.js géniale",
"main": "index.js",
"bin": {
"ma-super-cli": "./cli.js" // 'ma-super-cli' sera la commande globale
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["cli"],
"author": "Votre Nom",
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2", // Exemple avec Chalk v4 pour compatibilité CommonJS
"inquirer": "^8.2.4", // Exemple avec Inquirer v8 pour compatibilité CommonJS
"yargs": "^17.3.1"
}
}
Développement local : Pendant le développement, pour tester votre commande globale sans la publier, utilisez `npm link` à la racine de votre projet. Cela créera un lien symbolique permettant d'appeler `ma-super-cli` (dans l'exemple ci-dessus) directement depuis votre terminal.
Distribution : Une fois votre CLI prête, vous pouvez la publier sur le registre npm avec `npm publish` (après vous être connecté avec `npm login`). Les utilisateurs pourront alors l'installer globalement avec `npm install -g ma-super-cli` et l'utiliser depuis n'importe où.
Assurez-vous que votre CLI gère correctement les erreurs, fournit des messages clairs, et utilise les codes de sortie (`process.exit(0)` pour succès, `process.exit(1)` ou autre pour erreur) pour pouvoir être utilisée dans des scripts shell.