From c936f864b8e933c98e3d0884ab935a767bfb0137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Tue, 25 Jun 2024 14:33:24 +0200 Subject: [PATCH] fix(misc): register plugins correctly in migration generators (#26670) ## Current Behavior ## Expected Behavior ## Related Issue(s) Fixes # --- .../convert-to-inferred.spec.ts | 67 +-- .../convert-to-inferred.ts | 63 ++- .../executor-to-plugin-migrator.ts | 435 +++++++++++------- .../convert-to-inferred.ts | 33 +- .../convert-to-inferred.ts | 34 +- .../convert-to-inferred.ts | 15 +- .../convert-to-inferred.spec.ts | 2 +- .../convert-to-inferred.ts | 61 +-- .../convert-to-inferred.spec.ts | 4 +- .../convert-to-inferred.ts | 48 +- .../convert-to-inferred.spec.ts | 128 +++++- .../convert-to-inferred.ts | 95 ++-- .../convert-to-inferred.spec.ts | 184 +++++++- .../convert-to-inferred.ts | 99 ++-- 14 files changed, 763 insertions(+), 505 deletions(-) diff --git a/packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.spec.ts b/packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.spec.ts index 7552b98c30bb9..54825f1f67a2c 100644 --- a/packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.spec.ts +++ b/packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.spec.ts @@ -495,33 +495,46 @@ describe('Cypress - Convert Executors To Plugin', () => { // nx.json modifications const nxJsonPlugins = readNxJson(tree).plugins; - const addedTestCypressPlugin = nxJsonPlugins.find((plugin) => { - if ( - typeof plugin !== 'string' && - plugin.plugin === '@nx/cypress/plugin' && - plugin.include?.length === 2 - ) { - return true; - } - }); - expect(addedTestCypressPlugin).toBeTruthy(); - expect( - (addedTestCypressPlugin as ExpandedPluginConfiguration).include - ).toEqual(['myapp-e2e/**/*', 'second/**/*']); - - const addedIntegrationCypressPlugin = nxJsonPlugins.find((plugin) => { - if ( - typeof plugin !== 'string' && - plugin.plugin === '@nx/cypress/plugin' && - plugin.include?.length === 1 - ) { - return true; - } - }); - expect(addedIntegrationCypressPlugin).toBeTruthy(); - expect( - (addedIntegrationCypressPlugin as ExpandedPluginConfiguration).include - ).toEqual(['third/**/*']); + const addedCypressPlugins = nxJsonPlugins.filter( + (plugin) => + typeof plugin !== 'string' && plugin.plugin === '@nx/cypress/plugin' + ); + expect(addedCypressPlugins).toMatchInlineSnapshot(` + [ + { + "options": { + "ciTargetName": "e2e-ci", + "targetName": "e2e", + }, + "plugin": "@nx/cypress/plugin", + }, + { + "include": [ + "myapp-e2e/**/*", + "second/**/*", + ], + "options": { + "ciTargetName": "e2e-ci", + "componentTestingTargetName": "component-test", + "openTargetName": "open-cypress", + "targetName": "test", + }, + "plugin": "@nx/cypress/plugin", + }, + { + "include": [ + "third/**/*", + ], + "options": { + "ciTargetName": "e2e-ci", + "componentTestingTargetName": "component-test", + "openTargetName": "open-cypress", + "targetName": "integration", + }, + "plugin": "@nx/cypress/plugin", + }, + ] + `); }); it('should keep Cypress options in project.json', async () => { diff --git a/packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.ts index 95768403361e5..280e4bfd18d79 100644 --- a/packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.ts +++ b/packages/cypress/src/generators/convert-to-inferred/convert-to-inferred.ts @@ -4,16 +4,16 @@ import { type TargetConfiguration, type Tree, } from '@nx/devkit'; -import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; -import { createNodesV2 } from '../../plugins/plugin'; -import { targetOptionsToCliMap } from './lib/target-options-map'; -import { upsertBaseUrl } from './lib/upsert-baseUrl'; -import { addDevServerTargetToConfig } from './lib/add-dev-server-target-to-config'; -import { addExcludeSpecPattern } from './lib/add-exclude-spec-pattern'; +import { migrateProjectExecutorsToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; import { processTargetOutputs, toProjectRelativePath, } from '@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils'; +import { createNodesV2, type CypressPluginOptions } from '../../plugins/plugin'; +import { addDevServerTargetToConfig } from './lib/add-dev-server-target-to-config'; +import { addExcludeSpecPattern } from './lib/add-exclude-spec-pattern'; +import { targetOptionsToCliMap } from './lib/target-options-map'; +import { upsertBaseUrl } from './lib/upsert-baseUrl'; interface Schema { project?: string; @@ -23,38 +23,29 @@ interface Schema { export async function convertToInferred(tree: Tree, options: Schema) { const projectGraph = await createProjectGraphAsync(); - const migratedProjectsModern = await migrateExecutorToPlugin( - tree, - projectGraph, - '@nx/cypress:cypress', - '@nx/cypress/plugin', - (targetName) => ({ - targetName, - ciTargetName: 'e2e-ci', - }), - postTargetTransformer, - createNodesV2, - options.project - ); - - const migratedProjectsLegacy = await migrateExecutorToPlugin( - tree, - projectGraph, - '@nrwl/cypress:cypress', - '@nx/cypress/plugin', - (targetName) => ({ - targetName, - ciTargetName: 'e2e-ci', - }), - postTargetTransformer, - createNodesV2, - options.project - ); - const migratedProjects = - migratedProjectsModern.size + migratedProjectsLegacy.size; + await migrateProjectExecutorsToPlugin( + tree, + projectGraph, + '@nx/cypress/plugin', + createNodesV2, + { + targetName: 'cypress', + ciTargetName: 'e2e-ci', + componentTestingTargetName: 'component-test', + openTargetName: 'open-cypress', + }, + [ + { + executors: ['@nx/cypress:cypress', '@nrwl/cypress:cypress'], + postTargetTransformer, + targetPluginOptionMapper: (targetName) => ({ targetName }), + }, + ], + options.project + ); - if (migratedProjects === 0) { + if (migratedProjects.size === 0) { throw new Error('Could not find any targets to migrate.'); } diff --git a/packages/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator.ts b/packages/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator.ts index 0b663fb0ee6fb..88d319212a2bf 100644 --- a/packages/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator.ts +++ b/packages/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator.ts @@ -1,36 +1,32 @@ -import type { RunCommandsOptions } from 'nx/src/executors/run-commands/run-commands.impl'; - import { minimatch } from 'minimatch'; import { deepStrictEqual } from 'node:assert'; - -import { forEachExecutorOptions } from '../executor-options-utils'; -import { deleteMatchingProperties } from './plugin-migration-utils'; - +import type { + InputDefinition, + ProjectConfiguration, +} from 'nx/src/config/workspace-json-project-json'; import { readNxJson, + readProjectConfiguration, updateNxJson, updateProjectConfiguration, - readProjectConfiguration, - ProjectGraph, - ExpandedPluginConfiguration, - NxJsonConfiguration, - TargetConfiguration, - Tree, - CreateNodes, - CreateNodesV2, + type CreateNodes, + type CreateNodesV2, + type ExpandedPluginConfiguration, + type NxJsonConfiguration, + type ProjectGraph, + type TargetConfiguration, + type Tree, } from 'nx/src/devkit-exports'; - import { - mergeTargetConfigurations, - retrieveProjectConfigurations, LoadedNxPlugin, ProjectConfigurationsError, + mergeTargetConfigurations, + retrieveProjectConfigurations, } from 'nx/src/devkit-internals'; +import type { RunCommandsOptions } from 'nx/src/executors/run-commands/run-commands.impl'; import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils'; -import type { - InputDefinition, - ProjectConfiguration, -} from 'nx/src/config/workspace-json-project-json'; +import { forEachExecutorOptions } from '../executor-options-utils'; +import { deleteMatchingProperties } from './plugin-migration-utils'; type PluginOptionsBuilder = (targetName: string) => T; type PostTargetTransformer = ( @@ -100,7 +96,6 @@ class ExecutorToPluginMigrator { for (const targetName of this.#targetAndProjectsToMigrate.keys()) { await this.#migrateTarget(targetName); } - await this.#addPlugins(); } return this.#targetAndProjectsToMigrate; } @@ -235,91 +230,6 @@ class ExecutorToPluginMigrator { } } - async #pluginRequiresIncludes( - targetName: string, - plugin: ExpandedPluginConfiguration - ) { - const loadedPlugin = new LoadedNxPlugin( - { - createNodesV2: this.#createNodesV2, - createNodes: this.#createNodes, - name: this.#pluginPath, - }, - plugin - ); - - const originalResults = this.#createNodesResultsForTargets.get(targetName); - - let resultsWithIncludes: ConfigurationResult; - try { - resultsWithIncludes = await retrieveProjectConfigurations( - [loadedPlugin], - this.tree.root, - this.#nxJson - ); - } catch (e) { - if (e instanceof ProjectConfigurationsError) { - resultsWithIncludes = e.partialProjectConfigurationsResult; - } else { - throw e; - } - } - - return !deepEqual(originalResults, resultsWithIncludes); - } - - async #addPlugins() { - for (const [targetName, plugin] of this.#pluginToAddForTarget.entries()) { - const pluginOptions = this.#pluginOptionsBuilder(targetName); - - const existingPlugin = this.#nxJson.plugins.find( - (plugin: ExpandedPluginConfiguration) => { - if ( - typeof plugin === 'string' || - plugin.plugin !== this.#pluginPath - ) { - return; - } - - for (const key in plugin.options) { - if (plugin.options[key] !== pluginOptions[key]) { - return false; - } - } - - return true; - } - ) as ExpandedPluginConfiguration; - - if (existingPlugin?.include) { - // Add to the existing plugin includes - existingPlugin.include = existingPlugin.include.concat( - // Any include that is in the new plugin's include list - plugin.include.filter( - (projectPath) => - // And is not already covered by the existing plugin's include list - !existingPlugin.include.some((pluginIncludes) => - minimatch(projectPath, pluginIncludes, { dot: true }) - ) - ) - ); - - if (!(await this.#pluginRequiresIncludes(targetName, existingPlugin))) { - delete existingPlugin.include; - } - } - - if (!existingPlugin) { - if (!(await this.#pluginRequiresIncludes(targetName, plugin))) { - plugin.include = undefined; - } - this.#nxJson.plugins.push(plugin); - } - } - - updateNxJson(this.tree, this.#nxJson); - } - #getTargetAndProjectsToMigrate() { forEachExecutorOptions( this.tree, @@ -403,96 +313,289 @@ class ExecutorToPluginMigrator { } global.NX_GRAPH_CREATION = true; - for (const targetName of this.#targetAndProjectsToMigrate.keys()) { - const loadedPlugin = new LoadedNxPlugin( - { - createNodesV2: this.#createNodesV2, - createNodes: this.#createNodes, - name: this.#pluginPath, - }, - { - plugin: this.#pluginPath, - options: this.#pluginOptionsBuilder(targetName), - } - ); - let projectConfigs: ConfigurationResult; - try { - projectConfigs = await retrieveProjectConfigurations( - [loadedPlugin], - this.tree.root, + try { + for (const targetName of this.#targetAndProjectsToMigrate.keys()) { + const result = await getCreateNodesResultsForPlugin( + this.tree, + { + plugin: this.#pluginPath, + options: this.#pluginOptionsBuilder(targetName), + }, + this.#pluginPath, + this.#createNodes, + this.#createNodesV2, this.#nxJson ); - } catch (e) { - if (e instanceof ProjectConfigurationsError) { - projectConfigs = e.partialProjectConfigurationsResult; - } else { - global.NX_GRAPH_CREATION = false; - throw e; - } + this.#createNodesResultsForTargets.set(targetName, result); } - - this.#createNodesResultsForTargets.set(targetName, projectConfigs); + } finally { + global.NX_GRAPH_CREATION = false; } - global.NX_GRAPH_CREATION = false; } } -export async function migrateExecutorToPlugin( +export async function migrateProjectExecutorsToPlugin( tree: Tree, projectGraph: ProjectGraph, - executor: string, pluginPath: string, - pluginOptionsBuilder: PluginOptionsBuilder, - postTargetTransformer: PostTargetTransformer, - createNodes: CreateNodesV2, - specificProjectToMigrate?: string, - filters?: { + createNodesV2: CreateNodesV2, + defaultPluginOptions: T, + migrations: Array<{ + executors: string[]; + targetPluginOptionMapper: (targetName: string) => Partial; + postTargetTransformer: PostTargetTransformer; skipProjectFilter?: SkipProjectFilter; skipTargetFilter?: SkipTargetFilter; - } -): Promise>> { - const migrator = new ExecutorToPluginMigrator( + }>, + specificProjectToMigrate?: string +): Promise>> { + const projects = await migrateProjects( tree, projectGraph, - executor, pluginPath, - pluginOptionsBuilder, - postTargetTransformer, undefined, - createNodes, - specificProjectToMigrate, - filters + createNodesV2, + defaultPluginOptions, + migrations, + specificProjectToMigrate ); - return await migrator.run(); + + return projects; } -export async function migrateExecutorToPluginV1( +export async function migrateProjectExecutorsToPluginV1( tree: Tree, projectGraph: ProjectGraph, - executor: string, pluginPath: string, - pluginOptionsBuilder: PluginOptionsBuilder, - postTargetTransformer: PostTargetTransformer, createNodes: CreateNodes, - specificProjectToMigrate?: string, - filters?: { + defaultPluginOptions: T, + migrations: Array<{ + executors: string[]; + targetPluginOptionMapper: (targetName: string) => Partial; + postTargetTransformer: PostTargetTransformer; skipProjectFilter?: SkipProjectFilter; skipTargetFilter?: SkipTargetFilter; - } -): Promise>> { - const migrator = new ExecutorToPluginMigrator( + }>, + specificProjectToMigrate?: string +): Promise>> { + const projects = await migrateProjects( tree, projectGraph, - executor, pluginPath, - pluginOptionsBuilder, - postTargetTransformer, createNodes, undefined, - specificProjectToMigrate, - filters + defaultPluginOptions, + migrations, + specificProjectToMigrate ); - return await migrator.run(); + + return projects; +} + +async function migrateProjects( + tree: Tree, + projectGraph: ProjectGraph, + pluginPath: string, + createNodes: CreateNodes, + createNodesV2: CreateNodesV2, + defaultPluginOptions: T, + migrations: Array<{ + executors: string[]; + targetPluginOptionMapper: (targetName: string) => Partial; + postTargetTransformer: PostTargetTransformer; + skipProjectFilter?: SkipProjectFilter; + skipTargetFilter?: SkipTargetFilter; + }>, + specificProjectToMigrate?: string +): Promise>> { + const projects = new Map>(); + + for (const migration of migrations) { + for (const executor of migration.executors) { + const migrator = new ExecutorToPluginMigrator( + tree, + projectGraph, + executor, + pluginPath, + migration.targetPluginOptionMapper, + migration.postTargetTransformer, + createNodes, + createNodesV2, + specificProjectToMigrate, + { + skipProjectFilter: migration.skipProjectFilter, + skipTargetFilter: migration.skipTargetFilter, + } + ); + + const result = await migrator.run(); + + // invert the result to have a map of projects to their targets + for (const [target, projectList] of result.entries()) { + for (const project of projectList) { + if (!projects.has(project)) { + projects.set(project, {}); + } + + projects.set(project, { + ...projects.get(project), + ...migration.targetPluginOptionMapper(target), + }); + } + } + } + } + + // apply default options + for (const [project, pluginOptions] of projects.entries()) { + projects.set(project, { + ...defaultPluginOptions, + ...pluginOptions, + }); + } + + await addPluginRegistrations( + tree, + projects, + pluginPath, + createNodes, + createNodesV2, + defaultPluginOptions, + projectGraph + ); + + return projects; +} + +async function addPluginRegistrations( + tree: Tree, + projects: Map>, + pluginPath: string, + createNodes: CreateNodes | undefined, + createNodesV2: CreateNodesV2 | undefined, + defaultPluginOptions: T, + projectGraph: ProjectGraph +) { + const nxJson = readNxJson(tree); + + // collect createNodes results for each project before adding the plugins + const createNodesResults = new Map(); + global.NX_GRAPH_CREATION = true; + try { + for (const [project, options] of projects.entries()) { + const projectConfigs = await getCreateNodesResultsForPlugin( + tree, + { plugin: pluginPath, options }, + pluginPath, + createNodes, + createNodesV2, + nxJson + ); + + createNodesResults.set(project, projectConfigs); + } + } finally { + global.NX_GRAPH_CREATION = false; + } + + const arePluginIncludesRequired = async ( + project: string, + pluginConfiguration: ExpandedPluginConfiguration + ): Promise => { + global.NX_GRAPH_CREATION = true; + let result: ConfigurationResult; + try { + result = await getCreateNodesResultsForPlugin( + tree, + pluginConfiguration, + pluginPath, + createNodes, + createNodesV2, + nxJson + ); + } finally { + global.NX_GRAPH_CREATION = false; + } + + const originalResults = createNodesResults.get(project); + + return !deepEqual(originalResults, result); + }; + + for (const [project, options] of projects.entries()) { + const existingPlugin = nxJson.plugins?.find( + (plugin): plugin is ExpandedPluginConfiguration => + typeof plugin !== 'string' && + plugin.plugin === pluginPath && + Object.keys(options).every( + (key) => + plugin.options[key] === options[key] || + (plugin.options[key] === undefined && + options[key] === defaultPluginOptions[key]) + ) + ); + + const projectIncludeGlob = `${projectGraph.nodes[project].data.root}/**/*`; + if (!existingPlugin) { + nxJson.plugins ??= []; + const plugin: ExpandedPluginConfiguration = { + plugin: pluginPath, + options, + include: [projectIncludeGlob], + }; + + if (!(await arePluginIncludesRequired(project, plugin))) { + delete plugin.include; + } + + nxJson.plugins.push(plugin); + } else if (existingPlugin.include) { + if ( + !existingPlugin.include.some((include) => + minimatch(projectIncludeGlob, include, { dot: true }) + ) + ) { + existingPlugin.include.push(projectIncludeGlob); + + if (!(await arePluginIncludesRequired(project, existingPlugin))) { + delete existingPlugin.include; + } + } + } + } + + updateNxJson(tree, nxJson); +} + +async function getCreateNodesResultsForPlugin( + tree: Tree, + pluginConfiguration: ExpandedPluginConfiguration, + pluginPath: string, + createNodes: CreateNodes | undefined, + createNodesV2: CreateNodesV2 | undefined, + nxJson: NxJsonConfiguration +): Promise { + let projectConfigs: ConfigurationResult; + + try { + const plugin = new LoadedNxPlugin( + { createNodes, createNodesV2, name: pluginPath }, + pluginConfiguration + ); + projectConfigs = await retrieveProjectConfigurations( + [plugin], + tree.root, + nxJson + ); + } catch (e) { + if (e instanceof ProjectConfigurationsError) { + projectConfigs = e.partialProjectConfigurationsResult; + } else { + throw e; + } + } + + return projectConfigs; } // Checks if two objects are structurely equal, without caring diff --git a/packages/eslint/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/eslint/src/generators/convert-to-inferred/convert-to-inferred.ts index db4abfb969912..6114869c37c80 100644 --- a/packages/eslint/src/generators/convert-to-inferred/convert-to-inferred.ts +++ b/packages/eslint/src/generators/convert-to-inferred/convert-to-inferred.ts @@ -6,7 +6,7 @@ import { type Tree, } from '@nx/devkit'; import { createNodesV2, EslintPluginOptions } from '../../plugins/plugin'; -import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; +import { migrateProjectExecutorsToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; import { targetOptionsToCliMap } from './lib/target-options-map'; import { interpolate } from 'nx/src/tasks-runner/utils'; import { @@ -22,33 +22,24 @@ interface Schema { export async function convertToInferred(tree: Tree, options: Schema) { const projectGraph = await createProjectGraphAsync(); - const migratedProjectsModern = - await migrateExecutorToPlugin( - tree, - projectGraph, - '@nx/eslint:lint', - '@nx/eslint/plugin', - (targetName) => ({ targetName }), - postTargetTransformer, - createNodesV2, - options.project - ); - - const migratedProjectsLegacy = - await migrateExecutorToPlugin( + const migratedProjects = + await migrateProjectExecutorsToPlugin( tree, projectGraph, - '@nrwl/linter:eslint', '@nx/eslint/plugin', - (targetName) => ({ targetName }), - postTargetTransformer, createNodesV2, + { targetName: 'lint' }, + [ + { + executors: ['@nx/eslint:lint', '@nrwl/linter:eslint'], + postTargetTransformer, + targetPluginOptionMapper: (targetName) => ({ targetName }), + }, + ], options.project ); - const migratedProjects = - migratedProjectsModern.size + migratedProjectsLegacy.size; - if (migratedProjects === 0) { + if (migratedProjects.size === 0) { throw new Error('Could not find any targets to migrate.'); } diff --git a/packages/jest/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/jest/src/generators/convert-to-inferred/convert-to-inferred.ts index d0bb3282e538a..2e433e96b8222 100644 --- a/packages/jest/src/generators/convert-to-inferred/convert-to-inferred.ts +++ b/packages/jest/src/generators/convert-to-inferred/convert-to-inferred.ts @@ -5,7 +5,7 @@ import { type TargetConfiguration, type Tree, } from '@nx/devkit'; -import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; +import { migrateProjectExecutorsToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; import { processTargetOutputs, toProjectRelativePath, @@ -22,34 +22,24 @@ interface Schema { export async function convertToInferred(tree: Tree, options: Schema) { const projectGraph = await createProjectGraphAsync(); - const migratedProjectsModern = - await migrateExecutorToPlugin( - tree, - projectGraph, - '@nx/jest:jest', - '@nx/jest/plugin', - (targetName) => ({ targetName }), - postTargetTransformer, - createNodesV2, - options.project - ); - - const migratedProjectsLegacy = - await migrateExecutorToPlugin( + const migratedProjects = + await migrateProjectExecutorsToPlugin( tree, projectGraph, - '@nrwl/jest:jest', '@nx/jest/plugin', - (targetName) => ({ targetName }), - postTargetTransformer, createNodesV2, + { targetName: 'test' }, + [ + { + executors: ['@nx/jest:jest', '@nrwl/jest:jest'], + postTargetTransformer, + targetPluginOptionMapper: (targetName) => ({ targetName }), + }, + ], options.project ); - const migratedProjects = - migratedProjectsModern.size + migratedProjectsLegacy.size; - - if (migratedProjects === 0) { + if (migratedProjects.size === 0) { throw new Error('Could not find any targets to migrate.'); } diff --git a/packages/playwright/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/playwright/src/generators/convert-to-inferred/convert-to-inferred.ts index 114944bebabf1..849012751daab 100644 --- a/packages/playwright/src/generators/convert-to-inferred/convert-to-inferred.ts +++ b/packages/playwright/src/generators/convert-to-inferred/convert-to-inferred.ts @@ -6,7 +6,7 @@ import { type Tree, } from '@nx/devkit'; import { createNodesV2, PlaywrightPluginOptions } from '../../plugins/plugin'; -import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; +import { migrateProjectExecutorsToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; interface Schema { project?: string; @@ -17,14 +17,19 @@ interface Schema { export async function convertToInferred(tree: Tree, options: Schema) { const projectGraph = await createProjectGraphAsync(); const migratedProjects = - await migrateExecutorToPlugin( + await migrateProjectExecutorsToPlugin( tree, projectGraph, - '@nx/playwright:playwright', '@nx/playwright/plugin', - (targetName) => ({ targetName, ciTargetName: 'e2e-ci' }), - postTargetTransformer, createNodesV2, + { targetName: 'e2e', ciTargetName: 'e2e-ci' }, + [ + { + executors: ['@nx/playwright:playwright'], + postTargetTransformer, + targetPluginOptionMapper: (targetName) => ({ targetName }), + }, + ], options.project ); diff --git a/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.spec.ts b/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.spec.ts index aa3ff36130c85..98e1814bb6896 100644 --- a/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.spec.ts +++ b/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.spec.ts @@ -239,7 +239,7 @@ describe('Remix - Convert To Inferred', () => { plugin: '@nx/remix/plugin', options: { buildTargetName: 'build', - devTargetName: defaultTestProjectOptions.serveTargetName, + devTargetName: 'custom-dev', startTargetName: 'start', typecheckTargetName: 'typecheck', staticServeTargetName: 'static-serve', diff --git a/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.ts index 03633b86ec258..6dfb1edf87b80 100644 --- a/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.ts +++ b/packages/remix/src/generators/convert-to-inferred/convert-to-inferred.ts @@ -1,15 +1,9 @@ -import { - addDependenciesToPackageJson, - createProjectGraphAsync, - formatFiles, - runTasksInSerial, - type Tree, -} from '@nx/devkit'; +import { createProjectGraphAsync, formatFiles, type Tree } from '@nx/devkit'; import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util'; -import { migrateExecutorToPluginV1 } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; +import { migrateProjectExecutorsToPluginV1 } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; +import { createNodes } from '../../plugins/plugin'; import { buildPostTargetTransformer } from './lib/build-post-target-transformer'; import { servePostTargetTransformer } from './lib/serve-post-target-transformer'; -import { createNodes } from '../../plugins/plugin'; interface Schema { project?: string; @@ -19,43 +13,38 @@ interface Schema { export async function convertToInferred(tree: Tree, options: Schema) { const projectGraph = await createProjectGraphAsync(); const migrationLogs = new AggregatedLog(); - const migratedBuildProjects = await migrateExecutorToPluginV1( + const migratedProjects = await migrateProjectExecutorsToPluginV1( tree, projectGraph, - '@nx/remix:build', '@nx/remix/plugin', - (targetName) => ({ - buildTargetName: targetName, - devTargetName: 'dev', - startTargetName: 'start', - typecheckTargetName: 'typecheck', - staticServeTargetName: 'static-serve', - }), - buildPostTargetTransformer(migrationLogs), createNodes, - options.project - ); - - const migratedServeProjects = await migrateExecutorToPluginV1( - tree, - projectGraph, - '@nx/remix:serve', - '@nx/remix/plugin', - (targetName) => ({ + { buildTargetName: 'build', - devTargetName: targetName, + devTargetName: 'dev', startTargetName: 'start', - typecheckTargetName: 'typecheck', staticServeTargetName: 'static-serve', - }), - servePostTargetTransformer(migrationLogs), - createNodes, + typecheckTargetName: 'typecheck', + }, + [ + { + executors: ['@nx/remix:build'], + postTargetTransformer: buildPostTargetTransformer(migrationLogs), + targetPluginOptionMapper: (targetName) => ({ + buildTargetName: targetName, + }), + }, + { + executors: ['@nx/remix:serve'], + postTargetTransformer: servePostTargetTransformer(migrationLogs), + targetPluginOptionMapper: (targetName) => ({ + devTargetName: targetName, + }), + }, + ], options.project ); - const migratedProjects = - migratedBuildProjects.size + migratedServeProjects.size; - if (migratedProjects === 0) { + if (migratedProjects.size === 0) { throw new Error('Could not find any targets to migrate.'); } diff --git a/packages/storybook/src/generators/convert-to-inferred/convert-to-inferred.spec.ts b/packages/storybook/src/generators/convert-to-inferred/convert-to-inferred.spec.ts index 7139d65b88fc8..88009f5474dee 100644 --- a/packages/storybook/src/generators/convert-to-inferred/convert-to-inferred.spec.ts +++ b/packages/storybook/src/generators/convert-to-inferred/convert-to-inferred.spec.ts @@ -287,8 +287,8 @@ describe('Storybook - Convert To Inferred', () => { nxJson.plugins.push({ plugin: '@nx/storybook/plugin', options: { - buildTargetName: 'storybook-build', - serveTargetName: defaultTestProjectOptions.serveTargetName, + buildStorybookTargetName: 'storybook-build', + serveStorybookTargetName: 'custom-storybook', staticStorybookTargetName: 'static-storybook', testStorybookTargetName: 'test-storybook', }, diff --git a/packages/storybook/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/storybook/src/generators/convert-to-inferred/convert-to-inferred.ts index 9015fbada9894..a3f8808086cd9 100644 --- a/packages/storybook/src/generators/convert-to-inferred/convert-to-inferred.ts +++ b/packages/storybook/src/generators/convert-to-inferred/convert-to-inferred.ts @@ -6,7 +6,7 @@ import { type Tree, } from '@nx/devkit'; import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util'; -import { migrateExecutorToPluginV1 } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; +import { migrateProjectExecutorsToPluginV1 } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; import { buildPostTargetTransformer } from './lib/build-post-target-transformer'; import { servePostTargetTransformer } from './lib/serve-post-target-transformer'; import { createNodes } from '../../plugins/plugin'; @@ -20,41 +20,37 @@ interface Schema { export async function convertToInferred(tree: Tree, options: Schema) { const projectGraph = await createProjectGraphAsync(); const migrationLogs = new AggregatedLog(); - const migratedBuildProjects = await migrateExecutorToPluginV1( + const migratedProjects = await migrateProjectExecutorsToPluginV1( tree, projectGraph, - '@nx/storybook:build', '@nx/storybook/plugin', - (targetName) => ({ - buildStorybookTargetName: targetName, - serveStorybookTargetName: 'storybook', - staticStorybookTargetName: 'static-storybook', - testStorybookTargetName: 'test-storybook', - }), - buildPostTargetTransformer(migrationLogs), createNodes, - options.project - ); - - const migratedServeProjects = await migrateExecutorToPluginV1( - tree, - projectGraph, - '@nx/storybook:storybook', - '@nx/storybook/plugin', - (targetName) => ({ + { buildStorybookTargetName: 'build-storybook', - serveStorybookTargetName: targetName, + serveStorybookTargetName: 'storybook', staticStorybookTargetName: 'static-storybook', testStorybookTargetName: 'test-storybook', - }), - servePostTargetTransformer(migrationLogs), - createNodes, + }, + [ + { + executors: ['@nx/storybook:build', '@nrwl/storybook:build'], + postTargetTransformer: buildPostTargetTransformer(migrationLogs), + targetPluginOptionMapper: (targetName) => ({ + buildStorybookTargetName: targetName, + }), + }, + { + executors: ['@nx/storybook:storybook', '@nrwl/storybook:storybook'], + postTargetTransformer: servePostTargetTransformer(migrationLogs), + targetPluginOptionMapper: (targetName) => ({ + serveStorybookTargetName: targetName, + }), + }, + ], options.project ); - const migratedProjects = - migratedBuildProjects.size + migratedServeProjects.size; - if (migratedProjects === 0) { + if (migratedProjects.size === 0) { throw new Error('Could not find any targets to migrate.'); } diff --git a/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.spec.ts b/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.spec.ts index f603d4af2b425..5f8845e41288e 100644 --- a/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.spec.ts +++ b/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.spec.ts @@ -18,6 +18,7 @@ import { } from '@nx/devkit'; import { TempFs } from '@nx/devkit/internal-testing-utils'; import { join } from 'node:path'; +import type { VitePluginOptions } from '../../plugins/plugin'; let fs: TempFs; @@ -514,33 +515,116 @@ describe('Vite - Convert Executors To Plugin', () => { // nx.json modifications const nxJsonPlugins = readNxJson(tree).plugins; - const addedTestVitePlugin = nxJsonPlugins.find((plugin) => { - if ( - typeof plugin !== 'string' && - plugin.plugin === '@nx/vite/plugin' && - plugin.include?.length === 2 - ) { + const addedVitePlugins = nxJsonPlugins.filter((plugin) => { + if (typeof plugin !== 'string' && plugin.plugin === '@nx/vite/plugin') { return true; } }); - expect(addedTestVitePlugin).toBeTruthy(); - expect( - (addedTestVitePlugin as ExpandedPluginConfiguration).include - ).toEqual(['myapp/**/*', 'second/**/*']); + expect(addedVitePlugins).toMatchInlineSnapshot(` + [ + { + "options": { + "buildTargetName": "build", + "previewTargetName": "preview", + "serveTargetName": "serve", + "testTargetName": "test", + }, + "plugin": "@nx/vite/plugin", + }, + { + "include": [ + "myapp/**/*", + "second/**/*", + ], + "options": { + "buildTargetName": "bundle", + "previewTargetName": "preview", + "serveStaticTargetName": "serve-static", + "serveTargetName": "serve", + "testTargetName": "test", + }, + "plugin": "@nx/vite/plugin", + }, + { + "include": [ + "third/**/*", + ], + "options": { + "buildTargetName": "build-base", + "previewTargetName": "preview", + "serveStaticTargetName": "serve-static", + "serveTargetName": "serve", + "testTargetName": "test", + }, + "plugin": "@nx/vite/plugin", + }, + ] + `); + }); - const addedIntegrationVitePlugin = nxJsonPlugins.find((plugin) => { - if ( - typeof plugin !== 'string' && - plugin.plugin === '@nx/vite/plugin' && - plugin.include?.length === 1 - ) { - return true; - } + it('should handle multiple different target names for the same project', async () => { + const project1 = createTestProject(tree); + const project2 = createTestProject(tree, { + appRoot: 'project2', + appName: 'project2', }); - expect(addedIntegrationVitePlugin).toBeTruthy(); - expect( - (addedIntegrationVitePlugin as ExpandedPluginConfiguration).include - ).toEqual(['third/**/*']); + const project3 = createTestProject(tree, { + appRoot: 'project3', + appName: 'project3', + buildTargetName: 'vite-build', + serveTargetName: 'vite-serve', + }); + const project4 = createTestProject(tree, { + appRoot: 'project4', + appName: 'project4', + buildTargetName: 'build', + serveTargetName: 'vite-serve', + }); + const project5 = createTestProject(tree, { + appRoot: 'project5', + appName: 'project5', + buildTargetName: 'vite-build', + serveTargetName: 'serve', + }); + + await convertToInferred(tree, { skipFormat: true }); + + // nx.json modifications + const nxJsonPlugins = readNxJson(tree).plugins; + const vitePluginRegistrations = nxJsonPlugins.filter( + (plugin): plugin is ExpandedPluginConfiguration => + typeof plugin !== 'string' && plugin.plugin === '@nx/vite/plugin' + ); + expect(vitePluginRegistrations.length).toBe(4); + expect(vitePluginRegistrations[0].options.buildTargetName).toBe('build'); + expect(vitePluginRegistrations[0].options.serveTargetName).toBe('serve'); + expect(vitePluginRegistrations[0].include).toEqual([ + `${project1.root}/**/*`, + `${project2.root}/**/*`, + ]); + expect(vitePluginRegistrations[1].options.buildTargetName).toBe('build'); + expect(vitePluginRegistrations[1].options.serveTargetName).toBe( + 'vite-serve' + ); + expect(vitePluginRegistrations[1].include).toEqual([ + `${project4.root}/**/*`, + ]); + expect(vitePluginRegistrations[2].options.buildTargetName).toBe( + 'vite-build' + ); + expect(vitePluginRegistrations[2].options.serveTargetName).toBe( + 'vite-serve' + ); + expect(vitePluginRegistrations[2].include).toEqual([ + `${project3.root}/**/*`, + ]); + expect(vitePluginRegistrations[3].options.buildTargetName).toBe( + 'vite-build' + ); + expect(vitePluginRegistrations[3].options.serveTargetName).toBe('serve'); + expect(vitePluginRegistrations[3].include).toEqual([ + `${project5.root}/**/*`, + ]); }); it('should keep Vite options in project.json', async () => { diff --git a/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.ts index 3c85db64126cb..7ee000b715628 100644 --- a/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.ts +++ b/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.ts @@ -1,5 +1,5 @@ import { createProjectGraphAsync, formatFiles, type Tree } from '@nx/devkit'; -import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; +import { migrateProjectExecutorsToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; import { createNodesV2, VitePluginOptions } from '../../plugins/plugin'; import { buildPostTargetTransformer } from './lib/build-post-target-transformer'; import { servePostTargetTransformer } from './lib/serve-post-target-transformer'; @@ -15,81 +15,46 @@ interface Schema { export async function convertToInferred(tree: Tree, options: Schema) { const projectGraph = await createProjectGraphAsync(); const migrationLogs = new AggregatedLog(); - const migratedBuildProjects = - await migrateExecutorToPlugin( + + const migratedProjects = + await migrateProjectExecutorsToPlugin( tree, projectGraph, - '@nx/vite:build', '@nx/vite/plugin', - (targetName) => ({ - buildTargetName: targetName, - serveTargetName: 'serve', - previewTargetName: 'preview', - testTargetName: 'test', - serveStaticTargetName: 'serve-static', - }), - buildPostTargetTransformer, createNodesV2, - options.project - ); - const migratedServeProjects = - await migrateExecutorToPlugin( - tree, - projectGraph, - '@nx/vite:dev-server', - '@nx/vite/plugin', - (targetName) => ({ - buildTargetName: 'build', - serveTargetName: targetName, - previewTargetName: 'preview', - testTargetName: 'test', - serveStaticTargetName: 'serve-static', - }), - servePostTargetTransformer(migrationLogs), - createNodesV2, - options.project - ); - const migratedPreviewProjects = - await migrateExecutorToPlugin( - tree, - projectGraph, - '@nx/vite:preview-server', - '@nx/vite/plugin', - (targetName) => ({ + { buildTargetName: 'build', serveTargetName: 'serve', - previewTargetName: targetName, + previewTargetName: 'preview', testTargetName: 'test', serveStaticTargetName: 'serve-static', - }), - previewPostTargetTransformer(migrationLogs), - createNodesV2, + }, + [ + { + executors: ['@nx/vite:build'], + postTargetTransformer: buildPostTargetTransformer, + targetPluginOptionMapper: (target) => ({ buildTargetName: target }), + }, + { + executors: ['@nx/vite:dev-server'], + postTargetTransformer: servePostTargetTransformer(migrationLogs), + targetPluginOptionMapper: (target) => ({ serveTargetName: target }), + }, + { + executors: ['@nx/vite:preview-server'], + postTargetTransformer: previewPostTargetTransformer(migrationLogs), + targetPluginOptionMapper: (target) => ({ previewTargetName: target }), + }, + { + executors: ['@nx/vite:test'], + postTargetTransformer: testPostTargetTransformer, + targetPluginOptionMapper: (target) => ({ testTargetName: target }), + }, + ], options.project ); - const migratedTestProjects = await migrateExecutorToPlugin( - tree, - projectGraph, - '@nx/vite:test', - '@nx/vite/plugin', - (targetName) => ({ - buildTargetName: 'build', - serveTargetName: 'serve', - previewTargetName: 'preview', - testTargetName: targetName, - serveStaticTargetName: 'serve-static', - }), - testPostTargetTransformer, - createNodesV2, - options.project - ); - - const migratedProjects = - migratedBuildProjects.size + - migratedServeProjects.size + - migratedPreviewProjects.size + - migratedTestProjects.size; - if (migratedProjects === 0) { + if (migratedProjects.size === 0) { throw new Error('Could not find any targets to migrate.'); } diff --git a/packages/webpack/src/generators/convert-to-inferred/convert-to-inferred.spec.ts b/packages/webpack/src/generators/convert-to-inferred/convert-to-inferred.spec.ts index 266e28e52e2ad..9011bf887b903 100644 --- a/packages/webpack/src/generators/convert-to-inferred/convert-to-inferred.spec.ts +++ b/packages/webpack/src/generators/convert-to-inferred/convert-to-inferred.spec.ts @@ -3,6 +3,7 @@ import { joinPathFragments, readNxJson, readProjectConfiguration, + updateNxJson, updateProjectConfiguration, writeJson, type ExpandedPluginConfiguration, @@ -14,6 +15,7 @@ import { TempFs } from '@nx/devkit/internal-testing-utils'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { join } from 'node:path'; import { getRelativeProjectJsonSchemaPath } from 'nx/src/generators/utils/project-configuration'; +import type { WebpackPluginOptions } from '../../plugins/plugin'; import { convertToInferred } from './convert-to-inferred'; let fs: TempFs; @@ -377,6 +379,78 @@ describe('convert-to-inferred', () => { expect(updatedProject2.targets.build).toStrictEqual(project2BuildTarget); }); + it('should remove "includes" from the plugin registration when all projects are included', async () => { + const project1 = createProject(tree); + writeWebpackConfig(tree, project1.root); + const nxJson = readNxJson(tree); + nxJson.plugins ??= []; + nxJson.plugins.push({ + plugin: '@nx/webpack/plugin', + options: { + buildTargetName: 'build', + previewTargetName: 'preview', + serveStaticTargetName: 'serve-static', + serveTargetName: 'serve', + }, + include: [`${project1.root}/**/*`], + }); + updateNxJson(tree, nxJson); + const project2 = createProject(tree, { + appName: 'app2', + appRoot: 'apps/app2', + }); + writeWebpackConfig(tree, project2.root); + + await convertToInferred(tree, { project: project2.name }); + + // nx.json modifications + const nxJsonPlugins = readNxJson(tree).plugins; + const webpackPluginRegistrations = nxJsonPlugins.filter( + (plugin): plugin is ExpandedPluginConfiguration => + typeof plugin !== 'string' && plugin.plugin === '@nx/webpack/plugin' + ); + expect(webpackPluginRegistrations.length).toBe(1); + expect(webpackPluginRegistrations[0].include).toBeUndefined(); + }); + + it('should not add to "includes" when existing matching registration does not have it set', async () => { + const project1 = createProject(tree); + writeWebpackConfig(tree, project1.root); + const nxJson = readNxJson(tree); + nxJson.plugins ??= []; + nxJson.plugins.push({ + plugin: '@nx/webpack/plugin', + options: { + buildTargetName: 'build', + previewTargetName: 'preview', + serveStaticTargetName: 'serve-static', + serveTargetName: 'serve', + }, + }); + updateNxJson(tree, nxJson); + const project2 = createProject(tree, { + appName: 'app2', + appRoot: 'apps/app2', + }); + writeWebpackConfig(tree, project2.root); + const project3 = createProject(tree, { + appName: 'app3', + appRoot: 'apps/app3', + }); + writeWebpackConfig(tree, project3.root); + + await convertToInferred(tree, { project: project2.name }); + + // nx.json modifications + const nxJsonPlugins = readNxJson(tree).plugins; + const webpackPluginRegistrations = nxJsonPlugins.filter( + (plugin): plugin is ExpandedPluginConfiguration => + typeof plugin !== 'string' && plugin.plugin === '@nx/webpack/plugin' + ); + expect(webpackPluginRegistrations.length).toBe(1); + expect(webpackPluginRegistrations[0].include).toBeUndefined(); + }); + it('should move options to the webpack config file', async () => { const project = createProject(tree); writeWebpackConfig(tree, project.root); @@ -753,11 +827,26 @@ describe('convert-to-inferred', () => { appName: 'app3', appRoot: 'apps/app3', buildTargetName: 'build-webpack', + serveTargetName: 'serve-webpack', }); writeWebpackConfig(tree, project3.root); - const projectWithComposePlugins = createProject(tree, { + const project4 = createProject(tree, { appName: 'app4', appRoot: 'apps/app4', + buildTargetName: 'build', + serveTargetName: 'serve-webpack', + }); + writeWebpackConfig(tree, project4.root); + const project5 = createProject(tree, { + appName: 'app5', + appRoot: 'apps/app5', + buildTargetName: 'build-webpack', + serveTargetName: 'serve', + }); + writeWebpackConfig(tree, project5.root); + const projectWithComposePlugins = createProject(tree, { + appName: 'app6', + appRoot: 'apps/app6', }); const projectWithComposePluginsInitialTargets = projectWithComposePlugins.targets; @@ -783,8 +872,8 @@ module.exports = composePlugins( initialProjectWithComposePluginsWebpackConfig ); const projectWithNoNxAppWebpackPlugin = createProject(tree, { - appName: 'app5', - appRoot: 'apps/app5', + appName: 'app7', + appRoot: 'apps/app7', }); const projectWithNoNxAppWebpackPluginInitialTargets = projectWithNoNxAppWebpackPlugin.targets; @@ -829,6 +918,28 @@ module.exports = composePlugins( }); const updatedProject3 = readProjectConfiguration(tree, project3.name); expect(updatedProject3.targets).toStrictEqual({ + 'build-webpack': { + configurations: { development: {}, production: {} }, + defaultConfiguration: 'production', + }, + 'serve-webpack': { + configurations: { development: {}, production: {} }, + defaultConfiguration: 'development', + }, + }); + const updatedProject4 = readProjectConfiguration(tree, project4.name); + expect(updatedProject4.targets).toStrictEqual({ + build: { + configurations: { development: {}, production: {} }, + defaultConfiguration: 'production', + }, + 'serve-webpack': { + configurations: { development: {}, production: {} }, + defaultConfiguration: 'development', + }, + }); + const updatedProject5 = readProjectConfiguration(tree, project5.name); + expect(updatedProject5.targets).toStrictEqual({ 'build-webpack': { configurations: { development: {}, production: {} }, defaultConfiguration: 'production', @@ -882,6 +993,73 @@ module.exports = composePlugins( expect(updatedProjectWithNoNxAppWebpackPluginWebpackConfig).toBe( initialProjectWithNoNxAppWebpackPluginWebpackConfig ); + // nx.json modifications + const nxJsonPlugins = readNxJson(tree).plugins; + const webpackPluginRegistrations = nxJsonPlugins.filter( + (plugin): plugin is ExpandedPluginConfiguration => + typeof plugin !== 'string' && plugin.plugin === '@nx/webpack/plugin' + ); + expect(webpackPluginRegistrations.length).toBe(4); + expect(webpackPluginRegistrations[0].options.buildTargetName).toBe( + 'build' + ); + expect(webpackPluginRegistrations[0].options.serveTargetName).toBe( + 'serve' + ); + expect(webpackPluginRegistrations[0].include).toEqual([ + `${project1.root}/**/*`, + `${project2.root}/**/*`, + ]); + expect(webpackPluginRegistrations[1].options.buildTargetName).toBe( + 'build' + ); + expect(webpackPluginRegistrations[1].options.serveTargetName).toBe( + 'serve-webpack' + ); + expect(webpackPluginRegistrations[1].include).toEqual([ + `${project4.root}/**/*`, + ]); + expect(webpackPluginRegistrations[2].options.buildTargetName).toBe( + 'build-webpack' + ); + expect(webpackPluginRegistrations[2].options.serveTargetName).toBe( + 'serve-webpack' + ); + expect(webpackPluginRegistrations[2].include).toEqual([ + `${project3.root}/**/*`, + ]); + expect(webpackPluginRegistrations[3].options.buildTargetName).toBe( + 'build-webpack' + ); + expect(webpackPluginRegistrations[3].options.serveTargetName).toBe( + 'serve' + ); + expect(webpackPluginRegistrations[3].include).toEqual([ + `${project5.root}/**/*`, + ]); + }); + + it('should remove "includes" from the plugin registration when all projects are included', async () => { + const project1 = createProject(tree); + writeWebpackConfig(tree, project1.root); + const project2 = createProject(tree, { + appName: 'app2', + appRoot: 'apps/app2', + buildExecutor: '@nrwl/webpack:webpack', + serveExecutor: '@nrwl/webpack:dev-server', + }); + writeWebpackConfig(tree, project2.root); + + await convertToInferred(tree, {}); + + // nx.json modifications + const nxJsonPlugins = readNxJson(tree).plugins; + const webpackPluginRegistrations = nxJsonPlugins.filter( + (plugin): plugin is ExpandedPluginConfiguration => + typeof plugin !== 'string' && plugin.plugin === '@nx/webpack/plugin' + ); + expect(webpackPluginRegistrations.length).toBe(1); + expect(webpackPluginRegistrations[0].include).toBeUndefined(); }); it('should keep the higher "memoryLimit" value in the build configuration', async () => { diff --git a/packages/webpack/src/generators/convert-to-inferred/convert-to-inferred.ts b/packages/webpack/src/generators/convert-to-inferred/convert-to-inferred.ts index f3afe012ceec2..11b1a1d766777 100644 --- a/packages/webpack/src/generators/convert-to-inferred/convert-to-inferred.ts +++ b/packages/webpack/src/generators/convert-to-inferred/convert-to-inferred.ts @@ -2,20 +2,20 @@ import { addDependenciesToPackageJson, createProjectGraphAsync, formatFiles, - type ProjectConfiguration, runTasksInSerial, + type ProjectConfiguration, type Tree, } from '@nx/devkit'; import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util'; -import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; +import { migrateProjectExecutorsToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator'; import { tsquery } from '@phenomnomnominal/tsquery'; import * as ts from 'typescript'; import { createNodesV2, type WebpackPluginOptions } from '../../plugins/plugin'; import { webpackCliVersion } from '../../utils/versions'; import { buildPostTargetTransformerFactory, - type MigrationContext, servePostTargetTransformerFactory, + type MigrationContext, } from './utils'; interface Schema { @@ -31,85 +31,38 @@ export async function convertToInferred(tree: Tree, options: Schema) { workspaceRoot: tree.root, }; - // build - const migratedBuildProjects = - await migrateExecutorToPlugin( - tree, - projectGraph, - '@nx/webpack:webpack', - '@nx/webpack/plugin', - (targetName) => ({ - buildTargetName: targetName, - previewTargetName: 'preview', - serveStaticTargetName: 'serve-static', - serveTargetName: 'serve', - }), - buildPostTargetTransformerFactory(migrationContext), - createNodesV2, - options.project, - { skipProjectFilter: skipProjectFilterFactory(tree) } - ); - const migratedBuildProjectsLegacy = - await migrateExecutorToPlugin( - tree, - projectGraph, - '@nrwl/webpack:webpack', - '@nx/webpack/plugin', - (targetName) => ({ - buildTargetName: targetName, - previewTargetName: 'preview', - serveStaticTargetName: 'serve-static', - serveTargetName: 'serve', - }), - buildPostTargetTransformerFactory(migrationContext), - createNodesV2, - options.project, - { skipProjectFilter: skipProjectFilterFactory(tree) } - ); - - // serve - const migratedServeProjects = - await migrateExecutorToPlugin( + const migratedProjects = + await migrateProjectExecutorsToPlugin( tree, projectGraph, - '@nx/webpack:dev-server', '@nx/webpack/plugin', - (targetName) => ({ - buildTargetName: 'build', - previewTargetName: 'preview', - serveStaticTargetName: 'serve-static', - serveTargetName: targetName, - }), - servePostTargetTransformerFactory(migrationContext), createNodesV2, - options.project, - { skipProjectFilter: skipProjectFilterFactory(tree) } - ); - const migratedServeProjectsLegacy = - await migrateExecutorToPlugin( - tree, - projectGraph, - '@nrwl/webpack:dev-server', - '@nx/webpack/plugin', - (targetName) => ({ + { buildTargetName: 'build', previewTargetName: 'preview', serveStaticTargetName: 'serve-static', - serveTargetName: targetName, - }), - servePostTargetTransformerFactory(migrationContext), - createNodesV2, - options.project, - { skipProjectFilter: skipProjectFilterFactory(tree) } + serveTargetName: 'serve', + }, + [ + { + executors: ['@nx/webpack:webpack', '@nrwl/webpack:webpack'], + postTargetTransformer: + buildPostTargetTransformerFactory(migrationContext), + targetPluginOptionMapper: (target) => ({ buildTargetName: target }), + skipProjectFilter: skipProjectFilterFactory(tree), + }, + { + executors: ['@nx/webpack:dev-server', '@nrwl/webpack:dev-server'], + postTargetTransformer: + servePostTargetTransformerFactory(migrationContext), + targetPluginOptionMapper: (target) => ({ serveTargetName: target }), + skipProjectFilter: skipProjectFilterFactory(tree), + }, + ], + options.project ); - const migratedProjects = - migratedBuildProjects.size + - migratedBuildProjectsLegacy.size + - migratedServeProjects.size + - migratedServeProjectsLegacy.size; - - if (migratedProjects === 0) { + if (migratedProjects.size === 0) { throw new Error('Could not find any targets to migrate.'); }