From 0a5f68c79150e3d5f4e6c0a8c048d17c24d36b7b Mon Sep 17 00:00:00 2001 From: MeAkib Date: Tue, 20 Oct 2026 23:45:18 +0600 Subject: [PATCH] fix(@schematics/angular): use the generated class name when declaring in an NgModule With `addTypeToClassName: false`, the component and directive schematics name the class without the type, but the NgModule declaration was added with the type appended (e.g. `FooComponent`), so the module no longer compiled. Honour `addTypeToClassName` when computing the declared symbol. Fixes #34447 --- .../angular/component/index_spec.ts | 19 +++++++++++++++++++ .../angular/directive/index_spec.ts | 17 +++++++++++++++++ .../utility/add-declaration-to-ng-module.ts | 6 +++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/schematics/angular/component/index_spec.ts b/packages/schematics/angular/component/index_spec.ts index 1f03be864db9..aa8f348e7765 100644 --- a/packages/schematics/angular/component/index_spec.ts +++ b/packages/schematics/angular/component/index_spec.ts @@ -510,6 +510,25 @@ describe('Component Schematic', () => { expect(appModule).toMatch(/import { FooComponent } from '.\/foo\/foo.component'/); }); + it('should declare the class name without the type when addTypeToClassName is false', async () => { + const options = { + ...defaultNonStandaloneOptions, + module: 'app-module.ts', + addTypeToClassName: false, + export: true, + }; + + const tree = await schematicRunner.runSchematic('component', options, appTree); + const component = tree.readContent('/projects/baz/src/app/foo/foo.component.ts'); + const appModule = tree.readContent('/projects/baz/src/app/app-module.ts'); + + expect(component).toContain('export class Foo {'); + expect(appModule).toMatch(/import { Foo } from '.\/foo\/foo.component'/); + expect(appModule).toMatch(/declarations: \[\n\s*App,\n\s*Foo\n/); + expect(appModule).toMatch(/exports: \[\n(\s*) {2}Foo\n/); + expect(appModule).not.toContain('FooComponent'); + }); + it('should respect the sourceRoot value', async () => { const config = JSON.parse(appTree.readContent('/angular.json')); config.projects.baz.sourceRoot = 'projects/baz/custom'; diff --git a/packages/schematics/angular/directive/index_spec.ts b/packages/schematics/angular/directive/index_spec.ts index 870d8f0c78e0..83705d3ef851 100644 --- a/packages/schematics/angular/directive/index_spec.ts +++ b/packages/schematics/angular/directive/index_spec.ts @@ -255,6 +255,23 @@ describe('Directive Schematic', () => { expect(appModule).toMatch(/import { Foo } from '.\/foo'/); }); + it('should declare the class name without the type when addTypeToClassName is false', async () => { + const options = { + ...defaultNonStandaloneOptions, + module: 'app-module.ts', + type: 'directive', + addTypeToClassName: false, + }; + + const tree = await schematicRunner.runSchematic('directive', options, appTree); + const directive = tree.readContent('/projects/baz/src/app/foo.directive.ts'); + const appModule = tree.readContent('/projects/baz/src/app/app-module.ts'); + + expect(directive).toContain('export class Foo {'); + expect(appModule).toMatch(/import { Foo } from '.\/foo.directive'/); + expect(appModule).not.toContain('FooDirective'); + }); + it('should fail if specified module does not exist', async () => { const options = { ...defaultNonStandaloneOptions, diff --git a/packages/schematics/angular/utility/add-declaration-to-ng-module.ts b/packages/schematics/angular/utility/add-declaration-to-ng-module.ts index 96c978f94aab..8ac359956d12 100644 --- a/packages/schematics/angular/utility/add-declaration-to-ng-module.ts +++ b/packages/schematics/angular/utility/add-declaration-to-ng-module.ts @@ -20,6 +20,9 @@ export interface DeclarationToNgModuleOptions { export?: boolean; type: string; typeSeparator?: '.' | '-'; + + /** Whether the type is part of the class name. Defaults to `true`. */ + addTypeToClassName?: boolean; skipImport?: boolean; standalone?: boolean; } @@ -44,7 +47,8 @@ export function addDeclarationToNgModule(options: DeclarationToNgModuleOptions): const importPath = buildRelativePath(modulePath, filePath); const classifiedName = - strings.classify(options.name) + (options.type ? strings.classify(options.type) : ''); + strings.classify(options.name) + + (options.type && options.addTypeToClassName !== false ? strings.classify(options.type) : ''); const changes = addDeclarationToModule(source, modulePath, classifiedName, importPath); if (options.export) {