From 6640c327488de53242a025f7714fcfc929cabbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Tue, 22 Aug 2023 15:25:33 +0100 Subject: [PATCH] feat(web): use helper to determine project name and root in application generator (#18737) --- .../packages/web/generators/application.json | 11 +++- e2e/web/src/web.test.ts | 21 +++++++ packages/web/generators.json | 2 +- .../src/generators/application/application.ts | 55 +++++++++++-------- .../src/generators/application/schema.d.ts | 2 + .../src/generators/application/schema.json | 7 ++- 6 files changed, 71 insertions(+), 27 deletions(-) diff --git a/docs/generated/packages/web/generators/application.json b/docs/generated/packages/web/generators/application.json index dbc224acfd5b6..d2e3ece2bbfbd 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/e2e/web/src/web.test.ts b/e2e/web/src/web.test.ts index 4aaa1266a8883..bbed602ffbc72 100644 --- a/e2e/web/src/web.test.ts +++ b/e2e/web/src/web.test.ts @@ -317,6 +317,27 @@ describe('Web Components Applications', () => { runCLI(`build ${appName} --outputHashing none`); checkFilesExist(`dist/apps/${appName}/main.js`); }, 100000); + + it('should support generating applications with the new name and root format', () => { + const appName = uniq('app1'); + + runCLI( + `generate @nx/web:app ${appName} --bundler=webpack --project-name-and-root-format=as-provided --no-interactive` + ); + + // check files are generated without the layout directory ("apps/") and + // using the project name as the directory when no directory is provided + checkFilesExist(`${appName}/src/main.ts`); + // check build works + expect(runCLI(`build ${appName}`)).toContain( + `Successfully ran target build for project ${appName}` + ); + // check tests pass + const appTestResult = runCLI(`test ${appName}`); + expect(appTestResult).toContain( + `Successfully ran target test for project ${appName}` + ); + }, 500_000); }); describe('CLI - Environment Variables', () => { diff --git a/packages/web/generators.json b/packages/web/generators.json index 6fe495ef5ceea..d647f86dec2d3 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 ed785a7a37389..994069655d7e8 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, @@ -22,11 +20,11 @@ import { updateProjectConfiguration, writeJson, } 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'; @@ -180,7 +178,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[] = []; @@ -264,8 +269,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, }); @@ -341,23 +348,27 @@ 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 e2eProjectRoot = `${appProjectRoot}-e2e`; - 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 308d3ea0b3002..94ad327ecf443 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 c353cd0f202de..e53e263a9547e 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",