From bdde0ddc9835fa971e9aae69faf7248367288344 Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Tue, 31 Oct 2023 14:21:17 -0400 Subject: [PATCH] chore(devkit): add util for migrating to a plugin (#19942) --- ...-project-configuration-with-plugin.spec.ts | 447 ++++++++++++++++++ ...place-project-configuration-with-plugin.ts | 206 ++++++++ packages/nx/src/devkit-internals.ts | 1 + .../__snapshots__/generator.spec.ts.snap | 58 ++- .../update-17-2-0/add-__dirName__-plugin.ts | 23 + .../create-nodes-plugin/generator.spec.ts | 26 +- .../create-nodes-plugin/generator.ts | 21 +- 7 files changed, 749 insertions(+), 33 deletions(-) create mode 100644 packages/devkit/src/utils/replace-project-configuration-with-plugin.spec.ts create mode 100644 packages/devkit/src/utils/replace-project-configuration-with-plugin.ts create mode 100644 tools/workspace-plugin/src/generators/create-nodes-plugin/files/src/migrations/update-17-2-0/add-__dirName__-plugin.ts diff --git a/packages/devkit/src/utils/replace-project-configuration-with-plugin.spec.ts b/packages/devkit/src/utils/replace-project-configuration-with-plugin.spec.ts new file mode 100644 index 0000000000..6da602909d --- /dev/null +++ b/packages/devkit/src/utils/replace-project-configuration-with-plugin.spec.ts @@ -0,0 +1,447 @@ +import { Tree } from 'nx/src/generators/tree'; +import { CreateNodes } from 'nx/src/utils/nx-plugin'; +import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace'; +import { + addProjectConfiguration, + readProjectConfiguration, +} from 'nx/src/generators/utils/project-configuration'; + +import { replaceProjectConfigurationsWithPlugin } from './replace-project-configuration-with-plugin'; + +describe('replaceProjectConfigurationsWithPlugin', () => { + let tree: Tree; + let createNodes: CreateNodes; + + beforeEach(async () => { + tree = createTreeWithEmptyWorkspace(); + tree.write('proj/file.txt', ''); + createNodes = [ + 'proj/file.txt', + () => ({ + projects: { + proj: { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + dependsOn: ['^build-base'], + inputs: ['default', '^default'], + outputs: ['{options.output}', '{projectRoot}/outputs'], + options: { + configFile: 'file.txt', + }, + configurations: { + production: { + configFile: 'file.prod.txt', + }, + }, + }, + }, + }, + }, + }), + ]; + }); + + it('should not update the target when it uses a different executor', async () => { + const buildTarget = { + executor: 'nx:run-script', + inputs: ['default', '^default'], + outputs: ['{options.output}', '{projectRoot}/outputs'], + options: { + configFile: 'file.txt', + }, + }; + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: buildTarget, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual( + buildTarget + ); + }); + + describe('options', () => { + it('should be removed when there are no other options', async () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + inputs: ['default', '^default'], + outputs: ['{options.output}', '{projectRoot}/outputs'], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect( + readProjectConfiguration(tree, 'proj').targets.build + ).toBeUndefined(); + }); + + it('should not be removed when there are other options', async () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + inputs: ['default', '^default'], + outputs: ['{options.output}', '{projectRoot}/outputs'], + options: { + configFile: 'file.txt', + watch: false, + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + options: { + watch: false, + }, + }); + }); + }); + + describe('inputs', () => { + it('should not be removed if there are additional inputs', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + inputs: ['default', '^default', '{workspaceRoot}/file.txt'], + outputs: ['{options.output}', '{projectRoot}/outputs'], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + inputs: ['default', '^default', '{workspaceRoot}/file.txt'], + }); + }); + + it('should not be removed if there are additional inputs which are objects', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + inputs: [ + 'default', + '^default', + { + env: 'HOME', + }, + ], + outputs: ['{options.output}', '{projectRoot}/outputs'], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + inputs: [ + 'default', + '^default', + { + env: 'HOME', + }, + ], + }); + }); + + it('should not be removed if there are less inputs', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + inputs: ['default'], + outputs: ['{options.output}', '{projectRoot}/outputs'], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + inputs: ['default'], + }); + }); + }); + + describe('outputs', () => { + it('should not be removed if there are additional outputs', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + inputs: ['default', '^default'], + outputs: [ + '{options.output}', + '{projectRoot}/outputs', + '{projectRoot}/more-outputs', + ], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + outputs: [ + '{options.output}', + '{projectRoot}/outputs', + '{projectRoot}/more-outputs', + ], + }); + }); + + it('should not be removed if there are less outputs', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + outputs: ['{options.output}'], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + outputs: ['{options.output}'], + }); + }); + }); + + describe('dependsOn', () => { + it('should be removed when it is the same', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + dependsOn: ['^build-base'], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect( + readProjectConfiguration(tree, 'proj').targets.build + ).toBeUndefined(); + }); + + it('should not be removed when there are more dependent tasks', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + dependsOn: ['^build-base', 'prebuild'], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + dependsOn: ['^build-base', 'prebuild'], + }); + }); + + it('should not be removed when there are less dependent tasks', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + dependsOn: [], + options: { + configFile: 'file.txt', + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + dependsOn: [], + }); + }); + }); + + describe('defaultConfiguration', () => { + it('should not be removed when the defaultConfiguration is different', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + options: { + configFile: 'file.txt', + }, + defaultConfiguration: 'other', + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + defaultConfiguration: 'other', + }); + }); + }); + + describe('configurations', () => { + it('should not be removed when an additional configuration is defined', () => { + addProjectConfiguration(tree, 'proj', { + root: 'proj', + targets: { + build: { + executor: 'nx:run-commands', + options: { + configFile: 'file.txt', + }, + configurations: { + other: { + configFile: 'other-file.txt', + }, + }, + }, + }, + }); + + replaceProjectConfigurationsWithPlugin( + tree, + new Map([['proj', 'proj']]), + 'plugin-path', + createNodes, + {} + ); + + expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({ + configurations: { + other: { + configFile: 'other-file.txt', + }, + }, + }); + }); + }); +}); diff --git a/packages/devkit/src/utils/replace-project-configuration-with-plugin.ts b/packages/devkit/src/utils/replace-project-configuration-with-plugin.ts new file mode 100644 index 0000000000..fc3aedcae3 --- /dev/null +++ b/packages/devkit/src/utils/replace-project-configuration-with-plugin.ts @@ -0,0 +1,206 @@ +import type { + ProjectConfiguration, + TargetConfiguration, +} from 'nx/src/config/workspace-json-project-json'; +import type { Tree } from 'nx/src/generators/tree'; +import type { CreateNodes } from 'nx/src/utils/nx-plugin'; +import { requireNx } from '../../nx'; +const { + readNxJson, + updateNxJson, + glob, + hashObject, + findProjectForPath, + readProjectConfiguration, + updateProjectConfiguration, +} = requireNx(); + +export function replaceProjectConfigurationsWithPlugin( + tree: Tree, + rootMappings: Map, + pluginPath: string, + createNodes: CreateNodes, + pluginOptions: T +): void { + const nxJson = readNxJson(tree); + const hasPlugin = nxJson.plugins?.some((p) => + typeof p === 'string' ? p === pluginPath : p.plugin === pluginPath + ); + + if (hasPlugin) { + return; + } + + nxJson.plugins ??= []; + nxJson.plugins.push({ + plugin: pluginPath, + options: pluginOptions, + }); + updateNxJson(tree, nxJson); + + const [pluginGlob, createNodesFunction] = createNodes; + const configFiles = glob(tree, [pluginGlob]); + + for (const configFile of configFiles) { + try { + const projectName = findProjectForPath(configFile, rootMappings); + const projectConfig = readProjectConfiguration(tree, projectName); + const nodes = createNodesFunction(configFile, pluginOptions, { + workspaceRoot: tree.root, + nxJsonConfiguration: readNxJson(tree), + }); + const node = nodes.projects[Object.keys(nodes.projects)[0]]; + + for (const [targetName, targetConfig] of Object.entries(node.targets)) { + const targetFromProjectConfig = projectConfig.targets[targetName]; + + if (targetFromProjectConfig.executor !== targetConfig.executor) { + continue; + } + + const targetFromCreateNodes = node.targets[targetName]; + + removeConfigurationDefinedByPlugin( + targetName, + targetFromProjectConfig, + targetFromCreateNodes, + projectConfig + ); + } + + updateProjectConfiguration(tree, projectName, projectConfig); + } catch (e) { + console.error(e); + } + } +} + +function removeConfigurationDefinedByPlugin( + targetName: string, + targetFromProjectConfig: TargetConfiguration, + targetFromCreateNodes: TargetConfiguration, + projectConfig: ProjectConfiguration +) { + // Executor + delete targetFromProjectConfig.executor; + + // Default Configuration + if ( + targetFromProjectConfig.defaultConfiguration === + targetFromCreateNodes.defaultConfiguration + ) { + delete targetFromProjectConfig.defaultConfiguration; + } + + // Cache + if (targetFromProjectConfig.cache === targetFromCreateNodes.cache) { + delete targetFromProjectConfig.cache; + } + + // Depends On + if ( + targetFromProjectConfig.dependsOn && + shouldRemoveArrayProperty( + targetFromProjectConfig.dependsOn, + targetFromCreateNodes.dependsOn + ) + ) { + delete targetFromProjectConfig.dependsOn; + } + + // Outputs + if ( + targetFromProjectConfig.outputs && + shouldRemoveArrayProperty( + targetFromProjectConfig.outputs, + targetFromCreateNodes.outputs + ) + ) { + delete targetFromProjectConfig.outputs; + } + + // Inputs + if ( + targetFromProjectConfig.inputs && + shouldRemoveArrayProperty( + targetFromProjectConfig.inputs, + targetFromCreateNodes.inputs + ) + ) { + delete targetFromProjectConfig.inputs; + } + + // Options + for (const [optionName, optionValue] of Object.entries( + targetFromProjectConfig.options ?? {} + )) { + if (targetFromCreateNodes.options[optionName] === optionValue) { + delete targetFromProjectConfig.options[optionName]; + } + } + if (Object.keys(targetFromProjectConfig.options).length === 0) { + delete targetFromProjectConfig.options; + } + + // Configurations + for (const [configName, configOptions] of Object.entries( + targetFromProjectConfig.configurations ?? {} + )) { + for (const [optionName, optionValue] of Object.entries(configOptions)) { + if ( + targetFromCreateNodes.configurations?.[configName]?.[optionName] === + optionValue + ) { + delete targetFromProjectConfig.configurations[configName][optionName]; + } + } + if (Object.keys(configOptions).length === 0) { + delete targetFromProjectConfig.configurations[configName]; + } + } + if (Object.keys(targetFromProjectConfig.configurations ?? {}).length === 0) { + delete targetFromProjectConfig.configurations; + } + + if (Object.keys(targetFromProjectConfig).length === 0) { + delete projectConfig.targets[targetName]; + } +} + +function shouldRemoveArrayProperty( + arrayValuesFromProjectConfiguration: (object | string)[], + arrayValuesFromCreateNodes: (object | string)[] +) { + const setOfArrayValuesFromProjectConfiguration = new Set( + arrayValuesFromProjectConfiguration + ); + loopThroughArrayValuesFromCreateNodes: for (const arrayValueFromCreateNodes of arrayValuesFromCreateNodes) { + if (typeof arrayValueFromCreateNodes === 'string') { + if ( + !setOfArrayValuesFromProjectConfiguration.has(arrayValueFromCreateNodes) + ) { + // If the inputs from the project configuration is missing an input from createNodes it was removed + return false; + } else { + setOfArrayValuesFromProjectConfiguration.delete( + arrayValueFromCreateNodes + ); + } + } else { + for (const arrayValue of setOfArrayValuesFromProjectConfiguration.values()) { + if ( + typeof arrayValue !== 'string' && + hashObject(arrayValue) === hashObject(arrayValueFromCreateNodes) + ) { + setOfArrayValuesFromProjectConfiguration.delete(arrayValue); + // Continue the outer loop, breaking out of this loop + continue loopThroughArrayValuesFromCreateNodes; + } + } + // If an input was not matched, that means the input was removed + return false; + } + } + // If there are still inputs in the project configuration, they have added additional inputs + return setOfArrayValuesFromProjectConfiguration.size === 0; +} diff --git a/packages/nx/src/devkit-internals.ts b/packages/nx/src/devkit-internals.ts index ed7d2be4e3..b8e3cca1f8 100644 --- a/packages/nx/src/devkit-internals.ts +++ b/packages/nx/src/devkit-internals.ts @@ -14,6 +14,7 @@ export { sortObjectByKeys } from './utils/object-sort'; export { stripIndent } from './utils/logger'; export { readModulePackageJson } from './utils/package-json'; export { splitByColons } from './utils/split-target'; +export { hashObject } from './hasher/file-hasher'; export { createProjectRootMappingsFromProjectConfigurations, findProjectForPath, diff --git a/tools/workspace-plugin/src/generators/create-nodes-plugin/__snapshots__/generator.spec.ts.snap b/tools/workspace-plugin/src/generators/create-nodes-plugin/__snapshots__/generator.spec.ts.snap index e443843218..214e2bb562 100644 --- a/tools/workspace-plugin/src/generators/create-nodes-plugin/__snapshots__/generator.spec.ts.snap +++ b/tools/workspace-plugin/src/generators/create-nodes-plugin/__snapshots__/generator.spec.ts.snap @@ -6,7 +6,7 @@ exports[`create-nodes-plugin/generator generator should run successfully 1`] = ` CreateNodesContext, TargetConfiguration, } from '@nx/devkit'; -import { basename, dirname, extname, join, resolve } from "path"; +import { basename, dirname, extname, join, resolve } from 'path'; import { registerTsProject } from '@nx/js/src/internal'; import { getRootTsConfigPath } from '@nx/js'; @@ -45,7 +45,7 @@ export const createNodes: CreateNodes = [ configFilePath, projectRoot, options, - context, + context ), }, }, @@ -57,12 +57,9 @@ function buildEslintTargets( configFilePath: string, projectRoot: string, options: EslintPluginOptions, - context: CreateNodesContext, + context: CreateNodesContext ) { - const eslintConfig = getEslintConfig( - configFilePath, - context - ); + const eslintConfig = getEslintConfig(configFilePath, context); const targetDefaults = readTargetDefaultsForTarget( options.targetName, @@ -72,10 +69,7 @@ function buildEslintTargets( const namedInputs = getNamedInputs(projectRoot, context); - const targets: Record< - string, - TargetConfiguration - > = {}; + const targets: Record> = {}; const baseTargetConfig: TargetConfiguration = { executor: 'executorName', @@ -92,9 +86,7 @@ function buildEslintTargets( targetDefaults?.inputs ?? 'production' in namedInputs ? ['default', '^production'] : ['default', '^default'], - outputs: - targetDefaults?.outputs ?? - getOutputs(projectRoot), + outputs: targetDefaults?.outputs ?? getOutputs(projectRoot), options: { ...baseTargetConfig.options, }, @@ -129,9 +121,7 @@ function getEslintConfig( return module.default ?? module; } -function getOutputs( - projectRoot: string, -): string[] { +function getOutputs(projectRoot: string): string[] { function getOutput(path: string): string { if (path.startsWith('..')) { return join('{workspaceRoot}', join(projectRoot, path)); @@ -179,7 +169,7 @@ describe('@nx/eslint/plugin', () => { }); it('should create nodes', () => { - mockEslintConfig({}) + mockEslintConfig({}); const nodes = createNodesFunction( 'TODO', { @@ -192,10 +182,7 @@ describe('@nx/eslint/plugin', () => { }); }); - -function mockEslintConfig( - config: any -) { +function mockEslintConfig(config: any) { jest.mock( 'TODO', () => ({ @@ -208,3 +195,30 @@ function mockEslintConfig( } " `; + +exports[`create-nodes-plugin/generator generator should run successfully 3`] = ` +"import { formatFiles, getProjects, Tree } from '@nx/devkit'; +import { createNodes } from '../../plugins/plugin'; + +import { createProjectRootMappingsFromProjectConfigurations } from 'nx/src/project-graph/utils/find-project-for-path'; +import { replaceProjectConfigurationsWithPlugin } from '@nx/devkit/src/utils/replace-project-configuration-with-plugin'; + +export default async function update(tree: Tree) { + const proj = Object.fromEntries(getProjects(tree).entries()); + + const rootMappings = createProjectRootMappingsFromProjectConfigurations(proj); + + replaceProjectConfigurationsWithPlugin( + tree, + rootMappings, + '@nx/eslint/plugin', + createNodes, + { + targetName: 'TODO', + } + ); + + await formatFiles(tree); +} +" +`; diff --git a/tools/workspace-plugin/src/generators/create-nodes-plugin/files/src/migrations/update-17-2-0/add-__dirName__-plugin.ts b/tools/workspace-plugin/src/generators/create-nodes-plugin/files/src/migrations/update-17-2-0/add-__dirName__-plugin.ts new file mode 100644 index 0000000000..4d7ad936ef --- /dev/null +++ b/tools/workspace-plugin/src/generators/create-nodes-plugin/files/src/migrations/update-17-2-0/add-__dirName__-plugin.ts @@ -0,0 +1,23 @@ +import { formatFiles, getProjects, Tree } from '@nx/devkit'; +import { createNodes } from '../../plugins/plugin'; + +import { createProjectRootMappingsFromProjectConfigurations } from 'nx/src/project-graph/utils/find-project-for-path'; +import { replaceProjectConfigurationsWithPlugin } from '@nx/devkit/src/utils/replace-project-configuration-with-plugin'; + +export default async function update(tree: Tree) { + const proj = Object.fromEntries(getProjects(tree).entries()); + + const rootMappings = createProjectRootMappingsFromProjectConfigurations(proj); + + replaceProjectConfigurationsWithPlugin( + tree, + rootMappings, + '@nx/<%= dirName %>/plugin', + createNodes, + { + targetName: 'TODO', + } + ); + + await formatFiles(tree); +} diff --git a/tools/workspace-plugin/src/generators/create-nodes-plugin/generator.spec.ts b/tools/workspace-plugin/src/generators/create-nodes-plugin/generator.spec.ts index b0d375c463..19ac42bcb2 100644 --- a/tools/workspace-plugin/src/generators/create-nodes-plugin/generator.spec.ts +++ b/tools/workspace-plugin/src/generators/create-nodes-plugin/generator.spec.ts @@ -1,25 +1,43 @@ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { Tree } from '@nx/devkit'; +import { addProjectConfiguration, Tree, writeJson } from '@nx/devkit'; -import { generatorGenerator, GeneratorGeneratorSchema } from './generator'; +import { generatorGenerator } from './generator'; +import { setCwd } from '@nx/devkit/internal-testing-utils'; describe('create-nodes-plugin/generator generator', () => { let tree: Tree; - const options: GeneratorGeneratorSchema = {}; beforeEach(() => { tree = createTreeWithEmptyWorkspace(); + addProjectConfiguration(tree, 'eslint', { + root: 'packages/eslint', + targets: { + build: {}, + }, + }); + + writeJson(tree, 'packages/eslint/package.json', {}); + jest.spyOn(process, 'cwd').mockReturnValue('/virtual/packages/eslint'); + + setCwd('packages/eslint'); }); it('should run successfully', async () => { - await generatorGenerator(tree, options); + await generatorGenerator(tree); expect( tree.read('packages/eslint/src/plugins/plugin.ts').toString() ).toMatchSnapshot(); expect( tree.read('packages/eslint/src/plugins/plugin.spec.ts').toString() ).toMatchSnapshot(); + expect( + tree + .read( + 'packages/eslint/src/migrations/update-17-2-0/add-eslint-plugin.ts' + ) + .toString() + ).toMatchSnapshot(); }); }); diff --git a/tools/workspace-plugin/src/generators/create-nodes-plugin/generator.ts b/tools/workspace-plugin/src/generators/create-nodes-plugin/generator.ts index f4c4504573..5bee926ea1 100644 --- a/tools/workspace-plugin/src/generators/create-nodes-plugin/generator.ts +++ b/tools/workspace-plugin/src/generators/create-nodes-plugin/generator.ts @@ -1,20 +1,27 @@ -import { generateFiles, names, Tree } from '@nx/devkit'; +import { formatFiles, generateFiles, names, Tree } from '@nx/devkit'; import { basename, join, relative } from 'path'; +import migrationGenerator from '@nx/plugin/src/generators/migration/migration'; -export interface GeneratorGeneratorSchema {} - -export async function generatorGenerator( - tree: Tree, - options: GeneratorGeneratorSchema -) { +export async function generatorGenerator(tree: Tree) { const cwd = process.cwd(); const { className, propertyName } = names(basename(cwd)); + await migrationGenerator(tree, { + name: `add-${basename(cwd)}-plugin`, + packageVersion: '17.2.0-beta.0', + description: `Add @nx/${basename(cwd)}/plugin`, + nameAndDirectoryFormat: 'as-provided', + directory: `src/migrations/update-17-2-0`, + skipFormat: true, + }); + generateFiles(tree, join(__dirname, 'files'), relative(tree.root, cwd), { dirName: basename(cwd), className, propertyName, }); + + await formatFiles(tree); } export default generatorGenerator;