Ejecutar Scripts de Node.js en JavaScript
Cómo ejecutar archivos JavaScript planos con Node.js - ejecución directa, scripts de npm, líneas shebang, y análisis de argumentos de línea de comandos.
Busca en todas las páginas de la documentación
Cómo ejecutar archivos JavaScript planos con Node.js - ejecución directa, scripts de npm, líneas shebang, y análisis de argumentos de línea de comandos.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tarjeta de referencia rápida - lista para copiar y pegar.
# Ejecutar un script directamente
node script.js
# Ejecutar con reinicio automático cuando cambian archivos (Node 18.11+)
node --watch script.js
# Ejecutar con variables de entorno
NODE_ENV=production node script.js
# Pasar argumentos al script
node script.js --name Alice --verbose// package.json - scripts de npm
{
"name": "my-cli",
"version": "1.0.0",
"type": "module",
"bin": {
"my-cli": "./bin/cli.js"
},
"scripts": {
"start": "node script.js",
"dev": "node --watch script.js",
"cli": "node bin/cli.js"
}
}// bin/cli.js - con shebang
#!/usr/bin/env node
import { parseArgs } from 'node:util';
const { values } = parseArgs({
options: {
name: { type: 'string', short: 'n' },
verbose: { type: 'boolean', short: 'v' },
},
});
console.log(`Hello, ${values.name ?? 'world'}!`);# Hacer el script ejecutable (solo Unix/macOS)
chmod +x bin/cli.js
# Luego ejecutarlo directamente
./bin/cli.js --name AliceCuándo usarlo: Scripts de construcción, tareas de automatización, utilidades puntuales, herramientas CLI, o cualquier cosa que no quieras enviar a través de un navegador.
Una CLI de renombre de archivo completa que convierte nombres de archivo a minúsculas en un directorio.
#!/usr/bin/env node
// bin/rename-lower.js
import { readdir, rename } from 'node:fs/promises';
import { join } from 'node:path';
import { parseArgs } from 'node:util';
const { values, positionals } = parseArgs({
options: {
dry: { type: 'boolean', short: 'd' },
help: { type: 'boolean', short: 'h' },
},
allowPositionals: true,
});
if (values.help || positionals.length === 0) {
console.log('Usage: rename-lower <dir> [--dry]');
process.exit(values.help ? 0 : 1);
}
const dir = positionals[0];
try {
const files = await readdir(dir);
for (const file of files) {
const lower = file.toLowerCase();
if (file === lower) continue;
const from = join(dir, file);
const to = join(dir, lower);
if (values.dry) {
console.log(`[dry] ${from} -> ${to}`);
} else {
await rename(from, to);
console.log(`renamed ${from} -> ${to}`);
}
}
} catch (err) {
console.error('Error:', err.message);
process.exit(1);
}// package.json
{
"name": "rename-lower",
"version": "1.0.0",
"type": "module",
"bin": {
"rename-lower": "./bin/rename-lower.js"
}
}# Pruébalo localmente
npm link
rename-lower ./photos --dry
rename-lower ./photosLo que esto demuestra:
parseArgs de node:util para análisis de argv sin dependenciasbin en package.json para que npm link exponga el comando globalmenteCuando escribes node script.js, Node.js:
"type": "module" está en package.json o el archivo termina en .mjs, de lo contrario CommonJS.process, console, Buffer y compañía.process.exit().Una línea shebang como #!/usr/bin/env node le dice al cargador del sistema operativo qué intérprete usar cuando el archivo se ejecuta directamente. La herramienta env busca node en PATH, que es más portátil que una ruta codificada /usr/local/bin/node.
process.argv es un array donde el índice 0 es la ruta del binario de Node.js, el índice 1 es la ruta del script, y el índice 2+ son los argumentos suministrados por el usuario. Por eso parseArgs omite los primeros dos por defecto.
# Pasar argumentos a través de un script de npm (nota el --)
npm run cli -- --name Alice
# Variables de entorno en línea
API_KEY=abc node script.js
# Modo watch (Node 18.11+)
node --watch script.js
# Modo watch con una entrada específica
node --watch --watch-path=./src script.js
# Modo inspeccionar/depurar
node --inspect-brk script.js
# Analizar args con la utilidad integrada
node -e "console.log(require('node:util').parseArgs({options:{n:{type:'string'}}}))" -- -n hiIncluso para scripts JavaScript planos, puedes optar por type-checking sin agregar un paso de construcción habilitando // @ts-check en la parte superior del archivo y creando un jsconfig.json:
// jsconfig.json
{
"compilerOptions": {
"checkJs": true,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"lib": ["ES2022"]
},
"include": ["bin/**/*.js", "scripts/**/*.js"]
}Agrega tipos JSDoc para parámetros y el editor - y tsc --noEmit - capturará errores:
// @ts-check
/** @param {string} name @returns {string} */
function greet(name) {
return `Hello, ${name}!`;
}#!/usr/bin/env node, ejecutar el archivo directamente (./script.js) en shells Unix genera un error "exec format error" o ejecuta el archivo como un script de shell.chmod +x. Un shebang correcto es inútil si el archivo no es ejecutable. El permiso debe agregarse con chmod +x bin/cli.js y el bit debe ser confirmado en Git (git update-index --chmod=+x)..js en un paquete con "type": "module" es ESM; el mismo archivo en un paquete CJS es CommonJS. Usa .mjs/.cjs para ser explícito y evitar ambigüedad.__dirname es indefinido en ESM. En scripts ESM debes reconstruirlo: const __dirname = path.dirname(fileURLToPath(import.meta.url)).\r\n) falla en Linux/macOS con "bad interpreter". Fuerza LF a través de .gitattributes: *.js text eol=lf.-- en scripts de npm. npm run cli --name Alice pasa --name Alice a npm, no a tu script. Usa npm run cli -- --name Alice.process.argv. El índice 0 y 1 son el binario de Node y la ruta del script - tus argumentos de usuario comienzan en el índice 2.| Herramienta | Fortalezas | Debilidades |
|---|---|---|
node | Integrado, sin configuración, ampliamente compatible | Sin TS, sin modo watch antes de 18.11 |
bun | Inicio más rápido, incluido, ejecuta TS/JSX | Ecosistema más nuevo, algunos gaps de API |
deno | Seguro por defecto, TS nativo, librería estándar | Resolución de módulos diferente, ecosistema más pequeño |
tsx | Ejecutor TS/ESM rápido sobre Node | Dependencia adicional |
pnpm exec | Ejecuta binarios locales sin instalaciones globales | Solo un ejecutor, no un runtime |
node script.js invoca explícitamente el binario de Node.js. ./script.js depende del sistema operativo para leer la línea shebang y elegir el intérprete - así que solo funciona si el archivo comienza con #!/usr/bin/env node y tiene el bit de ejecución establecido.
Usa process.argv, que es un array que comienza con el binario de Node (índice 0) y la ruta del script (índice 1). Los argumentos del usuario comienzan en el índice 2. Para cualquier cosa más allá de banderas triviales, usa parseArgs de node:util.
Mapea un nombre de comando a un archivo de script. Cuando el paquete se instala globalmente - o se vincula a través de npm link - npm crea un symlink en su directorio bin para que puedas ejecutar el comando desde cualquier lugar.
Usa la bandera --watch integrada de Node (Node 18.11+): node --watch script.js. Para más control, emparéjala con --watch-path=./src para restringir los directorios supervisados.
Debes separar las banderas de npm de las banderas de tu script con --. Por ejemplo: npm run cli -- --name Alice. Sin el doble guión, npm consume los argumentos por sí solo.
Casi siempre terminaciones de línea Windows. El shell lee la línea shebang incluyendo el \r final e intenta ejecutar /usr/bin/env node\r, que no existe. Fuerza terminaciones LF para archivos de shell y script a través de .gitattributes.
Sí. Agrega // @ts-check en la parte superior de un archivo .js, crea un jsconfig.json con "checkJs": true, e anota con JSDoc. Ejecutar tsc --noEmit expone errores de tipo sin impacto en tiempo de ejecución.
Usa etiquetas JSDoc como /** @param {string} name @returns {Promise<void>} */. Los editores y tsc entienden estas anotaciones de la misma manera que entienden los tipos de TypeScript.
process.env son las variables de entorno en memoria que el proceso de Node.js heredó. Un archivo .env es solo un archivo de texto - Node.js no lo analiza automáticamente. Usa --env-file=.env (Node 20.6+) o una librería como dotenv.
Llama a process.exit(1) después de registrar el error, o lanza un error no capturado y deja que Node salga con el código 1 automáticamente. Reserva códigos distintos de cero para fallas reales para que pipelines de shell y sistemas de CI puedan detectarlos.
__dirname es un global solo de CommonJS que apunta al directorio del script actual. En ESM reconstruyes desde import.meta.url con fileURLToPath y path.dirname.
Solo en ESM - es decir, un archivo .mjs o un archivo en un paquete con "type": "module". En CommonJS debes envolver el código async dentro de un IIFE async.
Revisado por Chris St. John·Última actualización: 10 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥