Como o Node.js processa argumentos de linha de comando: do process.argv aos fontes
Imagine: você escreveu um script Node.js incrível que converte imagens. Mas ter que editar o código toda vez para especificar o nome do arquivo é deselegante. É muito mais legal executar: node convert.js --input photo.jpg --format webp e obter o resultado. Como fazer essa mágica? É exatamente a essa pergunta, uma das mais populares no Stack Overflow, que vamos dedicar nosso tempo hoje.
Parece simples, não é? Mas por baixo dos panos do Node.js (e do próprio V8) existem várias camadas que todo desenvolvedor deveria conhecer. Não vamos apenas mostrar como obter process.argv — vamos mergulhar nos fontes do Node.js e descobrir como os argumentos viajam do seu terminal para as variáveis de ambiente do processo. Vamos nessa!
Por que os argumentos de linha de comando são necessários?
Os argumentos de linha de comando (CLI arguments) são a principal forma de tornar seu programa flexível e reutilizável sem alterar o código. Em vez de hardcode, você passa dados como entrada. Essa é a base de praticamente todas as ferramentas DevOps, bundlers (Webpack, Vite) e utilitários. Saber trabalhar com eles é uma habilidade fundamental para o desenvolvedor JavaScript que deseja ir além do navegador.
Nível 1: Fundamentos — process.argv
A maneira mais simples e nativa de obter argumentos no Node.js é acessar o objeto global process.argv. É um array de strings. Vamos direto ao exemplo.
Arquivo: args-demo.js
// args-demo.jsconsole.log(process.argv);Vamos executar com argumentos:
node args-demo.js hello --name=World 42Saída no console:
[ '/usr/local/bin/node', // 0: caminho para o executável do Node.js '/home/user/args-demo.js', // 1: caminho para seu script 'hello', // 2: primeiro argumento do usuário '--name=World', // 3: segundo argumento '42' // 4: terceiro argumento]Essa é toda a mágica. process.argv é simplesmente uma fatia do que o sistema operacional passou para o processo. Os dois primeiros elementos são informações de serviço. Tudo que vem após o índice 2 são seus argumentos. Importante: todos são strings. O número 42 veio como string "42".
Como descartar os dois primeiros elementos?
Para conveniência, costuma-se usar desestruturação:
// Obtém apenas os argumentos do usuárioconst [, , ...userArgs] = process.argv;console.log(userArgs); // ['hello', '--name=World', '42']Ou simplesmente process.argv.slice(2).
Nível 2: Parse manual — sua própria implementação
Trabalhar com um array bruto de strings não é prático. Precisamos distinguir flags (--name) de valores (World). Vamos escrever um parser simples para entender a 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('--')) { // Remove '--' e procura o valor const key = arg.slice(2); const next = args[i + 1]; // Se o próximo argumento não começar com '--', é um valor if (next && !next.startsWith('--')) { parsed[key] = next; i++; // Pula o próximo passo da iteração } else { parsed[key] = true; // Flag booleana } } else if (arg.startsWith('-')) { // Flags curtas, por exemplo -v const key = arg.slice(1); parsed[key] = true; } else { // Apenas um 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 é a base de bibliotecas como minimist. Como você pode ver, nada complicado. Mas em projetos reais, é melhor usar bibliotecas prontas