Gerador de Migração Nx
Adiciona uma Migração Nx a um Plugin Nx. As migrações permitem que o nx migrate atualize automaticamente projetos que foram gerados por uma versão anterior do seu plugin quando você faz uma mudança incompatível — alvos renomeados, configuração fornecida alterada, ou uma atualização de dependência que requer mudanças de código correspondentes.
O gerador cria o scaffold da migração, registra-a no migrations.json do seu plugin e conecta o campo nx-migrations no package.json do seu plugin (criando ambos se eles ainda não existirem).
Gerar uma Migração
Seção intitulada “Gerar uma Migração”pnpm nx g @aws/nx-plugin:ts#nx-migrationyarn nx g @aws/nx-plugin:ts#nx-migrationnpx nx g @aws/nx-plugin:ts#nx-migrationbunx nx g @aws/nx-plugin:ts#nx-migrationVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#nx-migration --dry-runyarn nx g @aws/nx-plugin:ts#nx-migration --dry-runnpx nx g @aws/nx-plugin:ts#nx-migration --dry-runbunx nx g @aws/nx-plugin:ts#nx-migration --dry-run- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#nx-migration - Preencha os parâmetros obrigatórios
- Clique em
Generate
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| project Obrigatório | string | - | O projeto Nx Plugin ao qual adicionar a migração. Recomendamos usar o gerador ts#nx-plugin para criá-lo. |
| name Obrigatório | string | - | Nome da migração, em kebab-case (ex: rename-foo-target) |
| description | string | - | Uma descrição do que a migração faz, exibida por nx migrate |
| kind | deterministic | agentic | hybrid | deterministic | O tipo de migração: deterministic (um codemod), agentic (um prompt aplicado por IA para código de propriedade do usuário), ou hybrid (um codemod que faz a parte mecânica e depois passa para um agente) |
| preferInstallDependencies | boolean | true | Se deve preferir instalar dependências após a execução do gerador. Defina como false para adiar a instalação ao agrupar vários geradores (uma instalação ainda é executada se necessário para que os geradores subsequentes possam calcular o grafo de projetos Nx); instale uma vez no final. |
Os três tipos de migração
Seção intitulada “Os três tipos de migração”As migrações vêm em três formas, discriminadas por quais campos a entrada migrations.json carrega. Passe --kind para escolher (padrão deterministic):
- Determinística (
implementation) — um codemod com um antes/depois exato, e o primeiro a ser usado. Ele é executado sem supervisão, incluindo em CI e terminais não interativos, e se aplica de forma idêntica para cada usuário. Use-o sozinho quando a mudança tem uma forma que você pode corresponder de forma confiável em todos os lugares onde ela aparece, relatando qualquer coisa que ele pulou vianextSteps. - Híbrida (
implementation+prompt) — ambas as metades de uma mudança. Use-a quando uma mudança é incompatível e não pode ser totalmente aplicada mecanicamente: o codemod faz o máximo que pode corresponder com segurança e retornaagentContextdescrevendo o que mudou e o que pulou; o Nx passa esse contexto para opromptpareado, que direciona o agente para o resto. O prompt é o que impede que um workspace seja deixado quebrado quando as edições restantes abrangem muitas formas para fazer codemod. - Agêntica (
prompt) — um arquivo de instrução markdown aplicado pelo agente de codificação local do usuário através do fluxo agênticonx migratedo Nx. Use-o sozinho quando nenhum antes/depois mecânico se aplica, porque a edição correta depende do que o usuário construiu. Quando nenhum agente é executado (CI, nenhum agente instalado, consentimento recusado), o Nx apresenta o prompt como instruções manuais — então escreva prompts como etapas autocontidas e acionáveis por humanos.
Saída do Gerador
Seção intitulada “Saída do Gerador”O gerador cria os seguintes arquivos na pasta de origem do seu projeto de plugin, dependendo do kind escolhido:
Directorysrc/migrations/latest/<name>/
- migration.ts Implementação do codemod (determinística e híbrida)
- migration.spec.ts Testes para sua migração (determinística e híbrida)
- prompt.md Instruções para agente/humano (agêntica e híbrida)
- migrations.json Criado ou atualizado para registrar sua migração
- package.json Criado ou atualizado para adicionar uma entrada “nx-migrations”
Uma nova migração é colocada em latest/. Agrupar migrações pela versão que as envia mantém sua ordem visível rapidamente à medida que a coleção cresce — mova uma migração para uma pasta v<x.y.z>/ (e atualize seus caminhos em migrations.json) quando você atribuir sua versão.
Ela registra a migração em migrations.json com os campos para seu tipo e sem version — veja Versionamento abaixo. A chave registrada é prefixada com a pasta em que a migração está (latest-<name>), então reutilizar um nome para uma mudança posterior não pode sobrescrever silenciosamente uma migração que já foi enviada.
Implementando uma Migração
Seção intitulada “Implementando uma Migração”Uma migração determinística (ou híbrida) é uma função que muta o sistema de arquivos virtual (a Tree), retornando um MigrationReturnObject. Aqui está uma híbrida, que retorna 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— as ações de acompanhamento que o Nx imprime para o usuário após a execução donx migrate. Cada entrada deve ser algo que eles precisam fazer manualmente: reconciliar um arquivo que você pulou (veja as proteções abaixo), ou terminar algo além do alcance da migração, como uma instalação para atualizar um arquivo de bloqueio. Eles não são um registro do que você mudou — quando uma transformação se aplica de forma limpa, não adicione nada, caso contrário as entradas que realmente precisam de ação ficam enterradas.agentContext(apenas híbrida) — um resumo do que o codemod mudou, passado para opromptpareado quando ele é executado sob o fluxo agêntico do Nx para que o agente possa se concentrar nas partes de propriedade do usuário.
Para alguns exemplos de operações que você pode realizar em sua migração (gerar arquivos, modificar arquivos existentes usando GritQL, ler e atualizar JSON), consulte o guia do Gerador Nx.
Proteções
Seção intitulada “Proteções”As migrações são executadas em workspaces que você não controla, então é recomendado aderir ao seguinte (o esqueleto criado incorpora isso):
- Transforme código-fonte com GritQL. Use os auxiliares GritQL (
applyGritQL,matchGritQL) para editar código em vez de regexes ou substituições de string. O GritQL corresponde à AST, então um padrão funciona em toda a formatação, espaços em branco e aspas que a cópia de um usuário pode ter divergido, onde a correspondência de texto silenciosamente perde código equivalente ou corrompe o arquivo. UseupdateJsonpara JSON e o parser correspondente para outras configurações estruturadas. - Faça correspondência de padrão antes de escrever. Se um arquivo de destino divergiu da forma que seus geradores produzem, pule-o e relate-o via
nextSteps, ou considere uma migração híbrida, em vez de sobrescrever as mudanças do usuário.matchGritQLé uma maneira conveniente de fazer essa verificação. - Relate apenas o que resta fazer.
nextStepsé para trabalho que o usuário tem que assumir, não um registro das edições que você fez — essas já estão nogit diffdeles. - Idempotente. Re-executar a migração deve ser uma operação sem efeito. Proteja reescritas GritQL que injetam código com uma cláusula
where { ... <: not contains ... }para que uma segunda execução não anexe uma duplicata. - Formate o que você escreve. Termine com
formatFilesInSubtree(tree)para que os arquivos que sua migração escreveu sejam formatados corretamente.
Testando Sua Migração
Seção intitulada “Testando Sua Migração”O migration.spec.ts criado usa createTreeUsingTsSolutionSetup() para construir um workspace virtual correspondente à forma que nosso preset gera. Teste que a migração leva um workspace do estado “antes” para o estado alvo, pula (e relata) código que não reconhece, e é 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'); });});Testes unitários apenas provam que a migração faz o que você escreveu para fazer. Também teste-a contra um workspace gerado pela versão publicada do seu plugin — o estado do qual seus usuários atualizam — e confirme que o resultado corresponde ao que seus geradores produzem hoje: inspecione o diff, gere os mesmos projetos em um workspace novo e compare, verifique se o workspace ainda compila, então re-execute a migração para confirmar que ela não muda nada. Repita com um workspace que você personalizou primeiro, para verificar se a migração deixa suas edições sozinhas e as relata.
Versionamento
Seção intitulada “Versionamento”O Nx executa uma migração quando a versão do plugin instalado do usuário é menor que a version da migração. Este gerador não escreve um campo version — atribua-o (ou carimbe-o do seu processo de lançamento) quando você cortar o lançamento que envia a migração, para que ela seja executada para todos que atualizam de uma versão anterior.
Uma vez que você atribui versões, um migrations.json registrando uma migração de cada tipo se parece com isto:
{ "$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" } }}Executando Migrações
Seção intitulada “Executando Migrações”Os usuários aplicam as migrações do seu plugin com o fluxo de duas etapas nx migrate — gere as migrações pendentes, instale, então execute-as:
pnpm nx migrate my-plugin@latestpnpm nx migrate --run-migrationsyarn nx migrate my-plugin@latestyarn nx migrate --run-migrationsnpx nx migrate my-plugin@latestnpx nx migrate --run-migrationsbunx nx migrate my-plugin@latestbunx nx migrate --run-migrationsMigrações agênticas e híbridas são aplicadas pelo agente de codificação local do usuário durante nx migrate --run-migrations quando um está disponível.
Para o fluxo completo da perspectiva do usuário que está atualizando — incluindo saída de exemplo para migrações determinísticas e agênticas — veja Atualizando Seu Workspace.