Créer une CLI interactive avec Inquirer.js et chalk

Les interfaces en ligne de commande occupent une place grandissante dans les chaînes de production des développeurs. Qu'il s'agisse d'initialiser un projet, de générer du code ou d'orchestrer des déploiements, ces outils automatisent des tâches répétitives et accélèrent le travail quotidien. Une CLI bien conçue réduit la friction, guide l'utilisateur et reste accessible aux profils les moins techniques.

Inquirer.js et chalk se sont imposés comme des références pour bâtir ce type d'interfaces dans l'écosystème Node.js. Le premier facilite la création de questions interactives (choix unique, multi-sélection, saisies libres), tandis que le second sublime l'affichage grâce à la couleur, au gras et aux styles. Ensemble, ils transforment un script austère en une expérience claire et moderne, comparable à celle d'outils comme Yeoman ou Create Next App.

Ce guide pratique montre comment assembler ces deux bibliothèques pour produire un outil robuste. Nous verrons l'installation, la construction de formulaires, la mise en forme des retours et l'organisation du code pour rester maintenable. Les exemples fournis fonctionnent aussi bien sur macOS, Linux que Windows grâce à Node.js.

L'article s'adresse aux développeurs qui souhaitent se lancer dans la création d'un outil interne ou d'un projet open source. Quelques notions de JavaScript et de npm suffisent pour suivre les étapes. À la fin de la lecture, vous disposerez d'une base solide pour concevoir votre propre CLI.

Préparer l'environnement Node.js pour la CLI

Avant d'écrire la moindre ligne d'interaction, il faut poser les fondations du projet. Créez un répertoire dédié puis initialisez un package.json avec npm init -y. Ajoutez ensuite les deux dépendances principales : npm install inquirer chalk. Pour exploiter les modules ES modernes, configurez le champ "type": "module" dans le manifeste, ce qui simplifie l'écriture du code par rapport à l'ancienne syntaxe CommonJS.

Pensez également à déclarer un champ bin dans le package.json afin de rendre l'exécutable disponible via npm. Par exemple, "bin": { "monoutil": "./bin/index.js" }. Le fichier d'entrée doit commencer par un shebang #!/usr/bin/env node pour que le système reconnaisse l'interpréteur. Une fois publié, npm installera automatiquement un lien symbolique dans le PATH global de l'utilisateur.

Pour un projet de qualité professionnelle, organisez dès le départ vos dossiers : src pour le code applicatif, bin pour le point d'entrée, lib pour les modules métiers. Cette séparation devient vite indispensable quand l'outil grossit. Les développeurs qui adoptent une architecture propre en Node.js avec repositories et use cases retrouvent des réflexes directement applicables aux CLI, où chaque commande peut être traitée comme un cas d'usage métier.

Concevoir des invites interactives avec Inquirer.js

Inquirer.js expose une fonction prompt qui prend un tableau de questions et renvoie une promesse contenant les réponses. Chaque question se décrit par un type (input, confirm, list, checkbox, password, editor, etc.), un nom, un message et des options supplémentaires. Une première invite de type input peut demander le nom du projet, tandis qu'une list permet de sélectionner un framework cible.

Pour les cas plus complexes, combinez les invites en chaînant les appels await inquirer.prompt(...). Vous pouvez conditionner une question à la réponse précédente en la plaçant dans une fonction qui reçoit l'objet answers. Cette approche dynamique évite de demander des informations inutiles à l'utilisateur et raccourcit le parcours, ce qui améliore le taux d'achèvement de l'outil.

N'oubliez pas la validation : Inquirer.js accepte une fonction validate sur chaque question pour vérifier le format d'une adresse e-mail, la longueur minimale d'un identifiant ou la conformité d'un slug. Ajoutez également une fonction filter lorsque la valeur doit être normalisée (mise en minuscules, suppression des espaces, etc.) avant d'être stockée. Ces petits détails distinguent un outil amateur d'un utilitaire prêt pour la production.

Mettre en forme la sortie avec chalk

Chalk propose une API fluide pour colorer et styliser le texte. Après l'avoir importé, on écrit chalk.green("Succès") pour afficher un message positif ou chalk.bold.red("Erreur critique") pour signaler un problème bloquant. La bibliothèque détecte automatiquement le support des couleurs du terminal et désactive les styles si la sortie est redirigée vers un fichier, ce qui évite les caractères parasites dans les logs.

Pour aller plus loin, chalk offre des thèmes réutilisables. Définissez par exemple un thème contenant success: chalk.green, warning: chalk.yellow et info: chalk.cyan pour harmoniser tous vos retours. Un fichier lib/theme.js peut exporter ces helpers et être importé partout dans le projet, garantissant une cohérence visuelle entre les différentes commandes.

Côté expérience utilisateur, variez les intensités plutôt que d'empiler les couleurs. Un message d'erreur gagne à être gras plutôt qu'en rouge clignotant, qui peut devenir illisible. Pensez aussi aux personnes daltoniennes : un texte rouge sur fond sombre reste perceptible, mais une combinaison rouge-vert perd son sens. Les bonnes pratiques issues d'une comparaison des bases NoSQL rappellent qu'un choix technologique pertinent dépend toujours du contexte d'usage : il en va de même pour le rendu visuel d'une CLI.

Orchestrer invites et styles pour une expérience fluide

Une fois les invites et la mise en forme maîtrisées, l'enjeu consiste à orchestrer les deux pour produire un parcours cohérent. Commencez par afficher un en-tête stylisé avec chalk (par exemple chalk.bgBlue.white.bold(" Mon Outil v1.0 ")) pour accueillir l'utilisateur, puis lancez la série de questions. À chaque étape, renvoyez un retour contextualisé qui confirme la saisie ou explique la prochaine action attendue.

Pour les commandes longues, intégrez un indicateur de progression. Des bibliothèques comme ora complètent parfaitement chalk en affichant des spinners animés pendant les opérations asynchrones (await ora("Installation des dépendances").start()). À la fin, transformez le spinner en succès vert ou en échec rouge selon le résultat, ce qui offre un feedback immédiat et rassurant.

Si l'outil propose plusieurs sous-commandes, structurez-les avec un analyseur d'arguments tel que commander ou yargs. Ces parseurs s'intègrent naturellement avec Inquirer.js : les arguments fournis en ligne de commande pré-remplissent certaines questions, accélérant l'usage pour les utilisateurs experts. Cette hybridation entre arguments et invites interactives est la signature des CLI modernes comme Vite, npm ou pnpm.

Architecture modulaire et maintenance à long terme

Au fil des évolutions, une CLI peut accumuler des dizaines de commandes. Pour éviter un fichier monolithique, séparez chaque commande dans son propre module. Un dossier commands/init.js, commands/build.js, commands/deploy.js permettra à l'équipe de travailler en parallèle et facilitera les revues de code. Chaque module exporte une fonction qui reçoit les réponses d'Inquirer et exécute la logique métier associée.

La gestion d'état entre les invites mérite aussi une attention particulière. Vous pouvez conserver un objet context passé de commande en commande, ou utiliser des variables d'environnement pour partager des informations entre les sous-commandes. Les développeurs familiers des approches de gestion d'état côté front retrouvent des concepts similaires : un store centralisé, des actions atomiques et une source de vérité unique. Adopter la même rigueur côté CLI évite les incohérences lors de l'enchaînement des invites et simplifie le débogage.

Documentez enfin chaque commande dans un README clair, avec des exemples d'invocation et un tableau des options disponibles. Un outil bien documenté inspire confiance et réduit le volume de questions reçues sur les forums ou GitHub Issues.

Tester, valider et publier la CLI

Les tests constituent souvent le point faible des outils en ligne de commande. Pourtant, ils restent essentiels : un bug silencieux peut corrompre la machine d'un utilisateur. Utilisez un framework comme Vitest ou Node Test Runner pour simuler les réponses d'Inquirer.js et vérifier le comportement de chaque branche conditionnelle. Pour les sorties, capturez le flux stdout et comparez-le à un instantané contenant les balises ANSI attendues.

Avant la publication, validez le binaire en l'installant localement via npm link. Cela crée un lien global vers votre paquet et permet de tester la CLI dans des conditions réelles, hors du dossier de développement. Profitez-en pour exécuter l'outil dans un projet vierge et observer le comportement face à des entrées inattendues (caractères Unicode, chemins Windows, etc.).

Pour publier, vérifiez le numéro de version, rédigez un changelog et exécutez npm publish. Si l'outil reste privé, un simple npm pack génère une archive tarball distribuable en interne. Pensez aussi à configurer un pipeline CI (GitHub Actions, GitLab CI) qui lance les tests et bloque les régressions avant chaque fusion.

Recommandations pour une CLI durable

Ces principes, appliqués dès les premières lignes de code, prolongent la durée de vie d'une CLI et améliorent l'expérience de ses contributeurs. Un outil maintenu, documenté et testé attire naturellement une communauté et devient souvent un standard dans son écosystème. Lancez-vous dès aujourd'hui : npm init, installez Inquirer.js et chalk, et créez la commande qui simplifiera le quotidien de votre équipe. Le code source complet de ce guide peut servir de point de départ pour prototyper vos propres interactions en quelques minutes.