From f6fbe7a794cdc478655131b117b41c7835c04f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Mon, 21 Aug 2023 10:46:43 +0100 Subject: [PATCH] feat(web): use helper to determine project name and root in application generator --- .../packages/web/generators/application.json | 11 +++- packages/web/generators.json | 2 +- .../src/generators/application/application.ts | 63 ++++++++++++------- .../src/generators/application/schema.d.ts | 2 + .../src/generators/application/schema.json | 7 ++- 5 files changed, 57 insertions(+), 28 deletions(-) diff --git a/docs/generated/packages/web/generators/application.json b/docs/generated/packages/web/generators/application.json index dbc224acfd5b6e..d2e3ece2bbfbdc 100644 --- a/docs/generated/packages/web/generators/application.json +++ b/docs/generated/packages/web/generators/application.json @@ -1,6 +1,6 @@ { "name": "application", - "factory": "./src/generators/application/application#applicationGenerator", + "factory": "./src/generators/application/application#applicationGeneratorInternal", "schema": { "$schema": "http://json-schema.org/schema", "cli": "nx", @@ -14,12 +14,17 @@ "type": "string", "$default": { "$source": "argv", "index": 0 }, "x-prompt": "What name would you like to use for the application?", - "pattern": "^[a-zA-Z].*$" + "pattern": "^[a-zA-Z][^:]*$" }, "directory": { "description": "The directory of the new application.", "type": "string" }, + "projectNameAndRootFormat": { + "description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).", + "type": "string", + "enum": ["as-provided", "derived"] + }, "style": { "description": "The file extension to be used for style files.", "type": "string", @@ -107,7 +112,7 @@ "aliases": ["app"], "x-type": "application", "description": "Create an web application.", - "implementation": "/packages/web/src/generators/application/application#applicationGenerator.ts", + "implementation": "/packages/web/src/generators/application/application#applicationGeneratorInternal.ts", "hidden": false, "path": "/packages/web/src/generators/application/schema.json", "type": "generator" diff --git a/packages/web/generators.json b/packages/web/generators.json index 6fe495ef5ceea9..d647f86dec2d3a 100644 --- a/packages/web/generators.json +++ b/packages/web/generators.json @@ -10,7 +10,7 @@ "hidden": true }, "application": { - "factory": "./src/generators/application/application#applicationGenerator", + "factory": "./src/generators/application/application#applicationGeneratorInternal", "schema": "./src/generators/application/schema.json", "aliases": ["app"], "x-type": "application", diff --git a/packages/web/src/generators/application/application.ts b/packages/web/src/generators/application/application.ts index a1e8051ad3fce9..46fc29c546af51 100644 --- a/packages/web/src/generators/application/application.ts +++ b/packages/web/src/generators/application/application.ts @@ -1,10 +1,8 @@ -import { join } from 'path'; import { addDependenciesToPackageJson, addProjectConfiguration, convertNxGenerator, ensurePackage, - extractLayoutDirectory, formatFiles, generateFiles, GeneratorCallback, @@ -21,11 +19,11 @@ import { updateNxJson, updateProjectConfiguration, } from '@nx/devkit'; +import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils'; +import { getRelativePathToRootTsConfig } from '@nx/js'; import { swcCoreVersion } from '@nx/js/src/utils/versions'; import type { Linter } from '@nx/linter'; - -import { getRelativePathToRootTsConfig } from '@nx/js'; - +import { join } from 'path'; import { nxVersion, swcLoaderVersion } from '../../utils/versions'; import { webInitGenerator } from '../init/init'; import { Schema } from './schema'; @@ -179,7 +177,14 @@ function setDefaults(tree: Tree, options: NormalizedSchema) { } export async function applicationGenerator(host: Tree, schema: Schema) { - const options = normalizeOptions(host, schema); + return await applicationGeneratorInternal(host, { + projectNameAndRootFormat: 'derived', + ...schema, + }); +} + +export async function applicationGeneratorInternal(host: Tree, schema: Schema) { + const options = await normalizeOptions(host, schema); const tasks: GeneratorCallback[] = []; @@ -263,8 +268,10 @@ export async function applicationGenerator(host: Tree, schema: Schema) { >('@nx/cypress', nxVersion); const cypressTask = await cypressProjectGenerator(host, { ...options, - name: `${options.name}-e2e`, - directory: options.directory, + name: options.e2eProjectName, + directory: options.e2eProjectRoot, + // the name and root are already normalized, instruct the generator to use them as is + projectNameAndRootFormat: 'as-provided', project: options.projectName, skipFormat: true, }); @@ -328,23 +335,33 @@ export async function applicationGenerator(host: Tree, schema: Schema) { return runTasksInSerial(...tasks); } -function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { - const { layoutDirectory, projectDirectory } = extractLayoutDirectory( - options.directory - ); - - const appDirectory = projectDirectory - ? `${names(projectDirectory).fileName}/${names(options.name).fileName}` - : names(options.name).fileName; - - const { appsDir: defaultAppsDir, npmScope } = getWorkspaceLayout(host); - const appsDir = layoutDirectory ?? defaultAppsDir; +async function normalizeOptions( + host: Tree, + options: Schema +): Promise { + const { + projectName: appProjectName, + projectRoot: appProjectRoot, + projectNameAndRootFormat, + } = await determineProjectNameAndRootOptions(host, { + name: options.name, + projectType: 'application', + directory: options.directory, + projectNameAndRootFormat: options.projectNameAndRootFormat, + callingGenerator: '@nx/web:application', + }); + options.projectNameAndRootFormat = projectNameAndRootFormat; - const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-'); - const e2eProjectName = `${appProjectName}-e2e`; + const { projectName: e2eProjectName, projectRoot: e2eProjectRoot } = + await determineProjectNameAndRootOptions(host, { + name: `${options.name}-e2e`, + projectType: 'application', + directory: options.directory, + projectNameAndRootFormat: options.projectNameAndRootFormat, + callingGenerator: undefined, + }); - const appProjectRoot = joinPathFragments(appsDir, appDirectory); - const e2eProjectRoot = joinPathFragments(appsDir, `${appDirectory}-e2e`); + const { npmScope } = getWorkspaceLayout(host); const parsedTags = options.tags ? options.tags.split(',').map((s) => s.trim()) diff --git a/packages/web/src/generators/application/schema.d.ts b/packages/web/src/generators/application/schema.d.ts index 308d3ea0b3002d..94ad327ecf4437 100644 --- a/packages/web/src/generators/application/schema.d.ts +++ b/packages/web/src/generators/application/schema.d.ts @@ -1,3 +1,4 @@ +import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils'; import type { Linter } from '@nx/linter'; export interface Schema { @@ -8,6 +9,7 @@ export interface Schema { compiler?: 'babel' | 'swc'; skipFormat?: boolean; directory?: string; + projectNameAndRootFormat?: ProjectNameAndRootFormat; tags?: string; unitTestRunner?: 'jest' | 'vitest' | 'none'; inSourceTests?: boolean; diff --git a/packages/web/src/generators/application/schema.json b/packages/web/src/generators/application/schema.json index c353cd0f202ded..e53e263a9547e8 100644 --- a/packages/web/src/generators/application/schema.json +++ b/packages/web/src/generators/application/schema.json @@ -14,12 +14,17 @@ "index": 0 }, "x-prompt": "What name would you like to use for the application?", - "pattern": "^[a-zA-Z].*$" + "pattern": "^[a-zA-Z][^:]*$" }, "directory": { "description": "The directory of the new application.", "type": "string" }, + "projectNameAndRootFormat": { + "description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).", + "type": "string", + "enum": ["as-provided", "derived"] + }, "style": { "description": "The file extension to be used for style files.", "type": "string",