From b1e9e7a8f37bd50f5611c364f9edafd499f7a6d0 Mon Sep 17 00:00:00 2001 From: MeAkib Date: Sun, 2 Nov 2025 02:56:01 +0600 Subject: [PATCH] feat(@schematics/angular): add skipProvidedIn option to service schematic Add a new `skipProvidedIn` boolean option to the service schematic that allows developers to generate services without the `providedIn: 'root'` metadata in the @Injectable decorator. When set to true, the decorator will be rendered as `@Injectable()` instead of `@Injectable({ providedIn: 'root' })`. This provides flexibility for developers who prefer to manually provide services in specific modules or components rather than using the default tree-shakeable pattern. The option defaults to false to maintain backward compatibility and continue encouraging Angular's best practice of tree-shakeable services. fixes #31670 --- ...@dasherize__.__type@dasherize__.ts.template | 8 +++----- .../schematics/angular/service/index_spec.ts | 18 ++++++++++++++++++ .../schematics/angular/service/schema.json | 5 +++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template b/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template index de24346572c2..2c68b26e7c31 100644 --- a/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template +++ b/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template @@ -1,8 +1,6 @@ import { Injectable } from '@angular/core'; -@Injectable({ +<% if (!skipProvidedIn) { %>@Injectable({ providedIn: 'root', -}) -export class <%= classifiedName %> { - -} +})<% } else { %>@Injectable()<% } %> +export class <%= classifiedName %> {} diff --git a/packages/schematics/angular/service/index_spec.ts b/packages/schematics/angular/service/index_spec.ts index 56ae5edd2428..0ea6eca11eea 100644 --- a/packages/schematics/angular/service/index_spec.ts +++ b/packages/schematics/angular/service/index_spec.ts @@ -121,4 +121,22 @@ describe('Service Schematic', () => { expect(content).toContain('export class FooService {'); expect(testContent).toContain("describe('FooService', () => {"); }); + + it('should allow skipping providedIn when skipProvidedIn is true', async () => { + const options = { ...defaultOptions, skipProvidedIn: true }; + + const tree = await schematicRunner.runSchematic('service', options, appTree); + const content = tree.readContent('/projects/bar/src/app/foo/foo.ts'); + expect(content).not.toMatch(/providedIn/); + expect(content).toMatch(/^@Injectable\(\)$/m); + }); + + it('should include providedIn: "root" by default', async () => { + const options = { ...defaultOptions }; + + const tree = await schematicRunner.runSchematic('service', options, appTree); + const content = tree.readContent('/projects/bar/src/app/foo/foo.ts'); + expect(content).toMatch(/providedIn: 'root'/); + expect(content).toMatch(/^@Injectable\({$/m); + }); }); diff --git a/packages/schematics/angular/service/schema.json b/packages/schematics/angular/service/schema.json index 19afac150262..19e0005c3ce0 100644 --- a/packages/schematics/angular/service/schema.json +++ b/packages/schematics/angular/service/schema.json @@ -40,6 +40,11 @@ "description": "Skip the generation of a unit test file `spec.ts` for the service.", "default": false }, + "skipProvidedIn": { + "type": "boolean", + "default": false, + "description": "When true, does not add the providedIn property to the @Injectable decorator. The service must then be provided manually" + }, "type": { "type": "string", "description": "Append a custom type to the service's filename. For example, if you set the type to `service`, the file will be named `my-service.service.ts`."