Plugins y generadores
Los plugins son la forma en que Nx crece: aportan tres cosas —inferencia de targets que aparecen solos leyendo tu configuración, executors que ejecutan tareas y generadores que crean o modifican código de forma reproducible. Con nx add integras React o Vite, con nx generate haces scaffolding determinista, y las migraciones son generadores que actualizan tu repo entre versiones.
Nx viene con un núcleo pequeño y crece por plugins. Un plugin empaqueta tres capacidades que conviene distinguir con precisión: inferencia, que hace aparecer targets solos leyendo archivos de configuración que ya tienes; executors, que implementan esas tareas; y generadores, que crean o modifican código de forma reproducible. Con esta tríada, adoptar una tecnología —React, Vite— deja de ser copiar plantillas de internet y pasa a ser instalar un plugin que sabe cómo debe verse un proyecto de ese tipo en tu workspace, y cómo mantenerlo al día.
- Entender qué añade un plugin: inferencia de targets, executors y generadores.
- Ver la inferencia en acción: targets que aparecen sin escribir configuración.
- Usar generadores con
nx generatepara hacer scaffolding determinista. - Distinguir executor de generador, y ver las migraciones como generadores versionados.
Qué es un plugin de Nx
Un plugin es un paquete que extiende Nx en tres frentes. Aporta executors, las implementaciones de targets que viste en la lección anterior. Aporta generadores, funciones que crean o modifican archivos siguiendo una convención. Y aporta inferencia, la capacidad de mirar tu repositorio y añadir proyectos y targets al grafo sin que tú escribas nada.
Los plugins se registran en el array plugins de nx.json. A partir de ahí participan en la construcción del grafo y ponen sus generadores y executors a tu disposición. Instalar y configurar uno se hace con un solo comando, nx add, que además de añadir la dependencia ajusta la configuración necesaria.
# Instala el plugin y ajusta la configuracion del workspace
nx add @nx/react
nx add @nx/vite
Tras instalarlo, el plugin queda registrado en el array plugins de nx.json. Cada entrada puede llevar opciones que ajustan cómo infiere —por ejemplo, con qué nombre bautiza los targets que crea— de modo que dos equipos puedan usar el mismo plugin con convenciones distintas.
// nx.json: plugins registrados y sus opciones de inferencia
{
"plugins": [
{
"plugin": "@nx/vite/plugin",
"options": {
"buildTargetName": "build",
"serveTargetName": "serve",
"testTargetName": "test"
}
}
]
}
Puedes ver todos los plugins instalados y las capacidades de cada uno con nx list, y el detalle de uno concreto —sus generadores y executors— con nx list @nx/react. Es el primer sitio al que mirar cuando no recuerdas qué tienes disponible.
Inferencia: targets que aparecen solos
Esta es la pieza que más distingue al Nx moderno. Un plugin de inferencia registra un enganche —createNodes— que se dispara al encontrar cierto archivo. El plugin @nx/vite, por ejemplo, detecta cualquier vite.config.ts y, a partir de él, infiere un proyecto con sus targets build, serve, preview y test ya configurados, con sus inputs y outputs correctos. Tú no escribiste esos targets: existen porque el plugin sabe leer la configuración de Vite y traducirla al modelo de Nx.
flowchart LR C[vite.config.ts] --> P[plugin nx vite] P --> N[proyecto en el grafo] P --> B[target build] P --> S[target serve] P --> T[target test] style P fill:#cba6f7,color:#11111b style N fill:#a6e3a1,color:#11111b
La consecuencia es profunda: la configuración de tu herramienta —el vite.config.ts— se convierte en la única fuente de verdad, y la configuración de Nx se deriva de ella en lugar de duplicarla. Ya no tienes un project.json que repite las rutas que también están en la config de Vite, con el riesgo perpetuo de que se desincronicen. Puedes inspeccionar qué infirió un plugin con nx show project web, que te muestra los targets efectivos de un proyecto aunque no los hayas escrito en ningún sitio.
Que un target venga inferido no significa que estés atado a él. Puedes sobrescribir cualquier opción de un target inferido declarándola en el project.json del proyecto: lo que definas a mano gana sobre lo inferido. Así obtienes lo mejor de ambos mundos —cero configuración por defecto, control total cuando lo necesitas— sin renunciar a la sincronización automática para todo lo que no toques.
Generadores: scaffolding como función
Un generador es una función que crea o modifica código de forma reproducible. Donde antes copiabas una carpeta de ejemplo y editabas nombres a mano, ahora ejecutas nx generate y obtienes un artefacto correcto, integrado en el workspace y consistente con todos los demás del mismo tipo.
# Crea una libreria React ya cableada en el workspace
nx generate @nx/react:library ui --directory=libs/ui
# Anade un componente dentro de esa libreria
nx generate @nx/react:component boton --project=ui
Lo que distingue a un generador de un simple copiar y pegar es que entiende el workspace. Al crear la librería ui no solo escribe archivos: registra el proyecto, ajusta los paths del tsconfig para que el resto del repo pueda importarla, configura su linting y sus tests, y deja el project graph coherente. Todo lo que un humano olvidaría a mano queda hecho, y hecho igual cada vez. Esa reproducibilidad es el valor real: en un equipo de cincuenta personas, que toda librería nazca idéntica elimina una fuente enorme de deriva y de discusiones estériles.
Executor
Se ejecuta cada vez que corres una tarea. Recibe opciones y contexto y hace el trabajo: construir, testear, servir. Vive en el task graph.
Generador
Se ejecuta una vez, bajo demanda, para crear o modificar código. No corre en cada build: transforma el repositorio y termina.
Migración
Un generador especial que Nx ejecuta al actualizar de versión, para adaptar tu configuración y tu código al nuevo Nx automáticamente.
Escribir tu propio generador
El ecosistema de plugins es amplio, pero el poder real llega cuando escribes generadores para las convenciones de tu organización. Un generador es una función que recibe un Tree —un sistema de archivos virtual— y unas opciones, y aplica cambios sobre ese árbol; Nx no toca el disco hasta que el generador termina sin errores, así que la operación es atómica y previsualizable con --dry-run.
// Un generador minimo con la devkit de Nx
import { Tree, formatFiles, generateFiles } from "@nx/devkit";
import { join } from "path";
export default async function (tree: Tree, opciones: { nombre: string }) {
generateFiles(tree, join(__dirname, "files"), `libs/${opciones.nombre}`, opciones);
await formatFiles(tree);
}
Todo generador declara sus opciones en un schema.json, que Nx usa para validar los argumentos, pedir de forma interactiva los que falten y autocompletar en la terminal. La función y su esquema son las dos caras de un generador: una hace el trabajo, el otro define su interfaz.
// schema.json: la interfaz de opciones del generador
{
"$schema": "https://json-schema.org/schema",
"type": "object",
"properties": {
"nombre": {
"type": "string",
"description": "Nombre de la libreria a crear",
"x-prompt": "Como se llamara la libreria"
}
},
"required": ["nombre"]
}
Con @nx/devkit dispones de utilidades para leer y escribir la configuración de proyectos, renderizar plantillas y editar archivos de forma estructurada. La misma maquinaria alimenta las migraciones: cuando el equipo de Nx cambia un formato de configuración entre versiones, escribe un generador de migración que nx migrate ejecuta en tu repositorio para dejarlo al día, en lugar de obligarte a editar decenas de archivos a mano. Actualizar Nx es, por eso, en gran medida automático.
# Calcula las migraciones necesarias y escribe migrations.json
nx migrate latest
# Aplica las migraciones ya calculadas, revisables antes de confirmar
nx migrate --run-migrations
El flujo en dos pasos es deliberado: nx migrate latest no toca tu código, solo actualiza las versiones y deja un migrations.json con la lista de transformaciones pendientes. Las aplicas tú, cuando quieras, y las revisas como cualquier otro cambio antes de confirmarlas. La actualización deja de ser un salto de fe.
La documentación es el lugar donde van a morir las convenciones de equipo. Escribes en una wiki cómo debe estructurarse una librería nueva —dónde van los archivos, cómo se nombra, qué configuración de lint lleva, cómo se registra en el tsconfig— y a la tercera semana nadie la lee, cada quien copia la última librería que recuerda, y la deriva empieza: quince variantes ligeramente distintas de lo que debía ser un patrón único. Los generadores atacan esa entropía en su raíz porque transforman la convención de un documento que se lee a un programa que se ejecuta. La estructura correcta deja de ser algo que el desarrollador debe recordar y reproducir con disciplina, y pasa a ser la salida garantizada de un comando. Esto tiene un efecto de segundo orden que se subestima: cuando la convención cambia, no reeducas a cincuenta personas ni persigues quince variantes a mano —editas el generador, y a partir de ese momento todo lo nuevo nace ya con la forma nueva—. Y las migraciones extienden esa misma idea al pasado: si cambia el estándar, un generador de migración recorre lo que ya existe y lo actualiza. La lección profunda es que en un monorepo a escala la consistencia no se sostiene con voluntad ni con revisiones de código, sino automatizando su producción. Un plugin bien hecho no es una biblioteca de utilidades: es la memoria ejecutable de cómo tu organización decidió construir software, capaz de imponerse sola sobre cada archivo nuevo y de reescribir los viejos cuando la decisión cambia.
- Ejecuta
nx add @nx/viteen un proyecto y comprueba connx show projectqué targets aparecieron inferidos sin que los escribieras. - Sobrescribe una opción de un target inferido en el
project.jsony confirma que tu valor gana sobre el inferido. - Genera una librería con
nx generate ... --dry-runy estudia la lista de archivos y cambios de configuración antes de aplicarlos. - Escribe un generador mínimo con
@nx/devkitque cree un archivo a partir de una plantilla y ejecútalo con--dry-run. - Simula una actualización con
nx migrate latesty revisa el archivo de migraciones que genera antes de aplicarlo connx migrate --run-migrations.