Skip to content

Commit

Permalink
feat(angular): support generating artifacts using options as provided
Browse files Browse the repository at this point in the history
  • Loading branch information
leosvelperez committed Oct 10, 2023
1 parent f4c7ee2 commit 765f173
Show file tree
Hide file tree
Showing 12 changed files with 1,380 additions and 59 deletions.
43 changes: 28 additions & 15 deletions docs/generated/packages/angular/generators/component.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,44 @@
{
"name": "component",
"factory": "./src/generators/component/component",
"factory": "./src/generators/component/component#componentGeneratorInternal",
"schema": {
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "SchematicsAngularComponent",
"title": "Angular Component Schema",
"cli": "nx",
"type": "object",
"description": "Creates a new, generic Angular component definition in the given or default project.",
"description": "Creates a new Angular component.",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "The name of the component.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What name would you like to use for the component?"
},
"directory": {
"type": "string",
"description": "The directory at which to create the component file. When `--nameAndDirectoryFormat=as-provided`, it will be relative to the current working directory. Otherwise, it will be relative to the workspace root.",
"aliases": ["dir"],
"x-priority": "important"
},
"nameAndDirectoryFormat": {
"description": "Whether to generate the component in the directory as provided, relative to the current working directory and ignoring the project (`as-provided`) or generate it using the project and directory relative to the workspace root (`derived`).",
"type": "string",
"enum": ["as-provided", "derived"]
},
"path": {
"type": "string",
"format": "path",
"description": "The path at which to create the component file, relative to the current workspace. Default is a folder with the same name as the component in the project root.",
"visible": false
"description": "The directory at which to create the component file. When `--nameAndDirectoryFormat=as-provided`, it will be relative to the current working directory. Otherwise, it will be relative to the workspace root.",
"visible": false,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "projectName" },
"x-dropdown": "projects"
},
"name": {
"type": "string",
"description": "The name of the component.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What name would you like to use for the component?"
"x-dropdown": "projects",
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. The project will be determined from the directory provided. It will be removed in Nx v18."
},
"prefix": {
"type": "string",
Expand Down Expand Up @@ -89,7 +101,8 @@
"flat": {
"type": "boolean",
"description": "Create the new files at the top level of the current project.",
"default": false
"default": false,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"skipImport": {
"type": "boolean",
Expand Down Expand Up @@ -124,13 +137,13 @@
"x-priority": "internal"
}
},
"required": ["name", "project"],
"required": ["name"],
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Component\" %}\n\nCreate a component named `my-component`:\n\n```bash\nnx g @nx/angular:component my-component\n```\n\n{% /tab %}\n\n{% tab label=\"Standalone Component\" %}\n\nCreate a standalone component named `my-component`:\n\n```bash\nnx g @nx/angular:component my-component --standalone\n```\n\n{% /tab %}\n\n{% tab label=\"Single File Component\" %}\n\nCreate a component named `my-component` with inline styles and inline template:\n\n```bash\nnx g @nx/angular:component my-component --inlineStyle --inlineTemplate\n```\n\n{% /tab %}\n\n{% tab label=\"Component with OnPush Change Detection Strategy\" %}\n\nCreate a component named `my-component` with OnPush Change Detection Strategy:\n\n```bash\nnx g @nx/angular:component my-component --changeDetection=OnPush\n```\n\n{% /tab %}\n",
"presets": []
},
"aliases": ["c"],
"description": "Generate an Angular Component.",
"implementation": "/packages/angular/src/generators/component/component.ts",
"implementation": "/packages/angular/src/generators/component/component#componentGeneratorInternal.ts",
"hidden": false,
"path": "/packages/angular/src/generators/component/schema.json",
"type": "generator"
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
"description": "Creates an Angular application."
},
"component": {
"factory": "./src/generators/component/component",
"factory": "./src/generators/component/component#componentGeneratorInternal",
"schema": "./src/generators/component/schema.json",
"aliases": ["c"],
"description": "Generate an Angular Component."
Expand Down
16 changes: 13 additions & 3 deletions packages/angular/src/generators/component/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,18 @@ import {
import type { Schema } from './schema';

export async function componentGenerator(tree: Tree, rawOptions: Schema) {
await componentGeneratorInternal(tree, {
nameAndDirectoryFormat: 'derived',
...rawOptions,
});
}

export async function componentGeneratorInternal(
tree: Tree,
rawOptions: Schema
) {
validateOptions(tree, rawOptions);
const options = normalizeOptions(tree, rawOptions);
const options = await normalizeOptions(tree, rawOptions);

const componentNames = names(options.name);
const typeNames = names(options.type);
Expand Down Expand Up @@ -78,13 +88,13 @@ export async function componentGenerator(tree: Tree, rawOptions: Schema) {
);
addToNgModule(
tree,
options.path,
options.directory,
modulePath,
componentNames.fileName,
`${componentNames.className}${typeNames.className}`,
`${componentNames.fileName}.${typeNames.fileName}`,
'declarations',
options.flat,
true,
options.export
);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/src/generators/component/lib/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export function findModuleFromOptions(
if (!options.module) {
return normalizePath(findModule(tree, options.directory, projectRoot));
} else {
const modulePath = joinPathFragments(options.path, options.module);
const modulePath = joinPathFragments(options.directory, options.module);
const componentPath = options.directory;
const moduleBaseName = basename(modulePath);

const candidateSet = new Set<string>([options.path]);
const candidateSet = new Set<string>([options.directory]);

const projectRootParent = dirname(projectRoot);
for (let dir = modulePath; dir !== projectRootParent; dir = dirname(dir)) {
Expand Down
28 changes: 18 additions & 10 deletions packages/angular/src/generators/component/lib/normalize-options.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import type { Tree } from '@nx/devkit';
import { readProjectConfiguration } from '@nx/devkit';
import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
import type { AngularProjectConfiguration } from '../../../utils/types';
import { normalizeNameAndPaths } from '../../utils/path';
import { buildSelector } from '../../utils/selector';
import type { NormalizedSchema, Schema } from '../schema';

export function normalizeOptions(
export async function normalizeOptions(
tree: Tree,
options: Schema
): NormalizedSchema {
): Promise<NormalizedSchema> {
options.type ??= 'component';
const { directory, filePath, name, path, root, sourceRoot } =
normalizeNameAndPaths(tree, options);
const { directory, file, name, project } =
await determineArtifactNameAndDirectoryOptions(tree, {
artifactName: 'component',
callingGenerator: '@nx/angular:component',
name: options.name,
directory: options.directory ?? options.path,
flat: options.flat,
nameAndDirectoryFormat: options.nameAndDirectoryFormat,
project: options.project,
suffix: options.type ?? 'component',
});

const { prefix } = readProjectConfiguration(
const { prefix, root, sourceRoot } = readProjectConfiguration(
tree,
options.project
project
) as AngularProjectConfiguration;

const selector =
Expand All @@ -25,12 +34,11 @@ export function normalizeOptions(
return {
...options,
name,
project,
changeDetection: options.changeDetection ?? 'Default',
style: options.style ?? 'css',
flat: options.flat ?? false,
directory,
filePath,
path,
filePath: file.path,
projectSourceRoot: sourceRoot,
projectRoot: root,
selector,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import type { Tree } from '@nx/devkit';
import {
validatePathIsUnderProjectRoot,
validateProject,
validateStandaloneOption,
} from '../../utils/validations';
import { validateStandaloneOption } from '../../utils/validations';
import type { Schema } from '../schema';

export function validateOptions(tree: Tree, options: Schema): void {
validateProject(tree, options.project);
validatePathIsUnderProjectRoot(tree, options.project, options.path);
validateStandaloneOption(tree, options.standalone);
}
21 changes: 18 additions & 3 deletions packages/angular/src/generators/component/schema.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { NameAndDirectoryFormat } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';

export interface Schema {
name: string;
project: string;
path?: string;
directory?: string;
nameAndDirectoryFormat?: NameAndDirectoryFormat;
displayBlock?: boolean;
inlineStyle?: boolean;
inlineTemplate?: boolean;
Expand All @@ -11,19 +13,32 @@ export interface Schema {
style?: 'css' | 'scss' | 'sass' | 'less' | 'none';
skipTests?: boolean;
type?: string;
flat?: boolean;
skipImport?: boolean;
selector?: string;
module?: string;
skipSelector?: boolean;
export?: boolean;
prefix?: string;
skipFormat?: boolean;

/**
* @deprecated Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18.
*/
flat?: boolean;
/**
* @deprecated Provide the `directory` option instead. It will be removed in Nx v18.
*/
path?: string;
/**
* @deprecated Provide the `directory` option instead. The project will be determined from the directory provided. It will be removed in Nx v18.
*/
project?: string;
}

export interface NormalizedSchema extends Schema {
directory: string;
filePath: string;
project: string;
projectSourceRoot: string;
projectRoot: string;
selector: string;
Expand Down
45 changes: 29 additions & 16 deletions packages/angular/src/generators/component/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,43 @@
"title": "Angular Component Schema",
"cli": "nx",
"type": "object",
"description": "Creates a new, generic Angular component definition in the given or default project.",
"description": "Creates a new Angular component.",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "The name of the component.",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What name would you like to use for the component?"
},
"directory": {
"type": "string",
"description": "The directory at which to create the component file. When `--nameAndDirectoryFormat=as-provided`, it will be relative to the current working directory. Otherwise, it will be relative to the workspace root.",
"aliases": ["dir"],
"x-priority": "important"
},
"nameAndDirectoryFormat": {
"description": "Whether to generate the component in the directory as provided, relative to the current working directory and ignoring the project (`as-provided`) or generate it using the project and directory relative to the workspace root (`derived`).",
"type": "string",
"enum": ["as-provided", "derived"]
},
"path": {
"type": "string",
"format": "path",
"description": "The path at which to create the component file, relative to the current workspace. Default is a folder with the same name as the component in the project root.",
"visible": false
"description": "The directory at which to create the component file. When `--nameAndDirectoryFormat=as-provided`, it will be relative to the current working directory. Otherwise, it will be relative to the workspace root.",
"visible": false,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
},
"x-dropdown": "projects"
},
"name": {
"type": "string",
"description": "The name of the component.",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What name would you like to use for the component?"
"x-dropdown": "projects",
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. The project will be determined from the directory provided. It will be removed in Nx v18."
},
"prefix": {
"type": "string",
Expand Down Expand Up @@ -91,7 +103,8 @@
"flat": {
"type": "boolean",
"description": "Create the new files at the top level of the current project.",
"default": false
"default": false,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"skipImport": {
"type": "boolean",
Expand Down Expand Up @@ -126,6 +139,6 @@
"x-priority": "internal"
}
},
"required": ["name", "project"],
"required": ["name"],
"examplesFile": "../../../docs/component-examples.md"
}
2 changes: 2 additions & 0 deletions packages/angular/src/generators/utils/find-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export function addToNgModule(
className: string,
fileName: string,
ngModuleProperty: ngModuleDecoratorProperty,
// TODO(leo): remove once all consumers are updated
// // check if exported in the public api
isFlat = true,
isExported = false
) {
Expand Down
Loading

0 comments on commit 765f173

Please sign in to comment.