feat(angular): use consistent artifact generation for generators (#19601)

This commit is contained in:
Colum Ferry 2023-10-13 17:07:02 +01:00 committed by GitHub
parent 4697b06fd1
commit 5a6adb717c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 499 additions and 282 deletions

View File

@ -7,7 +7,7 @@
"cli": "nx",
"title": "Nx Angular Directive Options Schema",
"type": "object",
"description": "Creates a new, generic directive definition in the given project.",
"description": "Creates a new Angular directive.",
"additionalProperties": false,
"properties": {
"name": {
@ -16,18 +16,23 @@
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What name would you like to use for the directive?"
},
"path": {
"directory": {
"type": "string",
"format": "path",
"$default": { "$source": "workingDirectory" },
"description": "The path at which to create the interface that defines the directive, relative to the workspace root.",
"visible": false
"description": "The directory at which to create the directive 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 directive 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"]
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "projectName" },
"x-dropdown": "projects"
"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",
@ -61,7 +66,8 @@
"flat": {
"type": "boolean",
"description": "When true (the default), creates the new files at the top level of the current project.",
"default": true
"default": true,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"module": {
"type": "string",
@ -79,7 +85,7 @@
"description": "Skip formatting of files."
}
},
"required": ["name", "project"],
"required": ["name"],
"presets": []
},
"aliases": ["d"],

View File

@ -8,7 +8,7 @@
"type": "object",
"cli": "nx",
"additionalProperties": false,
"description": "Creates a new, generic pipe definition in the given project.",
"description": "Creates an Angular pipe.",
"properties": {
"name": {
"type": "string",
@ -16,23 +16,29 @@
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What name would you like to use for the pipe?"
},
"path": {
"directory": {
"type": "string",
"format": "path",
"$default": { "$source": "workingDirectory" },
"description": "The path at which to create the pipe, relative to the workspace root.",
"visible": false
"description": "The directory at which to create the pipe 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 pipe 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"]
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "projectName" },
"x-dropdown": "projects"
"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."
},
"flat": {
"type": "boolean",
"default": true,
"description": "When true (the default) creates files at the top level of the project."
"description": "When true (the default) creates files at the top level of the project.",
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"skipTests": {
"type": "boolean",
@ -65,7 +71,7 @@
"description": "Skip formatting of files."
}
},
"required": ["name", "project"],
"required": ["name"],
"presets": []
},
"description": "Generate an Angular Pipe",

View File

@ -9,25 +9,13 @@
"type": "object",
"examples": [
{
"command": "nx g @nx/angular:scam-directive --project=my-lib --flat=false my-sample",
"command": "nx g @nx/angular:scam-directive my-sample --directory=my-lib/src/lib/my-sample",
"description": "Generate a `MySampleDirective` directive in a `my-sample` folder in the `my-lib` library"
}
],
"description": "Creates a new, generic Angular directive definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {
"type": "string",
"format": "path",
"description": "The path at which to create the directive file, relative to the current workspace. Default is a folder with the same name as the directive in the project root.",
"visible": false
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "projectName" },
"x-dropdown": "projects"
},
"name": {
"type": "string",
"description": "The name of the directive.",
@ -35,6 +23,24 @@
"x-prompt": "What name would you like to use for the directive?",
"x-priority": "important"
},
"directory": {
"type": "string",
"description": "The directory at which to create the SCAM Directive files. 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",
"enum": ["as-provided", "derived"]
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "projectName" },
"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."
},
"skipTests": {
"type": "boolean",
"description": "Do not create `spec.ts` test files for the new directive.",
@ -49,7 +55,8 @@
"flat": {
"type": "boolean",
"description": "Create the new files at the top level of the current project.",
"default": true
"default": true,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"selector": {
"type": "string",
@ -72,7 +79,7 @@
"x-priority": "important"
}
},
"required": ["name", "project"],
"required": ["name"],
"presets": []
},
"description": "Generate a directive with an accompanying Single Component Angular Module (SCAM).",

View File

@ -16,18 +16,6 @@
"description": "Creates a new, generic Angular pipe definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {
"type": "string",
"format": "path",
"description": "The path at which to create the pipe file, relative to the current workspace. Default is a folder with the same name as the pipe in the project root.",
"visible": false
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "projectName" },
"x-dropdown": "projects"
},
"name": {
"type": "string",
"description": "The name of the pipe.",
@ -35,6 +23,24 @@
"x-prompt": "What name would you like to use for the pipe?",
"x-priority": "important"
},
"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",
"enum": ["as-provided", "derived"]
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "projectName" },
"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."
},
"skipTests": {
"type": "boolean",
"description": "Do not create `spec.ts` test files for the new pipe.",
@ -49,7 +55,8 @@
"flat": {
"type": "boolean",
"description": "Create the new files at the top level of the current project.",
"default": true
"default": true,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"export": {
"type": "boolean",
@ -58,7 +65,7 @@
"x-priority": "important"
}
},
"required": ["name", "project"],
"required": ["name"],
"presets": []
},
"description": "Generate a pipe with an accompanying Single Component Angular Module (SCAM).",

View File

@ -9,31 +9,37 @@
"type": "object",
"examples": [
{
"command": "nx g @nx/angular:scam --project=my-lib my-sample",
"command": "nx g @nx/angular:scam my-sample --directory=my-lib/src/lib/my-sample",
"description": "Generate a `MySampleComponent` component in the `my-lib` library."
}
],
"description": "Creates a new, generic Angular component definition in the given or default project.",
"description": "Creates a new Angular SCAM.",
"additionalProperties": false,
"properties": {
"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
},
"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?"
},
"directory": {
"type": "string",
"description": "The directory at which to create the SCAM files. 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 SCAM 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"]
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "projectName" },
"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."
},
"displayBlock": {
"description": "Specifies if the style will contain `:host { display: block; }`.",
"type": "boolean",
@ -85,7 +91,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."
},
"selector": {
"type": "string",
@ -124,7 +131,7 @@
"x-priority": "internal"
}
},
"required": ["name", "project"],
"required": ["name"],
"presets": []
},
"description": "Generate a component with an accompanying Single Component Angular Module (SCAM).",

View File

@ -11,9 +11,7 @@ import type { Schema } from './schema';
export async function directiveGenerator(tree: Tree, schema: Schema) {
validateOptions(tree, schema);
const options = normalizeOptions(tree, schema);
const directiveNames = names(options.name);
const options = await normalizeOptions(tree, schema);
generateFiles(
tree,
@ -21,8 +19,8 @@ export async function directiveGenerator(tree: Tree, schema: Schema) {
options.directory,
{
selector: options.selector,
directiveClassName: directiveNames.className,
directiveFileName: directiveNames.fileName,
symbolName: options.symbolName,
fileName: options.fileName,
standalone: options.standalone,
tpl: '',
}
@ -31,21 +29,21 @@ export async function directiveGenerator(tree: Tree, schema: Schema) {
if (options.skipTests) {
const pathToSpecFile = joinPathFragments(
options.directory,
`${directiveNames.fileName}.directive.spec.ts`
`${options.fileName}.spec.ts`
);
tree.delete(pathToSpecFile);
}
if (!options.skipImport && !options.standalone) {
const modulePath = findModule(tree, options.path, options.module);
const modulePath = findModule(tree, options.directory, options.module);
addToNgModule(
tree,
options.path,
options.directory,
modulePath,
directiveNames.fileName,
`${directiveNames.className}Directive`,
`${directiveNames.fileName}.directive`,
'',
options.symbolName,
options.fileName,
'declarations',
options.flat,
options.export

View File

@ -1,8 +0,0 @@
import { <%= directiveClassName %>Directive } from './<%= directiveFileName %>.directive';
describe('<%= directiveClassName %>Directive', () => {
it('should create an instance', () => {
const directive = new <%= directiveClassName %>Directive();
expect(directive).toBeTruthy();
});
});

View File

@ -0,0 +1,8 @@
import { <%= symbolName %> } from './<%= fileName %>';
describe('<%= symbolName %>', () => {
it('should create an instance', () => {
const directive = new <%= symbolName %>();
expect(directive).toBeTruthy();
});
});

View File

@ -4,7 +4,7 @@ import { Directive } from '@angular/core';
selector: '[<%= selector %>]'<% if(standalone) {%>,
standalone: true<%}%>
})
export class <%= directiveClassName %>Directive {
export class <%= symbolName %> {
constructor() { }

View File

@ -1,22 +1,38 @@
import type { Tree } from '@nx/devkit';
import { readProjectConfiguration } from '@nx/devkit';
import { names, readProjectConfiguration } from '@nx/devkit';
import type { AngularProjectConfiguration } from '../../../utils/types';
import { normalizeNameAndPaths } from '../../utils/path';
import { buildSelector } from '../../utils/selector';
import type { NormalizedSchema, Schema } from '../schema';
import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
export function normalizeOptions(
export async function normalizeOptions(
tree: Tree,
options: Schema
): NormalizedSchema {
const { directory, name, path } = normalizeNameAndPaths(tree, {
...options,
type: 'directive',
): Promise<NormalizedSchema> {
const {
artifactName: name,
directory,
fileName,
filePath,
project,
} = await determineArtifactNameAndDirectoryOptions(tree, {
artifactType: 'directive',
callingGenerator: '@nx/angular:directive',
name: options.name,
directory: options.directory ?? options.path,
flat: options.flat,
nameAndDirectoryFormat: options.nameAndDirectoryFormat,
project: options.project,
suffix: 'directive',
});
const { className } = names(name);
const { className: suffixClassName } = names('directive');
const symbolName = `${className}${suffixClassName}`;
const { prefix } = readProjectConfiguration(
tree,
options.project
project
) as AngularProjectConfiguration;
const selector =
@ -25,9 +41,12 @@ export function normalizeOptions(
return {
...options,
directory,
project,
name,
path,
directory,
fileName,
filePath,
symbolName,
selector,
};
}

View File

@ -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);
}

View File

@ -1,19 +1,35 @@
import { NameAndDirectoryFormat } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
export interface Schema {
name: string;
project: string;
path?: string;
directory?: string;
nameAndDirectoryFormat?: NameAndDirectoryFormat;
prefix?: string;
skipTests?: boolean;
skipImport?: boolean;
selector?: string;
standalone?: boolean;
flat?: boolean;
module?: string;
export?: boolean;
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;
path: string;
filePath: string;
fileName: string;
symbolName: string;
project: string;
}

View File

@ -4,7 +4,7 @@
"cli": "nx",
"title": "Nx Angular Directive Options Schema",
"type": "object",
"description": "Creates a new, generic directive definition in the given project.",
"description": "Creates a new Angular directive.",
"additionalProperties": false,
"properties": {
"name": {
@ -16,14 +16,16 @@
},
"x-prompt": "What name would you like to use for the directive?"
},
"path": {
"directory": {
"type": "string",
"format": "path",
"$default": {
"$source": "workingDirectory"
},
"description": "The path at which to create the interface that defines the directive, relative to the workspace root.",
"visible": false
"description": "The directory at which to create the directive 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 directive 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"]
},
"project": {
"type": "string",
@ -31,7 +33,8 @@
"$default": {
"$source": "projectName"
},
"x-dropdown": "projects"
"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",
@ -70,7 +73,8 @@
"flat": {
"type": "boolean",
"description": "When true (the default), creates the new files at the top level of the current project.",
"default": true
"default": true,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"module": {
"type": "string",
@ -88,5 +92,5 @@
"description": "Skip formatting of files."
}
},
"required": ["name", "project"]
"required": ["name"]
}

View File

@ -0,0 +1,8 @@
import { <%= symbolName %> } from './<%= fileName %>';
describe('<%= symbolName %>', () => {
it('create an instance', () => {
const pipe = new <%= symbolName %>();
expect(pipe).toBeTruthy();
});
});

View File

@ -1,10 +1,10 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: '<%= pipePropertyName %>'<% if(standalone) {%>,
name: '<%= selector %>'<% if(standalone) {%>,
standalone: true<%}%>
})
export class <%= pipeClassName %>Pipe implements PipeTransform {
export class <%= symbolName %> implements PipeTransform {
transform(value: unknown, ...args: unknown[]): unknown {
return null;

View File

@ -1,8 +0,0 @@
import { <%= pipeClassName %>Pipe } from './<%= pipeFileName %>.pipe';
describe('<%= pipeClassName %>Pipe', () => {
it('create an instance', () => {
const pipe = new <%= pipeClassName %>Pipe();
expect(pipe).toBeTruthy();
});
});

View File

@ -1,20 +1,40 @@
import type { Tree } from '@nx/devkit';
import { normalizeNameAndPaths } from '../../utils/path';
import type { NormalizedSchema, Schema } from '../schema';
import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
import { names } from '@nx/devkit';
export function normalizeOptions(
export async function normalizeOptions(
tree: Tree,
options: Schema
): NormalizedSchema {
const { directory, name, path } = normalizeNameAndPaths(tree, {
...options,
type: 'pipe',
): Promise<NormalizedSchema> {
const {
artifactName: name,
directory,
fileName,
filePath,
project,
} = await determineArtifactNameAndDirectoryOptions(tree, {
artifactType: 'pipe',
callingGenerator: '@nx/angular:pipe',
name: options.name,
directory: options.directory ?? options.path,
flat: options.flat,
nameAndDirectoryFormat: options.nameAndDirectoryFormat,
project: options.project,
suffix: 'pipe',
});
const { className } = names(name);
const { className: suffixClassName } = names('pipe');
const symbolName = `${className}${suffixClassName}`;
return {
...options,
directory,
project,
name,
path,
directory,
fileName,
filePath,
symbolName,
};
}

View File

@ -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);
}

View File

@ -11,7 +11,7 @@ import type { Schema } from './schema';
export async function pipeGenerator(tree: Tree, rawOptions: Schema) {
validateOptions(tree, rawOptions);
const options = normalizeOptions(tree, rawOptions);
const options = await normalizeOptions(tree, rawOptions);
const pipeNames = names(options.name);
@ -20,9 +20,9 @@ export async function pipeGenerator(tree: Tree, rawOptions: Schema) {
joinPathFragments(__dirname, 'files'),
options.directory,
{
pipeClassName: pipeNames.className,
pipeFileName: pipeNames.fileName,
pipePropertyName: pipeNames.propertyName,
symbolName: options.symbolName,
fileName: options.fileName,
selector: pipeNames.propertyName,
standalone: options.standalone,
tpl: '',
}
@ -31,21 +31,21 @@ export async function pipeGenerator(tree: Tree, rawOptions: Schema) {
if (options.skipTests) {
const pathToSpecFile = joinPathFragments(
options.directory,
`${pipeNames.fileName}.pipe.spec.ts`
`${options.fileName}.spec.ts`
);
tree.delete(pathToSpecFile);
}
if (!options.skipImport && !options.standalone) {
const modulePath = findModule(tree, options.path, options.module);
const modulePath = findModule(tree, options.directory, options.module);
addToNgModule(
tree,
options.path,
options.directory,
modulePath,
pipeNames.fileName,
`${pipeNames.className}Pipe`,
`${pipeNames.fileName}.pipe`,
'',
options.symbolName,
options.fileName,
'declarations',
options.flat,
options.export

View File

@ -1,17 +1,33 @@
import { NameAndDirectoryFormat } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
export interface Schema {
name: string;
project: string;
path?: string;
flat?: boolean;
directory?: string;
nameAndDirectoryFormat?: NameAndDirectoryFormat;
skipTests?: boolean;
skipImport?: boolean;
standalone?: boolean;
module?: string;
export?: boolean;
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;
path: string;
filePath: string;
project: string;
fileName: string;
symbolName: string;
}

View File

@ -5,7 +5,7 @@
"type": "object",
"cli": "nx",
"additionalProperties": false,
"description": "Creates a new, generic pipe definition in the given project.",
"description": "Creates an Angular pipe.",
"properties": {
"name": {
"type": "string",
@ -16,14 +16,16 @@
},
"x-prompt": "What name would you like to use for the pipe?"
},
"path": {
"directory": {
"type": "string",
"format": "path",
"$default": {
"$source": "workingDirectory"
},
"description": "The path at which to create the pipe, relative to the workspace root.",
"visible": false
"description": "The directory at which to create the pipe 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 pipe 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"]
},
"project": {
"type": "string",
@ -31,12 +33,14 @@
"$default": {
"$source": "projectName"
},
"x-dropdown": "projects"
"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."
},
"flat": {
"type": "boolean",
"default": true,
"description": "When true (the default) creates files at the top level of the project."
"description": "When true (the default) creates files at the top level of the project.",
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"skipTests": {
"type": "boolean",
@ -69,5 +73,5 @@
"description": "Skip formatting of files."
}
},
"required": ["name", "project"]
"required": ["name"]
}

View File

@ -32,6 +32,7 @@ describe('convertDirectiveToScam', () => {
flat: false,
inlineScam: true,
path: 'apps/app1/src/app',
symbolName: 'ExampleDirective',
});
// ASSERT
@ -87,6 +88,7 @@ describe('convertDirectiveToScam', () => {
flat: false,
inlineScam: false,
path: 'apps/app1/src/app',
symbolName: 'ExampleDirective',
});
// ASSERT
@ -136,6 +138,7 @@ describe('convertDirectiveToScam', () => {
inlineScam: true,
flat: true,
path: 'apps/app1/src/app',
symbolName: 'ExampleDirective',
});
// ASSERT
@ -191,6 +194,7 @@ describe('convertDirectiveToScam', () => {
inlineScam: false,
flat: true,
path: 'apps/app1/src/app',
symbolName: 'ExampleDirective',
});
// ASSERT
@ -241,6 +245,7 @@ describe('convertDirectiveToScam', () => {
flat: false,
inlineScam: true,
path: 'apps/app1/src/app/random',
symbolName: 'ExampleDirective',
});
// ASSERT
@ -297,6 +302,7 @@ describe('convertDirectiveToScam', () => {
flat: true,
inlineScam: true,
path: 'apps/app1/src/app/random',
symbolName: 'ExampleDirective',
});
// ASSERT

View File

@ -19,10 +19,6 @@ export function convertDirectiveToScam(
tsModule = ensureTypescript();
}
const directiveNames = names(options.name);
const typeNames = names('directive');
const directiveClassName = `${directiveNames.className}${typeNames.className}`;
if (options.inlineScam) {
const currentDirectiveContents = tree.read(options.filePath, 'utf-8');
let source = tsModule.createSourceFile(
@ -49,7 +45,7 @@ export function convertDirectiveToScam(
let updatedDirectiveSource = source.getText();
updatedDirectiveSource = `${updatedDirectiveSource}${getNgModuleDeclaration(
directiveClassName
options.symbolName
)}`;
tree.write(options.filePath, updatedDirectiveSource);
@ -58,12 +54,12 @@ export function convertDirectiveToScam(
const scamFilePath = joinPathFragments(
options.directory,
`${directiveNames.fileName}.module.ts`
`${options.name}.module.ts`
);
tree.write(
scamFilePath,
getModuleFileContent(directiveClassName, options.fileName)
getModuleFileContent(options.symbolName, options.fileName)
);
}

View File

@ -1,28 +1,43 @@
import type { Tree } from '@nx/devkit';
import { normalizeNameAndPaths } from '../../utils/path';
import type { NormalizedSchema, Schema } from '../schema';
import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
import { names } from '@nx/devkit';
export function normalizeOptions(
export async function normalizeOptions(
tree: Tree,
options: Schema
): NormalizedSchema {
const { directory, fileName, filePath, name, path } = normalizeNameAndPaths(
tree,
{
...options,
type: 'directive',
}
);
): Promise<NormalizedSchema> {
const {
artifactName: name,
directory,
fileName,
filePath,
project,
} = await determineArtifactNameAndDirectoryOptions(tree, {
artifactType: 'directive',
callingGenerator: '@nx/angular:scam-directive',
name: options.name,
directory: options.directory ?? options.path,
flat: options.flat,
nameAndDirectoryFormat: options.nameAndDirectoryFormat,
project: options.project,
suffix: 'directive',
});
const { className } = names(name);
const { className: suffixClassName } = names('directive');
const symbolName = `${className}${suffixClassName}`;
return {
...options,
export: options.export ?? true,
flat: options.flat ?? true,
inlineScam: options.inlineScam ?? true,
directory,
fileName,
filePath,
path,
name,
symbolName,
project,
};
}

View File

@ -21,7 +21,7 @@ export async function scamDirectiveGenerator(tree: Tree, rawOptions: Schema) {
skipFormat: true,
});
const options = normalizeOptions(tree, rawOptions);
const options = await normalizeOptions(tree, rawOptions);
convertDirectiveToScam(tree, options);
exportScam(tree, options);

View File

@ -1,13 +1,26 @@
import { NameAndDirectoryFormat } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
export interface Schema {
name: string;
project: string;
path?: string;
directory?: string;
nameAndDirectoryFormat?: NameAndDirectoryFormat;
skipTests?: boolean;
inlineScam?: boolean;
flat?: boolean;
prefix?: string;
selector?: string;
export?: 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 {
@ -15,7 +28,7 @@ export interface NormalizedSchema extends Schema {
export: boolean;
fileName: string;
filePath: string;
flat: boolean;
inlineScam: boolean;
path: string;
symbolName: string;
project: string;
}

View File

@ -6,27 +6,13 @@
"type": "object",
"examples": [
{
"command": "nx g @nx/angular:scam-directive --project=my-lib --flat=false my-sample",
"command": "nx g @nx/angular:scam-directive my-sample --directory=my-lib/src/lib/my-sample",
"description": "Generate a `MySampleDirective` directive in a `my-sample` folder in the `my-lib` library"
}
],
"description": "Creates a new, generic Angular directive definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {
"type": "string",
"format": "path",
"description": "The path at which to create the directive file, relative to the current workspace. Default is a folder with the same name as the directive in the project root.",
"visible": false
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
},
"x-dropdown": "projects"
},
"name": {
"type": "string",
"description": "The name of the directive.",
@ -37,6 +23,26 @@
"x-prompt": "What name would you like to use for the directive?",
"x-priority": "important"
},
"directory": {
"type": "string",
"description": "The directory at which to create the SCAM Directive files. 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",
"enum": ["as-provided", "derived"]
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
},
"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."
},
"skipTests": {
"type": "boolean",
"description": "Do not create `spec.ts` test files for the new directive.",
@ -51,7 +57,8 @@
"flat": {
"type": "boolean",
"description": "Create the new files at the top level of the current project.",
"default": true
"default": true,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"selector": {
"type": "string",
@ -79,5 +86,5 @@
"x-priority": "important"
}
},
"required": ["name", "project"]
"required": ["name"]
}

View File

@ -32,6 +32,7 @@ describe('convertPipeToScam', () => {
flat: false,
inlineScam: true,
path: 'apps/app1/src/app',
symbolName: 'ExamplePipe',
});
// ASSERT
@ -89,6 +90,7 @@ describe('convertPipeToScam', () => {
flat: false,
inlineScam: false,
path: 'apps/app1/src/app',
symbolName: 'ExamplePipe',
});
// ASSERT
@ -138,6 +140,7 @@ describe('convertPipeToScam', () => {
inlineScam: true,
flat: true,
path: 'apps/app1/src/app',
symbolName: 'ExamplePipe',
});
// ASSERT
@ -192,6 +195,7 @@ describe('convertPipeToScam', () => {
inlineScam: false,
flat: true,
path: 'apps/app1/src/app',
symbolName: 'ExamplePipe',
});
// ASSERT
@ -242,6 +246,7 @@ describe('convertPipeToScam', () => {
flat: false,
inlineScam: true,
path: 'apps/app1/src/app/random',
symbolName: 'ExamplePipe',
});
// ASSERT
@ -300,6 +305,7 @@ describe('convertPipeToScam', () => {
flat: true,
inlineScam: true,
path: 'apps/app1/src/app/random',
symbolName: 'ExamplePipe',
});
// ASSERT

View File

@ -16,10 +16,6 @@ export function convertPipeToScam(tree: Tree, options: NormalizedSchema) {
tsModule = ensureTypescript();
}
const pipeNames = names(options.name);
const typeNames = names('pipe');
const pipeClassName = `${pipeNames.className}${typeNames.className}`;
if (options.inlineScam) {
const currentPipeContents = tree.read(options.filePath, 'utf-8');
let source = tsModule.createSourceFile(
@ -46,7 +42,7 @@ export function convertPipeToScam(tree: Tree, options: NormalizedSchema) {
let updatedPipeSource = source.getText();
updatedPipeSource = `${updatedPipeSource}${getNgModuleDeclaration(
pipeClassName
options.symbolName
)}`;
tree.write(options.filePath, updatedPipeSource);
@ -55,12 +51,12 @@ export function convertPipeToScam(tree: Tree, options: NormalizedSchema) {
const scamFilePath = joinPathFragments(
options.directory,
`${pipeNames.fileName}.module.ts`
`${options.name}.module.ts`
);
tree.write(
scamFilePath,
getModuleFileContent(pipeClassName, options.fileName)
getModuleFileContent(options.symbolName, options.fileName)
);
}

View File

@ -1,28 +1,42 @@
import type { Tree } from '@nx/devkit';
import { normalizeNameAndPaths } from '../../utils/path';
import type { NormalizedSchema, Schema } from '../schema';
import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
import { names } from '@nx/devkit';
export function normalizeOptions(
export async function normalizeOptions(
tree: Tree,
options: Schema
): NormalizedSchema {
const { directory, fileName, filePath, name, path } = normalizeNameAndPaths(
tree,
{
...options,
type: 'pipe',
}
);
): Promise<NormalizedSchema> {
const {
artifactName: name,
directory,
fileName,
filePath,
project,
} = await determineArtifactNameAndDirectoryOptions(tree, {
artifactType: 'pipe',
callingGenerator: '@nx/angular:scam-pipe',
name: options.name,
directory: options.directory ?? options.path,
flat: options.flat,
nameAndDirectoryFormat: options.nameAndDirectoryFormat,
project: options.project,
suffix: 'pipe',
});
const { className } = names(name);
const { className: suffixClassName } = names('pipe');
const symbolName = `${className}${suffixClassName}`;
return {
...options,
export: options.export ?? true,
flat: options.flat ?? true,
inlineScam: options.inlineScam ?? true,
directory,
fileName,
filePath,
name,
path,
symbolName,
project,
};
}

View File

@ -17,7 +17,7 @@ export async function scamPipeGenerator(tree: Tree, rawOptions: Schema) {
skipFormat: true,
});
const options = normalizeOptions(tree, rawOptions);
const options = await normalizeOptions(tree, rawOptions);
convertPipeToScam(tree, options);
exportScam(tree, options);

View File

@ -1,11 +1,24 @@
import { NameAndDirectoryFormat } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
export interface Schema {
name: string;
project: string;
path?: string;
directory?: string;
nameAndDirectoryFormat?: NameAndDirectoryFormat;
skipTests?: boolean;
inlineScam?: boolean;
flat?: boolean;
export?: 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 {
@ -13,7 +26,7 @@ export interface NormalizedSchema extends Schema {
export: boolean;
fileName: string;
filePath: string;
flat: boolean;
inlineScam: boolean;
path: string;
project: string;
symbolName: string;
}

View File

@ -13,20 +13,6 @@
"description": "Creates a new, generic Angular pipe definition in the given or default project.",
"additionalProperties": false,
"properties": {
"path": {
"type": "string",
"format": "path",
"description": "The path at which to create the pipe file, relative to the current workspace. Default is a folder with the same name as the pipe in the project root.",
"visible": false
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
},
"x-dropdown": "projects"
},
"name": {
"type": "string",
"description": "The name of the pipe.",
@ -37,6 +23,26 @@
"x-prompt": "What name would you like to use for the pipe?",
"x-priority": "important"
},
"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",
"enum": ["as-provided", "derived"]
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
},
"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."
},
"skipTests": {
"type": "boolean",
"description": "Do not create `spec.ts` test files for the new pipe.",
@ -51,7 +57,8 @@
"flat": {
"type": "boolean",
"description": "Create the new files at the top level of the current project.",
"default": true
"default": true,
"x-deprecated": "Provide the `directory` option instead and use the `as-provided` format. It will be removed in Nx v18."
},
"export": {
"type": "boolean",
@ -60,5 +67,5 @@
"x-priority": "important"
}
},
"required": ["name", "project"]
"required": ["name"]
}

View File

@ -30,6 +30,7 @@ describe('convertComponentToScam', () => {
export: false,
inlineScam: true,
path: 'apps/app1/src/app',
symbolName: 'ExampleComponent',
});
// ASSERT
@ -83,6 +84,7 @@ describe('convertComponentToScam', () => {
export: false,
inlineScam: false,
path: 'apps/app1/src/app',
symbolName: 'ExampleComponent',
});
// ASSERT
@ -132,6 +134,7 @@ describe('convertComponentToScam', () => {
inlineScam: true,
flat: true,
path: 'apps/app1/src/app',
symbolName: 'ExampleComponent',
});
// ASSERT
@ -187,6 +190,7 @@ describe('convertComponentToScam', () => {
inlineScam: false,
flat: true,
path: 'apps/app1/src/app',
symbolName: 'ExampleComponent',
});
// ASSERT
@ -238,6 +242,7 @@ describe('convertComponentToScam', () => {
flat: true,
type: 'random',
path: 'apps/app1/src/app',
symbolName: 'ExampleRandom',
});
// ASSERT
@ -295,6 +300,7 @@ describe('convertComponentToScam', () => {
flat: true,
type: 'random',
path: 'apps/app1/src/app',
symbolName: 'ExampleRandom',
});
// ASSERT
@ -345,6 +351,7 @@ describe('convertComponentToScam', () => {
flat: false,
inlineScam: true,
path: 'apps/app1/src/app/random',
symbolName: 'ExampleComponent',
});
// ASSERT
@ -401,6 +408,7 @@ describe('convertComponentToScam', () => {
flat: true,
inlineScam: true,
path: 'apps/app1/src/app/random',
symbolName: 'ExampleComponent',
});
// ASSERT

View File

@ -13,10 +13,6 @@ export function convertComponentToScam(tree: Tree, options: NormalizedSchema) {
);
}
const componentNames = names(options.name);
const typeNames = names(options.type ?? 'component');
const componentClassName = `${componentNames.className}${typeNames.className}`;
if (!tsModule) {
tsModule = ensureTypescript();
}
@ -47,7 +43,7 @@ export function convertComponentToScam(tree: Tree, options: NormalizedSchema) {
let updatedComponentSource = source.getText();
updatedComponentSource = `${updatedComponentSource}${getNgModuleDeclaration(
componentClassName
options.symbolName
)}`;
tree.write(options.filePath, updatedComponentSource);
@ -56,12 +52,12 @@ export function convertComponentToScam(tree: Tree, options: NormalizedSchema) {
const moduleFilePath = joinPathFragments(
options.directory,
`${componentNames.fileName}.module.ts`
`${options.name}.module.ts`
);
tree.write(
moduleFilePath,
getModuleFileContent(componentClassName, options.fileName)
getModuleFileContent(options.symbolName, options.fileName)
);
}

View File

@ -1,16 +1,34 @@
import type { Tree } from '@nx/devkit';
import { normalizeNameAndPaths } from '../../utils/path';
import type { NormalizedSchema, Schema } from '../schema';
import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
import { names } from '@nx/devkit';
export function normalizeOptions(
export async function normalizeOptions(
tree: Tree,
options: Schema
): NormalizedSchema {
const { directory, fileName, filePath, name } = normalizeNameAndPaths(tree, {
...options,
type: options.type ?? 'component',
): Promise<NormalizedSchema> {
options.type ??= 'component';
const {
artifactName: name,
directory,
fileName,
filePath,
project,
} = await determineArtifactNameAndDirectoryOptions(tree, {
artifactType: options.type,
callingGenerator: '@nx/angular:scam',
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}`;
return {
...options,
export: options.export ?? true,
@ -19,5 +37,7 @@ export function normalizeOptions(
fileName,
filePath,
name,
symbolName,
project,
};
}

View File

@ -21,7 +21,7 @@ export async function scamGenerator(tree: Tree, rawOptions: Schema) {
skipFormat: true,
});
const options = normalizeOptions(tree, rawOptions);
const options = await normalizeOptions(tree, rawOptions);
convertComponentToScam(tree, options);
exportScam(tree, options);

View File

@ -1,7 +1,9 @@
import { 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;
@ -11,18 +13,31 @@ export interface Schema {
skipTests?: boolean;
inlineScam?: boolean;
type?: string;
flat?: boolean;
prefix?: string;
selector?: string;
skipSelector?: boolean;
export?: boolean;
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;
project: string;
fileName: string;
filePath: string;
symbolName: string;
export: boolean;
inlineScam: boolean;
}

View File

@ -6,27 +6,13 @@
"type": "object",
"examples": [
{
"command": "nx g @nx/angular:scam --project=my-lib my-sample",
"command": "nx g @nx/angular:scam my-sample --directory=my-lib/src/lib/my-sample",
"description": "Generate a `MySampleComponent` component in the `my-lib` library."
}
],
"description": "Creates a new, generic Angular component definition in the given or default project.",
"description": "Creates a new Angular SCAM.",
"additionalProperties": false,
"properties": {
"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
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
},
"x-dropdown": "projects"
},
"name": {
"type": "string",
"description": "The name of the component.",
@ -36,6 +22,26 @@
},
"x-prompt": "What name would you like to use for the component?"
},
"directory": {
"type": "string",
"description": "The directory at which to create the SCAM files. 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 SCAM 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"]
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
},
"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."
},
"displayBlock": {
"description": "Specifies if the style will contain `:host { display: block; }`.",
"type": "boolean",
@ -87,7 +93,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."
},
"selector": {
"type": "string",
@ -131,5 +138,5 @@
"x-priority": "internal"
}
},
"required": ["name", "project"]
"required": ["name"]
}