Skip to content

Commit

Permalink
feat(node): use helper to determine project name and root directory i…
Browse files Browse the repository at this point in the history
…n project generators (#18620)

Co-authored-by: FrozenPandaz <[email protected]>
  • Loading branch information
leosvelperez and FrozenPandaz authored Aug 21, 2023
1 parent 7900d56 commit d566055
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 101 deletions.
12 changes: 9 additions & 3 deletions docs/generated/packages/node/generators/application.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "application",
"factory": "./src/generators/application/application",
"factory": "./src/generators/application/application#applicationGeneratorInternal",
"schema": {
"$schema": "http://json-schema.org/schema",
"cli": "nx",
Expand All @@ -14,13 +14,19 @@
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What name would you like to use for the node application?",
"x-priority": "important"
"x-priority": "important",
"pattern": "^[a-zA-Z][^:]*$"
},
"directory": {
"description": "The directory of the new application.",
"type": "string",
"x-priority": "important"
},
"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"]
},
"skipFormat": {
"description": "Skip formatting files",
"type": "boolean",
Expand Down Expand Up @@ -131,7 +137,7 @@
"aliases": ["app"],
"x-type": "application",
"description": "Create a node application.",
"implementation": "/packages/node/src/generators/application/application.ts",
"implementation": "/packages/node/src/generators/application/application#applicationGeneratorInternal.ts",
"hidden": false,
"path": "/packages/node/src/generators/application/schema.json",
"type": "generator"
Expand Down
12 changes: 9 additions & 3 deletions docs/generated/packages/node/generators/library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "library",
"factory": "./src/generators/library/library",
"factory": "./src/generators/library/library#libraryGeneratorInternal",
"schema": {
"$schema": "http://json-schema.org/schema",
"cli": "nx",
Expand All @@ -19,13 +19,19 @@
"type": "string",
"description": "Library name",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What name would you like to use for the library?"
"x-prompt": "What name would you like to use for the library?",
"pattern": "(?:^@[a-zA-Z0-9-*~][a-zA-Z0-9-*._~]*\\/[a-zA-Z0-9-~][a-zA-Z0-9-._~]*|^[a-zA-Z][^:]*)$"
},
"directory": {
"type": "string",
"description": "A directory where the lib is placed",
"alias": "dir"
},
"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"]
},
"simpleModuleName": {
"description": "Keep the module name simple (when using `--directory`).",
"type": "boolean",
Expand Down Expand Up @@ -131,7 +137,7 @@
"aliases": ["lib"],
"x-type": "library",
"description": "Create a node library.",
"implementation": "/packages/node/src/generators/library/library.ts",
"implementation": "/packages/node/src/generators/library/library#libraryGeneratorInternal.ts",
"hidden": false,
"path": "/packages/node/src/generators/library/schema.json",
"type": "generator"
Expand Down
46 changes: 46 additions & 0 deletions e2e/node/src/node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,52 @@ ${jslib}();
checkFilesExist(`dist/apps/_should_keep.txt`);
}, 120000);

it('should support generating projects with the new name and root format', () => {
const appName = uniq('app1');
const libName = uniq('@my-org/lib1');

runCLI(
`generate @nx/node:app ${appName} --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}`
);

// assert scoped project names are not supported when --project-name-and-root-format=derived
expect(() =>
runCLI(
`generate @nx/node:lib ${libName} --buildable --project-name-and-root-format=derived --no-interactive`
)
).toThrow();

runCLI(
`generate @nx/node:lib ${libName} --buildable --project-name-and-root-format=as-provided --no-interactive`
);

// check files are generated without the layout directory ("libs/") and
// using the project name as the directory when no directory is provided
checkFilesExist(`${libName}/src/index.ts`);
// check build works
expect(runCLI(`build ${libName}`)).toContain(
`Successfully ran target build for project ${libName}`
);
// check tests pass
const libTestResult = runCLI(`test ${libName}`);
expect(libTestResult).toContain(
`Successfully ran target test for project ${libName}`
);
}, 500_000);

describe('NestJS', () => {
it('should have plugin output if specified in `tsPlugins`', async () => {
newProject();
Expand Down
4 changes: 2 additions & 2 deletions packages/node/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
"hidden": true
},
"application": {
"factory": "./src/generators/application/application",
"factory": "./src/generators/application/application#applicationGeneratorInternal",
"schema": "./src/generators/application/schema.json",
"aliases": ["app"],
"x-type": "application",
"description": "Create a node application."
},
"library": {
"factory": "./src/generators/library/library",
"factory": "./src/generators/library/library#libraryGeneratorInternal",
"schema": "./src/generators/library/schema.json",
"aliases": ["lib"],
"x-type": "library",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ describe('app', () => {

expect(() =>
readProjectConfiguration(tree, 'my-dir-my-node-app-e2e')
).toThrow(/Cannot find/);
).not.toThrow();
});

it('should update tags', async () => {
Expand Down
60 changes: 34 additions & 26 deletions packages/node/src/generators/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import {
addProjectConfiguration,
convertNxGenerator,
ensurePackage,
extractLayoutDirectory,
formatFiles,
generateFiles,
GeneratorCallback,
getWorkspaceLayout,
joinPathFragments,
logger,
names,
Expand All @@ -22,13 +20,13 @@ import {
updateProjectConfiguration,
updateTsConfigsToJs,
} from '@nx/devkit';
import { Linter, lintProjectGenerator } from '@nx/linter';
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
import { configurationGenerator } from '@nx/jest';

import { getRelativePathToRootTsConfig, tsConfigBaseOptions } from '@nx/js';
import { esbuildVersion } from '@nx/js/src/utils/versions';
import { Linter, lintProjectGenerator } from '@nx/linter';
import { mapLintPattern } from '@nx/linter/src/generators/lint-project/lint-project';
import { join } from 'path';

import { initGenerator } from '../init/init';
import {
expressTypingsVersion,
expressVersion,
Expand All @@ -41,11 +39,9 @@ import {
nxVersion,
} from '../../utils/versions';
import { e2eProjectGenerator } from '../e2e-project/e2e-project';
import { initGenerator } from '../init/init';
import { setupDockerGenerator } from '../setup-docker/setup-docker';

import { Schema } from './schema';
import { mapLintPattern } from '@nx/linter/src/generators/lint-project/lint-project';
import { esbuildVersion } from '@nx/js/src/utils/versions';

export interface NormalizedSchema extends Schema {
appProjectRoot: string;
Expand Down Expand Up @@ -364,7 +360,14 @@ function updateTsConfigOptions(tree: Tree, options: NormalizedSchema) {
}

export async function applicationGenerator(tree: Tree, schema: Schema) {
const options = normalizeOptions(tree, schema);
return await applicationGeneratorInternal(tree, {
projectNameAndRootFormat: 'derived',
...schema,
});
}

export async function applicationGeneratorInternal(tree: Tree, schema: Schema) {
const options = await normalizeOptions(tree, schema);
const tasks: GeneratorCallback[] = [];

if (options.framework === 'nest') {
Expand Down Expand Up @@ -414,6 +417,8 @@ export async function applicationGenerator(tree: Tree, schema: Schema) {
...options,
projectType: options.framework === 'none' ? 'cli' : 'server',
name: options.rootProject ? 'e2e' : `${options.name}-e2e`,
directory: options.rootProject ? 'e2e' : `${options.appProjectRoot}-e2e`,
projectNameAndRootFormat: 'as-provided',
project: options.name,
port: options.port,
isNest: options.isNest,
Expand Down Expand Up @@ -447,21 +452,24 @@ export async function applicationGenerator(tree: Tree, schema: Schema) {
return runTasksInSerial(...tasks);
}

function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
const { layoutDirectory, projectDirectory } = extractLayoutDirectory(
options.directory
);
const appsDir = layoutDirectory ?? getWorkspaceLayout(host).appsDir;

const appDirectory = projectDirectory
? `${names(projectDirectory).fileName}/${names(options.name).fileName}`
: names(options.name).fileName;

const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');

const appProjectRoot = options.rootProject
? '.'
: joinPathFragments(appsDir, appDirectory);
async function normalizeOptions(
host: Tree,
options: Schema
): Promise<NormalizedSchema> {
const {
projectName: appProjectName,
projectRoot: appProjectRoot,
projectNameAndRootFormat,
} = await determineProjectNameAndRootOptions(host, {
name: options.name,
projectType: 'application',
directory: options.directory,
projectNameAndRootFormat: options.projectNameAndRootFormat,
rootProject: options.rootProject,
callingGenerator: '@nx/node:application',
});
options.rootProject = appProjectRoot === '.';
options.projectNameAndRootFormat = projectNameAndRootFormat;

options.bundler = options.bundler ?? 'esbuild';
options.e2eTestRunner = options.e2eTestRunner ?? 'jest';
Expand All @@ -472,7 +480,7 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {

return {
...options,
name: names(appProjectName).fileName,
name: appProjectName,
frontendProject: options.frontendProject
? names(options.frontendProject).fileName
: undefined,
Expand Down
4 changes: 3 additions & 1 deletion packages/node/src/generators/application/schema.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Linter } from '@nx/linter';
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
import type { Linter } from '@nx/linter';

export interface Schema {
name: string;
skipFormat?: boolean;
skipPackageJson?: boolean;
directory?: string;
projectNameAndRootFormat?: ProjectNameAndRootFormat;
unitTestRunner?: 'jest' | 'none';
e2eTestRunner?: 'jest' | 'none';
linter?: Linter;
Expand Down
8 changes: 7 additions & 1 deletion packages/node/src/generators/application/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,19 @@
"index": 0
},
"x-prompt": "What name would you like to use for the node application?",
"x-priority": "important"
"x-priority": "important",
"pattern": "^[a-zA-Z][^:]*$"
},
"directory": {
"description": "The directory of the new application.",
"type": "string",
"x-priority": "important"
},
"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"]
},
"skipFormat": {
"description": "Skip formatting files",
"type": "boolean",
Expand Down
58 changes: 32 additions & 26 deletions packages/node/src/generators/e2e-project/e2e-project.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import * as path from 'path';
import {
addDependenciesToPackageJson,
addProjectConfiguration,
convertNxGenerator,
extractLayoutDirectory,
formatFiles,
generateFiles,
GeneratorCallback,
getWorkspaceLayout,
joinPathFragments,
names,
offsetFromRoot,
Expand All @@ -16,19 +13,30 @@ import {
Tree,
updateJson,
} from '@nx/devkit';
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
import { Linter, lintProjectGenerator } from '@nx/linter';

import { Schema } from './schema';
import { axiosVersion } from '../../utils/versions';
import { join } from 'path';
import {
globalJavaScriptOverrides,
globalTypeScriptOverrides,
} from '@nx/linter/src/generators/init/global-eslint-config';
import * as path from 'path';
import { join } from 'path';
import { axiosVersion } from '../../utils/versions';
import { Schema } from './schema';

export async function e2eProjectGenerator(host: Tree, _options: Schema) {
export async function e2eProjectGenerator(host: Tree, options: Schema) {
return await e2eProjectGeneratorInternal(host, {
projectNameAndRootFormat: 'derived',
...options,
});
}

export async function e2eProjectGeneratorInternal(
host: Tree,
_options: Schema
) {
const tasks: GeneratorCallback[] = [];
const options = normalizeOptions(host, _options);
const options = await normalizeOptions(host, _options);
const appProject = readProjectConfiguration(host, options.project);

addProjectConfiguration(host, options.e2eProjectName, {
Expand Down Expand Up @@ -146,25 +154,23 @@ export async function e2eProjectGenerator(host: Tree, _options: Schema) {
return runTasksInSerial(...tasks);
}

function normalizeOptions(
async function normalizeOptions(
tree: Tree,
options: Schema
): Omit<Schema, 'name'> & { e2eProjectRoot: string; e2eProjectName: string } {
const { layoutDirectory, projectDirectory } = extractLayoutDirectory(
options.directory
);
const appsDir = layoutDirectory ?? getWorkspaceLayout(tree).appsDir;
const name = options.name ?? `${options.project}-e2e`;

const appDirectory = projectDirectory
? `${names(projectDirectory).fileName}/${names(name).fileName}`
: names(name).fileName;

const e2eProjectName = appDirectory.replace(new RegExp('/', 'g'), '-');

const e2eProjectRoot = options.rootProject
? 'e2e'
: joinPathFragments(appsDir, appDirectory);
): Promise<
Omit<Schema, 'name'> & { e2eProjectRoot: string; e2eProjectName: string }
> {
const { projectName: e2eProjectName, projectRoot: e2eProjectRoot } =
await determineProjectNameAndRootOptions(tree, {
name: options.name ?? `${options.project}-e2e`,
projectType: 'library',
directory: options.rootProject ? 'e2e' : options.directory,
projectNameAndRootFormat: options.rootProject
? 'as-provided'
: options.projectNameAndRootFormat,
// this is an internal generator, don't save defaults
callingGenerator: null,
});

return {
...options,
Expand Down
Loading

1 comment on commit d566055

@vercel
Copy link

@vercel vercel bot commented on d566055 Aug 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-five.vercel.app
nx-dev-git-master-nrwl.vercel.app
nx-dev-nrwl.vercel.app
nx.dev

Please sign in to comment.