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 authored Oct 12, 2023
1 parent 5740a73 commit c0027de
Show file tree
Hide file tree
Showing 19 changed files with 1,365 additions and 101 deletions.
39 changes: 23 additions & 16 deletions docs/generated/packages/angular/generators/component.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
{
"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": {
"path": {
"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", "path"],
"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",
"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
"enum": ["as-provided", "derived"]
},
"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 +95,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 +131,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
38 changes: 22 additions & 16 deletions packages/angular/src/generators/component/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,27 @@ import {
import type { Schema } from './schema';

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

const componentNames = names(options.name);
const typeNames = names(options.type);
export async function componentGeneratorInternal(
tree: Tree,
rawOptions: Schema
) {
validateOptions(tree, rawOptions);
const options = await normalizeOptions(tree, rawOptions);

generateFiles(
tree,
joinPathFragments(__dirname, 'files'),
options.directory,
{
fileName: componentNames.fileName,
className: componentNames.className,
type: typeNames.fileName,
typeClassName: typeNames.className,
name: options.name,
fileName: options.fileName,
symbolName: options.symbolName,
style: options.style,
inlineStyle: options.inlineStyle,
inlineTemplate: options.inlineTemplate,
Expand All @@ -46,7 +52,7 @@ export async function componentGenerator(tree: Tree, rawOptions: Schema) {
if (options.skipTests) {
const pathToSpecFile = joinPathFragments(
options.directory,
`${componentNames.fileName}.${typeNames.fileName}.spec.ts`
`${options.fileName}.spec.ts`
);

tree.delete(pathToSpecFile);
Expand All @@ -55,7 +61,7 @@ export async function componentGenerator(tree: Tree, rawOptions: Schema) {
if (options.inlineTemplate) {
const pathToTemplateFile = joinPathFragments(
options.directory,
`${componentNames.fileName}.${typeNames.fileName}.html`
`${options.fileName}.html`
);

tree.delete(pathToTemplateFile);
Expand All @@ -64,7 +70,7 @@ export async function componentGenerator(tree: Tree, rawOptions: Schema) {
if (options.style === 'none' || options.inlineStyle) {
const pathToStyleFile = joinPathFragments(
options.directory,
`${componentNames.fileName}.${typeNames.fileName}.${options.style}`
`${options.fileName}.${options.style}`
);

tree.delete(pathToStyleFile);
Expand All @@ -78,13 +84,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}`,
options.name,
options.symbolName,
options.fileName,
'declarations',
options.flat,
true,
options.export
);
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p><%= name %> works!</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { <%= symbolName %> } from './<%= fileName %>';

describe('<%= symbolName %>', () => {
let component: <%= symbolName %>;
let fixture: ComponentFixture<<%= symbolName %>>;

beforeEach(async () => {
await TestBed.configureTestingModule({
<%= standalone ? 'imports' : 'declarations' %>: [ <%= symbolName %> ]
})
.compileComponents();

fixture = TestBed.createComponent(<%= symbolName %>);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import { CommonModule } from '@angular/common';<% } %>
selector: '<%= selector %>',<%}%><% if(standalone) {%>
standalone: true,
imports: [CommonModule],<%}%><% if(inlineTemplate) { %>
template: `<p><%= fileName %> works!</p>`<% } else { %>
templateUrl: './<%= fileName %><%= type ? '.' + type : '' %>.html'<% } if(inlineStyle) { %>,
template: `<p><%= name %> works!</p>`<% } else { %>
templateUrl: './<%= fileName %>.html'<% } if(inlineStyle) { %>,
styles: [<% if(displayBlock){ %>
`
:host {
display: block;
}
`<% } %>
]<% } else if (style !== 'none') { %>,
styleUrls: ['./<%= fileName %><%= type ? '.' + type : '' %>.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>,
styleUrls: ['./<%= fileName %>.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>,
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
})
export class <%= className %><%= typeClassName %> {}
export class <%= symbolName %> {}
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
39 changes: 29 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,39 @@
import type { Tree } from '@nx/devkit';
import { readProjectConfiguration } from '@nx/devkit';
import { names, 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 {
artifactName: name,
directory,
fileName,
filePath,
project,
} = await determineArtifactNameAndDirectoryOptions(tree, {
artifactType: '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 { className } = names(name);
const { className: suffixClassName } = names(options.type);
const symbolName = `${className}${suffixClassName}`;

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

const selector =
Expand All @@ -25,12 +43,13 @@ export function normalizeOptions(
return {
...options,
name,
project,
changeDetection: options.changeDetection ?? 'Default',
style: options.style ?? 'css',
flat: options.flat ?? false,
directory,
fileName,
filePath,
path,
symbolName,
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);
}
24 changes: 21 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,20 +13,36 @@ 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;

fileName: string;
symbolName: string;
}
Loading

0 comments on commit c0027de

Please sign in to comment.