Tres horas. Media hora para entender qué declara un proyecto de Node y qué implica añadirle código de terceros, y dos horas y media construyendo una herramienta de terminal que otra persona pueda usar sin que le expliques nada.
Hoy · Hoja de ruta
- 1. Aprende: Qué declara el
package.json, qué es el versionado semántico, qué hace el fichero de bloqueo y qué preguntas preceden a instalar una dependencia. - 2. Haz: Da a tu proyecto los comandos con los que se va a usar, analiza dependencias reales sin instalarlas y termina tu CLI del catálogo.
- 3. Comprueba: Un compañero arranca tu proyecto y usa tu herramienta sin preguntarte nada.
Antes de empezar · 5 minutos, sin apuntes
- Si te doy un proyecto que no conoces, ¿por dónde empiezas para saber cómo se arranca?
- ¿Qué riesgo tiene meter en tu proyecto código escrito por desconocidos?
- ¿Qué hace un buen programa de terminal cuando lo llamas mal?
Se explica
25 minutos · conceptos y demostración
Dos asuntos que parecen administrativos y no lo son: cómo se declara un proyecto para que cualquiera pueda arrancarlo, y qué significa de verdad añadirle una línea a dependencies.
La ficha del proyecto
Se genera con npm init -y y se completa a mano:
{
"name": "mi-api",
"version": "1.0.0",
"type": "module",
"main": "src/servidor.js",
"scripts": {
"start": "node src/servidor.js",
"dev": "node --watch src/servidor.js",
"cli": "node src/cli.js"
},
"engines": { "node": ">=22" },
"dependencies": {},
"devDependencies": {}
}
| Campo | Qué declara |
|---|---|
name, version |
Identidad del proyecto |
type |
Sistema de módulos: module para ESM |
scripts |
Los comandos del proyecto |
dependencies |
Lo que hace falta para ejecutar |
devDependencies |
Lo que hace falta solo para desarrollar |
engines |
Qué versión de Node necesita |
Los scripts son la interfaz del proyecto
El package.json es lo primero que abre quien llega a un proyecto que no conoce. Si arrancar requiere recordar una orden larga con banderas, alguien la escribirá mal.
Un proyecto bien montado se arranca con npm start, se desarrolla con npm run dev y se prueba con npm test, y esos nombres son los mismos en todas partes. Es la misma idea que verás en el módulo de servidor con Maven.
El --watch de Node reinicia el programa solo cuando cambia un fichero; antes hacía falta una dependencia para eso. Los dos guiones de npm run cli -- listar, por su parte, separan los argumentos del script de los de npm.
npm start
npm run dev
npm run cli -- listar
Qué ocurre al instalar
npm install express # dependencia de ejecución
npm install --save-dev nodemon # solo para desarrollar
npm install # todo lo declarado, en un proyecto clonado
npm outdated # qué se ha quedado atrás
npm audit # vulnerabilidades conocidas
Instalar crea o actualiza tres cosas: la entrada en package.json, el árbol real en node_modules y el package-lock.json.
4 . 21 . 2
│ │ └── parche · corrección compatible
│ └────── menor · funcionalidad nueva, compatible
└─────────── mayor · cambio que rompe
| Se escribe | Acepta |
|---|---|
4.21.2 |
Exactamente esa |
~4.21.2 |
Parches: 4.21.x |
^4.21.2 |
Menores y parches: 4.x.x |
* |
Cualquiera. No lo hagas |
El acento circunflejo es el valor por defecto de npm, y es la razón de que dos instalaciones del mismo package.json en días distintos puedan traer código distinto.
El package-lock.json se sube al repositorio
Guarda la versión exacta de cada paquete y de cada dependencia de cada paquete. Es lo que hace que tu proyecto instale hoy lo mismo que instaló ayer, y en el portátil del profesor lo mismo que en el tuyo.
Sin él, «en mi máquina funciona» deja de ser una broma. Y node_modules, en cambio, no se sube nunca: son miles de ficheros reconstruibles con un solo comando.
- ¿Lo resuelve Node de fábrica?
- ¿Cuántas dependencias arrastra consigo?
- ¿Se mantiene: última publicación, incidencias abiertas?
- ¿Sabría hacerlo sin ella si mañana desaparece?
Cada dependencia es código que se ejecuta con tus permisos, que puede tener vulnerabilidades y que alguien tiene que seguir manteniendo. En este proyecto vas a instalar exactamente una.
Una herramienta que otra persona pueda usar
const comandos = {
listar: listarProductos,
buscar: buscarProductos,
añadir: añadirProducto,
borrar: borrarProducto,
ayuda: mostrarAyuda
};
const [, , nombre, ...argumentos] = process.argv;
const comando = comandos[nombre];
if (!comando) {
console.error(`Comando desconocido: ${nombre ?? "(ninguno)"}`);
mostrarAyuda();
process.exit(1);
}
try {
await comando(argumentos);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
Es el objeto de funciones de la sesión 3 de la UD3, ahora con un cometido real. Añadir un comando es añadir una línea.
| Regla | Por qué |
|---|---|
| Ayuda con ejemplos reales | Nadie lee la documentación antes de probar |
| Mensajes de error que dicen qué hacer | «Falta el precio: añadir <nombre> <precio>» |
| Código de salida correcto | Para poder encadenarla con otras |
| Silencio cuando todo va bien | Un programa que grita no se puede automatizar |
| Confirmar lo destructivo | Borrar sin preguntar se paga caro |
El error dice qué esperaba y qué recibió
«Datos inválidos» no sirve para nada. «El precio debe ser un número mayor que cero; se recibió "abc"» dice el problema, el valor y la regla, y quien lo lee sabe qué escribir a continuación.
Es lo mismo que exigías a tus mensajes de validación en la UD4, ahora sin interfaz donde esconderlo.
console.table también existe en Node, y para una lista de objetos es la diferencia entre una salida que se lee y un volcado que no:
console.table(productos.map(({ id, nombre, precio, stock }) => ({
id, nombre, precio, stock
})));
Se trabaja
150 minutos · el proyecto y su herramienta de terminal
Los dos últimos pasos son los que de verdad evalúan lo anterior: uno pone tu herramienta en manos de otra persona, y el otro comprueba que tu proyecto se reconstruye desde cero.
Paso 1 · La interfaz del proyecto · 30 min
- Crea el
package.jsonconnpm inity añade"type": "module". - Define los scripts
start,devycli. - Declara
enginescon la versión de Node. - Comprueba que
npm run devrecarga al guardar. - Crea el
.gitignoreconnode_modulesy.env. - Escribe un
README.mdcon las tres órdenes y qué hace cada una.
Paso 2 · Analizar sin instalar · 35 min
- Busca la ficha de tres paquetes conocidos y anota versión, número de dependencias y fecha de la última publicación.
- Di qué versiones acepta cada una de estas especificaciones:
^2.4.1,~2.4.1,2.4.1. Escribe para cada una dos versiones que sí y una que no. - Instala Express, mira qué cambió en los tres sitios, y desinstálalo.
- Ejecuta
npm audity lee el informe. - Aplica a los tres paquetes las cuatro preguntas del diagrama y decide cuáles instalarías.
- Explica qué pasaría si uno de ellos publicara mañana una versión mayor con cambios incompatibles, teniendo tú un
^en elpackage.json.
Paso 3 · El CLI completo · 50 min
Es el trabajo central de la sesión.
- Implementa
listar,buscar,ver,añadir,borraryayuda. - Valida los argumentos de cada uno con las funciones de la UD3, sin reescribirlas.
- Escribe mensajes de error que digan qué se esperaba, qué se recibió y cuál es la forma correcta.
- Devuelve el código de salida correcto en cada caso.
- Pide confirmación antes de borrar.
- Usa
console.tablepara las listas y comprueba que la salida se lee.
Antes de continuar: llama a tu programa de cinco formas equivocadas distintas —sin comando, con un comando que no existe, sin argumentos, con un precio que no es número y con uno negativo— y comprueba que las cinco dan un mensaje útil y el código de salida correcto.
Paso 4 · Que lo use otra persona · 20 min
Dale el proyecto a un compañero sin explicarle nada, ni siquiera cómo se arranca.
| Momento | ¿Se atasca? | Qué le faltó | Qué has corregido |
|---|---|---|---|
| Arrancar el proyecto | |||
| Descubrir qué comandos hay | |||
| Ejecutar el primero | |||
| Entender un error | |||
| Borrar algo sin miedo |
Anota dónde se atasca sin ayudarle, que es la parte difícil. Cada atasco es un defecto de tu README, de tu ayuda o de tus mensajes, no de la persona.
Corrige lo que salga y vuelve a probarlo con alguien distinto.
Paso 5 · Reconstruir desde cero · 15 min
- Borra
node_modulespor completo y ejecutanpm install. Comprueba que todo vuelve. - Clona tu propio repositorio en otra carpeta, como si fueras otra persona, y arráncalo siguiendo solo el README.
- Comprueba qué falta: seguramente el
.env, que no está en el repositorio. ¿Explica tu README cómo crearlo a partir del.env.example? - Anota cuántos pasos han hecho falta desde clonar hasta ver funcionar el programa. Si son más de tres, mira cuál se puede automatizar en un script.
Ampliación si has completado el trabajo
Primero termina y comprueba los cinco pasos. El primer reto es de seguridad y conviene hacerlo antes de instalar la primera dependencia de la unidad.
Reto 1 · Cuando la dependencia es el problema
Cada paquete que instalas ejecuta código con tus permisos, y lo mismo hacen sus dependencias, y las de sus dependencias.
- Toma un paquete de uso corriente y averigua cuántos paquetes acabas instalando en total al añadirlo.
npm ls --allte lo dice después de instalar. El número suele sorprender. - Busca un caso real documentado de un paquete de npm que fue comprometido o retirado, y resume en cinco líneas: qué pasó, cómo llegó a los proyectos que lo usaban y qué lo habría evitado.
- Explica qué papel juega el
package-lock.jsonen ese escenario, y qué papel no juega. No protege de todo, y conviene saber de qué sí. - Busca qué es un typosquatting de paquetes y por qué el nombre que escribes en
npm installimporta. - Aplica las cuatro preguntas del diagrama a Express, que es la única dependencia que instalarás en esta unidad, y escribe si la instalarías. La respuesta es sí; lo que se evalúa es el razonamiento.
- Escribe en tres líneas qué política seguirías en un proyecto real para decidir cuándo actualizar dependencias, sabiendo que actualizar tarde y actualizar a ciegas son igual de malos.
Reto 2 · Una herramienta que se puede encadenar
Los programas de terminal valen mucho más cuando se combinan con otros. Para eso tienen que respetar unas convenciones.
- Añade una opción
--jsonque imprima el resultado como JSON sin ningún adorno, para poder pasárselo a otro programa. - Comprueba que funciona encadenándolo: redirige la salida a un fichero y ábrelo.
- Haz que los mensajes informativos vayan a la salida de error y solo el resultado a la estándar. Compruébalo redirigiendo solo una de las dos.
- Haz que el programa acepte datos por la entrada estándar además de por argumentos, de modo que se le pueda pasar un JSON desde otro proceso.
- Prueba la cadena completa: un comando que produce datos, el tuyo que los transforma, y la salida guardada en un fichero.
- Explica en tres líneas por qué «silencio cuando todo va bien» deja de ser una manía en cuanto tu programa forma parte de una cadena automatizada.
--json y entrada estándar.Cierre
5 minutos · comprobación y recuerdo
Lista de verificación de la sesión
- El proyecto se arranca leyendo solo el
package.jsony el README. - Los comandos habituales están como scripts.
- Interpretas una versión semántica y sus rangos.
- Sabes qué se sube al repositorio y qué no.
- Tu herramienta valida, informa y termina con el código correcto.
- Sigues sin instalar dependencias.
Antes de cerrar · 3 minutos, sin mirar
- ¿Qué diferencia hay entre
dependenciesydevDependencies? - ¿Para qué sirven los dos guiones en
npm run cli -- listar? - ¿Qué acepta
^1.2.3? - ¿Para qué sirve el fichero de bloqueo, y por qué no se sube
node_modules? - ¿Qué debe hacer un programa de terminal cuando lo llaman mal?
- ¿Por qué conviene que un programa esté callado cuando todo va bien?
Ver respuestas
1 · Las primeras hacen falta para ejecutar en producción; las segundas, solo para desarrollar y probar.
2 · Para separar los argumentos destinados al script de los de npm.
3 · Cualquier 1.x.x igual o posterior: menores y parches, no la versión mayor.
4 · Para que todas las instalaciones traigan exactamente las mismas versiones; node_modules no se sube porque es reconstruible y depende del sistema.
5 · Explicar cómo se usa y terminar con un código de error.
6 · Porque así se puede encadenar con otros programas sin que su salida estorbe.
Microprueba semanal 2 · 5–10 minutos
Individual, sin IA y sin apuntes.
- ¿Qué versiones acepta
^3.2.1? Escribe dos que sí y una que no. - Di cuáles de estos tres se suben al repositorio:
node_modules,package-lock.jsony.env. - Escribe dos preguntas que te harías antes de instalar una dependencia.