Salta ai contenuti

Generatore Nx Migration

Aggiunge una Nx Migration a un Nx Plugin. Le migrazioni consentono a nx migrate di aggiornare automaticamente i progetti generati da una versione precedente del tuo plugin quando apporti una modifica che rompe la compatibilità — target rinominati, configurazione fornita modificata o un aggiornamento di dipendenza che richiede modifiche al codice accompagnatorie.

Il generatore crea lo scaffold della migrazione, la registra nel migrations.json del tuo plugin e collega il campo nx-migrations nel package.json del tuo plugin (creandoli entrambi se non esistono ancora).

Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-migration
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-migration --dry-run
ParametroTipoPredefinitoDescrizione
project Obbligatoriostring-Il progetto Nx Plugin a cui aggiungere la migrazione. Si consiglia di utilizzare il generatore ts#nx-plugin per crearlo.
name Obbligatoriostring-Nome della migrazione, in formato kebab-case (es. rename-foo-target)
description string-Una descrizione di ciò che fa la migrazione, mostrata da nx migrate
kind deterministic | agentic | hybriddeterministicIl tipo di migrazione: deterministic (un codemod), agentic (un prompt applicato tramite AI per codice di proprietà dell'utente), o hybrid (un codemod che esegue la parte meccanica e poi passa il controllo a un agente)
preferInstallDependencies booleantrueSe preferire l'installazione delle dipendenze dopo l'esecuzione del generatore. Impostare su false per rimandare l'installazione quando si eseguono più generatori in batch (l'installazione viene comunque eseguita se necessaria affinché i generatori successivi possano calcolare il grafo dei progetti Nx); installare una volta alla fine.

Le migrazioni si presentano in tre forme, discriminate dai campi che contiene la voce migrations.json. Passa --kind per scegliere (predefinito deterministic):

  • Deterministic (implementation) — un codemod con un prima/dopo esatto, ed è quello da preferire per primo. Viene eseguito senza supervisione, anche in CI e terminali non interattivi, e si applica in modo identico per ogni utente. Usalo da solo quando la modifica ha una forma che puoi abbinare in modo affidabile ovunque appaia, segnalando tutto ciò che ha saltato tramite nextSteps.
  • Hybrid (implementation + prompt) — entrambe le metà di una modifica. Usalo quando una modifica rompe la compatibilità e non può essere applicata completamente in modo meccanico: il codemod fa tutto ciò che può abbinare in sicurezza e restituisce agentContext descrivendo cosa ha modificato e cosa ha saltato; Nx passa quel contesto al prompt abbinato, che dirige l’agente verso il resto. Il prompt è ciò che impedisce che un workspace venga lasciato rotto quando le modifiche rimanenti coprono troppe forme per essere codificate.
  • Agentic (prompt) — un file di istruzioni markdown applicato dall’agente di codifica locale dell’utente tramite il flusso agentico nx migrate di Nx. Usalo da solo quando non esiste alcun prima/dopo meccanico, perché la modifica corretta dipende da ciò che l’utente ha costruito. Quando nessun agente viene eseguito (CI, nessun agente installato, consenso negato), Nx presenta il prompt come istruzioni manuali — quindi scrivi i prompt come passaggi autonomi e attuabili dall’utente.

Il generatore crea i seguenti file nella cartella sorgente del progetto del tuo plugin, a seconda del kind scelto:

  • Directorysrc/migrations/latest/<name>/
    • migration.ts Implementazione del codemod (deterministic e hybrid)
    • migration.spec.ts Test per la tua migrazione (deterministic e hybrid)
    • prompt.md Istruzioni per agente/umano (agentic e hybrid)
  • migrations.json Creato o aggiornato per registrare la tua migrazione
  • package.json Creato o aggiornato per aggiungere una voce “nx-migrations”

Una nuova migrazione viene posizionata in latest/. Raggruppare le migrazioni per il rilascio che le spedisce mantiene il loro ordine visibile a colpo d’occhio man mano che la collezione cresce — sposta una migrazione in una cartella v<x.y.z>/ (e aggiorna i suoi percorsi in migrations.json) quando assegni la sua versione.

Registra la migrazione in migrations.json con i campi per il suo tipo e senza version — vedi Versionamento di seguito. La chiave registrata è prefissata con la cartella in cui vive la migrazione (latest-<name>), quindi riutilizzare un nome per una modifica successiva non può sovrascrivere silenziosamente una migrazione che è già stata spedita.

Una migrazione deterministica (o ibrida) è una funzione che muta il filesystem virtuale (il Tree), restituendo un MigrationReturnObject. Ecco una ibrida, che restituisce entrambi:

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 — le azioni di follow-up che Nx stampa per l’utente dopo l’esecuzione di nx migrate. Ogni voce dovrebbe essere qualcosa che devono fare manualmente: riconciliare un file che hai saltato (vedi le protezioni di seguito), o completare qualcosa oltre la portata della migrazione, come un’installazione per aggiornare un file di lock. Non sono un registro di ciò che hai modificato — quando una trasformazione si applica in modo pulito, non aggiungere nulla, altrimenti le voci che necessitano effettivamente di azione vengono sepolte.
  • agentContext (solo hybrid) — un riepilogo di ciò che il codemod ha modificato, passato al prompt abbinato quando viene eseguito sotto il flusso agentico di Nx in modo che l’agente possa concentrarsi sulle parti di proprietà dell’utente.

Per alcuni esempi di operazioni che puoi eseguire nella tua migrazione (generare file, modificare file esistenti usando GritQL, leggere e aggiornare JSON), fai riferimento alla guida Nx Generator.

Le migrazioni vengono eseguite su workspace che non controlli, quindi si consiglia di aderire a quanto segue (lo scheletro generato li incorpora):

  • Trasforma il codice sorgente con GritQL. Usa gli helper GritQL (applyGritQL, matchGritQL) per modificare il sorgente piuttosto che regex o sostituzioni di stringhe. GritQL abbina l’AST, quindi un pattern funziona attraverso la formattazione, gli spazi bianchi e le virgolette in cui la copia dell’utente potrebbe essere derivata, dove l’abbinamento del testo perde silenziosamente codice equivalente o corrompe il file. Usa updateJson per JSON e il parser corrispondente per altre configurazioni strutturate.
  • Abbina il pattern prima di scrivere. Se un file di destinazione è divergente dalla forma che i tuoi generatori producono, saltalo e segnalalo tramite nextSteps, o considera una migrazione ibrida, piuttosto che sovrascrivere le modifiche dell’utente. matchGritQL è un modo conveniente per effettuare quel controllo.
  • Segnala solo ciò che resta da fare. nextSteps è per il lavoro che l’utente deve riprendere, non un registro delle modifiche che hai fatto — quelle sono già nel loro git diff.
  • Idempotente. Rieseguire la migrazione deve essere un no-op. Proteggi le riscritture GritQL che iniettano codice con una clausola where { ... <: not contains ... } in modo che una seconda esecuzione non aggiunga un duplicato.
  • Formatta ciò che scrivi. Termina con formatFilesInSubtree(tree) in modo che i file scritti dalla tua migrazione siano formattati correttamente.

Il migration.spec.ts generato usa createTreeUsingTsSolutionSetup() per costruire un workspace virtuale che corrisponde alla forma generata dal nostro preset. Testa che la migrazione porti un workspace dallo stato “prima” allo stato target, salti (e segnali) il codice che non riconosce, e sia 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');
});
});

I test unitari dimostrano solo che la migrazione fa ciò che hai scritto che faccia. Testala anche su un workspace generato dalla versione pubblicata del tuo plugin — lo stato da cui i tuoi utenti aggiornano — e conferma che il risultato corrisponda a ciò che i tuoi generatori producono oggi: ispeziona il diff, genera gli stessi progetti in un workspace nuovo e confronta, controlla che il workspace si compili ancora, quindi riesegui la migrazione per confermare che non cambi nulla. Ripeti con un workspace che hai personalizzato prima, per verificare che la migrazione lasci le tue modifiche da sole e le segnali invece.

Nx esegue una migrazione quando la versione del plugin installata dall’utente è inferiore alla version della migrazione. Questo generatore non scrive alcun campo version — assegnalo (o stampalo dal tuo processo di rilascio) quando tagli il rilascio che spedisce la migrazione, in modo che venga eseguita per tutti coloro che aggiornano da una versione precedente.

Una volta assegnate le versioni, un migrations.json che registra una migrazione di ogni tipo appare così:

{
"$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"
}
}
}

Gli utenti applicano le migrazioni del tuo plugin con il flusso in due fasi nx migrate — genera le migrazioni in sospeso, installa, quindi eseguile:

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

Le migrazioni agentiche e ibride vengono applicate dall’agente di codifica locale dell’utente durante nx migrate --run-migrations quando ne è disponibile uno.

Per il flusso completo dalla prospettiva dell’utente che aggiorna — incluso l’output di esempio per migrazioni deterministiche e agentiche — vedi Aggiornare il Tuo Workspace.