Ir al contenido

Generador de Migración de Nx

Agrega una Migración de Nx a un Plugin de Nx. Las migraciones permiten que nx migrate actualice automáticamente los proyectos que fueron generados por una versión anterior de tu plugin cuando realizas un cambio incompatible — objetivos renombrados, configuración proporcionada modificada, o una actualización de dependencia que requiere cambios de código acompañantes.

El generador crea la estructura de la migración, la registra en el migrations.json de tu plugin, y conecta el campo nx-migrations en el package.json de tu plugin (creando ambos si aún no existen).

Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-migration
También puede realizar una ejecución en seco para ver qué archivos se cambiarían
Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-migration --dry-run
ParámetroTipoPredeterminadoDescripción
project Requeridostring-El proyecto Nx Plugin al que se agregará la migración. Recomendamos usar el generador ts#nx-plugin para crearlo.
name Requeridostring-Nombre de la migración, en kebab-case (ej. rename-foo-target)
description string-Una descripción de lo que hace la migración, mostrada por nx migrate
kind deterministic | agentic | hybriddeterministicEl tipo de migración: deterministic (un codemod), agentic (un prompt aplicado por IA para código propio del usuario), o hybrid (un codemod que realiza la parte mecánica y luego delega a un agente)
preferInstallDependencies booleantrueSi se prefiere instalar dependencias después de que el generador se ejecute. Establecer en false para diferir la instalación al agrupar múltiples generadores (la instalación aún se ejecuta si es necesaria para que los generadores subsiguientes puedan calcular el grafo de proyectos Nx); instalar una vez al final.

Las migraciones vienen en tres formas, discriminadas por qué campos lleva la entrada de migrations.json. Pasa --kind para elegir (por defecto deterministic):

  • Determinista (implementation) — un codemod con un antes/después exacto, y el primero al que recurrir. Se ejecuta sin supervisión, incluso en CI y terminales no interactivas, y se aplica de manera idéntica para cada usuario. Úsalo solo cuando el cambio tiene una forma que puedes hacer coincidir de manera confiable en todos los lugares donde aparece, reportando cualquier cosa que omitió a través de nextSteps.
  • Híbrida (implementation + prompt) — ambas mitades de un cambio. Úsala cuando un cambio es incompatible y no puede aplicarse completamente de manera mecánica: el codemod hace tanto como puede hacer coincidir de manera segura y devuelve agentContext describiendo qué cambió y qué omitió; Nx pasa ese contexto al prompt emparejado, que dirige al agente al resto. El prompt es lo que evita que un espacio de trabajo quede roto cuando las ediciones restantes abarcan demasiadas formas para hacer codemod.
  • Agéntica (prompt) — un archivo de instrucciones en markdown aplicado por el agente de codificación local del usuario a través del flujo agéntico nx migrate de Nx. Úsala sola cuando no existe ningún antes/después mecánico en absoluto, porque la edición correcta depende de lo que el usuario ha construido. Cuando no se ejecuta ningún agente (CI, ningún agente instalado, consentimiento rechazado), Nx muestra el prompt como instrucciones manuales — así que escribe los prompts como pasos autocontenidos y ejecutables por humanos.

El generador crea los siguientes archivos bajo la carpeta de origen de tu proyecto de plugin, dependiendo del kind elegido:

  • Directoriosrc/migrations/latest/<name>/
    • migration.ts Implementación del codemod (determinista e híbrida)
    • migration.spec.ts Pruebas para tu migración (determinista e híbrida)
    • prompt.md Instrucciones para agente/humano (agéntica e híbrida)
  • migrations.json Creado o actualizado para registrar tu migración
  • package.json Creado o actualizado para agregar una entrada “nx-migrations”

Una nueva migración se coloca en latest/. Agrupar las migraciones por el lanzamiento que las envía mantiene su orden visible de un vistazo a medida que la colección crece — mueve una migración a una carpeta v<x.y.z>/ (y actualiza sus rutas en migrations.json) cuando asignes su versión.

Registra la migración en migrations.json con los campos para su tipo y sin version — consulta Versionado a continuación. La clave registrada tiene como prefijo la carpeta en la que vive la migración (latest-<name>), por lo que reutilizar un nombre para un cambio posterior no puede sobrescribir silenciosamente una migración que ya se ha enviado.

Una migración determinista (o híbrida) es una función que muta el sistema de archivos virtual (el Tree), devolviendo un MigrationReturnObject. Aquí hay una híbrida, que devuelve ambos:

import { type MigrationReturnObject, type Tree } from '@nx/devkit';
import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
export default async function migration(
tree: Tree,
): Promise<MigrationReturnObject> {
const nextSteps: string[] = [];
const agentContext: string[] = [];
// Read and update the files your generators produce here.
await formatFilesInSubtree(tree);
return { nextSteps, agentContext };
}
  • nextSteps — las acciones de seguimiento que Nx imprime para el usuario después de que se ejecuta nx migrate. Cada entrada debe ser algo que necesiten hacer manualmente: reconciliar un archivo que omitiste (consulta las barreras de protección a continuación), o terminar algo más allá del alcance de la migración, como una instalación para actualizar un archivo de bloqueo. No son un registro de lo que cambiaste — cuando una transformación se aplica limpiamente, no agregues nada, de lo contrario las entradas que sí necesitan acción quedan enterradas.
  • agentContext (solo híbrida) — un resumen de lo que cambió el codemod, pasado al prompt emparejado cuando se ejecuta bajo el flujo agéntico de Nx para que el agente pueda enfocarse en las partes propiedad del usuario.

Para algunos ejemplos de operaciones que puedes realizar en tu migración (generar archivos, modificar archivos existentes usando GritQL, leer y actualizar JSON), consulta la guía de Generador de Nx.

Las migraciones se ejecutan contra espacios de trabajo que no controlas, por lo que se recomienda adherirse a lo siguiente (el esqueleto generado incorpora estos):

  • Transforma el código fuente con GritQL. Usa los helpers de GritQL (applyGritQL, matchGritQL) para editar el código fuente en lugar de expresiones regulares o reemplazos de cadenas. GritQL coincide con el AST, por lo que un patrón se mantiene a través del formato, espacios en blanco y comillas en los que la copia del usuario puede haberse desviado, donde la coincidencia de texto silenciosamente pierde código equivalente o corrompe el archivo. Usa updateJson para JSON y el analizador correspondiente para otra configuración estructurada.
  • Haz coincidencia de patrones antes de escribir. Si un archivo de destino se ha desviado de la forma que producen tus generadores, omítelo y repórtalo a través de nextSteps, o considera una migración híbrida, en lugar de sobrescribir los cambios del usuario. matchGritQL es una forma conveniente de hacer esa verificación.
  • Reporta solo lo que queda por hacer. nextSteps es para el trabajo que el usuario tiene que retomar, no un registro de las ediciones que hiciste — esas ya están en su git diff.
  • Idempotente. Volver a ejecutar la migración debe ser una operación sin efecto. Protege las reescrituras de GritQL que inyectan código con una cláusula where { ... <: not contains ... } para que una segunda ejecución no agregue un duplicado.
  • Formatea lo que escribes. Termina con formatFilesInSubtree(tree) para que los archivos que escribió tu migración estén formateados correctamente.

El migration.spec.ts generado usa createTreeUsingTsSolutionSetup() para construir un espacio de trabajo virtual que coincida con la forma que genera nuestro preset. Prueba que la migración lleva un espacio de trabajo del estado “antes” al estado objetivo, omite (y reporta) código que no reconoce, y es idempotente:

import type { Tree } from '@nx/devkit';
import { createTreeUsingTsSolutionSetup } from '@aws/nx-plugin/sdk/utils/test';
import migration from './migration.js';
describe('my migration', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeUsingTsSolutionSetup();
});
it('should update the generated shape', async () => {
// Set up a tree with the "before" state
tree.write('project.json', JSON.stringify({ targets: { foo: {} } }));
// Run the migration
await migration(tree);
// Check that the tree matches the target state
const projectJson = JSON.parse(tree.read('project.json', 'utf-8'));
expect(projectJson.targets).toHaveProperty('bar');
expect(projectJson.targets).not.toHaveProperty('foo');
});
});

Las pruebas unitarias solo prueban que la migración hace lo que escribiste que hiciera. También pruébala contra un espacio de trabajo generado por la versión publicada de tu plugin — el estado desde el que tus usuarios actualizan — y confirma que el resultado coincide con lo que tus generadores producen hoy: inspecciona el diff, genera los mismos proyectos en un espacio de trabajo nuevo y compara, verifica que el espacio de trabajo aún se construye, luego vuelve a ejecutar la migración para confirmar que no cambia nada. Repite con un espacio de trabajo que hayas personalizado primero, para verificar que la migración deja tus ediciones solas y las reporta en su lugar.

Nx ejecuta una migración cuando la versión del plugin instalado del usuario es menor que la version de la migración. Este generador no escribe ningún campo version — asígnalo (o márcalo desde tu proceso de lanzamiento) cuando cortes el lanzamiento que envía la migración, para que se ejecute para todos los que actualicen desde una versión anterior.

Una vez que asignes versiones, un migrations.json que registra una migración de cada tipo se ve así:

{
"$schema": "http://json-schema.org/schema",
"name": "my-plugin",
"generators": {
"v2.0.0-rename-foo-target": {
"version": "2.0.0",
"description": "Rename the foo target to bar",
"implementation": "./src/migrations/v2.0.0/rename-foo-target/migration"
},
"v3.0.0-migrate-custom-handlers": {
"version": "3.0.0",
"description": "Update custom handlers for the new API",
"prompt": "./src/migrations/v3.0.0/migrate-custom-handlers/prompt.md"
},
"v4.0.0-upgrade-framework": {
"version": "4.0.0",
"description": "Upgrade the framework and reconcile call sites",
"implementation": "./src/migrations/v4.0.0/upgrade-framework/migration",
"prompt": "./src/migrations/v4.0.0/upgrade-framework/prompt.md"
}
}
}

Los usuarios aplican las migraciones de tu plugin con el flujo de dos pasos nx migrate — genera las migraciones pendientes, instala, luego ejecútalas:

Terminal window
pnpm nx migrate my-plugin@latest
pnpm nx migrate --run-migrations

Las migraciones agénticas e híbridas son aplicadas por el agente de codificación local del usuario durante nx migrate --run-migrations cuando uno está disponible.

Para el flujo completo desde la perspectiva del usuario que actualiza — incluyendo salida de ejemplo para migraciones deterministas y agénticas — consulta Actualizar Tu Espacio de Trabajo.