@schematics/angular 21.0.0-next.0 → 21.0.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/ai-config/index.js +5 -3
  2. package/ai-config/schema.json +1 -17
  3. package/app-shell/index.d.ts +2 -1
  4. package/app-shell/index.js +16 -24
  5. package/application/index.js +29 -1
  6. package/application/schema.d.ts +19 -1
  7. package/application/schema.js +13 -1
  8. package/application/schema.json +12 -2
  9. package/collection.json +7 -0
  10. package/component/files/__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.spec.ts.template +6 -6
  11. package/component/files/__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.ts.template +1 -1
  12. package/component/index.d.ts +2 -1
  13. package/component/index.js +49 -53
  14. package/component/schema.d.ts +5 -0
  15. package/component/schema.json +5 -0
  16. package/config/index.d.ts +2 -1
  17. package/config/index.js +12 -19
  18. package/directive/files/__name@dasherize__.__type@dasherize__.spec.ts.template +3 -3
  19. package/directive/files/__name@dasherize__.__type@dasherize__.ts.template +1 -1
  20. package/directive/index.d.ts +2 -1
  21. package/directive/index.js +25 -27
  22. package/directive/schema.d.ts +5 -0
  23. package/directive/schema.json +5 -0
  24. package/migrations/karma/karma-config-comparer.js +1 -1
  25. package/migrations/migration-collection.json +1 -0
  26. package/module/index.d.ts +2 -1
  27. package/module/index.js +48 -50
  28. package/ng-new/index.js +1 -0
  29. package/ng-new/schema.d.ts +19 -2
  30. package/ng-new/schema.js +13 -2
  31. package/ng-new/schema.json +8 -2
  32. package/package.json +4 -4
  33. package/pipe/index.d.ts +2 -2
  34. package/pipe/index.js +16 -18
  35. package/server/index.d.ts +2 -1
  36. package/server/index.js +67 -71
  37. package/service/files/__name@dasherize__.__type@dasherize__.spec.ts.template +4 -4
  38. package/service/files/__name@dasherize__.__type@dasherize__.ts.template +1 -1
  39. package/service/index.d.ts +2 -1
  40. package/service/index.js +20 -4
  41. package/service/schema.d.ts +5 -0
  42. package/service/schema.json +5 -0
  43. package/service-worker/index.d.ts +2 -1
  44. package/service-worker/index.js +44 -81
  45. package/ssr/index.d.ts +2 -1
  46. package/ssr/index.js +32 -40
  47. package/tailwind/files/.postcssrc.json.template +5 -0
  48. package/tailwind/index.d.ts +12 -0
  49. package/tailwind/index.js +105 -0
  50. package/tailwind/schema.d.ts +7 -0
  51. package/tailwind/schema.js +4 -0
  52. package/tailwind/schema.json +15 -0
  53. package/utility/ast-utils.js +31 -17
  54. package/utility/generate-from-files.d.ts +1 -0
  55. package/utility/latest-versions/package.json +3 -1
  56. package/utility/latest-versions.js +3 -3
  57. package/utility/project.d.ts +25 -0
  58. package/utility/project.js +30 -0
  59. package/utility/standalone/rules.js +2 -3
  60. package/web-worker/index.d.ts +2 -1
  61. package/web-worker/index.js +60 -70
  62. package/workspace/schema.d.ts +0 -1
  63. package/workspace/schema.js +0 -1
  64. package/workspace/schema.json +1 -1
  65. /package/application/files/common-files/src/app/{app.html.template → app__suffix__.html.template} +0 -0
  66. /package/application/files/module-files/src/app/{app.spec.ts.template → app__suffix__.spec.ts.template} +0 -0
  67. /package/application/files/module-files/src/app/{app.ts.template → app__suffix__.ts.template} +0 -0
  68. /package/application/files/standalone-files/src/app/{app.spec.ts.template → app__suffix__.spec.ts.template} +0 -0
  69. /package/application/files/standalone-files/src/app/{app.ts.template → app__suffix__.ts.template} +0 -0
@@ -39,11 +39,13 @@ const AI_TOOLS = {
39
39
  },
40
40
  };
41
41
  function default_1({ tool }) {
42
- if (!tool || tool.includes(schema_1.Tool.None)) {
42
+ if (!tool) {
43
43
  return (0, schematics_1.noop)();
44
44
  }
45
- const files = tool.map((selectedTool) => AI_TOOLS[selectedTool]);
46
- const rules = files.map(({ rulesName, directory, frontmatter }) => (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files'), [
45
+ const rules = tool
46
+ .filter((tool) => tool !== schema_1.Tool.None)
47
+ .map((selectedTool) => AI_TOOLS[selectedTool])
48
+ .map(({ rulesName, directory, frontmatter }) => (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files'), [
47
49
  (0, schematics_1.applyTemplates)({
48
50
  ...schematics_1.strings,
49
51
  rulesName,
@@ -9,7 +9,7 @@
9
9
  "tool": {
10
10
  "type": "array",
11
11
  "uniqueItems": true,
12
- "default": "none",
12
+ "default": ["none"],
13
13
  "x-prompt": {
14
14
  "message": "Which AI tools do you want to configure with Angular best practices? https://angular.dev/ai/develop-with-ai",
15
15
  "type": "list",
@@ -50,21 +50,5 @@
50
50
  "enum": ["none", "gemini", "copilot", "claude", "cursor", "jetbrains", "windsurf"]
51
51
  }
52
52
  }
53
- },
54
- "if": {
55
- "properties": {
56
- "tool": {
57
- "contains": {
58
- "const": "none"
59
- }
60
- }
61
- }
62
- },
63
- "then": {
64
- "properties": {
65
- "tool": {
66
- "maxItems": 1
67
- }
68
- }
69
53
  }
70
54
  }
@@ -7,4 +7,5 @@
7
7
  */
8
8
  import { Rule } from '@angular-devkit/schematics';
9
9
  import { Schema as AppShellOptions } from './schema';
10
- export default function (options: AppShellOptions): Rule;
10
+ declare const _default: (options: AppShellOptions) => Rule;
11
+ export default _default;
@@ -10,14 +10,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
10
10
  return (mod && mod.__esModule) ? mod : { "default": mod };
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.default = default_1;
14
13
  const schematics_1 = require("@angular-devkit/schematics");
15
14
  const posix_1 = require("node:path/posix");
16
15
  const typescript_1 = __importDefault(require("../third_party/github.com/Microsoft/TypeScript/lib/typescript"));
17
16
  const ast_utils_1 = require("../utility/ast-utils");
18
17
  const change_1 = require("../utility/change");
19
18
  const ng_ast_utils_1 = require("../utility/ng-ast-utils");
20
- const project_targets_1 = require("../utility/project-targets");
19
+ const project_1 = require("../utility/project");
21
20
  const util_1 = require("../utility/standalone/util");
22
21
  const workspace_1 = require("../utility/workspace");
23
22
  function getSourceFile(host, path) {
@@ -149,25 +148,18 @@ function addServerRoutingConfig(options, isStandalone) {
149
148
  host.commitUpdate(recorder);
150
149
  };
151
150
  }
152
- function default_1(options) {
153
- return async (tree) => {
154
- const browserEntryPoint = await (0, util_1.getMainFilePath)(tree, options.project);
155
- const isStandalone = (0, ng_ast_utils_1.isStandaloneApp)(tree, browserEntryPoint);
156
- const workspace = await (0, workspace_1.getWorkspace)(tree);
157
- const project = workspace.projects.get(options.project);
158
- if (!project) {
159
- throw (0, project_targets_1.targetBuildNotFoundError)();
160
- }
161
- return (0, schematics_1.chain)([
162
- validateProject(browserEntryPoint),
163
- (0, schematics_1.schematic)('server', options),
164
- addServerRoutingConfig(options, isStandalone),
165
- (0, schematics_1.schematic)('component', {
166
- name: 'app-shell',
167
- module: 'app.module.server.ts',
168
- project: options.project,
169
- standalone: isStandalone,
170
- }),
171
- ]);
172
- };
173
- }
151
+ exports.default = (0, project_1.createProjectSchematic)(async (options, { tree }) => {
152
+ const browserEntryPoint = await (0, util_1.getMainFilePath)(tree, options.project);
153
+ const isStandalone = (0, ng_ast_utils_1.isStandaloneApp)(tree, browserEntryPoint);
154
+ return (0, schematics_1.chain)([
155
+ validateProject(browserEntryPoint),
156
+ (0, schematics_1.schematic)('server', options),
157
+ addServerRoutingConfig(options, isStandalone),
158
+ (0, schematics_1.schematic)('component', {
159
+ name: 'app-shell',
160
+ module: 'app.module.server.ts',
161
+ project: options.project,
162
+ standalone: isStandalone,
163
+ }),
164
+ ]);
165
+ });
@@ -36,7 +36,12 @@ function addTsProjectReference(...paths) {
36
36
  }
37
37
  function default_1(options) {
38
38
  return async (host) => {
39
+ const isTailwind = options.style === schema_1.Style.Tailwind;
40
+ if (isTailwind) {
41
+ options.style = schema_1.Style.Css;
42
+ }
39
43
  const { appDir, appRootSelector, componentOptions, folderName, sourceDir } = await getAppOptions(host, options);
44
+ const suffix = options.fileNameStyleGuide === '2016' ? '.component' : '';
40
45
  return (0, schematics_1.chain)([
41
46
  addAppToWorkspaceFile(options, appDir),
42
47
  addTsProjectReference('./' + (0, core_1.join)((0, core_1.normalize)(appDir), 'tsconfig.app.json')),
@@ -77,6 +82,7 @@ function default_1(options) {
77
82
  relativePathToWorkspaceRoot: (0, paths_1.relativePathToWorkspaceRoot)(appDir),
78
83
  appName: options.name,
79
84
  folderName,
85
+ suffix,
80
86
  }),
81
87
  (0, schematics_1.move)(appDir),
82
88
  ]), schematics_1.MergeStrategy.Overwrite),
@@ -85,7 +91,7 @@ function default_1(options) {
85
91
  ? (0, schematics_1.filter)((path) => !path.endsWith('tsconfig.spec.json.template'))
86
92
  : (0, schematics_1.noop)(),
87
93
  componentOptions.inlineTemplate
88
- ? (0, schematics_1.filter)((path) => !path.endsWith('app.html.template'))
94
+ ? (0, schematics_1.filter)((path) => !path.endsWith('app__suffix__.html.template'))
89
95
  : (0, schematics_1.noop)(),
90
96
  (0, schematics_1.applyTemplates)({
91
97
  utils: schematics_1.strings,
@@ -94,6 +100,7 @@ function default_1(options) {
94
100
  relativePathToWorkspaceRoot: (0, paths_1.relativePathToWorkspaceRoot)(appDir),
95
101
  appName: options.name,
96
102
  folderName,
103
+ suffix,
97
104
  }),
98
105
  (0, schematics_1.move)(appDir),
99
106
  ]), schematics_1.MergeStrategy.Overwrite),
@@ -104,6 +111,11 @@ function default_1(options) {
104
111
  })
105
112
  : (0, schematics_1.noop)(),
106
113
  options.skipPackageJson ? (0, schematics_1.noop)() : addDependenciesToPackageJson(options),
114
+ isTailwind
115
+ ? (0, schematics_1.schematic)('tailwind', {
116
+ project: options.name,
117
+ })
118
+ : (0, schematics_1.noop)(),
107
119
  ]);
108
120
  };
109
121
  }
@@ -172,6 +184,18 @@ function addAppToWorkspaceFile(options, appDir) {
172
184
  (schematics[`@schematics/angular:${type}`] ??= {}).standalone = false;
173
185
  });
174
186
  }
187
+ if (options.fileNameStyleGuide === '2016') {
188
+ const schematicsWithTypeSymbols = ['component', 'directive', 'service'];
189
+ schematicsWithTypeSymbols.forEach((type) => {
190
+ const schematicDefaults = (schematics[`@schematics/angular:${type}`] ??= {});
191
+ schematicDefaults.type = type;
192
+ schematicDefaults.addTypeToClassName = false;
193
+ });
194
+ const schematicsWithTypeSeparator = ['guard', 'interceptor', 'module', 'pipe', 'resolver'];
195
+ schematicsWithTypeSeparator.forEach((type) => {
196
+ (schematics[`@schematics/angular:${type}`] ??= {}).typeSeparator = '.';
197
+ });
198
+ }
175
199
  const sourceRoot = (0, core_1.join)((0, core_1.normalize)(projectRoot), 'src');
176
200
  let budgets = [];
177
201
  if (options.strict) {
@@ -308,5 +332,9 @@ function getComponentOptions(options) {
308
332
  style: options.style,
309
333
  viewEncapsulation: options.viewEncapsulation,
310
334
  };
335
+ if (options.fileNameStyleGuide === '2016') {
336
+ componentOptions.type = 'component';
337
+ componentOptions.addTypeToClassName = false;
338
+ }
311
339
  return componentOptions;
312
340
  }
@@ -5,6 +5,13 @@
5
5
  * routing, styling, and testing.
6
6
  */
7
7
  export type Schema = {
8
+ /**
9
+ * The file naming convention to use for generated files. The '2025' style guide (default)
10
+ * uses a concise format (e.g., `app.ts` for the root component), while the '2016' style
11
+ * guide includes the type in the file name (e.g., `app.component.ts`). For more
12
+ * information, see the Angular Style Guide (https://angular.dev/style-guide).
13
+ */
14
+ fileNameStyleGuide?: FileNameStyleGuide;
8
15
  /**
9
16
  * Include the styles for the root component directly within the `app.component.ts` file.
10
17
  * Only CSS styles can be included inline. By default, a separate stylesheet file (e.g.,
@@ -86,6 +93,16 @@ export type Schema = {
86
93
  */
87
94
  zoneless?: boolean;
88
95
  };
96
+ /**
97
+ * The file naming convention to use for generated files. The '2025' style guide (default)
98
+ * uses a concise format (e.g., `app.ts` for the root component), while the '2016' style
99
+ * guide includes the type in the file name (e.g., `app.component.ts`). For more
100
+ * information, see the Angular Style Guide (https://angular.dev/style-guide).
101
+ */
102
+ export declare enum FileNameStyleGuide {
103
+ The2016 = "2016",
104
+ The2025 = "2025"
105
+ }
89
106
  /**
90
107
  * The type of stylesheet files to be created for components in the application.
91
108
  */
@@ -93,7 +110,8 @@ export declare enum Style {
93
110
  Css = "css",
94
111
  Less = "less",
95
112
  Sass = "sass",
96
- Scss = "scss"
113
+ Scss = "scss",
114
+ Tailwind = "tailwind"
97
115
  }
98
116
  /**
99
117
  * Sets the view encapsulation mode for the application's components. This determines how
@@ -2,7 +2,18 @@
2
2
  // THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE
3
3
  // CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...).
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.ViewEncapsulation = exports.Style = void 0;
5
+ exports.ViewEncapsulation = exports.Style = exports.FileNameStyleGuide = void 0;
6
+ /**
7
+ * The file naming convention to use for generated files. The '2025' style guide (default)
8
+ * uses a concise format (e.g., `app.ts` for the root component), while the '2016' style
9
+ * guide includes the type in the file name (e.g., `app.component.ts`). For more
10
+ * information, see the Angular Style Guide (https://angular.dev/style-guide).
11
+ */
12
+ var FileNameStyleGuide;
13
+ (function (FileNameStyleGuide) {
14
+ FileNameStyleGuide["The2016"] = "2016";
15
+ FileNameStyleGuide["The2025"] = "2025";
16
+ })(FileNameStyleGuide || (exports.FileNameStyleGuide = FileNameStyleGuide = {}));
6
17
  /**
7
18
  * The type of stylesheet files to be created for components in the application.
8
19
  */
@@ -12,6 +23,7 @@ var Style;
12
23
  Style["Less"] = "less";
13
24
  Style["Sass"] = "sass";
14
25
  Style["Scss"] = "scss";
26
+ Style["Tailwind"] = "tailwind";
15
27
  })(Style || (exports.Style = Style = {}));
16
28
  /**
17
29
  * Sets the view encapsulation mode for the application's components. This determines how
@@ -54,15 +54,19 @@
54
54
  "description": "The type of stylesheet files to be created for components in the application.",
55
55
  "type": "string",
56
56
  "default": "css",
57
- "enum": ["css", "scss", "sass", "less"],
57
+ "enum": ["css", "scss", "sass", "less", "tailwind"],
58
58
  "x-prompt": {
59
- "message": "Which stylesheet format would you like to use?",
59
+ "message": "Which stylesheet system would you like to use?",
60
60
  "type": "list",
61
61
  "items": [
62
62
  {
63
63
  "value": "css",
64
64
  "label": "CSS [ https://developer.mozilla.org/docs/Web/CSS ]"
65
65
  },
66
+ {
67
+ "value": "tailwind",
68
+ "label": "Tailwind CSS [ https://tailwindcss.com ]"
69
+ },
66
70
  {
67
71
  "value": "scss",
68
72
  "label": "Sass (SCSS) [ https://sass-lang.com/documentation/syntax#scss ]"
@@ -123,6 +127,12 @@
123
127
  "x-prompt": "Do you want to create a 'zoneless' application without zone.js?",
124
128
  "type": "boolean",
125
129
  "default": false
130
+ },
131
+ "fileNameStyleGuide": {
132
+ "type": "string",
133
+ "enum": ["2016", "2025"],
134
+ "default": "2025",
135
+ "description": "The file naming convention to use for generated files. The '2025' style guide (default) uses a concise format (e.g., `app.ts` for the root component), while the '2016' style guide includes the type in the file name (e.g., `app.component.ts`). For more information, see the Angular Style Guide (https://angular.dev/style-guide)."
126
136
  }
127
137
  },
128
138
  "required": ["name"]
package/collection.json CHANGED
@@ -136,6 +136,13 @@
136
136
  "factory": "./ai-config",
137
137
  "schema": "./ai-config/schema.json",
138
138
  "description": "Generates an AI tool configuration file."
139
+ },
140
+ "tailwind": {
141
+ "factory": "./tailwind",
142
+ "schema": "./tailwind/schema.json",
143
+ "hidden": true,
144
+ "private": true,
145
+ "description": "[INTERNAL] Adds tailwind to a project. Intended for use for ng new/add."
139
146
  }
140
147
  }
141
148
  }
@@ -1,18 +1,18 @@
1
1
  import { ComponentFixture, TestBed } from '@angular/core/testing';
2
2
 
3
- import <% if(!exportDefault) { %>{ <% }%><%= classify(name) %><%= classify(type) %> <% if(!exportDefault) {%>} <% }%>from './<%= dasherize(name) %><%= type ? '.' + dasherize(type): '' %>';
3
+ import <% if(!exportDefault) { %>{ <% }%><%= classifiedName %> <% if(!exportDefault) {%>} <% }%>from './<%= dasherize(name) %><%= type ? '.' + dasherize(type): '' %>';
4
4
 
5
- describe('<%= classify(name) %><%= classify(type) %>', () => {
6
- let component: <%= classify(name) %><%= classify(type) %>;
7
- let fixture: ComponentFixture<<%= classify(name) %><%= classify(type) %>>;
5
+ describe('<%= classifiedName %>', () => {
6
+ let component: <%= classifiedName %>;
7
+ let fixture: ComponentFixture<<%= classifiedName %>>;
8
8
 
9
9
  beforeEach(async () => {
10
10
  await TestBed.configureTestingModule({
11
- <%= standalone ? 'imports' : 'declarations' %>: [<%= classify(name) %><%= classify(type) %>]
11
+ <%= standalone ? 'imports' : 'declarations' %>: [<%= classifiedName %>]
12
12
  })
13
13
  .compileComponents();
14
14
 
15
- fixture = TestBed.createComponent(<%= classify(name) %><%= classify(type) %>);
15
+ fixture = TestBed.createComponent(<%= classifiedName %>);
16
16
  component = fixture.componentInstance;
17
17
  fixture.detectChanges();
18
18
  });
@@ -19,6 +19,6 @@ import { <% if(changeDetection !== 'Default') { %>ChangeDetectionStrategy, <% }%
19
19
  encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
20
20
  changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
21
21
  })
22
- export <% if(exportDefault) {%>default <%}%>class <%= classify(name) %><%= classify(type) %> {
22
+ export <% if(exportDefault) {%>default <%}%>class <%= classifiedName %> {
23
23
 
24
24
  }
@@ -7,4 +7,5 @@
7
7
  */
8
8
  import { Rule } from '@angular-devkit/schematics';
9
9
  import { Schema as ComponentOptions } from './schema';
10
- export default function (options: ComponentOptions): Rule;
10
+ declare const _default: (options: ComponentOptions) => Rule;
11
+ export default _default;
@@ -7,11 +7,11 @@
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.default = default_1;
11
10
  const schematics_1 = require("@angular-devkit/schematics");
12
11
  const add_declaration_to_ng_module_1 = require("../utility/add-declaration-to-ng-module");
13
12
  const find_module_1 = require("../utility/find-module");
14
13
  const parse_name_1 = require("../utility/parse-name");
14
+ const project_1 = require("../utility/project");
15
15
  const validation_1 = require("../utility/validation");
16
16
  const workspace_1 = require("../utility/workspace");
17
17
  const schema_1 = require("./schema");
@@ -25,55 +25,51 @@ function buildSelector(options, projectPrefix) {
25
25
  }
26
26
  return selector;
27
27
  }
28
- function default_1(options) {
29
- return async (host) => {
30
- const workspace = await (0, workspace_1.getWorkspace)(host);
31
- const project = workspace.projects.get(options.project);
32
- if (!project) {
33
- throw new schematics_1.SchematicsException(`Project "${options.project}" does not exist.`);
34
- }
35
- if (options.path === undefined) {
36
- options.path = (0, workspace_1.buildDefaultPath)(project);
37
- }
38
- options.module = (0, find_module_1.findModuleFromOptions)(host, options);
39
- // Schematic templates require a defined type value
40
- options.type ??= '';
41
- const parsedPath = (0, parse_name_1.parseName)(options.path, options.name);
42
- options.name = parsedPath.name;
43
- options.path = parsedPath.path;
44
- options.selector =
45
- options.selector || buildSelector(options, (project && project.prefix) || '');
46
- (0, validation_1.validateHtmlSelector)(options.selector);
47
- (0, validation_1.validateClassName)(schematics_1.strings.classify(options.name));
48
- const skipStyleFile = options.inlineStyle || options.style === schema_1.Style.None;
49
- const templateSource = (0, schematics_1.apply)((0, schematics_1.url)('./files'), [
50
- options.skipTests ? (0, schematics_1.filter)((path) => !path.endsWith('.spec.ts.template')) : (0, schematics_1.noop)(),
51
- skipStyleFile ? (0, schematics_1.filter)((path) => !path.endsWith('.__style__.template')) : (0, schematics_1.noop)(),
52
- options.inlineTemplate ? (0, schematics_1.filter)((path) => !path.endsWith('.html.template')) : (0, schematics_1.noop)(),
53
- (0, schematics_1.applyTemplates)({
54
- ...schematics_1.strings,
55
- 'if-flat': (s) => (options.flat ? '' : s),
56
- 'ngext': options.ngHtml ? '.ng' : '',
57
- ...options,
58
- }),
59
- !options.type
60
- ? (0, schematics_1.forEach)(((file) => {
61
- return file.path.includes('..')
62
- ? {
63
- content: file.content,
64
- path: file.path.replace('..', '.'),
65
- }
66
- : file;
67
- }))
68
- : (0, schematics_1.noop)(),
69
- (0, schematics_1.move)(parsedPath.path),
70
- ]);
71
- return (0, schematics_1.chain)([
72
- (0, add_declaration_to_ng_module_1.addDeclarationToNgModule)({
73
- type: 'component',
74
- ...options,
75
- }),
76
- (0, schematics_1.mergeWith)(templateSource),
77
- ]);
78
- };
79
- }
28
+ exports.default = (0, project_1.createProjectSchematic)((options, { project, tree }) => {
29
+ if (options.path === undefined) {
30
+ options.path = (0, workspace_1.buildDefaultPath)(project);
31
+ }
32
+ options.module = (0, find_module_1.findModuleFromOptions)(tree, options);
33
+ // Schematic templates require a defined type value
34
+ options.type ??= '';
35
+ const parsedPath = (0, parse_name_1.parseName)(options.path, options.name);
36
+ options.name = parsedPath.name;
37
+ options.path = parsedPath.path;
38
+ options.selector = options.selector || buildSelector(options, (project && project.prefix) || '');
39
+ (0, validation_1.validateHtmlSelector)(options.selector);
40
+ const classifiedName = schematics_1.strings.classify(options.name) +
41
+ (options.addTypeToClassName && options.type ? schematics_1.strings.classify(options.type) : '');
42
+ (0, validation_1.validateClassName)(classifiedName);
43
+ const skipStyleFile = options.inlineStyle || options.style === schema_1.Style.None;
44
+ const templateSource = (0, schematics_1.apply)((0, schematics_1.url)('./files'), [
45
+ options.skipTests ? (0, schematics_1.filter)((path) => !path.endsWith('.spec.ts.template')) : (0, schematics_1.noop)(),
46
+ skipStyleFile ? (0, schematics_1.filter)((path) => !path.endsWith('.__style__.template')) : (0, schematics_1.noop)(),
47
+ options.inlineTemplate ? (0, schematics_1.filter)((path) => !path.endsWith('.html.template')) : (0, schematics_1.noop)(),
48
+ (0, schematics_1.applyTemplates)({
49
+ ...schematics_1.strings,
50
+ 'if-flat': (s) => (options.flat ? '' : s),
51
+ 'ngext': options.ngHtml ? '.ng' : '',
52
+ ...options,
53
+ // Add a new variable for the classified name, conditionally including the type
54
+ classifiedName,
55
+ }),
56
+ !options.type
57
+ ? (0, schematics_1.forEach)(((file) => {
58
+ return file.path.includes('..')
59
+ ? {
60
+ content: file.content,
61
+ path: file.path.replace('..', '.'),
62
+ }
63
+ : file;
64
+ }))
65
+ : (0, schematics_1.noop)(),
66
+ (0, schematics_1.move)(parsedPath.path),
67
+ ]);
68
+ return (0, schematics_1.chain)([
69
+ (0, add_declaration_to_ng_module_1.addDeclarationToNgModule)({
70
+ type: 'component',
71
+ ...options,
72
+ }),
73
+ (0, schematics_1.mergeWith)(templateSource),
74
+ ]);
75
+ });
@@ -4,6 +4,11 @@
4
4
  * optional CSS stylesheet. Use this schematic to generate a new component in your project.
5
5
  */
6
6
  export type Schema = {
7
+ /**
8
+ * When true, the 'type' option will be appended to the generated class name. When false,
9
+ * only the file name will include the type.
10
+ */
11
+ addTypeToClassName?: boolean;
7
12
  /**
8
13
  * Configures the change detection strategy for the component.
9
14
  */
@@ -95,6 +95,11 @@
95
95
  "type": "string",
96
96
  "description": "Append a custom type to the component's filename. For example, if you set the type to `container`, the file will be named `my-component.container.ts`."
97
97
  },
98
+ "addTypeToClassName": {
99
+ "type": "boolean",
100
+ "default": true,
101
+ "description": "When true, the 'type' option will be appended to the generated class name. When false, only the file name will include the type."
102
+ },
98
103
  "skipTests": {
99
104
  "type": "boolean",
100
105
  "description": "Skip the generation of unit test files `spec.ts`.",
package/config/index.d.ts CHANGED
@@ -7,4 +7,5 @@
7
7
  */
8
8
  import { Rule } from '@angular-devkit/schematics';
9
9
  import { Schema as ConfigOptions } from './schema';
10
- export default function (options: ConfigOptions): Rule;
10
+ declare const _default: (options: ConfigOptions) => Rule;
11
+ export default _default;
package/config/index.js CHANGED
@@ -7,39 +7,32 @@
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.default = default_1;
11
10
  const schematics_1 = require("@angular-devkit/schematics");
12
11
  const promises_1 = require("node:fs/promises");
13
12
  const node_path_1 = require("node:path");
14
13
  const paths_1 = require("../utility/paths");
14
+ const project_1 = require("../utility/project");
15
15
  const workspace_1 = require("../utility/workspace");
16
16
  const workspace_models_1 = require("../utility/workspace-models");
17
17
  const schema_1 = require("./schema");
18
- function default_1(options) {
18
+ exports.default = (0, project_1.createProjectSchematic)((options, { project }) => {
19
19
  switch (options.type) {
20
20
  case schema_1.Type.Karma:
21
21
  return addKarmaConfig(options);
22
22
  case schema_1.Type.Browserslist:
23
- return addBrowserslistConfig(options);
23
+ return addBrowserslistConfig(project.root);
24
24
  default:
25
25
  throw new schematics_1.SchematicsException(`"${options.type}" is an unknown configuration file type.`);
26
26
  }
27
- }
28
- function addBrowserslistConfig(options) {
29
- return async (host) => {
30
- const workspace = await (0, workspace_1.getWorkspace)(host);
31
- const project = workspace.projects.get(options.project);
32
- if (!project) {
33
- throw new schematics_1.SchematicsException(`Project name "${options.project}" doesn't not exist.`);
34
- }
35
- // Read Angular's default vendored `.browserslistrc` file.
36
- const config = await (0, promises_1.readFile)(node_path_1.posix.join(__dirname, '.browserslistrc'), 'utf8');
37
- return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files'), [
38
- (0, schematics_1.filter)((p) => p.endsWith('.browserslistrc.template')),
39
- (0, schematics_1.applyTemplates)({ config }),
40
- (0, schematics_1.move)(project.root),
41
- ]));
42
- };
27
+ });
28
+ async function addBrowserslistConfig(projectRoot) {
29
+ // Read Angular's default vendored `.browserslistrc` file.
30
+ const config = await (0, promises_1.readFile)(node_path_1.posix.join(__dirname, '.browserslistrc'), 'utf8');
31
+ return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files'), [
32
+ (0, schematics_1.filter)((p) => p.endsWith('.browserslistrc.template')),
33
+ (0, schematics_1.applyTemplates)({ config }),
34
+ (0, schematics_1.move)(projectRoot),
35
+ ]));
43
36
  }
44
37
  function addKarmaConfig(options) {
45
38
  return (0, workspace_1.updateWorkspace)((workspace) => {
@@ -1,8 +1,8 @@
1
- import { <%= classify(name) %><%= classify(type) %> } from './<%= dasherize(name) %><%= type ? '.' + dasherize(type) : '' %>';
1
+ import { <%= classifiedName %> } from './<%= dasherize(name) %><%= type ? '.' + dasherize(type) : '' %>';
2
2
 
3
- describe('<%= classify(name) %><%= classify(type) %>', () => {
3
+ describe('<%= classifiedName %>', () => {
4
4
  it('should create an instance', () => {
5
- const directive = new <%= classify(name) %><%= classify(type) %>();
5
+ const directive = new <%= classifiedName %>();
6
6
  expect(directive).toBeTruthy();
7
7
  });
8
8
  });
@@ -4,7 +4,7 @@ import { Directive } from '@angular/core';
4
4
  selector: '[<%= selector %>]'<% if(!standalone) {%>,
5
5
  standalone: false<%}%>
6
6
  })
7
- export class <%= classify(name) %><%= classify(type) %> {
7
+ export class <%= classifiedName %> {
8
8
 
9
9
  constructor() { }
10
10
 
@@ -7,4 +7,5 @@
7
7
  */
8
8
  import { Rule } from '@angular-devkit/schematics';
9
9
  import { Schema as DirectiveOptions } from './schema';
10
- export default function (options: DirectiveOptions): Rule;
10
+ declare const _default: (options: DirectiveOptions) => Rule;
11
+ export default _default;