Actualización monorepo Nx
Foto de Edge2Edge Media en Unsplash

En un artículo anterior, os mostramos la creación de un monorepo Nx para reunir todos los proyectos de los artículos publicados en este blog en un solo repositorio de github.

Obviamente ofrece muchas ventajas al respecto y una de ellas es la que vamos a aprovechar. Se trata de actualizar nuestro código a la última versión de angular a día de creación de este artículo (Angular 15) de una tacada, es decir, se actualizarán todos los proyectos a la vez. Si no tuviéramos el monorepo, entonces hubiéramos tenido que actualizar repositorio a repositorio… o sea, pérdida de tiempo.

Estado inicial

La versión de Nx con la que se parte es la 15.1.1 que lleva consigo la versión 14.2.0 de Angular. Normalmente al aparecer una nueva versión de Angular al poco se publica una nueva versión de Nx que incorpore estos nuevos cambios.

La versión de Nodejs con la que realizamos la migración en este caso es node 14.21.2

Actualización

La actualización se realiza en dos simples comandos explicada en Nx.

npx nx migrate @nrwl/workspace@latest

La ejecución del comando generará un archivo llamado «migrations.json» que contiene las acciones que se deben realizar para la migración y modificará el «package.json» con las versiones actualizadas tanto de Nx como de Angular (Ojo: si no ha migrado la versión de typescript, tenemos que modificarla a mano poniendo una compatible, por ejemplo la 4.8.4 ya que en el segundo comando posiblemente aparezca algún error). Hay que tener en cuenta sobre todo los saltos de «major version» de Angular. En nuestro  caso es un salto de Angular desde la v.14 a la v.15 y en el momento de escribir este artículo el «latest» de Nx es 15.6.3 (que nos instala Angular 15.1.2). La pregunta quizás del millón es si tenemos un monorepo de Angular 12… ¿Puedo hacer un salto directo a Angular 15? Bueno, quizás mejor hacer saltos de versión mayor de uno en uno… Y para ello recomiendo mirar la matriz de Nx y Angular.

Ejemplo del contenido del archivo «migrations.json»:

{
  "migrations": [
    {
      "cli": "nx",
      "version": "15.2.0-beta.0",
      "description": "Update the @angular/cli package version to ~15.0.0.",
      "factory": "./src/migrations/update-15-2-0/update-angular-cli",
      "package": "@nrwl/angular",
      "name": "update-angular-cli-version-15-0-0"
    },
    {
      "cli": "nx",
      "version": "15.2.0-beta.0",
      "description": "Remove browserlist config as it's handled by build-angular",
      "factory": "./src/migrations/update-15-2-0/remove-browserlist-config",
      "package": "@nrwl/angular",
      "name": "remove-browserlist-config"
    },
    {
      "cli": "nx",
      "version": "15.2.0-beta.0",
      "description": "Update typescript target to ES2022",
      "factory": "./src/migrations/update-15-2-0/update-typescript-target",
      "package": "@nrwl/angular",
      "name": "update-typescript-target"
    },
    {
      "cli": "nx",
      "version": "15.2.0-beta.0",
      "description": "Remove bundleDependencies from server targets",
      "factory": "./src/migrations/update-15-2-0/update-workspace-config",
      "package": "@nrwl/angular",
      "name": "update-workspace-config"
    },
    {
      "cli": "ng",
      "version": "15.2.0-beta.0",
      "description": "Remove exported `@angular/platform-server` `renderModule` method. The `renderModule` method is now exported by the Angular CLI.",
      "factory": "./src/migrations/update-15-2-0/remove-platform-server-exports",
      "package": "@nrwl/angular",
      "name": "update-platform-server-exports"
    },
    {
      "cli": "ng",
      "version": "15.2.0-beta.0",
      "description": "Remove no longer needed require calls in Karma builder main file.",
      "factory": "./src/migrations/update-15-2-0/update-karma-main-file",
      "package": "@nrwl/angular",
      "name": "update-karma-main-file"
    },
    {
      "cli": "nx",
      "version": "15.5.0-beta.0",
      "description": "Update the @angular/cli package version to ~15.1.0.",
      "factory": "./src/migrations/update-15-5-0/update-angular-cli",
      "package": "@nrwl/angular",
      "name": "update-angular-cli-version-15-1-0"
    },
    {
      "version": "15.0.0",
      "description": "Since Angular v15, the `RouterLink` contains the logic of the `RouterLinkWithHref` directive. This migration replaces all `RouterLinkWithHref` references with `RouterLink`.",
      "factory": "./migrations/router-link-with-href/bundle",
      "package": "@angular/core",
      "name": "migration-v15-router-link-with-href"
    },
    {
      "version": "15.0.0",
      "description": "In Angular version 15, the deprecated `relativeLinkResolution` config parameter of the Router is removed. This migration removes all `relativeLinkResolution` fields from the Router config objects.",
      "factory": "./migrations/relative-link-resolution/bundle",
      "package": "@angular/core",
      "name": "migration-v15-relative-link-resolution"
    }
  ]
}
npx nx migrate --run-migrations

Realiza las acciones necesarias para ejecutar la migración. Utiliza el json generado «migrations.json». Y con esto ya está realizada la migración. Obviamente, hay que chequear que todo siga funcionando correctamente: build, serve… y hacer el commit y push.

Llegados a este punto, la versión de Node utilizada para hacer esta actualización ha sido la 14.21.2, pero después de llegar a Angular 15, será conveniente instalar una versión de Node superior, como por ejemplo la 18.13.0 (la versión actual) y realizar una instalación «npm install» desde cero, es decir, eliminar la carpeta “node_modules” y el archivo «package-lock.json». Seguramente tengamos que actualizar algún paquete por errores de dependencias (o bien hacer un «npm install –legacy-peer-deps»).

Finalmente ya se puede eliminar el fichero «migrations.json».

Actualizar dependencias anticuadas

Esta migración migra tanto Nx como Angular, pero no las demás librerías de terceros. Aprovechando esta migración del monorepo, vamos a actualizar el resto de dependencias (karma, eslint, ….) que nos ayudará estar a la última siempre que sea compatible con el resto de paquetes y código.

Para ello, ejecutamos «npm outdated». Este comando nos presentará una lista de las dependencias que están desactualizadas. En nuestro caso:

Package                                  Current    Wanted    Latest  Location                                             Depended by
@angular-eslint/builder                   14.4.0    14.4.0    15.2.0  node_modules/@angular-eslint/builder                 monorepo
@angular-eslint/eslint-plugin             15.0.0    15.0.0    15.2.0  node_modules/@angular-eslint/eslint-plugin           monorepo
@angular-eslint/eslint-plugin-template    15.0.0    15.0.0    15.2.0  node_modules/@angular-eslint/eslint-plugin-template  monorepo
@angular-eslint/schematics                15.1.0    15.1.0    15.2.0  node_modules/@angular-eslint/schematics              monorepo
@angular-eslint/template-parser           15.0.0    15.0.0    15.2.0  node_modules/@angular-eslint/template-parser         monorepo
@types/jasmine                            3.6.11    3.6.11     4.3.1  node_modules/@types/jasmine                          monorepo
@types/node                             12.20.55  12.20.55  18.11.18  node_modules/@types/node                             monorepo
core-js                                   2.6.12    2.6.12    3.27.2  node_modules/core-js                                 monorepo
eslint                                    8.15.0    8.15.0    8.33.0  node_modules/eslint                                  monorepo
eslint-config-prettier                     8.1.0     8.1.0     8.6.0  node_modules/eslint-config-prettier                  monorepo
jasmine-core                               3.8.0     3.8.0     4.5.0  node_modules/jasmine-core                            monorepo
jasmine-spec-reporter                      5.0.2     5.0.2     7.0.0  node_modules/jasmine-spec-reporter                   monorepo
karma-jasmine                              4.0.2     4.0.2     5.1.0  node_modules/karma-jasmine                           monorepo
karma-jasmine-html-reporter                1.7.0     1.7.0     2.0.0  node_modules/karma-jasmine-html-reporter             monorepo
rxjs                                       7.5.7     7.5.7     7.8.0  node_modules/rxjs                                    monorepo
ts-node                                    7.0.1     7.0.1    10.9.1  node_modules/ts-node                                 monorepo
typescript                                 4.8.4     4.8.4     4.9.4  node_modules/typescript                              monorepo
zone.js                                   0.11.8    0.11.8    0.12.0  node_modules/zone.js                                 monorepo

Siendo todas las librerías actualizadas al hacer un «npm audit» prácticamente no se encuentran vulnerabilidades.

Optimizaciones

Después de realizar la actualización y tener todas las librerías a la última se pueden realizar una serie de optimizaciones:

  1. Llevar al tsconfig.base.json aquellas propiedades comunes a todos los proyectos.
  2. Quitar el «destroyAfterEach» de todos los archivos de los «test.ts» si no da problemas.

Como siempre aquí os dejo el link al repositorio en github.

Posteriores actualizaciones

Conforme pasa el tiempo siempre aparecerán nuevas actualizaciones. Así por ejemplo si ahora queremos pasar a la versión 15.8, basta con escribir:

npx nx migrate @nrwl/workspace@15.8

y luego ejecutar las migraciones:

npx nx migrate --run-migrations

haciendo a posteriori los diferentes ajustes o fixes.

Cabe destacar que en este monorepo están moviendo el scope @nrwl a @nx, con lo que podemos cambiar el namespace con algún script o a mano y hacer después el “npm i”, aunque a partir de la versión 16 de Nx lo hará de forma automática.

De esta forma, si queremos ir a la versión 16 para que nos cambie automáticamente el scope, manteniendo angular 15.2.10, podemos llegar a la versión 16.0.3 de Nx con el siguiente comando:

npx nx migrate 16.0.3

Respondiendo a las preguntas que nos realice, como por ejemplo si queremos activar Nx cache cloud, que de momento podemos saltarnos y continuar.

y ejecutar las migraciones:

npx nx migrate --run-migrations

después toca validar si todo funciona correctamente: lint, build y test.

¿Cómo desacoplar los ciclos de releases Nx y Angular?

A partir de la versión 15.7 de Nx, se puede desacoplar la versión de Nx de la versión de Angular tal y como comentan en su blog, cosa que antes al cambiar de versión de Nx, te podía cambiar la versión de Angular (matriz versiones entre Nx y Angular). De esta manera si por necesidades necesitamos mantener la versión de Angular pero queremos disfrutar de las mejoras y nuevas funcionalidades de Nx lo podemos hacer utilizando la migración en modo interactivo y seleccionar que queremos y que no migrar con el siguiente comando tantas veces como nuevas versiones queramos de Nx manteniendo la misma versión de Angular:

nx migrate latest --interactive

aquí he puesto la última versión de Nx «latest», pero podría haber sido otra cualquiera.

Si más adelante queremos por algún motivo ir a una versión de Angular más reciente, entonces tendremos que ejecutar el comando:

nx migrate latest --from=nx@<version> --exclude-applied-migrations

Donde <version> será a partir de la versión de Nx inmediatamente siguiente que hemos omitido la actualización.

Pongamos un ejemplo:

Partimos de la versión de Nx 16.0.3 con Angular 15.2.10 y queremos ir a la versión de Nx 16.1.0 pero manteniendo la versión de Angular. Ejecutamos en la consola:

npx nx migrate 16.1.0 --interactive

A la pregunta «Do you want to update the Angular version to v16?» le decimos que (n)o. Y ejecutamos las migraciones:

npx nx migrate --run-migrations

Ya disponemos de la nueva versión de Nx 16.1.0 con el Angular 15.2.10. Si más adelante queremos actualizar Angular a la 16, basta con ejecutar:

npx nx migrate 16.1.0 --from nx@16.1.0-beta.1 --exclude-applied-migrations

Realizaría las migraciones contenidas en el rango [16.1.0-beta.1 hasta 16.1.0] y actualizaría angular a la versión que Nx 16.1.0 tiene como referencia.

Para más información podéis ir a su web: https://nx.dev/recipes/tips-n-tricks/advanced-update#choosing-optional-package-updates-to-apply