diff --git a/docs/generated/packages/nx-plugin/generators/executor.json b/docs/generated/packages/nx-plugin/generators/executor.json index f149c115e8ef35..bf88b95a5ab945 100644 --- a/docs/generated/packages/nx-plugin/generators/executor.json +++ b/docs/generated/packages/nx-plugin/generators/executor.json @@ -48,7 +48,7 @@ "skipLintChecks": { "type": "boolean", "default": false, - "description": "Do not eslint configuration for plugin json files." + "description": "Do not add an eslint configuration for plugin json files." } }, "required": ["project", "name"], diff --git a/docs/generated/packages/nx-plugin/generators/generator.json b/docs/generated/packages/nx-plugin/generators/generator.json index 225102d9d2bee0..c1b0e78ba7b8bd 100644 --- a/docs/generated/packages/nx-plugin/generators/generator.json +++ b/docs/generated/packages/nx-plugin/generators/generator.json @@ -44,7 +44,13 @@ "skipLintChecks": { "type": "boolean", "default": false, - "description": "Do not eslint configuration for plugin json files." + "description": "Do not add an eslint configuration for plugin json files." + }, + "skipFormat": { + "type": "boolean", + "default": false, + "description": "Do not format files with prettier.", + "x-priority": "internal" } }, "required": ["project", "name"], diff --git a/docs/generated/packages/nx-plugin/generators/plugin-lint-checks.json b/docs/generated/packages/nx-plugin/generators/plugin-lint-checks.json index 5ff3727e42ca2d..b8d94bed459657 100644 --- a/docs/generated/packages/nx-plugin/generators/plugin-lint-checks.json +++ b/docs/generated/packages/nx-plugin/generators/plugin-lint-checks.json @@ -14,6 +14,12 @@ "description": "Which project should be the configuration be added to?", "$default": { "$source": "projectName" }, "x-priority": "important" + }, + "skipFormat": { + "type": "boolean", + "x-priority": "internal", + "description": "Skip formatting files with prettier.", + "default": false } }, "required": ["projectName"], diff --git a/e2e/nx-plugin/src/nx-plugin.test.ts b/e2e/nx-plugin/src/nx-plugin.test.ts index 4b72ec603b1297..5c32f5392e9293 100644 --- a/e2e/nx-plugin/src/nx-plugin.test.ts +++ b/e2e/nx-plugin/src/nx-plugin.test.ts @@ -48,21 +48,6 @@ describe('Nx Plugin', () => { }); }, 90000); - // the test invoke ensureNxProject, which points to @nrwl/workspace collection - // which walks up the directory to find it in the next repo itself, so it - // doesn't use the collection we are building - // we should change it to point to the right collection using relative path - // TODO: Re-enable this to work with pnpm - it(`should run the plugin's e2e tests`, async () => { - const plugin = uniq('plugin-name'); - runCLI( - `generate @nrwl/nx-plugin:plugin ${plugin} --linter=eslint --e2eTestRunner jest` - ); - const e2eResults = runCLI(`e2e ${plugin}-e2e`); - expect(e2eResults).toContain('Successfully ran target e2e'); - expect(await killPorts()).toBeTruthy(); - }, 250000); - it('should be able to generate a migration', async () => { const plugin = uniq('plugin'); const version = '1.0.0'; diff --git a/e2e/utils/create-project-utils.ts b/e2e/utils/create-project-utils.ts index 706edbc3a957e4..d60ac9e50e352d 100644 --- a/e2e/utils/create-project-utils.ts +++ b/e2e/utils/create-project-utils.ts @@ -215,12 +215,10 @@ export function runCreateWorkspace( export function runCreatePlugin( name: string, { - pluginName, packageManager, extraArgs, useDetectedPm = false, }: { - pluginName?: string; packageManager?: 'npm' | 'yarn' | 'pnpm'; extraArgs?: string; useDetectedPm?: boolean; @@ -232,11 +230,7 @@ export function runCreatePlugin( let command = `${ pm.runUninstalledPackage - } create-nx-plugin@${getPublishedVersion()} ${name}`; - - if (pluginName) { - command += ` --pluginName=${pluginName} --no-nxCloud`; - } + } create-nx-plugin@${getPublishedVersion()} ${name} --no-nxCloud`; if (packageManager && !useDetectedPm) { command += ` --package-manager=${packageManager}`; diff --git a/e2e/workspace-create/src/create-nx-plugin.test.ts b/e2e/workspace-create/src/create-nx-plugin.test.ts index 4611479b83609a..6ffb3b967eee84 100644 --- a/e2e/workspace-create/src/create-nx-plugin.test.ts +++ b/e2e/workspace-create/src/create-nx-plugin.test.ts @@ -14,13 +14,11 @@ describe('create-nx-plugin', () => { afterEach(() => cleanupProject()); - it('should be able to create a plugin repo and run plugin e2e', () => { - const wsName = uniq('ws-plugin'); + it('should be able to create a plugin repo build a plugin', () => { const pluginName = uniq('plugin'); - runCreatePlugin(wsName, { + runCreatePlugin(pluginName, { packageManager, - pluginName, }); checkFilesExist( @@ -31,6 +29,12 @@ describe('create-nx-plugin', () => { `executors.json` ); - expect(() => runCLI(`e2e e2e`)).not.toThrow(); + runCLI(`build ${pluginName}`); + + checkFilesExist( + `dist/package.json`, + `dist/generators.json`, + `dist/executors.json` + ); }); }); diff --git a/packages/nx-plugin/src/generators/e2e-project/files/tests/__pluginName__.spec.ts__tmpl__ b/packages/nx-plugin/src/generators/e2e-project/files/tests/__pluginName__.spec.ts__tmpl__ index 263d10f7838ebb..cd101be7877143 100644 --- a/packages/nx-plugin/src/generators/e2e-project/files/tests/__pluginName__.spec.ts__tmpl__ +++ b/packages/nx-plugin/src/generators/e2e-project/files/tests/__pluginName__.spec.ts__tmpl__ @@ -27,11 +27,11 @@ describe('<%= pluginName %> e2e', () => { // Add some tests here to check that your plugin functionality works as expected. // A sample test is included below to give you some ideas. - // - // it('should be able to build generated projects', async () => { - // const name = uniq('proj') - // await runNxCommandAsync(`generate <%= npmPackageName %>:{my-generator} --name ${name}`); - // expect(() => runNxCommand('build ${proj}')).not.toThrow(); - // expect(() => checkFilesExist([`dist/${name}/index.js`])).not.toThrow(); - // }); + xit('should be able to build generated projects', async () => { + const name = uniq('proj'); + const generator = 'PLACEHOLDER'; + await runNxCommandAsync(`generate <%= npmPackageName %>:${generator} --name ${name}`); + expect(() => runNxCommand('build ${proj}')).not.toThrow(); + expect(() => checkFilesExist([`dist/${name}/index.js`])).not.toThrow(); + }); }); diff --git a/packages/nx-plugin/src/generators/executor/executor.spec.ts b/packages/nx-plugin/src/generators/executor/executor.spec.ts index dfeca846f5a775..ba46b4227c6b59 100644 --- a/packages/nx-plugin/src/generators/executor/executor.spec.ts +++ b/packages/nx-plugin/src/generators/executor/executor.spec.ts @@ -158,7 +158,7 @@ describe('NxPlugin Executor Generator', () => { * you can consume workspace details from the context. */ export const myExecutorHasher: CustomHasher = async (task, context) => { - return context.hasher.hashTask(task) + return context.hasher.hashTask(task); }; export default myExecutorHasher; diff --git a/packages/nx-plugin/src/generators/executor/files/hasher/__fileName__/hasher.ts__tmpl__ b/packages/nx-plugin/src/generators/executor/files/hasher/__fileName__/hasher.ts__tmpl__ index 96f2a77e130605..90639ebc53b6a3 100644 --- a/packages/nx-plugin/src/generators/executor/files/hasher/__fileName__/hasher.ts__tmpl__ +++ b/packages/nx-plugin/src/generators/executor/files/hasher/__fileName__/hasher.ts__tmpl__ @@ -6,7 +6,7 @@ import { CustomHasher } from '@nx/devkit'; * you can consume workspace details from the context. */ export const <%=propertyName%>Hasher: CustomHasher = async (task, context) => { - return context.hasher.hashTask(task) + return context.hasher.hashTask(task); }; export default <%=propertyName%>Hasher; diff --git a/packages/nx-plugin/src/generators/executor/schema.json b/packages/nx-plugin/src/generators/executor/schema.json index fb89cc3a8c5d97..e7d3f1b9467a28 100644 --- a/packages/nx-plugin/src/generators/executor/schema.json +++ b/packages/nx-plugin/src/generators/executor/schema.json @@ -50,7 +50,7 @@ "skipLintChecks": { "type": "boolean", "default": false, - "description": "Do not eslint configuration for plugin json files." + "description": "Do not add an eslint configuration for plugin json files." } }, "required": ["project", "name"], diff --git a/packages/nx-plugin/src/generators/generator/generator.spec.ts b/packages/nx-plugin/src/generators/generator/generator.spec.ts index e7b2d904ae048c..96df48903232d3 100644 --- a/packages/nx-plugin/src/generators/generator/generator.spec.ts +++ b/packages/nx-plugin/src/generators/generator/generator.spec.ts @@ -65,6 +65,21 @@ describe('NxPlugin Generator Generator', () => { ); }); + it('should throw if recreating an existing generator', async () => { + await generatorGenerator(tree, { + project: projectName, + name: 'my-generator', + unitTestRunner: 'jest', + }); + expect( + generatorGenerator(tree, { + project: projectName, + name: 'my-generator', + unitTestRunner: 'jest', + }) + ).rejects.toThrow('Generator my-generator already exists'); + }); + it('should update generators.json with the same path as where the generator files folder is located', async () => { const generatorName = 'myGenerator'; const generatorFileName = 'my-generator'; diff --git a/packages/nx-plugin/src/generators/generator/generator.ts b/packages/nx-plugin/src/generators/generator/generator.ts index de62f26ea48edc..a54670b6d17941 100644 --- a/packages/nx-plugin/src/generators/generator/generator.ts +++ b/packages/nx-plugin/src/generators/generator/generator.ts @@ -1,4 +1,10 @@ -import { GeneratorsJson, joinPathFragments, Tree, writeJson } from '@nx/devkit'; +import { + formatFiles, + GeneratorsJson, + joinPathFragments, + Tree, + writeJson, +} from '@nx/devkit'; import { convertNxGenerator, generateFiles, @@ -10,6 +16,7 @@ import { } from '@nx/devkit'; import { PackageJson } from 'nx/src/utils/package-json'; import * as path from 'path'; +import { hasGenerator } from '../../utils/has-generator'; import pluginLintCheckGenerator from '../lint-checks/generator'; import type { Schema } from './schema'; @@ -86,7 +93,8 @@ export async function createGeneratorsJson( host: Tree, projectRoot: string, projectName: string, - skipLintChecks?: boolean + skipLintChecks?: boolean, + skipFormat?: boolean ) { updateJson( host, @@ -106,6 +114,7 @@ export async function createGeneratorsJson( if (!skipLintChecks) { await pluginLintCheckGenerator(host, { projectName, + skipFormat, }); } } @@ -129,9 +138,9 @@ async function updateGeneratorJson(host: Tree, options: NormalizedSchema) { host, options.projectRoot, options.project, - options.skipLintChecks + options.skipLintChecks, + options.skipFormat ); - console.log('CREATED GENERATORS.JSON'); } updateJson(host, generatorsPath, (json) => { @@ -153,10 +162,17 @@ async function updateGeneratorJson(host: Tree, options: NormalizedSchema) { export async function generatorGenerator(host: Tree, schema: Schema) { const options = normalizeOptions(host, schema); + if (hasGenerator(host, options.project, options.name)) { + throw new Error(`Generator ${options.name} already exists.`); + } addFiles(host, options); await updateGeneratorJson(host, options); + + if (!options.skipFormat) { + await formatFiles(host); + } } export default generatorGenerator; diff --git a/packages/nx-plugin/src/generators/generator/schema.d.ts b/packages/nx-plugin/src/generators/generator/schema.d.ts index de575df6129595..653e5d118012d4 100644 --- a/packages/nx-plugin/src/generators/generator/schema.d.ts +++ b/packages/nx-plugin/src/generators/generator/schema.d.ts @@ -4,4 +4,5 @@ export interface Schema { description?: string; unitTestRunner: 'jest' | 'none'; skipLintChecks?: boolean; + skipFormat?: boolean; } diff --git a/packages/nx-plugin/src/generators/generator/schema.json b/packages/nx-plugin/src/generators/generator/schema.json index d7a945ee8d5db4..64797fec52f101 100644 --- a/packages/nx-plugin/src/generators/generator/schema.json +++ b/packages/nx-plugin/src/generators/generator/schema.json @@ -46,7 +46,13 @@ "skipLintChecks": { "type": "boolean", "default": false, - "description": "Do not eslint configuration for plugin json files." + "description": "Do not add an eslint configuration for plugin json files." + }, + "skipFormat": { + "type": "boolean", + "default": false, + "description": "Do not format files with prettier.", + "x-priority": "internal" } }, "required": ["project", "name"], diff --git a/packages/nx-plugin/src/generators/lint-checks/generator.ts b/packages/nx-plugin/src/generators/lint-checks/generator.ts index 56fd064972e2fb..8f0a43edd148c1 100644 --- a/packages/nx-plugin/src/generators/lint-checks/generator.ts +++ b/packages/nx-plugin/src/generators/lint-checks/generator.ts @@ -1,5 +1,6 @@ import { addDependenciesToPackageJson, + formatFiles, joinPathFragments, logger, ProjectConfiguration, @@ -60,6 +61,11 @@ export default async function pluginLintCheckGenerator( {}, { 'jsonc-eslint-parser': jsoncEslintParserVersion } ); + + if (!options.skipFormat) { + await formatFiles(host); + } + return () => installTask; } @@ -186,27 +192,35 @@ function updateProjectEslintConfig( const eslintPath = `${options.root}/.eslintrc.json`; const eslintConfig = readJson(host, eslintPath); eslintConfig.overrides ??= []; - if ( - !eslintConfig.overrides.some( + let entry: ESLint.ConfigOverride = + eslintConfig.overrides.find( (x) => Object.keys(x.rules ?? {}).includes('@nx/nx/nx-plugin-checks') || Object.keys(x.rules ?? {}).includes('@nrwl/nx/nx-plugin-checks') - ) - ) { - eslintConfig.overrides.push({ - files: [ + ); + const newentry = !entry; + entry ??= { files: [] }; + entry.files = [ + ...new Set([ + ...(entry.files ?? []), + ...[ './package.json', packageJson.generators, packageJson.executors, packageJson.schematics, packageJson.builders, ].filter((f) => !!f), - parser: 'jsonc-eslint-parser', - rules: { - '@nx/nx/nx-plugin-checks': 'error', - }, - }); + ]), + ]; + entry.parser = 'jsonc-eslint-parser'; + entry.rules ??= { + '@nx/nx/nx-plugin-checks': 'error', + }; + + if (newentry) { + eslintConfig.overrides.push(entry); } + writeJson(host, eslintPath, eslintConfig); } diff --git a/packages/nx-plugin/src/generators/lint-checks/schema.d.ts b/packages/nx-plugin/src/generators/lint-checks/schema.d.ts index e268defed66be8..055d6f4c2baeff 100644 --- a/packages/nx-plugin/src/generators/lint-checks/schema.d.ts +++ b/packages/nx-plugin/src/generators/lint-checks/schema.d.ts @@ -1,3 +1,4 @@ export interface PluginLintChecksGeneratorSchema { projectName: string; + skipFormat?: boolean; } diff --git a/packages/nx-plugin/src/generators/lint-checks/schema.json b/packages/nx-plugin/src/generators/lint-checks/schema.json index 8120a6bcf56538..a82390b6a3c525 100644 --- a/packages/nx-plugin/src/generators/lint-checks/schema.json +++ b/packages/nx-plugin/src/generators/lint-checks/schema.json @@ -13,6 +13,12 @@ "$source": "projectName" }, "x-priority": "important" + }, + "skipFormat": { + "type": "boolean", + "x-priority": "internal", + "description": "Skip formatting files with prettier.", + "default": false } }, "required": ["projectName"] diff --git a/packages/nx-plugin/src/generators/migration/files/migration/__name__/__name__.spec.ts__tmpl__ b/packages/nx-plugin/src/generators/migration/files/migration/__name__/__name__.spec.ts__tmpl__ new file mode 100644 index 00000000000000..70e86d21509628 --- /dev/null +++ b/packages/nx-plugin/src/generators/migration/files/migration/__name__/__name__.spec.ts__tmpl__ @@ -0,0 +1,17 @@ +import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; +import { Tree } from '@nx/devkit'; + +import update from './<%= name %>'; + +describe('<%= name %> migration', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace({layout: 'apps-libs'}); + }); + + it('should run successfully', async () => { + await update(tree); + // ... expect changes made + }); +}); diff --git a/packages/nx-plugin/src/generators/preset/generator.spec.ts b/packages/nx-plugin/src/generators/preset/generator.spec.ts index 0b1c1fb7021474..b6e401ffca9d15 100644 --- a/packages/nx-plugin/src/generators/preset/generator.spec.ts +++ b/packages/nx-plugin/src/generators/preset/generator.spec.ts @@ -23,8 +23,7 @@ describe('preset generator', () => { const config = readProjectConfiguration(tree, 'my-plugin'); expect(config).toBeDefined(); const packageJson = readJson(tree, 'package.json'); - expect(packageJson.generators).toEqual('./generators.json'); - expect(packageJson.executors).toEqual('./executors.json'); + expect(packageJson.dependencies).toHaveProperty('@nx/devkit'); expect(readNxJson(tree).npmScope).not.toBeDefined(); }); }); diff --git a/packages/nx-plugin/src/generators/preset/generator.ts b/packages/nx-plugin/src/generators/preset/generator.ts index f4379c077e5eef..332b506a3cea6c 100644 --- a/packages/nx-plugin/src/generators/preset/generator.ts +++ b/packages/nx-plugin/src/generators/preset/generator.ts @@ -28,10 +28,6 @@ export default async function (tree: Tree, options: PresetGeneratorSchema) { e2eTestRunner: 'jest', }); - const { root } = readProjectConfiguration(tree, options.pluginName); - await createExecutorsJson(tree, root, options.pluginName); - await createGeneratorsJson(tree, root, options.pluginName); - removeNpmScope(tree); moveNxPluginToDevDeps(tree); diff --git a/packages/nx-plugin/src/utils/has-generator.ts b/packages/nx-plugin/src/utils/has-generator.ts new file mode 100644 index 00000000000000..ef9abe8a2fee25 --- /dev/null +++ b/packages/nx-plugin/src/utils/has-generator.ts @@ -0,0 +1,29 @@ +import { + GeneratorsJson, + joinPathFragments, + readJson, + readProjectConfiguration, + Tree, +} from '@nx/devkit'; +import { PackageJson } from 'nx/src/utils/package-json'; + +export function hasGenerator( + tree: Tree, + projectName: string, + generatorName: string +): boolean { + const project = readProjectConfiguration(tree, projectName); + const packageJson = readJson( + tree, + joinPathFragments(project.root, 'package.json') + ); + if (!packageJson.generators && !packageJson.schematics) { + return false; + } + const generatorsPath = joinPathFragments( + project.root, + packageJson.generators ?? packageJson.schematics + ); + const generatorsJson = readJson(tree, generatorsPath); + return generatorsJson.generators[generatorName] !== undefined; +}