feat(nest): use helper to determine project name and root in project generators (#18701)

This commit is contained in:
Leosvel Pérez Espinosa 2023-08-22 15:26:10 +01:00 committed by GitHub
parent a668e0b7d6
commit e1b76e26f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 107 additions and 65 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "application", "name": "application",
"factory": "./src/generators/application/application", "factory": "./src/generators/application/application#applicationGeneratorInternal",
"schema": { "schema": {
"$schema": "http://json-schema.org/schema", "$schema": "http://json-schema.org/schema",
"$id": "NxNestApplicationGenerator", "$id": "NxNestApplicationGenerator",
@ -13,12 +13,18 @@
"description": "The name of the application.", "description": "The name of the application.",
"type": "string", "type": "string",
"$default": { "$source": "argv", "index": 0 }, "$default": { "$source": "argv", "index": 0 },
"x-prompt": "What name would you like to use for the node application?" "x-prompt": "What name would you like to use for the node application?",
"pattern": "^[a-zA-Z][^:]*$"
}, },
"directory": { "directory": {
"description": "The directory of the new application.", "description": "The directory of the new application.",
"type": "string" "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"]
},
"skipFormat": { "skipFormat": {
"description": "Skip formatting files.", "description": "Skip formatting files.",
"type": "boolean", "type": "boolean",
@ -82,7 +88,7 @@
"aliases": ["app"], "aliases": ["app"],
"x-type": "application", "x-type": "application",
"description": "Create a NestJS application.", "description": "Create a NestJS application.",
"implementation": "/packages/nest/src/generators/application/application.ts", "implementation": "/packages/nest/src/generators/application/application#applicationGeneratorInternal.ts",
"hidden": false, "hidden": false,
"path": "/packages/nest/src/generators/application/schema.json", "path": "/packages/nest/src/generators/application/schema.json",
"type": "generator" "type": "generator"

View File

@ -1,6 +1,6 @@
{ {
"name": "library", "name": "library",
"factory": "./src/generators/library/library", "factory": "./src/generators/library/library#libraryGeneratorInternal",
"schema": { "schema": {
"$schema": "http://json-schema.org/schema", "$schema": "http://json-schema.org/schema",
"$id": "NxNestLibraryGenerator", "$id": "NxNestLibraryGenerator",
@ -19,13 +19,19 @@
"description": "Library name.", "description": "Library name.",
"type": "string", "type": "string",
"$default": { "$source": "argv", "index": 0 }, "$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": { "directory": {
"description": "A directory where the library is placed.", "description": "A directory where the library is placed.",
"type": "string", "type": "string",
"alias": "dir" "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"]
},
"linter": { "linter": {
"description": "The tool to use for running lint checks.", "description": "The tool to use for running lint checks.",
"type": "string", "type": "string",
@ -143,7 +149,7 @@
"aliases": ["lib"], "aliases": ["lib"],
"x-type": "library", "x-type": "library",
"description": "Create a new NestJS library.", "description": "Create a new NestJS library.",
"implementation": "/packages/nest/src/generators/library/library.ts", "implementation": "/packages/nest/src/generators/library/library#libraryGeneratorInternal.ts",
"hidden": false, "hidden": false,
"path": "/packages/nest/src/generators/library/schema.json", "path": "/packages/nest/src/generators/library/schema.json",
"type": "generator" "type": "generator"

View File

@ -108,7 +108,7 @@
}, },
"generators": { "generators": {
"application": { "application": {
"factory": "./src/generators/application/application", "factory": "./src/generators/application/application#applicationGeneratorInternal",
"schema": "./src/generators/application/schema.json", "schema": "./src/generators/application/schema.json",
"aliases": ["app"], "aliases": ["app"],
"x-type": "application", "x-type": "application",
@ -128,7 +128,7 @@
"hidden": true "hidden": true
}, },
"library": { "library": {
"factory": "./src/generators/library/library", "factory": "./src/generators/library/library#libraryGeneratorInternal",
"schema": "./src/generators/library/schema.json", "schema": "./src/generators/library/schema.json",
"aliases": ["lib"], "aliases": ["lib"],
"x-type": "library", "x-type": "library",

View File

@ -15,7 +15,17 @@ export async function applicationGenerator(
tree: Tree, tree: Tree,
rawOptions: ApplicationGeneratorOptions rawOptions: ApplicationGeneratorOptions
): Promise<GeneratorCallback> { ): Promise<GeneratorCallback> {
const options = normalizeOptions(tree, rawOptions); return await applicationGeneratorInternal(tree, {
projectNameAndRootFormat: 'derived',
...rawOptions,
});
}
export async function applicationGeneratorInternal(
tree: Tree,
rawOptions: ApplicationGeneratorOptions
): Promise<GeneratorCallback> {
const options = await normalizeOptions(tree, rawOptions);
const initTask = await initGenerator(tree, { const initTask = await initGenerator(tree, {
skipPackageJson: options.skipPackageJson, skipPackageJson: options.skipPackageJson,
unitTestRunner: options.unitTestRunner, unitTestRunner: options.unitTestRunner,

View File

@ -9,7 +9,7 @@ export function createFiles(tree: Tree, options: NormalizedOptions): void {
joinPathFragments(options.appProjectRoot, 'src'), joinPathFragments(options.appProjectRoot, 'src'),
{ {
tmpl: '', tmpl: '',
name: options.name, name: options.appProjectName,
root: options.appProjectRoot, root: options.appProjectRoot,
} }
); );

View File

@ -1,31 +1,32 @@
import { extractLayoutDirectory, Tree } from '@nx/devkit'; import { Tree } from '@nx/devkit';
import { getWorkspaceLayout, joinPathFragments, names } from '@nx/devkit'; import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
import { Linter } from '@nx/linter'; import { Linter } from '@nx/linter';
import type { Schema as NodeApplicationGeneratorOptions } from '@nx/node/src/generators/application/schema'; import type { Schema as NodeApplicationGeneratorOptions } from '@nx/node/src/generators/application/schema';
import type { ApplicationGeneratorOptions, NormalizedOptions } from '../schema'; import type { ApplicationGeneratorOptions, NormalizedOptions } from '../schema';
export function normalizeOptions( export async function normalizeOptions(
tree: Tree, tree: Tree,
options: ApplicationGeneratorOptions options: ApplicationGeneratorOptions
): NormalizedOptions { ): Promise<NormalizedOptions> {
const { layoutDirectory, projectDirectory } = extractLayoutDirectory( const {
options.directory projectName: appProjectName,
); projectRoot: appProjectRoot,
projectNameAndRootFormat,
const appDirectory = projectDirectory } = await determineProjectNameAndRootOptions(tree, {
? `${names(projectDirectory).fileName}/${names(options.name).fileName}` name: options.name,
: names(options.name).fileName; projectType: 'application',
directory: options.directory,
const appProjectRoot = options.rootProject projectNameAndRootFormat: options.projectNameAndRootFormat,
? '.' rootProject: options.rootProject,
: joinPathFragments( callingGenerator: '@nx/nest:application',
layoutDirectory ?? getWorkspaceLayout(tree).appsDir, });
appDirectory options.rootProject = appProjectRoot === '.';
); options.projectNameAndRootFormat = projectNameAndRootFormat;
return { return {
...options, ...options,
strict: options.strict ?? false, strict: options.strict ?? false,
appProjectName,
appProjectRoot, appProjectRoot,
linter: options.linter ?? Linter.EsLint, linter: options.linter ?? Linter.EsLint,
unitTestRunner: options.unitTestRunner ?? 'jest', unitTestRunner: options.unitTestRunner ?? 'jest',

View File

@ -1,8 +1,10 @@
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 ApplicationGeneratorOptions { export interface ApplicationGeneratorOptions {
name: string; name: string;
directory?: string; directory?: string;
projectNameAndRootFormat?: ProjectNameAndRootFormat;
frontendProject?: string; frontendProject?: string;
linter?: Linter; linter?: Linter;
skipFormat?: boolean; skipFormat?: boolean;
@ -17,5 +19,6 @@ export interface ApplicationGeneratorOptions {
} }
interface NormalizedOptions extends ApplicationGeneratorOptions { interface NormalizedOptions extends ApplicationGeneratorOptions {
appProjectName: string;
appProjectRoot: Path; appProjectRoot: Path;
} }

View File

@ -13,12 +13,18 @@
"$source": "argv", "$source": "argv",
"index": 0 "index": 0
}, },
"x-prompt": "What name would you like to use for the node application?" "x-prompt": "What name would you like to use for the node application?",
"pattern": "^[a-zA-Z][^:]*$"
}, },
"directory": { "directory": {
"description": "The directory of the new application.", "description": "The directory of the new application.",
"type": "string" "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"]
},
"skipFormat": { "skipFormat": {
"description": "Skip formatting files.", "description": "Skip formatting files.",
"type": "boolean", "type": "boolean",

View File

@ -15,10 +15,7 @@ export function addProject(tree: Tree, options: NormalizedOptions): void {
executor: '@nx/js:tsc', executor: '@nx/js:tsc',
outputs: ['{options.outputPath}'], outputs: ['{options.outputPath}'],
options: { options: {
outputPath: outputPath: `dist/${options.projectRoot}`,
options.libsDir && options.libsDir !== '.'
? `dist/${options.libsDir}/${options.projectDirectory}`
: `dist/${options.projectDirectory}`,
tsConfig: `${options.projectRoot}/tsconfig.lib.json`, tsConfig: `${options.projectRoot}/tsconfig.lib.json`,
packageJson: `${options.projectRoot}/package.json`, packageJson: `${options.projectRoot}/package.json`,
main: `${options.projectRoot}/src/index.ts`, main: `${options.projectRoot}/src/index.ts`,

View File

@ -1,33 +1,31 @@
import { Tree } from '@nx/devkit';
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope';
import type { LibraryGeneratorSchema as JsLibraryGeneratorSchema } from '@nx/js/src/utils/schema'; import type { LibraryGeneratorSchema as JsLibraryGeneratorSchema } from '@nx/js/src/utils/schema';
import { Linter } from '@nx/linter'; import { Linter } from '@nx/linter';
import {
extractLayoutDirectory,
getWorkspaceLayout,
joinPathFragments,
names,
Tree,
} from '@nx/devkit';
import type { LibraryGeneratorOptions, NormalizedOptions } from '../schema'; import type { LibraryGeneratorOptions, NormalizedOptions } from '../schema';
import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope';
export function normalizeOptions( export async function normalizeOptions(
tree: Tree, tree: Tree,
options: LibraryGeneratorOptions options: LibraryGeneratorOptions
): NormalizedOptions { ): Promise<NormalizedOptions> {
const { layoutDirectory, projectDirectory } = extractLayoutDirectory( const {
options.directory projectName,
); names: projectNames,
const { libsDir: defaultLibsDir } = getWorkspaceLayout(tree); projectRoot,
const libsDir = layoutDirectory ?? defaultLibsDir; importPath,
const name = names(options.name).fileName; } = await determineProjectNameAndRootOptions(tree, {
const fullProjectDirectory = projectDirectory name: options.name,
? `${names(projectDirectory).fileName}/${name}` projectType: 'library',
: name; directory: options.directory,
importPath: options.importPath,
const projectName = fullProjectDirectory.replace(new RegExp('/', 'g'), '-'); projectNameAndRootFormat: options.projectNameAndRootFormat,
const fileName = options.simpleName ? name : projectName; callingGenerator: '@nx/nest:library',
const projectRoot = joinPathFragments(libsDir, fullProjectDirectory); });
const fileName = options.simpleName
? projectNames.projectSimpleName
: projectNames.projectFileName;
const parsedTags = options.tags const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim()) ? options.tags.split(',').map((s) => s.trim())
: []; : [];
@ -41,14 +39,13 @@ export function normalizeOptions(
linter: options.linter ?? Linter.EsLint, linter: options.linter ?? Linter.EsLint,
parsedTags, parsedTags,
prefix: getNpmScope(tree), // we could also allow customizing this prefix: getNpmScope(tree), // we could also allow customizing this
projectDirectory: fullProjectDirectory,
projectName, projectName,
projectRoot, projectRoot,
importPath,
service: options.service ?? false, service: options.service ?? false,
target: options.target ?? 'es6', target: options.target ?? 'es6',
testEnvironment: options.testEnvironment ?? 'node', testEnvironment: options.testEnvironment ?? 'node',
unitTestRunner: options.unitTestRunner ?? 'jest', unitTestRunner: options.unitTestRunner ?? 'jest',
libsDir,
}; };
return normalized; return normalized;

View File

@ -17,7 +17,17 @@ export async function libraryGenerator(
tree: Tree, tree: Tree,
rawOptions: LibraryGeneratorOptions rawOptions: LibraryGeneratorOptions
): Promise<GeneratorCallback> { ): Promise<GeneratorCallback> {
const options = normalizeOptions(tree, rawOptions); return await libraryGeneratorInternal(tree, {
projectNameAndRootFormat: 'derived',
...rawOptions,
});
}
export async function libraryGeneratorInternal(
tree: Tree,
rawOptions: LibraryGeneratorOptions
): Promise<GeneratorCallback> {
const options = await normalizeOptions(tree, rawOptions);
await jsLibraryGenerator(tree, toJsLibraryGeneratorOptions(options)); await jsLibraryGenerator(tree, toJsLibraryGeneratorOptions(options));
const installDepsTask = addDependencies(tree); const installDepsTask = addDependencies(tree);
deleteFiles(tree, options); deleteFiles(tree, options);

View File

@ -1,11 +1,13 @@
import { Linter } from '@nx/linter'; import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
import { UnitTestRunner } from '../utils'; import type { Linter } from '@nx/linter';
import type { UnitTestRunner } from '../utils';
export interface LibraryGeneratorOptions { export interface LibraryGeneratorOptions {
name: string; name: string;
buildable?: boolean; buildable?: boolean;
controller?: boolean; controller?: boolean;
directory?: string; directory?: string;
projectNameAndRootFormat?: ProjectNameAndRootFormat;
global?: boolean; global?: boolean;
importPath?: string; importPath?: string;
linter?: Linter; linter?: Linter;
@ -38,8 +40,6 @@ export interface NormalizedOptions extends LibraryGeneratorOptions {
fileName: string; fileName: string;
parsedTags: string[]; parsedTags: string[];
prefix: string; prefix: string;
projectDirectory: string;
projectName: string; projectName: string;
projectRoot: Path; projectRoot: Path;
libsDir: string;
} }

View File

@ -19,13 +19,19 @@
"$source": "argv", "$source": "argv",
"index": 0 "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": { "directory": {
"description": "A directory where the library is placed.", "description": "A directory where the library is placed.",
"type": "string", "type": "string",
"alias": "dir" "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"]
},
"linter": { "linter": {
"description": "The tool to use for running lint checks.", "description": "The tool to use for running lint checks.",
"type": "string", "type": "string",