feat(misc): introduce a way to set the project name/root format for all generators (#18971)

This commit is contained in:
Jason Jean 2023-09-01 16:58:14 -04:00 committed by GitHub
parent 0cc6ba996f
commit bd1b0b70fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 107 additions and 35 deletions

View File

@ -191,7 +191,8 @@ Where new apps + libs should be placed
#### Type declaration #### Type declaration
| Name | Type | | Name | Type |
| :-------- | :------- | | :-------------------------- | :----------------------------- |
| `appsDir` | `string` | | `appsDir?` | `string` |
| `libsDir` | `string` | | `libsDir?` | `string` |
| `projectNameAndRootFormat?` | `"as-provided"` \| `"derived"` |

View File

@ -267,10 +267,11 @@ Where new apps + libs should be placed
#### Type declaration #### Type declaration
| Name | Type | | Name | Type |
| :-------- | :------- | | :-------------------------- | :----------------------------- |
| `appsDir` | `string` | | `appsDir?` | `string` |
| `libsDir` | `string` | | `libsDir?` | `string` |
| `projectNameAndRootFormat?` | `"as-provided"` \| `"derived"` |
#### Inherited from #### Inherited from

View File

@ -558,7 +558,7 @@ describe('Linter', () => {
bundler: 'vite', bundler: 'vite',
e2eTestRunner: 'none', e2eTestRunner: 'none',
}); });
runCLI(`generate @nx/js:lib ${mylib}`); runCLI(`generate @nx/js:lib ${mylib} --directory libs/${mylib}`);
// migrate to flat structure // migrate to flat structure
runCLI(`generate @nx/linter:convert-to-flat-config`); runCLI(`generate @nx/linter:convert-to-flat-config`);

View File

@ -28,7 +28,7 @@ import {
RunCmdOpts, RunCmdOpts,
runCommand, runCommand,
} from './command-utils'; } from './command-utils';
import { output } from '@nx/devkit'; import { NxJsonConfiguration, output } from '@nx/devkit';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
@ -41,6 +41,7 @@ let projName: string;
export function newProject({ export function newProject({
name = uniq('proj'), name = uniq('proj'),
packageManager = getSelectedPackageManager(), packageManager = getSelectedPackageManager(),
unsetProjectNameAndRootFormat = true,
} = {}): string { } = {}): string {
try { try {
const projScope = 'proj'; const projScope = 'proj';
@ -51,6 +52,13 @@ export function newProject({
packageManager, packageManager,
}); });
if (unsetProjectNameAndRootFormat) {
updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
delete nxJson.workspaceLayout;
return nxJson;
});
}
// Temporary hack to prevent installing with `--frozen-lockfile` // Temporary hack to prevent installing with `--frozen-lockfile`
if (isCI && packageManager === 'pnpm') { if (isCI && packageManager === 'pnpm') {
updateFile( updateFile(

View File

@ -348,10 +348,21 @@ describe('determineProjectNameAndRootOptions', () => {
expect(promptSpy).toHaveBeenCalledTimes(2); expect(promptSpy).toHaveBeenCalledTimes(2);
expect(readNxJson(tree).generators['@nx/some-plugin:app']).toEqual({ expect(readNxJson(tree).workspaceLayout).toEqual({
projectNameAndRootFormat: 'as-provided', 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 // restore original interactive mode
restoreOriginalInteractiveMode(); restoreOriginalInteractiveMode();
}); });

View File

@ -75,7 +75,9 @@ export async function determineProjectNameAndRootOptions(
const formats = getProjectNameAndRootFormats(tree, options); const formats = getProjectNameAndRootFormats(tree, options);
const format = const format =
options.projectNameAndRootFormat ?? options.projectNameAndRootFormat ??
(await determineFormat(tree, formats, options.callingGenerator)); (getDefaultProjectNameAndRootFormat(tree) === 'as-provided'
? 'as-provided'
: await determineFormat(tree, formats, options.callingGenerator));
return { return {
...formats[format], ...formats[format],
@ -167,11 +169,7 @@ async function determineFormat(
initial: true, initial: true,
}); });
if (saveDefault) { if (saveDefault) {
const nxJson = readNxJson(tree); setProjectNameAndRootFormatDefault(tree);
nxJson.generators ??= {};
nxJson.generators[callingGenerator] ??= {};
nxJson.generators[callingGenerator].projectNameAndRootFormat = result;
updateNxJson(tree, nxJson);
} else { } else {
logger.warn(deprecationWarning); logger.warn(deprecationWarning);
} }
@ -183,6 +181,18 @@ async function determineFormat(
return result; 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( function getProjectNameAndRootFormats(
tree: Tree, tree: Tree,
options: ProjectGenerationOptions options: ProjectGenerationOptions

View File

@ -41,6 +41,7 @@ export function toNodeApplicationGeneratorOptions(
name: options.name, name: options.name,
directory: options.directory, directory: options.directory,
frontendProject: options.frontendProject, frontendProject: options.frontendProject,
projectNameAndRootFormat: options.projectNameAndRootFormat,
linter: options.linter, linter: options.linter,
skipFormat: true, skipFormat: true,
skipPackageJson: options.skipPackageJson, skipPackageJson: options.skipPackageJson,

View File

@ -56,6 +56,11 @@
"appsDir": { "appsDir": {
"type": "string", "type": "string",
"description": "Default folder name for apps." "description": "Default folder name for apps."
},
"projectNameAndRootFormat": {
"type": "string",
"description": "Default method of handling arguments for generating projects",
"enum": ["as-provided", "derived"]
} }
}, },
"additionalProperties": false "additionalProperties": false

View File

@ -86,8 +86,9 @@ export interface NxJsonConfiguration<T = '*' | string[]> {
* Where new apps + libs should be placed * Where new apps + libs should be placed
*/ */
workspaceLayout?: { workspaceLayout?: {
libsDir: string; libsDir?: string;
appsDir: string; appsDir?: string;
projectNameAndRootFormat?: 'as-provided' | 'derived';
}; };
/** /**
* Available Task Runners * Available Task Runners

View File

@ -53,5 +53,8 @@ exports[`new should generate an empty nx.json 1`] = `
"runner": "nx/tasks-runners/default", "runner": "nx/tasks-runners/default",
}, },
}, },
"workspaceLayout": {
"projectNameAndRootFormat": "as-provided",
},
} }
`; `;

View File

@ -122,6 +122,9 @@ describe('@nx/workspace:generateWorkspaceFiles', () => {
"runner": "nx/tasks-runners/default", "runner": "nx/tasks-runners/default",
}, },
}, },
"workspaceLayout": {
"projectNameAndRootFormat": "as-provided",
},
} }
`); `);
const validateNxJson = ajv.compile(nxSchema); const validateNxJson = ajv.compile(nxSchema);
@ -174,6 +177,9 @@ describe('@nx/workspace:generateWorkspaceFiles', () => {
"runner": "nx/tasks-runners/default", "runner": "nx/tasks-runners/default",
}, },
}, },
"workspaceLayout": {
"projectNameAndRootFormat": "as-provided",
},
} }
`); `);
}); });

View File

@ -98,6 +98,9 @@ function createNxJson(
}, },
}, },
}, },
workspaceLayout: {
projectNameAndRootFormat: 'as-provided',
},
}; };
nxJson.targetDefaults = { nxJson.targetDefaults = {

View File

@ -41,27 +41,28 @@ export interface NormalizedSchema extends Schema {
isCustomPreset: boolean; isCustomPreset: boolean;
} }
export async function newGenerator(host: Tree, opts: Schema) { export async function newGenerator(tree: Tree, opts: Schema) {
const options = normalizeOptions(opts); 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); addPresetDependencies(tree, options);
addCloudDependencies(host, options);
addCloudDependencies(tree, options);
return async () => { return async () => {
const pmc = getPackageManagerCommand(options.packageManager); const pmc = getPackageManagerCommand(options.packageManager);
if (pmc.preInstall) { if (pmc.preInstall) {
execSync(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', 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 // TODO: move all of these into create-nx-workspace
if (options.preset !== Preset.NPM && !options.isCustomPreset) { if (options.preset !== Preset.NPM && !options.isCustomPreset) {
await generatePreset(host, options); await generatePreset(tree, options);
} }
}; };
} }

View File

@ -7,6 +7,7 @@ import {
} from '@nx/devkit'; } from '@nx/devkit';
import { Schema } from './schema'; import { Schema } from './schema';
import { Preset } from '../utils/presets'; import { Preset } from '../utils/presets';
import { join } from 'path';
export async function presetGenerator(tree: Tree, options: Schema) { export async function presetGenerator(tree: Tree, options: Schema) {
options = normalizeOptions(options); options = normalizeOptions(options);
@ -29,6 +30,8 @@ async function createPreset(tree: Tree, options: Schema) {
return angularApplicationGenerator(tree, { return angularApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
standalone: options.standaloneApi, standalone: options.standaloneApi,
@ -42,6 +45,8 @@ async function createPreset(tree: Tree, options: Schema) {
return angularApplicationGenerator(tree, { return angularApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: '.',
projectNameAndRootFormat: 'as-provided',
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
routing: options.routing, routing: options.routing,
@ -55,6 +60,8 @@ async function createPreset(tree: Tree, options: Schema) {
return reactApplicationGenerator(tree, { return reactApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
bundler: options.bundler ?? 'webpack', bundler: options.bundler ?? 'webpack',
@ -66,6 +73,8 @@ async function createPreset(tree: Tree, options: Schema) {
return reactApplicationGenerator(tree, { return reactApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: '.',
projectNameAndRootFormat: 'as-provided',
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
rootProject: true, rootProject: true,
@ -79,6 +88,8 @@ async function createPreset(tree: Tree, options: Schema) {
return nextApplicationGenerator(tree, { return nextApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
appDir: options.nextAppDir, appDir: options.nextAppDir,
@ -89,6 +100,8 @@ async function createPreset(tree: Tree, options: Schema) {
'/next'); '/next');
return nextApplicationGenerator(tree, { return nextApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: '.',
projectNameAndRootFormat: 'as-provided',
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
appDir: options.nextAppDir, appDir: options.nextAppDir,
@ -101,6 +114,8 @@ async function createPreset(tree: Tree, options: Schema) {
return webApplicationGenerator(tree, { return webApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
bundler: 'vite', bundler: 'vite',
@ -112,6 +127,8 @@ async function createPreset(tree: Tree, options: Schema) {
return nestApplicationGenerator(tree, { return nestApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'jest', e2eTestRunner: options.e2eTestRunner ?? 'jest',
}); });
@ -121,6 +138,8 @@ async function createPreset(tree: Tree, options: Schema) {
} = require('@nx' + '/express'); } = require('@nx' + '/express');
return expressApplicationGenerator(tree, { return expressApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'jest', e2eTestRunner: options.e2eTestRunner ?? 'jest',
}); });
@ -129,6 +148,8 @@ async function createPreset(tree: Tree, options: Schema) {
'/react-native'); '/react-native');
return reactNativeApplicationGenerator(tree, { return reactNativeApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'detox', e2eTestRunner: options.e2eTestRunner ?? 'detox',
}); });
@ -136,24 +157,20 @@ async function createPreset(tree: Tree, options: Schema) {
const { expoApplicationGenerator } = require('@nx' + '/expo'); const { expoApplicationGenerator } = require('@nx' + '/expo');
return expoApplicationGenerator(tree, { return expoApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'detox', e2eTestRunner: options.e2eTestRunner ?? 'detox',
}); });
} else if (options.preset === Preset.TS) { } else if (options.preset === Preset.TS) {
const c = readNxJson(tree);
const { initGenerator } = require('@nx' + '/js'); const { initGenerator } = require('@nx' + '/js');
c.workspaceLayout = {
appsDir: 'packages',
libsDir: 'packages',
};
updateNxJson(tree, c);
return initGenerator(tree, {}); return initGenerator(tree, {});
} else if (options.preset === Preset.TsStandalone) { } else if (options.preset === Preset.TsStandalone) {
const c = readNxJson(tree);
const { libraryGenerator } = require('@nx' + '/js'); const { libraryGenerator } = require('@nx' + '/js');
updateNxJson(tree, c);
return libraryGenerator(tree, { return libraryGenerator(tree, {
name: options.name, name: options.name,
directory: join('packages', options.name),
projectNameAndRootFormat: 'as-provided',
bundler: 'tsc', bundler: 'tsc',
unitTestRunner: 'vitest', unitTestRunner: 'vitest',
testEnvironment: 'node', testEnvironment: 'node',
@ -167,6 +184,8 @@ async function createPreset(tree: Tree, options: Schema) {
return nodeApplicationGenerator(tree, { return nodeApplicationGenerator(tree, {
bundler, bundler,
name: options.name, name: options.name,
directory: '.',
projectNameAndRootFormat: 'as-provided',
linter: options.linter, linter: options.linter,
standaloneConfig: options.standaloneConfig, standaloneConfig: options.standaloneConfig,
framework: options.framework, framework: options.framework,
@ -181,6 +200,8 @@ async function createPreset(tree: Tree, options: Schema) {
return nodeApplicationGenerator(tree, { return nodeApplicationGenerator(tree, {
bundler, bundler,
name: options.name, name: options.name,
directory: join('apps', options.name),
projectNameAndRootFormat: 'as-provided',
linter: options.linter, linter: options.linter,
framework: options.framework, framework: options.framework,
docker: options.docker, docker: options.docker,