From bd1b0b70fd35e3b336ef0ad167bd58f79c027d2b Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Fri, 1 Sep 2023 16:58:14 -0400 Subject: [PATCH] feat(misc): introduce a way to set the project name/root format for all generators (#18971) --- docs/generated/devkit/NxJsonConfiguration.md | 9 +++-- docs/generated/devkit/Workspace.md | 9 +++-- e2e/linter/src/linter.test.ts | 2 +- e2e/utils/create-project-utils.ts | 10 ++++- .../project-name-and-root-utils.spec.ts | 13 ++++++- .../generators/project-name-and-root-utils.ts | 22 ++++++++--- .../application/lib/normalize-options.ts | 1 + packages/nx/schemas/nx-schema.json | 5 +++ packages/nx/src/config/nx-json.ts | 5 ++- .../new/__snapshots__/new.spec.ts.snap | 3 ++ .../new/generate-workspace-files.spec.ts | 6 +++ .../new/generate-workspace-files.ts | 3 ++ packages/workspace/src/generators/new/new.ts | 17 +++++---- .../workspace/src/generators/preset/preset.ts | 37 +++++++++++++++---- 14 files changed, 107 insertions(+), 35 deletions(-) diff --git a/docs/generated/devkit/NxJsonConfiguration.md b/docs/generated/devkit/NxJsonConfiguration.md index 5ab22b869e5a1..eaa6e3478f0a1 100644 --- a/docs/generated/devkit/NxJsonConfiguration.md +++ b/docs/generated/devkit/NxJsonConfiguration.md @@ -191,7 +191,8 @@ Where new apps + libs should be placed #### Type declaration -| Name | Type | -| :-------- | :------- | -| `appsDir` | `string` | -| `libsDir` | `string` | +| Name | Type | +| :-------------------------- | :----------------------------- | +| `appsDir?` | `string` | +| `libsDir?` | `string` | +| `projectNameAndRootFormat?` | `"as-provided"` \| `"derived"` | diff --git a/docs/generated/devkit/Workspace.md b/docs/generated/devkit/Workspace.md index 0bdeecc6db6f1..87a5801b57b99 100644 --- a/docs/generated/devkit/Workspace.md +++ b/docs/generated/devkit/Workspace.md @@ -267,10 +267,11 @@ Where new apps + libs should be placed #### Type declaration -| Name | Type | -| :-------- | :------- | -| `appsDir` | `string` | -| `libsDir` | `string` | +| Name | Type | +| :-------------------------- | :----------------------------- | +| `appsDir?` | `string` | +| `libsDir?` | `string` | +| `projectNameAndRootFormat?` | `"as-provided"` \| `"derived"` | #### Inherited from diff --git a/e2e/linter/src/linter.test.ts b/e2e/linter/src/linter.test.ts index 9e1ce7db7dc52..7ee1e2dab05fb 100644 --- a/e2e/linter/src/linter.test.ts +++ b/e2e/linter/src/linter.test.ts @@ -558,7 +558,7 @@ describe('Linter', () => { bundler: 'vite', e2eTestRunner: 'none', }); - runCLI(`generate @nx/js:lib ${mylib}`); + runCLI(`generate @nx/js:lib ${mylib} --directory libs/${mylib}`); // migrate to flat structure runCLI(`generate @nx/linter:convert-to-flat-config`); diff --git a/e2e/utils/create-project-utils.ts b/e2e/utils/create-project-utils.ts index 7c2e9b1c7e014..c4440c2ce2e04 100644 --- a/e2e/utils/create-project-utils.ts +++ b/e2e/utils/create-project-utils.ts @@ -28,7 +28,7 @@ import { RunCmdOpts, runCommand, } from './command-utils'; -import { output } from '@nx/devkit'; +import { NxJsonConfiguration, output } from '@nx/devkit'; import { readFileSync } from 'fs'; import { join } from 'path'; @@ -41,6 +41,7 @@ let projName: string; export function newProject({ name = uniq('proj'), packageManager = getSelectedPackageManager(), + unsetProjectNameAndRootFormat = true, } = {}): string { try { const projScope = 'proj'; @@ -51,6 +52,13 @@ export function newProject({ packageManager, }); + if (unsetProjectNameAndRootFormat) { + updateJson('nx.json', (nxJson) => { + delete nxJson.workspaceLayout; + return nxJson; + }); + } + // Temporary hack to prevent installing with `--frozen-lockfile` if (isCI && packageManager === 'pnpm') { updateFile( diff --git a/packages/devkit/src/generators/project-name-and-root-utils.spec.ts b/packages/devkit/src/generators/project-name-and-root-utils.spec.ts index ae90411bd3cda..793d76b00159e 100644 --- a/packages/devkit/src/generators/project-name-and-root-utils.spec.ts +++ b/packages/devkit/src/generators/project-name-and-root-utils.spec.ts @@ -348,10 +348,21 @@ describe('determineProjectNameAndRootOptions', () => { expect(promptSpy).toHaveBeenCalledTimes(2); - expect(readNxJson(tree).generators['@nx/some-plugin:app']).toEqual({ + expect(readNxJson(tree).workspaceLayout).toEqual({ projectNameAndRootFormat: 'as-provided', }); + promptSpy.mockReset(); + + await determineProjectNameAndRootOptions(tree, { + name: 'libName', + projectType: 'library', + directory: 'shared', + callingGenerator: '@nx/some-plugin:app', + }); + + expect(promptSpy).not.toHaveBeenCalled(); + // restore original interactive mode restoreOriginalInteractiveMode(); }); diff --git a/packages/devkit/src/generators/project-name-and-root-utils.ts b/packages/devkit/src/generators/project-name-and-root-utils.ts index ce40e4afb93cf..e6a525d0ebbb8 100644 --- a/packages/devkit/src/generators/project-name-and-root-utils.ts +++ b/packages/devkit/src/generators/project-name-and-root-utils.ts @@ -75,7 +75,9 @@ export async function determineProjectNameAndRootOptions( const formats = getProjectNameAndRootFormats(tree, options); const format = options.projectNameAndRootFormat ?? - (await determineFormat(tree, formats, options.callingGenerator)); + (getDefaultProjectNameAndRootFormat(tree) === 'as-provided' + ? 'as-provided' + : await determineFormat(tree, formats, options.callingGenerator)); return { ...formats[format], @@ -167,11 +169,7 @@ async function determineFormat( initial: true, }); if (saveDefault) { - const nxJson = readNxJson(tree); - nxJson.generators ??= {}; - nxJson.generators[callingGenerator] ??= {}; - nxJson.generators[callingGenerator].projectNameAndRootFormat = result; - updateNxJson(tree, nxJson); + setProjectNameAndRootFormatDefault(tree); } else { logger.warn(deprecationWarning); } @@ -183,6 +181,18 @@ async function determineFormat( return result; } +function setProjectNameAndRootFormatDefault(tree: Tree) { + const nxJson = readNxJson(tree); + nxJson.workspaceLayout ??= {}; + nxJson.workspaceLayout.projectNameAndRootFormat = 'as-provided'; + updateNxJson(tree, nxJson); +} + +function getDefaultProjectNameAndRootFormat(tree: Tree) { + const nxJson = readNxJson(tree); + return nxJson.workspaceLayout?.projectNameAndRootFormat ?? 'derived'; +} + function getProjectNameAndRootFormats( tree: Tree, options: ProjectGenerationOptions diff --git a/packages/nest/src/generators/application/lib/normalize-options.ts b/packages/nest/src/generators/application/lib/normalize-options.ts index 3c2d402439da1..6116615458f14 100644 --- a/packages/nest/src/generators/application/lib/normalize-options.ts +++ b/packages/nest/src/generators/application/lib/normalize-options.ts @@ -41,6 +41,7 @@ export function toNodeApplicationGeneratorOptions( name: options.name, directory: options.directory, frontendProject: options.frontendProject, + projectNameAndRootFormat: options.projectNameAndRootFormat, linter: options.linter, skipFormat: true, skipPackageJson: options.skipPackageJson, diff --git a/packages/nx/schemas/nx-schema.json b/packages/nx/schemas/nx-schema.json index 6bc91856699bc..091b7d0169510 100644 --- a/packages/nx/schemas/nx-schema.json +++ b/packages/nx/schemas/nx-schema.json @@ -56,6 +56,11 @@ "appsDir": { "type": "string", "description": "Default folder name for apps." + }, + "projectNameAndRootFormat": { + "type": "string", + "description": "Default method of handling arguments for generating projects", + "enum": ["as-provided", "derived"] } }, "additionalProperties": false diff --git a/packages/nx/src/config/nx-json.ts b/packages/nx/src/config/nx-json.ts index afc501337d657..5751858accffd 100644 --- a/packages/nx/src/config/nx-json.ts +++ b/packages/nx/src/config/nx-json.ts @@ -86,8 +86,9 @@ export interface NxJsonConfiguration { * Where new apps + libs should be placed */ workspaceLayout?: { - libsDir: string; - appsDir: string; + libsDir?: string; + appsDir?: string; + projectNameAndRootFormat?: 'as-provided' | 'derived'; }; /** * Available Task Runners diff --git a/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap b/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap index ec1603d79d675..693757e1212cb 100644 --- a/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap +++ b/packages/workspace/src/generators/new/__snapshots__/new.spec.ts.snap @@ -53,5 +53,8 @@ exports[`new should generate an empty nx.json 1`] = ` "runner": "nx/tasks-runners/default", }, }, + "workspaceLayout": { + "projectNameAndRootFormat": "as-provided", + }, } `; diff --git a/packages/workspace/src/generators/new/generate-workspace-files.spec.ts b/packages/workspace/src/generators/new/generate-workspace-files.spec.ts index 7f84b9dd89fb3..2ab57aac5a84a 100644 --- a/packages/workspace/src/generators/new/generate-workspace-files.spec.ts +++ b/packages/workspace/src/generators/new/generate-workspace-files.spec.ts @@ -122,6 +122,9 @@ describe('@nx/workspace:generateWorkspaceFiles', () => { "runner": "nx/tasks-runners/default", }, }, + "workspaceLayout": { + "projectNameAndRootFormat": "as-provided", + }, } `); const validateNxJson = ajv.compile(nxSchema); @@ -174,6 +177,9 @@ describe('@nx/workspace:generateWorkspaceFiles', () => { "runner": "nx/tasks-runners/default", }, }, + "workspaceLayout": { + "projectNameAndRootFormat": "as-provided", + }, } `); }); diff --git a/packages/workspace/src/generators/new/generate-workspace-files.ts b/packages/workspace/src/generators/new/generate-workspace-files.ts index 28a2e28b2dc5c..c19920900fc4f 100644 --- a/packages/workspace/src/generators/new/generate-workspace-files.ts +++ b/packages/workspace/src/generators/new/generate-workspace-files.ts @@ -98,6 +98,9 @@ function createNxJson( }, }, }, + workspaceLayout: { + projectNameAndRootFormat: 'as-provided', + }, }; nxJson.targetDefaults = { diff --git a/packages/workspace/src/generators/new/new.ts b/packages/workspace/src/generators/new/new.ts index bc78ae98e3277..1a52b8e600832 100644 --- a/packages/workspace/src/generators/new/new.ts +++ b/packages/workspace/src/generators/new/new.ts @@ -41,27 +41,28 @@ export interface NormalizedSchema extends Schema { isCustomPreset: boolean; } -export async function newGenerator(host: Tree, opts: Schema) { +export async function newGenerator(tree: Tree, opts: Schema) { const options = normalizeOptions(opts); - validateOptions(options, host); + validateOptions(options, tree); - await generateWorkspaceFiles(host, { ...options, nxCloud: undefined } as any); + await generateWorkspaceFiles(tree, { ...options, nxCloud: undefined } as any); - addPresetDependencies(host, options); - addCloudDependencies(host, options); + addPresetDependencies(tree, options); + + addCloudDependencies(tree, options); return async () => { const pmc = getPackageManagerCommand(options.packageManager); if (pmc.preInstall) { execSync(pmc.preInstall, { - cwd: joinPathFragments(host.root, options.directory), + cwd: joinPathFragments(tree.root, options.directory), stdio: process.env.NX_GENERATE_QUIET === 'true' ? 'ignore' : 'inherit', }); } - installPackagesTask(host, false, options.directory, options.packageManager); + installPackagesTask(tree, false, options.directory, options.packageManager); // TODO: move all of these into create-nx-workspace if (options.preset !== Preset.NPM && !options.isCustomPreset) { - await generatePreset(host, options); + await generatePreset(tree, options); } }; } diff --git a/packages/workspace/src/generators/preset/preset.ts b/packages/workspace/src/generators/preset/preset.ts index 025873d2a09c5..b3e722bcb9857 100644 --- a/packages/workspace/src/generators/preset/preset.ts +++ b/packages/workspace/src/generators/preset/preset.ts @@ -7,6 +7,7 @@ import { } from '@nx/devkit'; import { Schema } from './schema'; import { Preset } from '../utils/presets'; +import { join } from 'path'; export async function presetGenerator(tree: Tree, options: Schema) { options = normalizeOptions(options); @@ -29,6 +30,8 @@ async function createPreset(tree: Tree, options: Schema) { return angularApplicationGenerator(tree, { name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', style: options.style, linter: options.linter, standalone: options.standaloneApi, @@ -42,6 +45,8 @@ async function createPreset(tree: Tree, options: Schema) { return angularApplicationGenerator(tree, { name: options.name, + directory: '.', + projectNameAndRootFormat: 'as-provided', style: options.style, linter: options.linter, routing: options.routing, @@ -55,6 +60,8 @@ async function createPreset(tree: Tree, options: Schema) { return reactApplicationGenerator(tree, { name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', style: options.style, linter: options.linter, bundler: options.bundler ?? 'webpack', @@ -66,6 +73,8 @@ async function createPreset(tree: Tree, options: Schema) { return reactApplicationGenerator(tree, { name: options.name, + directory: '.', + projectNameAndRootFormat: 'as-provided', style: options.style, linter: options.linter, rootProject: true, @@ -79,6 +88,8 @@ async function createPreset(tree: Tree, options: Schema) { return nextApplicationGenerator(tree, { name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', style: options.style, linter: options.linter, appDir: options.nextAppDir, @@ -89,6 +100,8 @@ async function createPreset(tree: Tree, options: Schema) { '/next'); return nextApplicationGenerator(tree, { name: options.name, + directory: '.', + projectNameAndRootFormat: 'as-provided', style: options.style, linter: options.linter, appDir: options.nextAppDir, @@ -101,6 +114,8 @@ async function createPreset(tree: Tree, options: Schema) { return webApplicationGenerator(tree, { name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', style: options.style, linter: options.linter, bundler: 'vite', @@ -112,6 +127,8 @@ async function createPreset(tree: Tree, options: Schema) { return nestApplicationGenerator(tree, { name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', linter: options.linter, e2eTestRunner: options.e2eTestRunner ?? 'jest', }); @@ -121,6 +138,8 @@ async function createPreset(tree: Tree, options: Schema) { } = require('@nx' + '/express'); return expressApplicationGenerator(tree, { name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', linter: options.linter, e2eTestRunner: options.e2eTestRunner ?? 'jest', }); @@ -129,6 +148,8 @@ async function createPreset(tree: Tree, options: Schema) { '/react-native'); return reactNativeApplicationGenerator(tree, { name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', linter: options.linter, e2eTestRunner: options.e2eTestRunner ?? 'detox', }); @@ -136,24 +157,20 @@ async function createPreset(tree: Tree, options: Schema) { const { expoApplicationGenerator } = require('@nx' + '/expo'); return expoApplicationGenerator(tree, { name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', linter: options.linter, e2eTestRunner: options.e2eTestRunner ?? 'detox', }); } else if (options.preset === Preset.TS) { - const c = readNxJson(tree); const { initGenerator } = require('@nx' + '/js'); - c.workspaceLayout = { - appsDir: 'packages', - libsDir: 'packages', - }; - updateNxJson(tree, c); return initGenerator(tree, {}); } else if (options.preset === Preset.TsStandalone) { - const c = readNxJson(tree); const { libraryGenerator } = require('@nx' + '/js'); - updateNxJson(tree, c); return libraryGenerator(tree, { name: options.name, + directory: join('packages', options.name), + projectNameAndRootFormat: 'as-provided', bundler: 'tsc', unitTestRunner: 'vitest', testEnvironment: 'node', @@ -167,6 +184,8 @@ async function createPreset(tree: Tree, options: Schema) { return nodeApplicationGenerator(tree, { bundler, name: options.name, + directory: '.', + projectNameAndRootFormat: 'as-provided', linter: options.linter, standaloneConfig: options.standaloneConfig, framework: options.framework, @@ -181,6 +200,8 @@ async function createPreset(tree: Tree, options: Schema) { return nodeApplicationGenerator(tree, { bundler, name: options.name, + directory: join('apps', options.name), + projectNameAndRootFormat: 'as-provided', linter: options.linter, framework: options.framework, docker: options.docker,