From 8249ace5a2da6697d659095d9f89b101b8b28446 Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Fri, 13 Oct 2023 13:20:10 -0400 Subject: [PATCH] feat(nx-plugin): add as provided prompt for executor generator (#19602) --- .../packages/plugin/generators/executor.json | 20 ++- packages/devkit/internal-testing-utils.ts | 1 + .../artifact-name-and-directory-utils.spec.ts | 55 +++++++- .../artifact-name-and-directory-utils.ts | 40 ++++-- packages/plugin/generators.json | 2 +- packages/plugin/project.json | 2 +- .../src/generators/executor/executor.spec.ts | 120 +++++++++++++----- .../src/generators/executor/executor.ts | 87 ++++++++----- ...c.ts__tmpl__ => executor.spec.ts.template} | 0 ...ecutor.ts__tmpl__ => executor.ts.template} | 0 ...hema.d.ts__tmpl__ => schema.d.ts.template} | 0 ...hema.json__tmpl__ => schema.json.template} | 0 ...pec.ts__tmpl__ => hasher.spec.ts.template} | 0 .../hasher.ts__tmpl__ => hasher.ts.template} | 0 .../src/generators/executor/schema.d.ts | 6 +- .../src/generators/executor/schema.json | 16 ++- 16 files changed, 253 insertions(+), 96 deletions(-) rename packages/plugin/src/generators/executor/files/executor/{__fileName__/executor.spec.ts__tmpl__ => executor.spec.ts.template} (100%) rename packages/plugin/src/generators/executor/files/executor/{__fileName__/executor.ts__tmpl__ => executor.ts.template} (100%) rename packages/plugin/src/generators/executor/files/executor/{__fileName__/schema.d.ts__tmpl__ => schema.d.ts.template} (100%) rename packages/plugin/src/generators/executor/files/executor/{__fileName__/schema.json__tmpl__ => schema.json.template} (100%) rename packages/plugin/src/generators/executor/files/hasher/{__fileName__/hasher.spec.ts__tmpl__ => hasher.spec.ts.template} (100%) rename packages/plugin/src/generators/executor/files/hasher/{__fileName__/hasher.ts__tmpl__ => hasher.ts.template} (100%) diff --git a/docs/generated/packages/plugin/generators/executor.json b/docs/generated/packages/plugin/generators/executor.json index c051d667f6d52..f01e864586914 100644 --- a/docs/generated/packages/plugin/generators/executor.json +++ b/docs/generated/packages/plugin/generators/executor.json @@ -1,6 +1,6 @@ { "name": "executor", - "factory": "./src/generators/executor/executor", + "factory": "./src/generators/executor/executor#executorGeneratorInternal", "schema": { "$schema": "http://json-schema.org/schema", "cli": "nx", @@ -20,9 +20,7 @@ "type": "string", "description": "The name of the project.", "alias": "p", - "$default": { "$source": "projectName" }, - "x-prompt": "What is the name of the project for the executor?", - "x-priority": "important" + "$default": { "$source": "projectName" } }, "name": { "type": "string", @@ -31,6 +29,11 @@ "x-prompt": "What name would you like to use for the executor?", "x-priority": "important" }, + "directory": { + "type": "string", + "description": "The directory at which to create the executor file. When `--nameAndDirectoryFormat=as-provided`, it will be relative to the current working directory. Otherwise, it will be relative to the workspace root.", + "aliases": ["dir"] + }, "description": { "type": "string", "description": "Executor description." @@ -51,6 +54,11 @@ "default": false, "description": "Do not add an eslint configuration for plugin json files." }, + "nameAndDirectoryFormat": { + "description": "Whether to generate the component in the directory as provided, relative to the current working directory and ignoring the project (`as-provided`) or generate it using the project and directory relative to the workspace root (`derived`).", + "type": "string", + "enum": ["as-provided", "derived"] + }, "skipFormat": { "type": "boolean", "description": "Skip formatting files.", @@ -58,12 +66,12 @@ "x-priority": "internal" } }, - "required": ["project", "name"], + "required": ["name"], "additionalProperties": false, "presets": [] }, "description": "Create an executor for an Nx Plugin.", - "implementation": "/packages/plugin/src/generators/executor/executor.ts", + "implementation": "/packages/plugin/src/generators/executor/executor#executorGeneratorInternal.ts", "aliases": [], "hidden": false, "path": "/packages/plugin/src/generators/executor/schema.json", diff --git a/packages/devkit/internal-testing-utils.ts b/packages/devkit/internal-testing-utils.ts index edef8ec37b65e..a17b1b90ebf93 100644 --- a/packages/devkit/internal-testing-utils.ts +++ b/packages/devkit/internal-testing-utils.ts @@ -3,3 +3,4 @@ export * from 'nx/src/internal-testing-utils/assert-valid-migrations'; export * from 'nx/src/internal-testing-utils/run-migration-against-this-workspace'; export * from 'nx/src/internal-testing-utils/with-environment'; export * from 'nx/src/internal-testing-utils/temp-fs'; +export { setCwd } from './src/generators/artifact-name-and-directory-utils'; diff --git a/packages/devkit/src/generators/artifact-name-and-directory-utils.spec.ts b/packages/devkit/src/generators/artifact-name-and-directory-utils.spec.ts index 2b4656c63a769..da5d29ff5a068 100644 --- a/packages/devkit/src/generators/artifact-name-and-directory-utils.spec.ts +++ b/packages/devkit/src/generators/artifact-name-and-directory-utils.spec.ts @@ -2,9 +2,10 @@ import * as enquirer from 'enquirer'; import { addProjectConfiguration } from 'nx/src/devkit-exports'; import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace'; import type { Tree } from 'nx/src/generators/tree'; -import { workspaceRoot } from 'nx/src/utils/workspace-root'; -import { join } from 'path'; -import { determineArtifactNameAndDirectoryOptions } from './artifact-name-and-directory-utils'; +import { + determineArtifactNameAndDirectoryOptions, + setCwd, +} from './artifact-name-and-directory-utils'; describe('determineArtifactNameAndDirectoryOptions', () => { let tree: Tree; @@ -25,10 +26,6 @@ describe('determineArtifactNameAndDirectoryOptions', () => { process.stdout.isTTY = originalIsTTYValue; } - function setCwd(path: string) { - process.env.INIT_CWD = join(workspaceRoot, path); - } - function restoreCwd() { if (originalInitCwd === undefined) { delete process.env.INIT_CWD; @@ -39,6 +36,7 @@ describe('determineArtifactNameAndDirectoryOptions', () => { beforeEach(() => { tree = createTreeWithEmptyWorkspace(); + setCwd(''); jest.clearAllMocks(); originalInteractiveValue = process.env.NX_INTERACTIVE; @@ -47,6 +45,49 @@ describe('determineArtifactNameAndDirectoryOptions', () => { originalInitCwd = process.env.INIT_CWD; }); + it('should accept a derivedDirectory which is relative to the project source root', async () => { + addProjectConfiguration(tree, 'app1', { + root: 'apps/app1', + sourceRoot: 'apps/app1/src', + projectType: 'application', + }); + + const res = await determineArtifactNameAndDirectoryOptions(tree, { + name: 'myComponent', + artifactType: 'component', + callingGenerator: '@my-org/my-plugin:component', + directory: 'components', + derivedDirectory: 'components', + project: 'app1', + nameAndDirectoryFormat: 'derived', + }); + + expect(res.filePath).toEqual( + 'apps/app1/src/components/my-component/my-component.ts' + ); + }); + + it('should accept a derivedDirectory which is relative to the project root', async () => { + addProjectConfiguration(tree, 'app1', { + root: 'apps/app1', + projectType: 'application', + }); + + const res = await determineArtifactNameAndDirectoryOptions(tree, { + name: 'myComponent', + artifactType: 'component', + callingGenerator: '@my-org/my-plugin:component', + directory: 'components', + derivedDirectory: 'components', + project: 'app1', + nameAndDirectoryFormat: 'derived', + }); + + expect(res.filePath).toEqual( + 'apps/app1/components/my-component/my-component.ts' + ); + }); + it('should throw an error when the resolver directory is not under any project root', async () => { addProjectConfiguration(tree, 'app1', { root: 'apps/app1', diff --git a/packages/devkit/src/generators/artifact-name-and-directory-utils.ts b/packages/devkit/src/generators/artifact-name-and-directory-utils.ts index faa34888e5692..fcf2872ee4248 100644 --- a/packages/devkit/src/generators/artifact-name-and-directory-utils.ts +++ b/packages/devkit/src/generators/artifact-name-and-directory-utils.ts @@ -1,7 +1,7 @@ import { prompt } from 'enquirer'; import type { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json'; import type { Tree } from 'nx/src/generators/tree'; -import { relative } from 'path'; +import { join, relative } from 'path'; import { requireNx } from '../../nx'; import { names } from '../utils/names'; @@ -31,6 +31,7 @@ export type ArtifactGenerationOptions = { pascalCaseFile?: boolean; project?: string; suffix?: string; + derivedDirectory?: string; }; export type NameAndDirectoryOptions = { @@ -74,6 +75,7 @@ export async function determineArtifactNameAndDirectoryOptions( options.nameAndDirectoryFormat ?? (await determineFormat(formats, options)); validateResolvedProject( + tree, formats[format]?.project, options, formats[format]?.directory @@ -173,6 +175,7 @@ function getNameAndDirectoryOptionFormats( if (!options.project) { validateResolvedProject( + tree, asProvidedOptions.project, options, asProvidedOptions.directory @@ -278,14 +281,25 @@ function getDerivedOptions( project.projectType === 'application' ? 'app' : 'lib', extractedDirectory ?? '' ); - const derivedDirectory = options.flat - ? normalizePath(baseDirectory) - : joinPathFragments( - baseDirectory, - options.pascalCaseDirectory - ? names(derivedName).className - : names(derivedName).fileName - ); + const derivedDirectory = + typeof options.derivedDirectory === 'string' + ? joinPathFragments( + project.sourceRoot ?? project.root, + options.derivedDirectory, + options.flat + ? '' + : options.pascalCaseDirectory + ? names(derivedName).className + : names(derivedName).fileName + ) + : options.flat + ? normalizePath(baseDirectory) + : joinPathFragments( + baseDirectory, + options.pascalCaseDirectory + ? names(derivedName).className + : names(derivedName).fileName + ); if ( options.directory && @@ -333,6 +347,7 @@ function getDerivedOptions( } function validateResolvedProject( + tree: Tree, project: string | undefined, options: ArtifactGenerationOptions, normalizedDirectory: string @@ -392,6 +407,13 @@ function getRelativeCwd(): string { return normalizePath(relative(workspaceRoot, getCwd())); } +/** + * Function for setting cwd during testing + */ +export function setCwd(path: string): void { + process.env.INIT_CWD = join(workspaceRoot, path); +} + function getCwd(): string { return process.env.INIT_CWD?.startsWith(workspaceRoot) ? process.env.INIT_CWD diff --git a/packages/plugin/generators.json b/packages/plugin/generators.json index 8b86e8349bd12..9c49a325b27b5 100644 --- a/packages/plugin/generators.json +++ b/packages/plugin/generators.json @@ -29,7 +29,7 @@ "description": "Create a generator for an Nx Plugin." }, "executor": { - "factory": "./src/generators/executor/executor", + "factory": "./src/generators/executor/executor#executorGeneratorInternal", "schema": "./src/generators/executor/schema.json", "description": "Create an executor for an Nx Plugin." }, diff --git a/packages/plugin/project.json b/packages/plugin/project.json index 9e08c7cdfb686..5f4961d05f456 100644 --- a/packages/plugin/project.json +++ b/packages/plugin/project.json @@ -1,7 +1,7 @@ { "name": "plugin", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "packages/plugin", + "sourceRoot": "packages/plugin/src", "projectType": "library", "targets": { "test": {}, diff --git a/packages/plugin/src/generators/executor/executor.spec.ts b/packages/plugin/src/generators/executor/executor.spec.ts index 5ae34d90c3e03..274530a5db5f9 100644 --- a/packages/plugin/src/generators/executor/executor.spec.ts +++ b/packages/plugin/src/generators/executor/executor.spec.ts @@ -3,6 +3,7 @@ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { executorGenerator } from './executor'; import { pluginGenerator } from '../plugin/plugin'; import { libraryGenerator as jsLibraryGenerator } from '@nx/js'; +import { setCwd } from '@nx/devkit/internal-testing-utils'; describe('NxPlugin Executor Generator', () => { let tree: Tree; @@ -10,7 +11,8 @@ describe('NxPlugin Executor Generator', () => { beforeEach(async () => { projectName = 'my-plugin'; - tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); + tree = createTreeWithEmptyWorkspace(); + setCwd(''); await pluginGenerator(tree, { name: projectName, @@ -18,6 +20,52 @@ describe('NxPlugin Executor Generator', () => { }); it('should generate files', async () => { + await executorGenerator(tree, { + name: 'my-executor', + directory: 'my-plugin/src/executors/my-executor', + unitTestRunner: 'jest', + includeHasher: false, + nameAndDirectoryFormat: 'as-provided', + }); + + expect( + tree.exists('my-plugin/src/executors/my-executor/schema.d.ts') + ).toBeTruthy(); + expect( + tree.exists('my-plugin/src/executors/my-executor/schema.json') + ).toBeTruthy(); + expect( + tree.exists('my-plugin/src/executors/my-executor/executor.ts') + ).toBeTruthy(); + expect( + tree.exists('my-plugin/src/executors/my-executor/executor.spec.ts') + ).toBeTruthy(); + }); + + it('should generate files relative to the cwd', async () => { + setCwd('my-plugin/src/executors/my-executor'); + await executorGenerator(tree, { + name: 'my-executor', + unitTestRunner: 'jest', + includeHasher: false, + nameAndDirectoryFormat: 'as-provided', + }); + + expect( + tree.exists('my-plugin/src/executors/my-executor/schema.d.ts') + ).toBeTruthy(); + expect( + tree.exists('my-plugin/src/executors/my-executor/schema.json') + ).toBeTruthy(); + expect( + tree.exists('my-plugin/src/executors/my-executor/executor.ts') + ).toBeTruthy(); + expect( + tree.exists('my-plugin/src/executors/my-executor/executor.spec.ts') + ).toBeTruthy(); + }); + + it('should generate files for derived', async () => { await executorGenerator(tree, { project: projectName, name: 'my-executor', @@ -26,29 +74,29 @@ describe('NxPlugin Executor Generator', () => { }); expect( - tree.exists('libs/my-plugin/src/executors/my-executor/schema.d.ts') + tree.exists('my-plugin/src/executors/my-executor/schema.d.ts') ).toBeTruthy(); expect( - tree.exists('libs/my-plugin/src/executors/my-executor/schema.json') + tree.exists('my-plugin/src/executors/my-executor/schema.json') ).toBeTruthy(); expect( - tree.exists('libs/my-plugin/src/executors/my-executor/executor.ts') + tree.exists('my-plugin/src/executors/my-executor/executor.ts') ).toBeTruthy(); expect( - tree.exists('libs/my-plugin/src/executors/my-executor/executor.spec.ts') + tree.exists('my-plugin/src/executors/my-executor/executor.spec.ts') ).toBeTruthy(); }); it('should update executors.json', async () => { await executorGenerator(tree, { - project: projectName, name: 'my-executor', - description: 'my-executor description', + directory: 'my-plugin/src/executors/my-executor', unitTestRunner: 'jest', includeHasher: false, + nameAndDirectoryFormat: 'as-provided', }); - const executorJson = readJson(tree, 'libs/my-plugin/executors.json'); + const executorJson = readJson(tree, 'my-plugin/executors.json'); expect(executorJson.executors['my-executor'].implementation).toEqual( './src/executors/my-executor/executor' @@ -57,11 +105,11 @@ describe('NxPlugin Executor Generator', () => { './src/executors/my-executor/schema.json' ); expect(executorJson.executors['my-executor'].description).toEqual( - 'my-executor description' + 'my-executor executor' ); }); - it('should generate default description', async () => { + it('should update executors.json for derived', async () => { await executorGenerator(tree, { project: projectName, name: 'my-executor', @@ -69,23 +117,30 @@ describe('NxPlugin Executor Generator', () => { includeHasher: false, }); - const executorsJson = readJson(tree, 'libs/my-plugin/executors.json'); + const executorJson = readJson(tree, 'my-plugin/executors.json'); - expect(executorsJson.executors['my-executor'].description).toEqual( + expect(executorJson.executors['my-executor'].implementation).toEqual( + './src/executors/my-executor/executor' + ); + expect(executorJson.executors['my-executor'].schema).toEqual( + './src/executors/my-executor/schema.json' + ); + expect(executorJson.executors['my-executor'].description).toEqual( 'my-executor executor' ); }); it('should generate custom description', async () => { await executorGenerator(tree, { - project: projectName, name: 'my-executor', + directory: 'my-plugin/src/executors/my-executor', description: 'my-executor custom description', unitTestRunner: 'jest', includeHasher: false, + nameAndDirectoryFormat: 'as-provided', }); - const executorsJson = readJson(tree, 'libs/my-plugin/executors.json'); + const executorsJson = readJson(tree, 'my-plugin/executors.json'); expect(executorsJson.executors['my-executor'].description).toEqual( 'my-executor custom description' @@ -96,13 +151,16 @@ describe('NxPlugin Executor Generator', () => { await jsLibraryGenerator(tree, { name: 'test-js-lib', bundler: 'tsc', + projectNameAndRootFormat: 'as-provided', }); const libConfig = readProjectConfiguration(tree, 'test-js-lib'); + await executorGenerator(tree, { - project: 'test-js-lib', - includeHasher: false, name: 'test-executor', + directory: 'test-js-lib/src/executors/my-executor', unitTestRunner: 'jest', + includeHasher: false, + nameAndDirectoryFormat: 'as-provided', }); expect(() => tree.exists(`${libConfig.root}/executors.json`)).not.toThrow(); @@ -115,20 +173,18 @@ describe('NxPlugin Executor Generator', () => { describe('none', () => { it('should not generate unit test files', async () => { await executorGenerator(tree, { - project: projectName, name: 'my-executor', - description: 'my-executor description', + directory: 'my-plugin/src/executors/my-executor', unitTestRunner: 'none', - includeHasher: true, + includeHasher: false, + nameAndDirectoryFormat: 'as-provided', }); expect( - tree.exists( - 'libs/my-plugin/src/executors/my-executor/executor.spec.ts' - ) + tree.exists('my-plugin/src/executors/my-executor/executor.spec.ts') ).toBeFalsy(); expect( - tree.exists('libs/my-plugin/src/executors/my-executor/hasher.spec.ts') + tree.exists('my-plugin/src/executors/my-executor/hasher.spec.ts') ).toBeFalsy(); }); }); @@ -137,18 +193,17 @@ describe('NxPlugin Executor Generator', () => { describe('--includeHasher', () => { it('should generate hasher files', async () => { await executorGenerator(tree, { - project: projectName, name: 'my-executor', - includeHasher: true, + directory: 'my-plugin/src/executors/my-executor', unitTestRunner: 'jest', + includeHasher: true, + nameAndDirectoryFormat: 'as-provided', }); expect( - tree.exists('libs/my-plugin/src/executors/my-executor/hasher.spec.ts') + tree.exists('my-plugin/src/executors/my-executor/hasher.spec.ts') ).toBeTruthy(); expect( - tree - .read('libs/my-plugin/src/executors/my-executor/hasher.ts') - .toString() + tree.read('my-plugin/src/executors/my-executor/hasher.ts').toString() ).toMatchInlineSnapshot(` "import { CustomHasher } from '@nx/devkit'; @@ -168,13 +223,14 @@ describe('NxPlugin Executor Generator', () => { it('should update executors.json', async () => { await executorGenerator(tree, { - project: projectName, name: 'my-executor', - includeHasher: true, + directory: 'my-plugin/src/executors/my-executor', unitTestRunner: 'jest', + includeHasher: true, + nameAndDirectoryFormat: 'as-provided', }); - const executorsJson = readJson(tree, 'libs/my-plugin/executors.json'); + const executorsJson = readJson(tree, 'my-plugin/executors.json'); expect(executorsJson.executors['my-executor'].hasher).toEqual( './src/executors/my-executor/hasher' ); diff --git a/packages/plugin/src/generators/executor/executor.ts b/packages/plugin/src/generators/executor/executor.ts index 5c8ffda94120c..8004653f20bee 100644 --- a/packages/plugin/src/generators/executor/executor.ts +++ b/packages/plugin/src/generators/executor/executor.ts @@ -3,12 +3,12 @@ import { names, generateFiles, updateJson, - getWorkspaceLayout, joinPathFragments, writeJson, readJson, ExecutorsJson, formatFiles, + normalizePath, } from '@nx/devkit'; import type { Tree } from '@nx/devkit'; import type { Schema } from './schema'; @@ -16,13 +16,17 @@ import * as path from 'path'; import { PackageJson } from 'nx/src/utils/package-json'; import pluginLintCheckGenerator from '../lint-checks/generator'; import { nxVersion } from '../../utils/versions'; +import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope'; +import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generators/artifact-name-and-directory-utils'; +import { join, relative } from 'path'; interface NormalizedSchema extends Schema { fileName: string; className: string; propertyName: string; projectRoot: string; - projectSourceRoot: string; + filePath: string; + directory: string; npmScope: string; } @@ -30,22 +34,14 @@ function addFiles(host: Tree, options: NormalizedSchema) { generateFiles( host, path.join(__dirname, './files/executor'), - `${options.projectSourceRoot}/executors`, + options.directory, { ...options, - tmpl: '', } ); if (options.unitTestRunner === 'none') { - host.delete( - joinPathFragments( - options.projectSourceRoot, - 'executors', - options.fileName, - `executor.spec.ts` - ) - ); + host.delete(joinPathFragments(options.directory, `executor.spec.ts`)); } } @@ -53,22 +49,14 @@ function addHasherFiles(host: Tree, options: NormalizedSchema) { generateFiles( host, path.join(__dirname, './files/hasher'), - `${options.projectSourceRoot}/executors`, + options.directory, { ...options, - tmpl: '', } ); if (options.unitTestRunner === 'none') { - host.delete( - joinPathFragments( - options.projectSourceRoot, - 'executors', - options.fileName, - 'hasher.spec.ts' - ) - ); + host.delete(joinPathFragments(options.directory, 'hasher.spec.ts')); } } @@ -139,14 +127,18 @@ async function updateExecutorJson(host: Tree, options: NormalizedSchema) { let executors = json.executors ?? json.builders; executors ||= {}; executors[options.name] = { - implementation: `./src/executors/${options.fileName}/executor`, - schema: `./src/executors/${options.fileName}/schema.json`, + implementation: `./${normalizePath( + relative(options.projectRoot, join(options.directory, 'executor')) + )}`, + schema: `./${normalizePath( + relative(options.projectRoot, join(options.directory, 'schema.json')) + )}`, description: options.description, }; if (options.includeHasher) { - executors[ - options.name - ].hasher = `./src/executors/${options.fileName}/hasher`; + executors[options.name].hasher = `./${normalizePath( + relative(options.projectRoot, join(options.directory, 'hasher')) + )}`; } json.executors = executors; @@ -154,12 +146,28 @@ async function updateExecutorJson(host: Tree, options: NormalizedSchema) { }); } -function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { - const { npmScope } = getWorkspaceLayout(host); - const { fileName, className, propertyName } = names(options.name); +async function normalizeOptions( + tree: Tree, + options: Schema +): Promise { + const npmScope = getNpmScope(tree); + + const res = await determineArtifactNameAndDirectoryOptions(tree, { + artifactType: 'executor', + callingGenerator: '@nx/plugin:executor', + name: options.name, + nameAndDirectoryFormat: options.nameAndDirectoryFormat, + project: options.project, + directory: options.directory, + fileName: 'executor', + derivedDirectory: 'executors', + }); + + const { project, fileName, artifactName, filePath, directory } = res; - const { root: projectRoot, sourceRoot: projectSourceRoot } = - readProjectConfiguration(host, options.project); + const { className, propertyName } = names(artifactName); + + const { root: projectRoot } = readProjectConfiguration(tree, project); let description: string; if (options.description) { @@ -170,18 +178,27 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { return { ...options, + filePath, + project, + directory, fileName, className, propertyName, description, projectRoot, - projectSourceRoot, npmScope, }; } -export async function executorGenerator(host: Tree, schema: Schema) { - const options = normalizeOptions(host, schema); +export async function executorGenerator(tree: Tree, rawOptions: Schema) { + await executorGeneratorInternal(tree, { + nameAndDirectoryFormat: 'derived', + ...rawOptions, + }); +} + +export async function executorGeneratorInternal(host: Tree, schema: Schema) { + const options = await normalizeOptions(host, schema); addFiles(host, options); if (options.includeHasher) { diff --git a/packages/plugin/src/generators/executor/files/executor/__fileName__/executor.spec.ts__tmpl__ b/packages/plugin/src/generators/executor/files/executor/executor.spec.ts.template similarity index 100% rename from packages/plugin/src/generators/executor/files/executor/__fileName__/executor.spec.ts__tmpl__ rename to packages/plugin/src/generators/executor/files/executor/executor.spec.ts.template diff --git a/packages/plugin/src/generators/executor/files/executor/__fileName__/executor.ts__tmpl__ b/packages/plugin/src/generators/executor/files/executor/executor.ts.template similarity index 100% rename from packages/plugin/src/generators/executor/files/executor/__fileName__/executor.ts__tmpl__ rename to packages/plugin/src/generators/executor/files/executor/executor.ts.template diff --git a/packages/plugin/src/generators/executor/files/executor/__fileName__/schema.d.ts__tmpl__ b/packages/plugin/src/generators/executor/files/executor/schema.d.ts.template similarity index 100% rename from packages/plugin/src/generators/executor/files/executor/__fileName__/schema.d.ts__tmpl__ rename to packages/plugin/src/generators/executor/files/executor/schema.d.ts.template diff --git a/packages/plugin/src/generators/executor/files/executor/__fileName__/schema.json__tmpl__ b/packages/plugin/src/generators/executor/files/executor/schema.json.template similarity index 100% rename from packages/plugin/src/generators/executor/files/executor/__fileName__/schema.json__tmpl__ rename to packages/plugin/src/generators/executor/files/executor/schema.json.template diff --git a/packages/plugin/src/generators/executor/files/hasher/__fileName__/hasher.spec.ts__tmpl__ b/packages/plugin/src/generators/executor/files/hasher/hasher.spec.ts.template similarity index 100% rename from packages/plugin/src/generators/executor/files/hasher/__fileName__/hasher.spec.ts__tmpl__ rename to packages/plugin/src/generators/executor/files/hasher/hasher.spec.ts.template diff --git a/packages/plugin/src/generators/executor/files/hasher/__fileName__/hasher.ts__tmpl__ b/packages/plugin/src/generators/executor/files/hasher/hasher.ts.template similarity index 100% rename from packages/plugin/src/generators/executor/files/hasher/__fileName__/hasher.ts__tmpl__ rename to packages/plugin/src/generators/executor/files/hasher/hasher.ts.template diff --git a/packages/plugin/src/generators/executor/schema.d.ts b/packages/plugin/src/generators/executor/schema.d.ts index 4e896a3f909f6..025ca41cf4f3e 100644 --- a/packages/plugin/src/generators/executor/schema.d.ts +++ b/packages/plugin/src/generators/executor/schema.d.ts @@ -1,9 +1,13 @@ +import type { NameAndDirectoryFormat } from '@nx/devkit/src/generators/artifact-name-and-directory-utils'; + export interface Schema { - project: string; + project?: string; name: string; + directory?: string; description?: string; unitTestRunner: 'jest' | 'none'; includeHasher: boolean; + nameAndDirectoryFormat?: NameAndDirectoryFormat; skipLintChecks?: boolean; skipFormat?: boolean; } diff --git a/packages/plugin/src/generators/executor/schema.json b/packages/plugin/src/generators/executor/schema.json index 211c25b01a1df..8ae5d22b5fdb2 100644 --- a/packages/plugin/src/generators/executor/schema.json +++ b/packages/plugin/src/generators/executor/schema.json @@ -19,9 +19,7 @@ "alias": "p", "$default": { "$source": "projectName" - }, - "x-prompt": "What is the name of the project for the executor?", - "x-priority": "important" + } }, "name": { "type": "string", @@ -33,6 +31,11 @@ "x-prompt": "What name would you like to use for the executor?", "x-priority": "important" }, + "directory": { + "type": "string", + "description": "The directory at which to create the executor file. When `--nameAndDirectoryFormat=as-provided`, it will be relative to the current working directory. Otherwise, it will be relative to the workspace root.", + "aliases": ["dir"] + }, "description": { "type": "string", "description": "Executor description." @@ -53,6 +56,11 @@ "default": false, "description": "Do not add an eslint configuration for plugin json files." }, + "nameAndDirectoryFormat": { + "description": "Whether to generate the component in the directory as provided, relative to the current working directory and ignoring the project (`as-provided`) or generate it using the project and directory relative to the workspace root (`derived`).", + "type": "string", + "enum": ["as-provided", "derived"] + }, "skipFormat": { "type": "boolean", "description": "Skip formatting files.", @@ -60,6 +68,6 @@ "x-priority": "internal" } }, - "required": ["project", "name"], + "required": ["name"], "additionalProperties": false }