Cómo Node.js maneja los argumentos de línea de comandos: desde process.argv hasta las entrañas
Imagine: escribió un script genial en Node.js que convierte imágenes. Pero tener que modificar el código cada vez para indicar el nombre del archivo es de mal gusto. Es mucho mejor ejecutar: node convert.js --input photo.jpg --format webp y obtener el resultado. ¿Cómo hacer esa magia? Precisamente a esta pregunta, una de las más populares en Stack Overflow, le dedicaremos tiempo hoy.
¿Qué podría ser más simple, aparentemente? Pero bajo el capó de Node.js (y el propio V8) se esconden varias capas que es útil que todo desarrollador conozca. No solo mostraremos cómo obtener process.argv — nos adentraremos en el código fuente de Node.js y descubriremos cómo viajan los argumentos desde su terminal hasta las variables de entorno del proceso. ¡Allá vamos!
¿Para qué sirven los argumentos de línea de comandos?
Los argumentos de línea de comandos (CLI arguments) son la forma principal de hacer que su programa sea flexible y reutilizable sin modificar el código. En lugar de hardcodear, pasa datos de entrada. Esta es la base de prácticamente todas las herramientas DevOps, empaquetadores (Webpack, Vite) y utilidades. Saber trabajar con ellos es una habilidad básica para un desarrollador JavaScript que quiera salir del navegador.
Nivel 1: Fundamentos — process.argv
La forma más simple y nativa de obtener argumentos en Node.js es acceder al objeto global process.argv. Es un array de cadenas. Veamos directamente con un ejemplo.
Archivo: args-demo.js
// args-demo.jsconsole.log(process.argv);Ejecutemos con argumentos:
node args-demo.js hello --name=World 42Salida en consola:
[ '/usr/local/bin/node', // 0: ruta al ejecutable de Node.js '/home/user/args-demo.js', // 1: ruta a su script 'hello', // 2: primer argumento de usuario '--name=World', // 3: segundo argumento '42' // 4: tercer argumento]Esa es toda la magia. process.argv es simplemente un fragmento de lo que el sistema operativo pasó al proceso. Los dos primeros elementos son información de servicio. Todo lo que viene después del índice 2 son sus argumentos. Importante: todos son cadenas. El número 42 llegó como la cadena "42".
¿Cómo descartar los dos primeros elementos?
Por comodidad, a menudo se usa la desestructuración:
// Obtenemos solo los argumentos de usuarioconst [, , ...userArgs] = process.argv;console.log(userArgs); // ['hello', '--name=World', '42']O simplemente process.argv.slice(2).
Nivel 2: Parseo manual — nuestro propio invento
Trabajar con el array de cadenas sin procesar es incómodo. Necesitamos distinguir las banderas (--name) de los valores (World). Escribamos un parser simple para entender la lógica.
// simple-parser.jsconst args = process.argv.slice(2);const parsed = {};
for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg.startsWith('--')) { // Quitamos '--' y buscamos el valor const key = arg.slice(2); const next = args[i + 1]; // Si el siguiente argumento no comienza con '--', es un valor if (next && !next.startsWith('--')) { parsed[key] = next; i++; // Saltamos el siguiente paso de la iteración } else { parsed[key] = true; // Bandera booleana } } else if (arg.startsWith('-')) { // Banderas cortas, por ejemplo -v const key = arg.slice(1); parsed[key] = true; } else { // Simplemente un argumento posicional if (!parsed._) { parsed._ = []; } parsed._.push(arg); }}
console.log(parsed);// node simple-parser.js --name World -v hello// { name: 'World', v: true, _: ['hello'] }Este código es la base de bibliotecas como minimist. Como puede ver, nada complicado. Pero en proyectos reales es mejor usar soluciones ya hechas