diff --git a/packages/angular/src/generators/directive/directive.spec.ts b/packages/angular/src/generators/directive/directive.spec.ts index 183eee863e683..767528d92924b 100644 --- a/packages/angular/src/generators/directive/directive.spec.ts +++ b/packages/angular/src/generators/directive/directive.spec.ts @@ -12,10 +12,11 @@ describe('directive generator', () => { addProjectConfiguration(tree, 'test', { root: 'test', sourceRoot: 'test/src', + projectType: 'application', }); tree.write( - 'test/src/test.module.ts', + 'test/src/app/test.module.ts', `import {NgModule} from "@angular/core"; @NgModule({ imports: [], @@ -33,11 +34,13 @@ describe('directive generator', () => { await generateDirectiveWithDefaultOptions(tree); // ASSERT - expect(tree.read('test/src/test.directive.ts', 'utf-8')).toMatchSnapshot(); expect( - tree.read('test/src/test.directive.spec.ts', 'utf-8') + tree.read('test/src/app/test.directive.ts', 'utf-8') ).toMatchSnapshot(); - expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot(); + expect( + tree.read('test/src/app/test.directive.spec.ts', 'utf-8') + ).toMatchSnapshot(); + expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot(); }); it('should import the directive correctly when flat=false', async () => { @@ -48,12 +51,12 @@ describe('directive generator', () => { // ASSERT expect( - tree.read('test/src/test/test.directive.ts', 'utf-8') + tree.read('test/src/app/test/test.directive.ts', 'utf-8') ).toMatchSnapshot(); expect( - tree.read('test/src/test/test.directive.spec.ts', 'utf-8') + tree.read('test/src/app/test/test.directive.spec.ts', 'utf-8') ).toMatchSnapshot(); - expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot(); + expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot(); }); it('should not import the directive when standalone=true', async () => { @@ -63,11 +66,13 @@ describe('directive generator', () => { await generateDirectiveWithDefaultOptions(tree, { standalone: true }); // ASSERT - expect(tree.read('test/src/test.directive.ts', 'utf-8')).toMatchSnapshot(); expect( - tree.read('test/src/test.directive.spec.ts', 'utf-8') + tree.read('test/src/app/test.directive.ts', 'utf-8') + ).toMatchSnapshot(); + expect( + tree.read('test/src/app/test.directive.spec.ts', 'utf-8') ).toMatchSnapshot(); - expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot(); + expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot(); }); it('should import the directive correctly when flat=false and path is nested deeper', async () => { @@ -76,17 +81,20 @@ describe('directive generator', () => { // ACT await generateDirectiveWithDefaultOptions(tree, { flat: false, - path: 'test/src/my-directives', + path: 'test/src/app/my-directives', }); // ASSERT expect( - tree.read('test/src/my-directives/test/test.directive.ts', 'utf-8') + tree.read('test/src/app/my-directives/test/test.directive.ts', 'utf-8') ).toMatchSnapshot(); expect( - tree.read('test/src/my-directives/test/test.directive.spec.ts', 'utf-8') + tree.read( + 'test/src/app/my-directives/test/test.directive.spec.ts', + 'utf-8' + ) ).toMatchSnapshot(); - expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot(); + expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot(); }); it('should export the directive correctly when flat=false and path is nested deeper', async () => { @@ -95,18 +103,21 @@ describe('directive generator', () => { // ACT await generateDirectiveWithDefaultOptions(tree, { flat: false, - path: 'test/src/my-directives', + path: 'test/src/app/my-directives', export: true, }); // ASSERT expect( - tree.read('test/src/my-directives/test/test.directive.ts', 'utf-8') + tree.read('test/src/app/my-directives/test/test.directive.ts', 'utf-8') ).toMatchSnapshot(); expect( - tree.read('test/src/my-directives/test/test.directive.spec.ts', 'utf-8') + tree.read( + 'test/src/app/my-directives/test/test.directive.spec.ts', + 'utf-8' + ) ).toMatchSnapshot(); - expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot(); + expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot(); }); it('should not import the directive when skipImport=true', async () => { @@ -115,18 +126,21 @@ describe('directive generator', () => { // ACT await generateDirectiveWithDefaultOptions(tree, { flat: false, - path: 'test/src/my-directives', + path: 'test/src/app/my-directives', skipImport: true, }); // ASSERT expect( - tree.read('test/src/my-directives/test/test.directive.ts', 'utf-8') + tree.read('test/src/app/my-directives/test/test.directive.ts', 'utf-8') ).toMatchSnapshot(); expect( - tree.read('test/src/my-directives/test/test.directive.spec.ts', 'utf-8') + tree.read( + 'test/src/app/my-directives/test/test.directive.spec.ts', + 'utf-8' + ) ).toMatchSnapshot(); - expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot(); + expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot(); }); it('should not generate test file when skipTests=true', async () => { @@ -135,18 +149,18 @@ describe('directive generator', () => { // ACT await generateDirectiveWithDefaultOptions(tree, { flat: false, - path: 'test/src/my-directives', + path: 'test/src/app/my-directives', skipTests: true, }); // ASSERT expect( - tree.read('test/src/my-directives/test/test.directive.ts', 'utf-8') + tree.read('test/src/app/my-directives/test/test.directive.ts', 'utf-8') ).toMatchSnapshot(); expect( - tree.exists('test/src/my-directives/test/test.directive.spec.ts') + tree.exists('test/src/app/my-directives/test/test.directive.spec.ts') ).toBeFalsy(); - expect(tree.read('test/src/test.module.ts', 'utf-8')).toMatchSnapshot(); + expect(tree.read('test/src/app/test.module.ts', 'utf-8')).toMatchSnapshot(); }); }); diff --git a/packages/angular/src/generators/directive/directive.ts b/packages/angular/src/generators/directive/directive.ts index 26fe9d2655d9a..ddf161cbb14b3 100644 --- a/packages/angular/src/generators/directive/directive.ts +++ b/packages/angular/src/generators/directive/directive.ts @@ -1,58 +1,40 @@ -import type { ProjectConfiguration, Tree } from '@nrwl/devkit'; +import type { Tree } from '@nrwl/devkit'; import { formatFiles, generateFiles, - getProjects, joinPathFragments, names, - readNxJson, - readProjectConfiguration, } from '@nrwl/devkit'; -import type { Schema } from './schema'; -import { checkPathUnderProjectRoot } from '../utils/path'; import { addToNgModule, findModule } from '../utils'; - -let tsModule: typeof import('typescript'); +import { normalizeOptions, validateOptions } from './lib'; +import type { Schema } from './schema'; export async function directiveGenerator(tree: Tree, schema: Schema) { - const projects = getProjects(tree); - if (!projects.has(schema.project)) { - throw new Error(`Project "${schema.project}" does not exist!`); - } - - checkPathUnderProjectRoot(tree, schema.project, schema.path); + validateOptions(tree, schema); + const options = normalizeOptions(tree, schema); - const project = readProjectConfiguration( - tree, - schema.project - ) as ProjectConfiguration & { prefix?: string }; - - const path = schema.path ?? `${project.sourceRoot}`; - const directiveNames = names(schema.name); - const selector = - schema.selector ?? - buildSelector(tree, schema.name, schema.prefix ?? project.prefix); + const directiveNames = names(options.name); - const pathToGenerateFiles = schema.flat + const pathToGenerateFiles = options.flat ? './files/__directiveFileName__' : './files'; - await generateFiles( + generateFiles( tree, joinPathFragments(__dirname, pathToGenerateFiles), - path, + options.path, { - selector, + selector: options.selector, directiveClassName: directiveNames.className, directiveFileName: directiveNames.fileName, - standalone: schema.standalone, + standalone: options.standalone, tpl: '', } ); - if (schema.skipTests) { + if (options.skipTests) { const pathToSpecFile = joinPathFragments( - path, - `${!schema.flat ? `${directiveNames.fileName}/` : ``}${ + options.path, + `${!options.flat ? `${directiveNames.fileName}/` : ``}${ directiveNames.fileName }.directive.spec.ts` ); @@ -60,31 +42,24 @@ export async function directiveGenerator(tree: Tree, schema: Schema) { tree.delete(pathToSpecFile); } - if (!schema.skipImport && !schema.standalone) { - const modulePath = findModule(tree, path, schema.module); + if (!options.skipImport && !options.standalone) { + const modulePath = findModule(tree, options.path, options.module); addToNgModule( tree, - path, + options.path, modulePath, directiveNames.fileName, `${directiveNames.className}Directive`, `${directiveNames.fileName}.directive`, 'declarations', - schema.flat, - schema.export + options.flat, + options.export ); } - if (!schema.skipFormat) { + if (!options.skipFormat) { await formatFiles(tree); } } -function buildSelector(tree: Tree, name: string, prefix: string) { - let selector = names(name).fileName; - const selectorPrefix = names(prefix ?? readNxJson(tree).npmScope).fileName; - - return names(`${selectorPrefix}-${selector}`).propertyName; -} - export default directiveGenerator; diff --git a/packages/angular/src/generators/directive/lib/index.ts b/packages/angular/src/generators/directive/lib/index.ts new file mode 100644 index 0000000000000..da94c6f89ca24 --- /dev/null +++ b/packages/angular/src/generators/directive/lib/index.ts @@ -0,0 +1,2 @@ +export * from './normalize-options'; +export * from './validate-options'; diff --git a/packages/angular/src/generators/directive/lib/normalize-options.ts b/packages/angular/src/generators/directive/lib/normalize-options.ts new file mode 100644 index 0000000000000..0de3c9bfed870 --- /dev/null +++ b/packages/angular/src/generators/directive/lib/normalize-options.ts @@ -0,0 +1,54 @@ +import type { ProjectConfiguration, Tree } from '@nrwl/devkit'; +import { + joinPathFragments, + names, + readNxJson, + readProjectConfiguration, +} from '@nrwl/devkit'; +import { parseName } from '../../utils/names'; +import type { Schema } from '../schema'; + +export function normalizeOptions(tree: Tree, options: Schema) { + const { prefix, projectType, root, sourceRoot } = readProjectConfiguration( + tree, + options.project + ) as ProjectConfiguration & { prefix?: string }; + + const projectSourceRoot = sourceRoot ?? joinPathFragments(root, 'src'); + const { name, path: namePath } = parseName(options.name); + + const path = + options.path ?? + joinPathFragments( + projectSourceRoot, + projectType === 'application' ? 'app' : 'lib', + namePath + ); + + const selector = + options.selector ?? buildSelector(tree, name, options.prefix, prefix); + + return { + ...options, + name, + path, + projectRoot: root, + projectSourceRoot, + selector, + }; +} + +function buildSelector( + tree: Tree, + name: string, + prefix: string, + projectPrefix: string +): string { + let selector = name; + prefix ??= projectPrefix ?? readNxJson(tree).npmScope; + if (prefix) { + selector = `${prefix}-${selector}`; + } + + return names(selector).propertyName; +} diff --git a/packages/angular/src/generators/directive/lib/validate-options.ts b/packages/angular/src/generators/directive/lib/validate-options.ts new file mode 100644 index 0000000000000..a73bcccdc8878 --- /dev/null +++ b/packages/angular/src/generators/directive/lib/validate-options.ts @@ -0,0 +1,13 @@ +import type { Tree } from '@nrwl/devkit'; +import { getProjects } from '@nrwl/devkit'; +import { checkPathUnderProjectRoot } from '../../utils/path'; +import type { Schema } from '../schema'; + +export function validateOptions(tree: Tree, options: Schema): void { + const projects = getProjects(tree); + if (!projects.has(options.project)) { + throw new Error(`Project "${options.project}" does not exist!`); + } + + checkPathUnderProjectRoot(tree, options.project, options.path); +} diff --git a/packages/angular/src/generators/utils/names.ts b/packages/angular/src/generators/utils/names.ts new file mode 100644 index 0000000000000..ec0afe5f72e9b --- /dev/null +++ b/packages/angular/src/generators/utils/names.ts @@ -0,0 +1,11 @@ +import { normalizePath } from '@nrwl/devkit'; + +export type NameInfo = { name: string; path: string }; + +export function parseName(rawName: string): NameInfo { + const parsedName = normalizePath(rawName).split('/'); + const name = parsedName.pop(); + const path = parsedName.join('/'); + + return { name, path }; +}