Creando nuestro propio CLI con Node.js
En esta clase vamos a dar un paso muy importante: crear nuestro primer programa de línea de comandos con Node.js.
Un CLI (Command Line Interface) no es algo raro ni avanzado. De hecho, lo usas todos los días aunque no te des cuenta: ls, cd, npm, git… todos ellos son programas de línea de comandos. Y con Node.js, crear los nuestros es sorprendentemente sencillo.
¿Qué es un CLI?
Un CLI es simplemente un programa que se ejecuta desde la terminal. En lugar de tener una interfaz gráfica (GUI) con botones y ventanas, interactuamos con él a través de texto y argumentos.
Accediendo a los argumentos de la terminal
La base de cualquier CLI es poder leer los argumentos con los que se ejecuta. Node.js nos proporciona esto a través del objeto global process:
process.argv
process.argv es un array que contiene todos los argumentos pasados al proceso al iniciarse.
¿Qué contiene exactamente process.argv?
Cuando ejecutas un comando como node index.js ./carpeta, el array process.argv contiene:
- Posición 0: La ruta absoluta del ejecutable de Node.js.
- Posición 1: La ruta absoluta del archivo que se está ejecutando.
- Posición 2 y en adelante: Los argumentos reales que has pasado (en este caso,
./carpeta).
Regla de oro: Por norma general, los argumentos que te interesan para tu lógica siempre empiezan a partir de la posición 2.
Nuestro objetivo: Crear un mini ls
Como ejercicio práctico, vamos a construir un clon simplificado del comando ls. Nuestra herramienta hará lo siguiente:
- Aceptar una ruta por argumento (o usar la actual por defecto).
- Listar todos los archivos y carpetas.
- Diferenciar visualmente entre ficheros y directorios.
- Mostrar el tamaño de cada archivo de forma legible.
Usando módulos nativos: fs y path
Para este proyecto no necesitamos ninguna librería externa. Utilizaremos:
node:fs/promises: Para leer el contenido de los directorios y obtener información (stats) de los archivos de forma asíncrona.node:path: Para construir rutas de forma segura y multiplataforma.
Paso 1: Determinar la carpeta a listar
Lo primero es capturar el argumento que nos pasa el usuario. Si no nos pasa nada, listaremos la carpeta actual (.):
import path from 'node:path'
// El argumento 2 es nuestra carpeta, si no existe usamos '.'
const folder = process.argv[2] ?? '.'
Paso 2: Leer el contenido del directorio
Usaremos readdir para obtener la lista de nombres de archivos en esa carpeta:
import { readdir } from 'node:fs/promises'
let files
try {
files = await readdir(folder)
} catch {
console.error(`❌ No se pudo leer el directorio: ${folder}`)
process.exit(1)
}
Paso 3: Obtener información detallada de cada archivo
Solo con el nombre no sabemos si es una carpeta o cuánto pesa. Necesitamos usar stat para obtener los metadatos:
import { stat } from 'node:fs/promises'
const filePromises = files.map(async (file) => {
const filePath = path.join(folder, file)
const stats = await stat(filePath)
const isDirectory = stats.isDirectory()
const fileType = isDirectory ? '📁' : '📄'
const fileSize = stats.size.toString()
const fileModified = stats.mtime.toLocaleString()
return `${fileType} ${file.padEnd(20)} ${fileSize.padStart(10)} ${fileModified}`
})
const filesInfo = await Promise.all(filePromises)
filesInfo.forEach((line) => console.log(line))
Paso 4: Formateando el tamaño para humanos
Mostrar 1048576 bytes no es muy útil. Vamos a crear una pequeña utilidad para mostrar KB o MB de forma legible:
function formatSize(bytes) {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
Renderizando el resultado final
Para que nuestro CLI se vea profesional, usamos padding (padEnd y padStart) para alinear las columnas. Esto hace que la información sea mucho más fácil de leer de un vistazo.
El resultado final es una herramienta que funciona tanto con rutas relativas como absolutas y se comporta de forma muy similar al ls real, dándonos un control total sobre qué información queremos mostrar.
Ideas para mejorar tu CLI
Este ejercicio es la base perfecta para practicar Node.js real. Puedes intentar añadir estas funcionalidades:
- Ordenar: Mostrar primero los directorios y luego los archivos, u ordenar alfabéticamente.
- Filtrar: Añadir flags como
--only-fileso--only-dirspara filtrar el contenido. - Extensiones: Filtrar por tipo de archivo (ej: solo archivos
.js). - Colores: Usar códigos ANSI o librerías externas para resaltar información importante.
En esta clase has aprendido que…
- Crear herramientas de terminal con Node.js es potente y accesible.
process.argves la base de cualquier CLI para interactuar con el usuario.- Los módulos nativos de Node.js son suficientes para construir herramientas profesionales sin depender de librerías externas.
Dominar la creación de CLIs te permitirá automatizar tus propios flujos de trabajo y entender mucho mejor cómo funcionan por dentro las herramientas que usas a diario en tu carrera como desarrollador.