From e1dfe6ea09d6ab555bd70c7e1f3ab8ad1140e9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Wed, 18 Jun 2025 12:31:48 +0200 Subject: [PATCH] fix(angular): handle inferred projects without project configuration files in migrations (#31633) ## Current Behavior Some Angular migrations collect the Angular projects from the project graph using the dependencies information. When reading the project configuration for those projects, it can throw an error if trying to do it for a completely inferred project (it doesn't have a project configuration file). ## Expected Behavior The Angular migrations collecting Angular projects from the project graph using the dependencies information should gracefully handle projects that were completely inferred when trying to read the project configuration. In fact, the current migrations didn't need to read the project configuration and could use the project graph information directly, so the call to read the project configuration was removed. ## Related Issue(s) Fixes #31607 --- ...-operators-imports-to-ngrx-router-store.ts | 3 +-- .../replace-nguniversal-engines.ts | 4 ++-- .../update-zone-js-deep-import.ts | 6 +++--- ...isable-angular-eslint-prefer-standalone.ts | 7 +++---- .../remove-angular-eslint-rules.ts | 7 +++---- ...lar-ssr-imports-to-use-node-entry-point.ts | 6 +++--- ...s-to-ngrx-router-store-data-persistence.ts | 3 +-- ...migrate-provide-server-rendering-import.ts | 6 +++--- .../replace-provide-server-routing.ts | 6 +++--- .../update-21-2-0/update-module-resolution.ts | 6 +++--- .../angular/src/migrations/utils/projects.ts | 21 ++++--------------- 11 files changed, 29 insertions(+), 46 deletions(-) diff --git a/packages/angular/src/migrations/update-16-2-0/switch-data-persistence-operators-imports-to-ngrx-router-store.ts b/packages/angular/src/migrations/update-16-2-0/switch-data-persistence-operators-imports-to-ngrx-router-store.ts index 73852baef9..2359fae092 100644 --- a/packages/angular/src/migrations/update-16-2-0/switch-data-persistence-operators-imports-to-ngrx-router-store.ts +++ b/packages/angular/src/migrations/update-16-2-0/switch-data-persistence-operators-imports-to-ngrx-router-store.ts @@ -25,7 +25,6 @@ const newImportPath = '@ngrx/router-store/data-persistence'; export default async function (tree: Tree): Promise { const projects = await getProjectsFilteredByDependencies( - tree, angularPluginTargetNames ); @@ -38,7 +37,7 @@ export default async function (tree: Tree): Promise { const cachedFileMap = readFileMapCache().fileMap.projectFileMap; const filesWithNxAngularImports: FileData[] = []; - for (const { graphNode } of projects) { + for (const graphNode of projects) { const files = filterFilesWithNxAngularDep( cachedFileMap[graphNode.name] || [] ); diff --git a/packages/angular/src/migrations/update-17-1-0/replace-nguniversal-engines.ts b/packages/angular/src/migrations/update-17-1-0/replace-nguniversal-engines.ts index 5d60998555..80b563c3b2 100644 --- a/packages/angular/src/migrations/update-17-1-0/replace-nguniversal-engines.ts +++ b/packages/angular/src/migrations/update-17-1-0/replace-nguniversal-engines.ts @@ -43,11 +43,11 @@ export default async function (tree: Tree) { return; } - const projects = await getProjectsFilteredByDependencies(tree, [ + const projects = await getProjectsFilteredByDependencies([ 'npm:@nguniversal/common', 'npm:@nguniversal/express-engine', ]); - for (const { project } of projects) { + for (const { data: project } of projects) { if (project.projectType !== 'application') { continue; } diff --git a/packages/angular/src/migrations/update-17-1-0/update-zone-js-deep-import.ts b/packages/angular/src/migrations/update-17-1-0/update-zone-js-deep-import.ts index e1180ef6ac..0b189360ec 100644 --- a/packages/angular/src/migrations/update-17-1-0/update-zone-js-deep-import.ts +++ b/packages/angular/src/migrations/update-17-1-0/update-zone-js-deep-import.ts @@ -2,7 +2,7 @@ import { formatFiles, visitNotIgnoredFiles, type Tree } from '@nx/devkit'; import { getProjectsFilteredByDependencies } from '../utils/projects'; export default async function (tree: Tree) { - const angularProjects = await getProjectsFilteredByDependencies(tree, [ + const angularProjects = await getProjectsFilteredByDependencies([ 'npm:@angular/core', ]); @@ -13,8 +13,8 @@ export default async function (tree: Tree) { const zoneJsImportRegex = /(['"`])zone\.js\/dist\/zone(['"`])/g; const zoneJsTestingImportRegex = /(['"`])zone\.js\/dist\/zone-testing(['"`])/g; - for (const { project } of angularProjects) { - visitNotIgnoredFiles(tree, project.root, (file) => { + for (const graphNode of angularProjects) { + visitNotIgnoredFiles(tree, graphNode.data.root, (file) => { // we are only interested in .ts files if (!file.endsWith('.ts')) { return; diff --git a/packages/angular/src/migrations/update-20-2-0/disable-angular-eslint-prefer-standalone.ts b/packages/angular/src/migrations/update-20-2-0/disable-angular-eslint-prefer-standalone.ts index deab7f750f..5f9ed91a41 100644 --- a/packages/angular/src/migrations/update-20-2-0/disable-angular-eslint-prefer-standalone.ts +++ b/packages/angular/src/migrations/update-20-2-0/disable-angular-eslint-prefer-standalone.ts @@ -10,13 +10,12 @@ import { getProjectsFilteredByDependencies } from '../utils/projects'; const preferStandaloneRule = '@angular-eslint/prefer-standalone'; export default async function (tree: Tree) { - const projects = await getProjectsFilteredByDependencies(tree, [ + const projects = await getProjectsFilteredByDependencies([ 'npm:@angular/core', ]); - for (const { - project: { root }, - } of projects) { + for (const graphNode of projects) { + const root = graphNode.data.root; if (!isEslintConfigSupported(tree, root)) { // ESLint config is not supported, skip continue; diff --git a/packages/angular/src/migrations/update-20-2-0/remove-angular-eslint-rules.ts b/packages/angular/src/migrations/update-20-2-0/remove-angular-eslint-rules.ts index abab46fc44..6d49fb186b 100644 --- a/packages/angular/src/migrations/update-20-2-0/remove-angular-eslint-rules.ts +++ b/packages/angular/src/migrations/update-20-2-0/remove-angular-eslint-rules.ts @@ -14,14 +14,13 @@ export const rulesToRemove = [ ]; export default async function (tree: Tree) { - const projects = await getProjectsFilteredByDependencies(tree, [ + const projects = await getProjectsFilteredByDependencies([ 'npm:@angular/core', ]); let hasRootProject = false; - for (const { - project: { root }, - } of projects) { + for (const graphNode of projects) { + const root = graphNode.data.root; if (!isEslintConfigSupported(tree, root)) { // ESLint config is not supported, skip continue; diff --git a/packages/angular/src/migrations/update-20-2-0/update-angular-ssr-imports-to-use-node-entry-point.ts b/packages/angular/src/migrations/update-20-2-0/update-angular-ssr-imports-to-use-node-entry-point.ts index 906377f8d0..c6b9e3c85d 100644 --- a/packages/angular/src/migrations/update-20-2-0/update-angular-ssr-imports-to-use-node-entry-point.ts +++ b/packages/angular/src/migrations/update-20-2-0/update-angular-ssr-imports-to-use-node-entry-point.ts @@ -4,12 +4,12 @@ import { FileChangeRecorder } from '../../utils/file-change-recorder'; import { getProjectsFilteredByDependencies } from '../utils/projects'; export default async function (tree: Tree) { - const projects = await getProjectsFilteredByDependencies(tree, [ + const projects = await getProjectsFilteredByDependencies([ 'npm:@angular/ssr', ]); - for (const { project } of projects) { - visitNotIgnoredFiles(tree, project.root, (path) => { + for (const graphNode of projects) { + visitNotIgnoredFiles(tree, graphNode.data.root, (path) => { if (!path.endsWith('.ts') || path.endsWith('.d.ts')) { return; } diff --git a/packages/angular/src/migrations/update-21-0-0/change-data-persistence-operators-imports-to-ngrx-router-store-data-persistence.ts b/packages/angular/src/migrations/update-21-0-0/change-data-persistence-operators-imports-to-ngrx-router-store-data-persistence.ts index 62b11fd8fc..7393671aca 100644 --- a/packages/angular/src/migrations/update-21-0-0/change-data-persistence-operators-imports-to-ngrx-router-store-data-persistence.ts +++ b/packages/angular/src/migrations/update-21-0-0/change-data-persistence-operators-imports-to-ngrx-router-store-data-persistence.ts @@ -26,7 +26,6 @@ const newImportPath = '@ngrx/router-store/data-persistence'; export default async function (tree: Tree): Promise { const projects = await getProjectsFilteredByDependencies( - tree, angularPluginTargetNames ); @@ -39,7 +38,7 @@ export default async function (tree: Tree): Promise { const cachedFileMap = readFileMapCache().fileMap.projectFileMap; const filesWithNxAngularImports: FileData[] = []; - for (const { graphNode } of projects) { + for (const graphNode of projects) { const files = filterFilesWithNxAngularDep( cachedFileMap[graphNode.name] || [] ); diff --git a/packages/angular/src/migrations/update-21-2-0/migrate-provide-server-rendering-import.ts b/packages/angular/src/migrations/update-21-2-0/migrate-provide-server-rendering-import.ts index cba6e817ce..0249ae4912 100644 --- a/packages/angular/src/migrations/update-21-2-0/migrate-provide-server-rendering-import.ts +++ b/packages/angular/src/migrations/update-21-2-0/migrate-provide-server-rendering-import.ts @@ -10,7 +10,7 @@ import { angularDevkitVersion } from '../../utils/versions'; import { getProjectsFilteredByDependencies } from '../utils/projects'; export default async function (tree: Tree) { - const projects = await getProjectsFilteredByDependencies(tree, [ + const projects = await getProjectsFilteredByDependencies([ 'npm:@angular/platform-server', ]); @@ -19,8 +19,8 @@ export default async function (tree: Tree) { } let isSsrInstalled = false; - for (const { project } of projects) { - visitNotIgnoredFiles(tree, project.root, (file) => { + for (const graphNode of projects) { + visitNotIgnoredFiles(tree, graphNode.data.root, (file) => { if (!file.endsWith('.ts') || file.endsWith('.d.ts')) { return; } diff --git a/packages/angular/src/migrations/update-21-2-0/replace-provide-server-routing.ts b/packages/angular/src/migrations/update-21-2-0/replace-provide-server-routing.ts index a3ba24f302..c10c57e3a6 100644 --- a/packages/angular/src/migrations/update-21-2-0/replace-provide-server-routing.ts +++ b/packages/angular/src/migrations/update-21-2-0/replace-provide-server-routing.ts @@ -5,7 +5,7 @@ import { FileChangeRecorder } from '../../utils/file-change-recorder'; import { getProjectsFilteredByDependencies } from '../utils/projects'; export default async function (tree: Tree) { - const projects = await getProjectsFilteredByDependencies(tree, [ + const projects = await getProjectsFilteredByDependencies([ 'npm:@angular/ssr', ]); @@ -13,8 +13,8 @@ export default async function (tree: Tree) { return; } - for (const { project } of projects) { - visitNotIgnoredFiles(tree, project.root, (file) => { + for (const graphNode of projects) { + visitNotIgnoredFiles(tree, graphNode.data.root, (file) => { if (!file.endsWith('.ts') || file.endsWith('.d.ts')) { return; } diff --git a/packages/angular/src/migrations/update-21-2-0/update-module-resolution.ts b/packages/angular/src/migrations/update-21-2-0/update-module-resolution.ts index f9534b40c9..3d5ffe1693 100644 --- a/packages/angular/src/migrations/update-21-2-0/update-module-resolution.ts +++ b/packages/angular/src/migrations/update-21-2-0/update-module-resolution.ts @@ -12,12 +12,12 @@ type TsConfig = { export default async function (tree: Tree) { const uniqueTsConfigs = new Set(); - const projects = await getProjectsFilteredByDependencies(tree, [ + const projects = await getProjectsFilteredByDependencies([ 'npm:@angular/core', ]); - for (const { project } of projects) { - for (const [, target] of allProjectTargets(project)) { + for (const graphNode of projects) { + for (const [, target] of allProjectTargets(graphNode.data)) { for (const [, options] of allTargetOptions<{ tsConfig?: string }>( target )) { diff --git a/packages/angular/src/migrations/utils/projects.ts b/packages/angular/src/migrations/utils/projects.ts index 8012c8465e..90c190572d 100644 --- a/packages/angular/src/migrations/utils/projects.ts +++ b/packages/angular/src/migrations/utils/projects.ts @@ -1,19 +1,9 @@ -import type { - ProjectConfiguration, - ProjectGraphProjectNode, - Tree, -} from '@nx/devkit'; -import { createProjectGraphAsync, readProjectConfiguration } from '@nx/devkit'; +import type { ProjectGraphProjectNode } from '@nx/devkit'; +import { createProjectGraphAsync } from '@nx/devkit'; export async function getProjectsFilteredByDependencies( - tree: Tree, dependencies: string[] -): Promise< - Array<{ - project: ProjectConfiguration; - graphNode: ProjectGraphProjectNode; - }> -> { +): Promise { const projectGraph = await createProjectGraphAsync(); return Object.entries(projectGraph.dependencies) @@ -22,8 +12,5 @@ export async function getProjectsFilteredByDependencies( !projectGraph.externalNodes?.[node] && deps.some(({ target }) => dependencies.includes(target)) ) - .map(([projectName]) => ({ - project: readProjectConfiguration(tree, projectName), - graphNode: projectGraph.nodes[projectName], - })); + .map(([projectName]) => projectGraph.nodes[projectName]); }