@schematics/angular 20.0.0-next.4 → 20.0.0-next.5

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 (77) hide show
  1. package/application/files/module-files/src/app/{app.module.ts.template → app-module.ts.template} +1 -1
  2. package/application/files/module-files/src/app/app.ts.template +1 -1
  3. package/application/files/module-files/src/main.ts.template +1 -1
  4. package/application/files/standalone-files/src/app/app.ts.template +1 -1
  5. package/application/index.js +1 -1
  6. package/class/index.js +0 -1
  7. package/component/index.js +10 -1
  8. package/component/schema.d.ts +0 -1
  9. package/component/schema.json +2 -2
  10. package/directive/index.js +10 -3
  11. package/enum/index.d.ts +2 -2
  12. package/enum/index.js +0 -1
  13. package/guard/implements-files/{__name@dasherize__.guard.spec.ts.template → __name@dasherize____typeSeparator__guard.spec.ts.template} +1 -1
  14. package/guard/schema.d.ts +13 -0
  15. package/guard/schema.js +10 -1
  16. package/guard/schema.json +6 -0
  17. package/guard/type-files/{__name@dasherize__.guard.spec.ts.template → __name@dasherize____typeSeparator__guard.spec.ts.template} +1 -1
  18. package/interceptor/class-files/{__name@dasherize@if-flat__/__name@dasherize__.interceptor.spec.ts.template → __name@dasherize____typeSeparator__interceptor.spec.ts.template} +1 -1
  19. package/interceptor/functional-files/{__name@dasherize@if-flat__/__name@dasherize__.interceptor.spec.ts.template → __name@dasherize____typeSeparator__interceptor.spec.ts.template} +1 -1
  20. package/interceptor/index.d.ts +2 -2
  21. package/interceptor/index.js +2 -9
  22. package/interceptor/schema.d.ts +13 -0
  23. package/interceptor/schema.js +10 -0
  24. package/interceptor/schema.json +6 -0
  25. package/interface/index.js +0 -1
  26. package/library/files/src/__entryFile__.ts.template +1 -1
  27. package/library/index.js +4 -3
  28. package/migrations/migration-collection.json +5 -0
  29. package/migrations/previous-style-guide/migration.d.ts +9 -0
  30. package/migrations/previous-style-guide/migration.js +42 -0
  31. package/module/files/__name@dasherize@if-flat__/{__name@dasherize__.module.ts.template → __name@dasherize____typeSeparator__module.ts.template} +1 -1
  32. package/module/index.js +17 -6
  33. package/module/schema.d.ts +13 -0
  34. package/module/schema.js +10 -1
  35. package/module/schema.json +6 -0
  36. package/package.json +3 -3
  37. package/pipe/files/{__name@dasherize@if-flat__/__name@dasherize__.pipe.spec.ts.template → __name@dasherize____typeSeparator__pipe.spec.ts.template} +1 -1
  38. package/pipe/index.js +12 -11
  39. package/pipe/schema.d.ts +13 -0
  40. package/pipe/schema.js +10 -0
  41. package/pipe/schema.json +6 -0
  42. package/resolver/class-files/{__name@dasherize__.resolver.spec.ts.template → __name@dasherize____typeSeparator__resolver.spec.ts.template} +1 -1
  43. package/resolver/functional-files/{__name@dasherize__.resolver.spec.ts.template → __name@dasherize____typeSeparator__resolver.spec.ts.template} +1 -1
  44. package/resolver/index.d.ts +3 -3
  45. package/resolver/index.js +2 -3
  46. package/resolver/schema.d.ts +13 -0
  47. package/resolver/schema.js +10 -0
  48. package/resolver/schema.json +6 -0
  49. package/server/files/application-builder/ngmodule-src/app/app.module.server.ts.template +4 -4
  50. package/server/files/application-builder/standalone-src/main.server.ts.template +2 -2
  51. package/server/files/server-builder/ngmodule-src/app/app.module.server.ts.template +4 -4
  52. package/server/files/server-builder/standalone-src/main.server.ts.template +2 -2
  53. package/server/index.js +13 -0
  54. package/service/index.js +1 -8
  55. package/utility/add-declaration-to-ng-module.d.ts +1 -0
  56. package/utility/add-declaration-to-ng-module.js +3 -3
  57. package/utility/ast-utils.d.ts +1 -1
  58. package/utility/ast-utils.js +1 -4
  59. package/utility/generate-from-files.js +2 -0
  60. package/utility/latest-versions.js +3 -3
  61. package/utility/standalone/app_component.d.ts +29 -0
  62. package/utility/standalone/app_component.js +109 -0
  63. /package/application/files/common-files/src/app/{app.ng.html.template → app.html.template} +0 -0
  64. /package/class/files/{__name@dasherize____type__.spec.ts.template → __name@dasherize__.__type__.spec.ts.template} +0 -0
  65. /package/class/files/{__name@dasherize____type__.ts.template → __name@dasherize__.__type__.ts.template} +0 -0
  66. /package/enum/files/{__name@dasherize____type__.ts.template → __name@dasherize__.__type__.ts.template} +0 -0
  67. /package/guard/implements-files/{__name@dasherize__.guard.ts.template → __name@dasherize____typeSeparator__guard.ts.template} +0 -0
  68. /package/guard/type-files/{__name@dasherize__.guard.ts.template → __name@dasherize____typeSeparator__guard.ts.template} +0 -0
  69. /package/interceptor/class-files/{__name@dasherize@if-flat__/__name@dasherize__.interceptor.ts.template → __name@dasherize____typeSeparator__interceptor.ts.template} +0 -0
  70. /package/interceptor/functional-files/{__name@dasherize@if-flat__/__name@dasherize__.interceptor.ts.template → __name@dasherize____typeSeparator__interceptor.ts.template} +0 -0
  71. /package/interface/files/{__name@dasherize____type__.ts.template → __name@dasherize__.__type__.ts.template} +0 -0
  72. /package/module/files/__name@dasherize@if-flat__/{__name@dasherize__-routing.module.ts.template → __name@dasherize__-routing__typeSeparator__module.ts.template} +0 -0
  73. /package/pipe/files/{__name@dasherize@if-flat__/__name@dasherize__.pipe.ts.template → __name@dasherize____typeSeparator__pipe.ts.template} +0 -0
  74. /package/resolver/class-files/{__name@dasherize__.resolver.ts.template → __name@dasherize____typeSeparator__resolver.ts.template} +0 -0
  75. /package/resolver/functional-files/{__name@dasherize__.resolver.ts.template → __name@dasherize____typeSeparator__resolver.ts.template} +0 -0
  76. /package/service/files/{__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.spec.ts.template → __name@dasherize__.__type@dasherize__.spec.ts.template} +0 -0
  77. /package/service/files/{__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.ts.template → __name@dasherize__.__type@dasherize__.ts.template} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { NgModule<% if(experimentalZoneless) { %>, provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core';
2
2
  import { BrowserModule } from '@angular/platform-browser';
3
3
  <% if (routing) { %>
4
- import { AppRoutingModule } from './app-routing.module';<% } %>
4
+ import { AppRoutingModule } from './app-routing-module';<% } %>
5
5
  import { App } from './app';
6
6
 
7
7
  @NgModule({
@@ -9,7 +9,7 @@ import { Component } from '@angular/core';
9
9
  %><router-outlet /><%
10
10
  } %>
11
11
  `,<% } else { %>
12
- templateUrl: './app.ng.html',<% } %>
12
+ templateUrl: './app.html',<% } %>
13
13
  standalone: false,<% if(inlineStyle) { %>
14
14
  styles: []<% } else { %>
15
15
  styleUrl: './app.<%= style %>'<% } %>
@@ -1,6 +1,6 @@
1
1
  <% if(!!viewEncapsulation) { %>import { ViewEncapsulation } from '@angular/core';
2
2
  <% }%>import { platformBrowser } from '@angular/platform-browser';
3
- import { AppModule } from './app/app.module';
3
+ import { AppModule } from './app/app-module';
4
4
 
5
5
  platformBrowser().bootstrapModule(AppModule, {
6
6
  <% if(!experimentalZoneless) { %>ngZoneEventCoalescing: true,<% } %><% if(!!viewEncapsulation) { %>
@@ -11,7 +11,7 @@ import { RouterOutlet } from '@angular/router';<% } %>
11
11
  %><router-outlet /><%
12
12
  } %>
13
13
  `,<% } else { %>
14
- templateUrl: './app.ng.html',<% } if(inlineStyle) { %>
14
+ templateUrl: './app.html',<% } if(inlineStyle) { %>
15
15
  styles: [],<% } else { %>
16
16
  styleUrl: './app.<%= style %>'<% } %>
17
17
  })
@@ -63,7 +63,7 @@ function default_1(options) {
63
63
  ? (0, schematics_1.filter)((path) => !path.endsWith('tsconfig.spec.json.template'))
64
64
  : (0, schematics_1.noop)(),
65
65
  componentOptions.inlineTemplate
66
- ? (0, schematics_1.filter)((path) => !path.endsWith('.ng.html.template'))
66
+ ? (0, schematics_1.filter)((path) => !path.endsWith('app.html.template'))
67
67
  : (0, schematics_1.noop)(),
68
68
  (0, schematics_1.applyTemplates)({
69
69
  utils: schematics_1.strings,
package/class/index.js CHANGED
@@ -10,6 +10,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.default = default_1;
11
11
  const generate_from_files_1 = require("../utility/generate-from-files");
12
12
  function default_1(options) {
13
- options.type = options.type ? `.${options.type}` : '';
14
13
  return (0, generate_from_files_1.generateFromFiles)(options);
15
14
  }
@@ -35,7 +35,16 @@ function default_1(options) {
35
35
  if (options.path === undefined) {
36
36
  options.path = (0, workspace_1.buildDefaultPath)(project);
37
37
  }
38
- options.module = (0, find_module_1.findModuleFromOptions)(host, options);
38
+ try {
39
+ options.module = (0, find_module_1.findModuleFromOptions)(host, options);
40
+ }
41
+ catch {
42
+ options.module = (0, find_module_1.findModuleFromOptions)(host, {
43
+ ...options,
44
+ moduleExt: '-module.ts',
45
+ routingModuleExt: '-routing-module.ts',
46
+ });
47
+ }
39
48
  // Schematic templates require a defined type value
40
49
  options.type ??= '';
41
50
  const parsedPath = (0, parse_name_1.parseName)(options.path, options.name);
@@ -51,7 +51,6 @@ export type Schema = {
51
51
  name: string;
52
52
  /**
53
53
  * Generate component template files with an '.ng.html' file extension instead of '.html'.
54
- * The '.ng.html' file extension is recommended by the Angular style guide.
55
54
  */
56
55
  ngHtml?: boolean;
57
56
  /**
@@ -137,8 +137,8 @@
137
137
  },
138
138
  "ngHtml": {
139
139
  "type": "boolean",
140
- "default": true,
141
- "description": "Generate component template files with an '.ng.html' file extension instead of '.html'. The '.ng.html' file extension is recommended by the Angular style guide."
140
+ "default": false,
141
+ "description": "Generate component template files with an '.ng.html' file extension instead of '.html'."
142
142
  }
143
143
  },
144
144
  "required": ["name", "project"]
@@ -35,9 +35,16 @@ function default_1(options) {
35
35
  if (options.path === undefined) {
36
36
  options.path = (0, workspace_1.buildDefaultPath)(project);
37
37
  }
38
- options.module = (0, find_module_1.findModuleFromOptions)(host, options);
39
- // Schematic templates require a defined type value
40
- options.type ??= '';
38
+ try {
39
+ options.module = (0, find_module_1.findModuleFromOptions)(host, options);
40
+ }
41
+ catch {
42
+ options.module = (0, find_module_1.findModuleFromOptions)(host, {
43
+ ...options,
44
+ moduleExt: '-module.ts',
45
+ routingModuleExt: '-routing-module.ts',
46
+ });
47
+ }
41
48
  const parsedPath = (0, parse_name_1.parseName)(options.path, options.name);
42
49
  options.name = parsedPath.name;
43
50
  options.path = parsedPath.path;
package/enum/index.d.ts CHANGED
@@ -5,6 +5,6 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import { Rule } from '@angular-devkit/schematics';
9
- import { Schema as EnumOptions } from './schema';
8
+ import type { Rule } from '@angular-devkit/schematics';
9
+ import type { Schema as EnumOptions } from './schema';
10
10
  export default function (options: EnumOptions): Rule;
package/enum/index.js CHANGED
@@ -10,6 +10,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.default = default_1;
11
11
  const generate_from_files_1 = require("../utility/generate-from-files");
12
12
  function default_1(options) {
13
- options.type = options.type ? `.${options.type}` : '';
14
13
  return (0, generate_from_files_1.generateFromFiles)(options);
15
14
  }
@@ -1,6 +1,6 @@
1
1
  import { TestBed } from '@angular/core/testing';
2
2
 
3
- import { <%= classify(name) %>Guard } from './<%= dasherize(name) %>.guard';
3
+ import { <%= classify(name) %>Guard } from './<%= dasherize(name) %><%= typeSeparator %>guard';
4
4
 
5
5
  describe('<%= classify(name) %>Guard', () => {
6
6
  let guard: <%= classify(name) %>Guard;
package/guard/schema.d.ts CHANGED
@@ -40,6 +40,11 @@ export type Schema = {
40
40
  * Skip the generation of a unit test file `spec.ts` for the new guard.
41
41
  */
42
42
  skipTests?: boolean;
43
+ /**
44
+ * The separator character to use before the type within the generated file's name. For
45
+ * example, if you set the option to `.`, the file will be named `example.guard.ts`.
46
+ */
47
+ typeSeparator?: TypeSeparator;
43
48
  };
44
49
  export declare enum Implement {
45
50
  CanActivate = "CanActivate",
@@ -47,3 +52,11 @@ export declare enum Implement {
47
52
  CanDeactivate = "CanDeactivate",
48
53
  CanMatch = "CanMatch"
49
54
  }
55
+ /**
56
+ * The separator character to use before the type within the generated file's name. For
57
+ * example, if you set the option to `.`, the file will be named `example.guard.ts`.
58
+ */
59
+ export declare enum TypeSeparator {
60
+ Empty = "-",
61
+ TypeSeparator = "."
62
+ }
package/guard/schema.js CHANGED
@@ -2,7 +2,7 @@
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.Implement = void 0;
5
+ exports.TypeSeparator = exports.Implement = void 0;
6
6
  var Implement;
7
7
  (function (Implement) {
8
8
  Implement["CanActivate"] = "CanActivate";
@@ -10,3 +10,12 @@ var Implement;
10
10
  Implement["CanDeactivate"] = "CanDeactivate";
11
11
  Implement["CanMatch"] = "CanMatch";
12
12
  })(Implement || (exports.Implement = Implement = {}));
13
+ /**
14
+ * The separator character to use before the type within the generated file's name. For
15
+ * example, if you set the option to `.`, the file will be named `example.guard.ts`.
16
+ */
17
+ var TypeSeparator;
18
+ (function (TypeSeparator) {
19
+ TypeSeparator["Empty"] = "-";
20
+ TypeSeparator["TypeSeparator"] = ".";
21
+ })(TypeSeparator || (exports.TypeSeparator = TypeSeparator = {}));
package/guard/schema.json CHANGED
@@ -58,6 +58,12 @@
58
58
  },
59
59
  "default": ["CanActivate"],
60
60
  "x-prompt": "Which type of guard would you like to create?"
61
+ },
62
+ "typeSeparator": {
63
+ "type": "string",
64
+ "default": "-",
65
+ "enum": ["-", "."],
66
+ "description": "The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.guard.ts`."
61
67
  }
62
68
  },
63
69
  "required": ["name", "project"]
@@ -1,7 +1,7 @@
1
1
  import { TestBed } from '@angular/core/testing';
2
2
  import { <%= guardType %> } from '@angular/router';
3
3
 
4
- import { <%= camelize(name) %>Guard } from './<%= dasherize(name) %>.guard';
4
+ import { <%= camelize(name) %>Guard } from './<%= dasherize(name) %><%= typeSeparator %>guard';
5
5
 
6
6
  describe('<%= camelize(name) %>Guard', () => {
7
7
  const executeGuard: <%= guardType %><% if (guardType === 'CanDeactivateFn') { %><unknown><% } %> = (...guardParameters) =>
@@ -1,6 +1,6 @@
1
1
  import { TestBed } from '@angular/core/testing';
2
2
 
3
- import { <%= classify(name) %>Interceptor } from './<%= dasherize(name) %>.interceptor';
3
+ import { <%= classify(name) %>Interceptor } from './<%= dasherize(name) %><%= typeSeparator %>interceptor';
4
4
 
5
5
  describe('<%= classify(name) %>Interceptor', () => {
6
6
  beforeEach(() => TestBed.configureTestingModule({
@@ -1,7 +1,7 @@
1
1
  import { TestBed } from '@angular/core/testing';
2
2
  import { HttpInterceptorFn } from '@angular/common/http';
3
3
 
4
- import { <%= camelize(name) %>Interceptor } from './<%= dasherize(name) %>.interceptor';
4
+ import { <%= camelize(name) %>Interceptor } from './<%= dasherize(name) %><%= typeSeparator %>interceptor';
5
5
 
6
6
  describe('<%= camelize(name) %>Interceptor', () => {
7
7
  const interceptor: HttpInterceptorFn = (req, next) =>
@@ -5,6 +5,6 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import { Rule } from '@angular-devkit/schematics';
9
- import { Schema as InterceptorOptions } from './schema';
8
+ import type { Rule } from '@angular-devkit/schematics';
9
+ import type { Schema as InterceptorOptions } from './schema';
10
10
  export default function (options: InterceptorOptions): Rule;
@@ -10,13 +10,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.default = default_1;
11
11
  const generate_from_files_1 = require("../utility/generate-from-files");
12
12
  function default_1(options) {
13
- // This schematic uses an older method to implement the flat option
14
- const flat = options.flat;
15
- options.flat = true;
16
- const extraTemplateValues = {
17
- 'if-flat': (s) => (flat ? '' : s),
18
- };
19
- return options.functional
20
- ? (0, generate_from_files_1.generateFromFiles)({ ...options, templateFilesDirectory: './functional-files' }, extraTemplateValues)
21
- : (0, generate_from_files_1.generateFromFiles)({ ...options, templateFilesDirectory: './class-files' }, extraTemplateValues);
13
+ const templateFilesDirectory = options.functional ? './functional-files' : './class-files';
14
+ return (0, generate_from_files_1.generateFromFiles)({ ...options, templateFilesDirectory });
22
15
  }
@@ -35,4 +35,17 @@ export type Schema = {
35
35
  * Skip the generation of a unit test file `spec.ts` for the new interceptor.
36
36
  */
37
37
  skipTests?: boolean;
38
+ /**
39
+ * The separator character to use before the type within the generated file's name. For
40
+ * example, if you set the option to `.`, the file will be named `example.interceptor.ts`.
41
+ */
42
+ typeSeparator?: TypeSeparator;
38
43
  };
44
+ /**
45
+ * The separator character to use before the type within the generated file's name. For
46
+ * example, if you set the option to `.`, the file will be named `example.interceptor.ts`.
47
+ */
48
+ export declare enum TypeSeparator {
49
+ Empty = "-",
50
+ TypeSeparator = "."
51
+ }
@@ -2,3 +2,13 @@
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.TypeSeparator = void 0;
6
+ /**
7
+ * The separator character to use before the type within the generated file's name. For
8
+ * example, if you set the option to `.`, the file will be named `example.interceptor.ts`.
9
+ */
10
+ var TypeSeparator;
11
+ (function (TypeSeparator) {
12
+ TypeSeparator["Empty"] = "-";
13
+ TypeSeparator["TypeSeparator"] = ".";
14
+ })(TypeSeparator || (exports.TypeSeparator = TypeSeparator = {}));
@@ -45,6 +45,12 @@
45
45
  "type": "boolean",
46
46
  "description": "Creates the interceptor as a function `HttpInterceptorFn` instead of a class. Functional interceptors can be simpler for basic scenarios.",
47
47
  "default": true
48
+ },
49
+ "typeSeparator": {
50
+ "type": "string",
51
+ "default": "-",
52
+ "enum": ["-", "."],
53
+ "description": "The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.interceptor.ts`."
48
54
  }
49
55
  },
50
56
  "required": ["name", "project"]
@@ -10,6 +10,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.default = default_1;
11
11
  const generate_from_files_1 = require("../utility/generate-from-files");
12
12
  function default_1(options) {
13
- options.type = options.type ? `.${options.type}` : '';
14
13
  return (0, generate_from_files_1.generateFromFiles)(options);
15
14
  }
@@ -3,4 +3,4 @@
3
3
  */
4
4
 
5
5
  export * from './lib/<%= dasherize(name) %>';<% if (!standalone) { %>
6
- export * from './lib/<%= dasherize(name) %>.module';<% } %>
6
+ export * from './lib/<%= dasherize(name) %>-module';<% } %>
package/library/index.js CHANGED
@@ -72,9 +72,6 @@ function addLibToWorkspaceFile(options, projectRoot, projectName) {
72
72
  build: {
73
73
  builder: workspace_models_1.Builders.BuildNgPackagr,
74
74
  defaultConfiguration: 'production',
75
- options: {
76
- project: `${projectRoot}/ng-package.json`,
77
- },
78
75
  configurations: {
79
76
  production: {
80
77
  tsConfig: `${projectRoot}/tsconfig.lib.prod.json`,
@@ -154,6 +151,10 @@ function default_1(options) {
154
151
  export: true,
155
152
  standalone: options.standalone,
156
153
  project: packageName,
154
+ // Explicitly set an empty `type` since it doesn't necessarily make sense in a library.
155
+ // This also ensures that the generated files are valid even if the `component` schematic
156
+ // inherits its `type` from the workspace.
157
+ type: '',
157
158
  }),
158
159
  (_tree, context) => {
159
160
  if (!options.skipPackageJson && !options.skipInstall) {
@@ -15,6 +15,11 @@
15
15
  "factory": "./update-module-resolution/migration",
16
16
  "description": "Update 'moduleResolution' to 'bundler' in TypeScript configurations. You can read more about this, here: https://www.typescriptlang.org/tsconfig/#moduleResolution"
17
17
  },
18
+ "previous-style-guide": {
19
+ "version": "20.0.0",
20
+ "factory": "./previous-style-guide/migration",
21
+ "description": "Update workspace generation defaults to maintain previous style guide behavior."
22
+ },
18
23
  "use-application-builder": {
19
24
  "version": "20.0.0",
20
25
  "factory": "./use-application-builder/migration",
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { Rule } from '@angular-devkit/schematics';
9
+ export default function (): Rule;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.default = default_1;
11
+ const workspace_1 = require("../../utility/workspace");
12
+ const TYPE_SCHEMATICS = ['component', 'directive', 'service'];
13
+ const SEPARATOR_SCHEMATICS = ['guard', 'interceptor', 'module', 'pipe', 'resolver'];
14
+ function default_1() {
15
+ return (0, workspace_1.updateWorkspace)((workspace) => {
16
+ let schematicsDefaults = workspace.extensions['schematics'];
17
+ // Ensure "schematics" field is an object
18
+ if (!schematicsDefaults ||
19
+ typeof schematicsDefaults !== 'object' ||
20
+ Array.isArray(schematicsDefaults)) {
21
+ schematicsDefaults = workspace.extensions['schematics'] = {};
22
+ }
23
+ // Add "type" value for each schematic to continue generating a type suffix.
24
+ // New default is an empty type value.
25
+ for (const schematicName of TYPE_SCHEMATICS) {
26
+ const schematic = (schematicsDefaults[`@schematics/angular:${schematicName}`] ??= {});
27
+ if (typeof schematic === 'object' && !Array.isArray(schematic) && !('type' in schematic)) {
28
+ schematic['type'] = schematicName;
29
+ }
30
+ }
31
+ // Add "typeSeparator" value for each schematic to continue generating "." before type.
32
+ // New default is an "-" type value.
33
+ for (const schematicName of SEPARATOR_SCHEMATICS) {
34
+ const schematic = (schematicsDefaults[`@schematics/angular:${schematicName}`] ??= {});
35
+ if (typeof schematic === 'object' &&
36
+ !Array.isArray(schematic) &&
37
+ !('typeSeparator' in schematic)) {
38
+ schematic['typeSeparator'] = '.';
39
+ }
40
+ }
41
+ });
42
+ }
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';<% if (commonModule) { %>
2
2
  import { CommonModule } from '@angular/common';<% } %><% if (lazyRouteWithoutRouteModule) { %>
3
3
  import { Routes, RouterModule } from '@angular/router';<% } %>
4
4
  <% if ((!lazyRoute && routing) || lazyRouteWithRouteModule) { %>
5
- import { <%= classify(name) %>RoutingModule } from './<%= dasherize(name) %>-routing.module';<% } %>
5
+ import { <%= classify(name) %>RoutingModule } from './<%= dasherize(name) %>-routing<%= typeSeparator %>module';<% } %>
6
6
  <% if (lazyRouteWithoutRouteModule) { %>
7
7
  const routes: Routes = [
8
8
  { path: '', component: <%= classify(name) %> }
package/module/index.js CHANGED
@@ -52,7 +52,7 @@ const validation_1 = require("../utility/validation");
52
52
  const workspace_1 = require("../utility/workspace");
53
53
  const schema_1 = require("./schema");
54
54
  function buildRelativeModulePath(options, modulePath) {
55
- const importModulePath = (0, posix_1.join)(options.path ?? '', options.flat ? '' : schematics_1.strings.dasherize(options.name), schematics_1.strings.dasherize(options.name) + '.module');
55
+ const importModulePath = (0, posix_1.join)(options.path ?? '', options.flat ? '' : schematics_1.strings.dasherize(options.name), schematics_1.strings.dasherize(options.name) + options.typeSeparator + 'module');
56
56
  return (0, find_module_1.buildRelativePath)(modulePath, importModulePath);
57
57
  }
58
58
  function addImportToNgModule(options) {
@@ -99,9 +99,11 @@ function addRouteDeclarationToNgModule(options, routingModulePath) {
99
99
  };
100
100
  }
101
101
  function getRoutingModulePath(host, modulePath) {
102
- const routingModulePath = modulePath.endsWith(find_module_1.ROUTING_MODULE_EXT)
102
+ const routingModulePath = modulePath.endsWith(find_module_1.ROUTING_MODULE_EXT) || modulePath.endsWith('-routing-module.ts')
103
103
  ? modulePath
104
- : modulePath.replace(find_module_1.MODULE_EXT, find_module_1.ROUTING_MODULE_EXT);
104
+ : modulePath
105
+ .replace(find_module_1.MODULE_EXT, find_module_1.ROUTING_MODULE_EXT)
106
+ .replace('-module.ts', '-routing-module.ts');
105
107
  return host.exists(routingModulePath) ? routingModulePath : undefined;
106
108
  }
107
109
  function buildRoute(options, modulePath) {
@@ -116,7 +118,16 @@ function default_1(options) {
116
118
  options.path = await (0, workspace_1.createDefaultPath)(host, options.project);
117
119
  }
118
120
  if (options.module) {
119
- options.module = (0, find_module_1.findModuleFromOptions)(host, options);
121
+ try {
122
+ options.module = (0, find_module_1.findModuleFromOptions)(host, options);
123
+ }
124
+ catch {
125
+ options.module = (0, find_module_1.findModuleFromOptions)(host, {
126
+ ...options,
127
+ moduleExt: '-module.ts',
128
+ routingModuleExt: '-routing-module.ts',
129
+ });
130
+ }
120
131
  }
121
132
  let routingModulePath;
122
133
  const isLazyLoadedModuleGen = !!(options.route && options.module);
@@ -131,7 +142,7 @@ function default_1(options) {
131
142
  const templateSource = (0, schematics_1.apply)((0, schematics_1.url)('./files'), [
132
143
  options.routing || (isLazyLoadedModuleGen && routingModulePath)
133
144
  ? (0, schematics_1.noop)()
134
- : (0, schematics_1.filter)((path) => !path.endsWith('-routing.module.ts.template')),
145
+ : (0, schematics_1.filter)((path) => !path.includes('-routing')),
135
146
  (0, schematics_1.applyTemplates)({
136
147
  ...schematics_1.strings,
137
148
  'if-flat': (s) => (options.flat ? '' : s),
@@ -143,7 +154,7 @@ function default_1(options) {
143
154
  (0, schematics_1.move)(parsedPath.path),
144
155
  ]);
145
156
  const moduleDasherized = schematics_1.strings.dasherize(options.name);
146
- const modulePath = `${!options.flat ? moduleDasherized + '/' : ''}${moduleDasherized}.module.ts`;
157
+ const modulePath = `${!options.flat ? moduleDasherized + '/' : ''}${moduleDasherized}${options.typeSeparator}module.ts`;
147
158
  const componentOptions = {
148
159
  module: modulePath,
149
160
  flat: options.flat,
@@ -40,6 +40,11 @@ export type Schema = {
40
40
  * The scope for the new routing module.
41
41
  */
42
42
  routingScope?: RoutingScope;
43
+ /**
44
+ * The separator character to use before the type within the generated file's name. For
45
+ * example, if you set the option to `.`, the file will be named `example.module.ts`.
46
+ */
47
+ typeSeparator?: TypeSeparator;
43
48
  };
44
49
  /**
45
50
  * The scope for the new routing module.
@@ -48,3 +53,11 @@ export declare enum RoutingScope {
48
53
  Child = "Child",
49
54
  Root = "Root"
50
55
  }
56
+ /**
57
+ * The separator character to use before the type within the generated file's name. For
58
+ * example, if you set the option to `.`, the file will be named `example.module.ts`.
59
+ */
60
+ export declare enum TypeSeparator {
61
+ Empty = "-",
62
+ TypeSeparator = "."
63
+ }
package/module/schema.js CHANGED
@@ -2,7 +2,7 @@
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.RoutingScope = void 0;
5
+ exports.TypeSeparator = exports.RoutingScope = void 0;
6
6
  /**
7
7
  * The scope for the new routing module.
8
8
  */
@@ -11,3 +11,12 @@ var RoutingScope;
11
11
  RoutingScope["Child"] = "Child";
12
12
  RoutingScope["Root"] = "Root";
13
13
  })(RoutingScope || (exports.RoutingScope = RoutingScope = {}));
14
+ /**
15
+ * The separator character to use before the type within the generated file's name. For
16
+ * example, if you set the option to `.`, the file will be named `example.module.ts`.
17
+ */
18
+ var TypeSeparator;
19
+ (function (TypeSeparator) {
20
+ TypeSeparator["Empty"] = "-";
21
+ TypeSeparator["TypeSeparator"] = ".";
22
+ })(TypeSeparator || (exports.TypeSeparator = TypeSeparator = {}));
@@ -62,6 +62,12 @@
62
62
  "type": "string",
63
63
  "description": "The declaring NgModule.",
64
64
  "alias": "m"
65
+ },
66
+ "typeSeparator": {
67
+ "type": "string",
68
+ "default": "-",
69
+ "enum": ["-", "."],
70
+ "description": "The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.module.ts`."
65
71
  }
66
72
  },
67
73
  "required": ["name", "project"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schematics/angular",
3
- "version": "20.0.0-next.4",
3
+ "version": "20.0.0-next.5",
4
4
  "description": "Schematics specific to Angular",
5
5
  "homepage": "https://github.com/angular/angular-cli",
6
6
  "keywords": [
@@ -22,8 +22,8 @@
22
22
  },
23
23
  "schematics": "./collection.json",
24
24
  "dependencies": {
25
- "@angular-devkit/core": "20.0.0-next.4",
26
- "@angular-devkit/schematics": "20.0.0-next.4",
25
+ "@angular-devkit/core": "20.0.0-next.5",
26
+ "@angular-devkit/schematics": "20.0.0-next.5",
27
27
  "jsonc-parser": "3.3.1"
28
28
  },
29
29
  "repository": {
@@ -1,4 +1,4 @@
1
- import { <%= classify(name) %>Pipe } from './<%= dasherize(name) %>.pipe';
1
+ import { <%= classify(name) %>Pipe } from './<%= dasherize(name) %><%= typeSeparator %>pipe';
2
2
 
3
3
  describe('<%= classify(name) %>Pipe', () => {
4
4
  it('create an instance', () => {
package/pipe/index.js CHANGED
@@ -11,32 +11,33 @@ exports.default = default_1;
11
11
  const schematics_1 = require("@angular-devkit/schematics");
12
12
  const add_declaration_to_ng_module_1 = require("../utility/add-declaration-to-ng-module");
13
13
  const find_module_1 = require("../utility/find-module");
14
+ const generate_from_files_1 = require("../utility/generate-from-files");
14
15
  const parse_name_1 = require("../utility/parse-name");
15
16
  const validation_1 = require("../utility/validation");
16
17
  const workspace_1 = require("../utility/workspace");
17
18
  function default_1(options) {
18
19
  return async (host) => {
19
20
  options.path ??= await (0, workspace_1.createDefaultPath)(host, options.project);
20
- options.module = (0, find_module_1.findModuleFromOptions)(host, options);
21
+ try {
22
+ options.module = (0, find_module_1.findModuleFromOptions)(host, options);
23
+ }
24
+ catch {
25
+ options.module = (0, find_module_1.findModuleFromOptions)(host, {
26
+ ...options,
27
+ moduleExt: '-module.ts',
28
+ routingModuleExt: '-routing-module.ts',
29
+ });
30
+ }
21
31
  const parsedPath = (0, parse_name_1.parseName)(options.path, options.name);
22
32
  options.name = parsedPath.name;
23
33
  options.path = parsedPath.path;
24
34
  (0, validation_1.validateClassName)(schematics_1.strings.classify(options.name));
25
- const templateSource = (0, schematics_1.apply)((0, schematics_1.url)('./files'), [
26
- options.skipTests ? (0, schematics_1.filter)((path) => !path.endsWith('.spec.ts.template')) : (0, schematics_1.noop)(),
27
- (0, schematics_1.applyTemplates)({
28
- ...schematics_1.strings,
29
- 'if-flat': (s) => (options.flat ? '' : s),
30
- ...options,
31
- }),
32
- (0, schematics_1.move)(parsedPath.path),
33
- ]);
34
35
  return (0, schematics_1.chain)([
35
36
  (0, add_declaration_to_ng_module_1.addDeclarationToNgModule)({
36
37
  type: 'pipe',
37
38
  ...options,
38
39
  }),
39
- (0, schematics_1.mergeWith)(templateSource),
40
+ (0, generate_from_files_1.generateFromFiles)(options),
40
41
  ]);
41
42
  };
42
43
  }
package/pipe/schema.d.ts CHANGED
@@ -49,4 +49,17 @@ export type Schema = {
49
49
  * standalone components, directives, or pipes.
50
50
  */
51
51
  standalone?: boolean;
52
+ /**
53
+ * The separator character to use before the type within the generated file's name. For
54
+ * example, if you set the option to `.`, the file will be named `example.pipe.ts`.
55
+ */
56
+ typeSeparator?: TypeSeparator;
52
57
  };
58
+ /**
59
+ * The separator character to use before the type within the generated file's name. For
60
+ * example, if you set the option to `.`, the file will be named `example.pipe.ts`.
61
+ */
62
+ export declare enum TypeSeparator {
63
+ Empty = "-",
64
+ TypeSeparator = "."
65
+ }
package/pipe/schema.js CHANGED
@@ -2,3 +2,13 @@
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.TypeSeparator = void 0;
6
+ /**
7
+ * The separator character to use before the type within the generated file's name. For
8
+ * example, if you set the option to `.`, the file will be named `example.pipe.ts`.
9
+ */
10
+ var TypeSeparator;
11
+ (function (TypeSeparator) {
12
+ TypeSeparator["Empty"] = "-";
13
+ TypeSeparator["TypeSeparator"] = ".";
14
+ })(TypeSeparator || (exports.TypeSeparator = TypeSeparator = {}));
package/pipe/schema.json CHANGED
@@ -61,6 +61,12 @@
61
61
  "type": "boolean",
62
62
  "default": false,
63
63
  "description": "Automatically export the pipe from the specified NgModule, making it accessible to other modules in the application."
64
+ },
65
+ "typeSeparator": {
66
+ "type": "string",
67
+ "default": "-",
68
+ "enum": ["-", "."],
69
+ "description": "The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.pipe.ts`."
64
70
  }
65
71
  },
66
72
  "required": ["name", "project"]
@@ -1,6 +1,6 @@
1
1
  import { TestBed } from '@angular/core/testing';
2
2
 
3
- import { <%= classify(name) %>Resolver } from './<%= dasherize(name) %>.resolver';
3
+ import { <%= classify(name) %>Resolver } from './<%= dasherize(name) %><%= typeSeparator %>resolver';
4
4
 
5
5
  describe('<%= classify(name) %>Resolver', () => {
6
6
  let resolver: <%= classify(name) %>Resolver;
@@ -1,7 +1,7 @@
1
1
  import { TestBed } from '@angular/core/testing';
2
2
  import { ResolveFn } from '@angular/router';
3
3
 
4
- import { <%= camelize(name) %>Resolver } from './<%= dasherize(name) %>.resolver';
4
+ import { <%= camelize(name) %>Resolver } from './<%= dasherize(name) %><%= typeSeparator %>resolver';
5
5
 
6
6
  describe('<%= camelize(name) %>Resolver', () => {
7
7
  const executeResolver: ResolveFn<boolean> = (...resolverParameters) =>
@@ -5,6 +5,6 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import { Rule } from '@angular-devkit/schematics';
9
- import { Schema } from './schema';
10
- export default function (options: Schema): Rule;
8
+ import type { Rule } from '@angular-devkit/schematics';
9
+ import type { Schema as ResolverOptions } from './schema';
10
+ export default function (options: ResolverOptions): Rule;
package/resolver/index.js CHANGED
@@ -10,7 +10,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.default = default_1;
11
11
  const generate_from_files_1 = require("../utility/generate-from-files");
12
12
  function default_1(options) {
13
- return options.functional
14
- ? (0, generate_from_files_1.generateFromFiles)({ ...options, templateFilesDirectory: './functional-files' })
15
- : (0, generate_from_files_1.generateFromFiles)({ ...options, templateFilesDirectory: './class-files' });
13
+ const templateFilesDirectory = options.functional ? './functional-files' : './class-files';
14
+ return (0, generate_from_files_1.generateFromFiles)({ ...options, templateFilesDirectory });
16
15
  }
@@ -34,4 +34,17 @@ export type Schema = {
34
34
  * Skip the generation of a unit test file `spec.ts` for the new resolver.
35
35
  */
36
36
  skipTests?: boolean;
37
+ /**
38
+ * The separator character to use before the type within the generated file's name. For
39
+ * example, if you set the option to `.`, the file will be named `example.resolver.ts`.
40
+ */
41
+ typeSeparator?: TypeSeparator;
37
42
  };
43
+ /**
44
+ * The separator character to use before the type within the generated file's name. For
45
+ * example, if you set the option to `.`, the file will be named `example.resolver.ts`.
46
+ */
47
+ export declare enum TypeSeparator {
48
+ Empty = "-",
49
+ TypeSeparator = "."
50
+ }
@@ -2,3 +2,13 @@
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.TypeSeparator = void 0;
6
+ /**
7
+ * The separator character to use before the type within the generated file's name. For
8
+ * example, if you set the option to `.`, the file will be named `example.resolver.ts`.
9
+ */
10
+ var TypeSeparator;
11
+ (function (TypeSeparator) {
12
+ TypeSeparator["Empty"] = "-";
13
+ TypeSeparator["TypeSeparator"] = ".";
14
+ })(TypeSeparator || (exports.TypeSeparator = TypeSeparator = {}));
@@ -45,6 +45,12 @@
45
45
  "$default": {
46
46
  "$source": "projectName"
47
47
  }
48
+ },
49
+ "typeSeparator": {
50
+ "type": "string",
51
+ "default": "-",
52
+ "enum": ["-", "."],
53
+ "description": "The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.resolver.ts`."
48
54
  }
49
55
  },
50
56
  "required": ["name", "project"]
@@ -1,12 +1,12 @@
1
1
  import { NgModule } from '@angular/core';
2
2
  import { provideServerRendering, withRoutes } from '@angular/ssr';
3
- import { App } from './app';
4
- import { AppModule } from './app.module';
3
+ import { <%= appComponentName %> } from '<%= appComponentPath %>';
4
+ import { <%= appModuleName %> } from '<%= appModulePath %>';
5
5
  import { serverRoutes } from './app.routes.server';
6
6
 
7
7
  @NgModule({
8
- imports: [AppModule],
8
+ imports: [<%= appModuleName %>],
9
9
  providers: [provideServerRendering(withRoutes(serverRoutes))],
10
- bootstrap: [App],
10
+ bootstrap: [<%= appComponentName %>],
11
11
  })
12
12
  export class AppServerModule {}
@@ -1,7 +1,7 @@
1
1
  import { bootstrapApplication } from '@angular/platform-browser';
2
- import { App } from './app/app';
2
+ import { <%= appComponentName %> } from '<%= appComponentPath %>';
3
3
  import { config } from './app/app.config.server';
4
4
 
5
- const bootstrap = () => bootstrapApplication(App, config);
5
+ const bootstrap = () => bootstrapApplication(<%= appComponentName %>, config);
6
6
 
7
7
  export default bootstrap;
@@ -1,14 +1,14 @@
1
1
  import { NgModule } from '@angular/core';
2
2
  import { ServerModule } from '@angular/platform-server';
3
3
 
4
- import { AppModule } from './app.module';
5
- import { App } from './app';
4
+ import { <%= appModuleName %> } from '<%= appModulePath %>';
5
+ import { <%= appComponentName %> } from '<%= appComponentPath %>';
6
6
 
7
7
  @NgModule({
8
8
  imports: [
9
- AppModule,
9
+ <%= appModuleName %>,
10
10
  ServerModule,
11
11
  ],
12
- bootstrap: [App],
12
+ bootstrap: [<%= appComponentName %>],
13
13
  })
14
14
  export class AppServerModule {}
@@ -1,7 +1,7 @@
1
1
  import { bootstrapApplication } from '@angular/platform-browser';
2
- import { App } from './app/app';
2
+ import { <%= appComponentName %> } from '<%= appComponentPath %>';
3
3
  import { config } from './app/app.config.server';
4
4
 
5
- const bootstrap = () => bootstrapApplication(App, config);
5
+ const bootstrap = () => bootstrapApplication(<%= appComponentName %>, config);
6
6
 
7
7
  export default bootstrap;
package/server/index.js CHANGED
@@ -18,6 +18,7 @@ const latest_versions_1 = require("../utility/latest-versions");
18
18
  const ng_ast_utils_1 = require("../utility/ng-ast-utils");
19
19
  const paths_1 = require("../utility/paths");
20
20
  const project_targets_1 = require("../utility/project-targets");
21
+ const app_component_1 = require("../utility/standalone/app_component");
21
22
  const util_1 = require("../utility/standalone/util");
22
23
  const workspace_1 = require("../utility/workspace");
23
24
  const workspace_models_1 = require("../utility/workspace-models");
@@ -150,10 +151,22 @@ function default_1(options) {
150
151
  const sourceRoot = clientProject.sourceRoot ?? (0, core_1.join)((0, core_1.normalize)(clientProject.root), 'src');
151
152
  let filesUrl = `./files/${usingApplicationBuilder ? 'application-builder/' : 'server-builder/'}`;
152
153
  filesUrl += isStandalone ? 'standalone-src' : 'ngmodule-src';
154
+ const { componentName, componentImportPathInSameFile, moduleName, moduleImportPathInSameFile } = (0, app_component_1.resolveBootstrappedComponentData)(host, browserEntryPoint) || {
155
+ componentName: 'App',
156
+ componentImportPathInSameFile: './app/app',
157
+ moduleName: 'AppModule',
158
+ moduleImportPathInSameFile: './app/app.module',
159
+ };
153
160
  const templateSource = (0, schematics_1.apply)((0, schematics_1.url)(filesUrl), [
154
161
  (0, schematics_1.applyTemplates)({
155
162
  ...schematics_1.strings,
156
163
  ...options,
164
+ appComponentName: componentName,
165
+ appComponentPath: componentImportPathInSameFile,
166
+ appModuleName: moduleName,
167
+ appModulePath: moduleImportPathInSameFile === null
168
+ ? null
169
+ : `./${node_path_1.posix.basename(moduleImportPathInSameFile)}`,
157
170
  }),
158
171
  (0, schematics_1.move)(sourceRoot),
159
172
  ]);
package/service/index.js CHANGED
@@ -10,12 +10,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.default = default_1;
11
11
  const generate_from_files_1 = require("../utility/generate-from-files");
12
12
  function default_1(options) {
13
- // This schematic uses an older method to implement the flat option
14
- const flat = options.flat;
15
- options.flat = true;
16
- // Schematic templates require a defined type value
17
- options.type ??= '';
18
- return (0, generate_from_files_1.generateFromFiles)(options, {
19
- 'if-flat': (s) => (flat ? '' : s),
20
- });
13
+ return (0, generate_from_files_1.generateFromFiles)(options);
21
14
  }
@@ -13,6 +13,7 @@ export interface DeclarationToNgModuleOptions {
13
13
  flat?: boolean;
14
14
  export?: boolean;
15
15
  type: string;
16
+ typeSeparator?: '.' | '-';
16
17
  skipImport?: boolean;
17
18
  standalone?: boolean;
18
19
  }
@@ -52,15 +52,15 @@ function addDeclarationToNgModule(options) {
52
52
  if (options.skipImport || options.standalone || !modulePath) {
53
53
  return host;
54
54
  }
55
+ const typeSeparator = options.typeSeparator ?? '.';
55
56
  const sourceText = host.readText(modulePath);
56
57
  const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
57
58
  const filePath = `/${options.path}/` +
58
59
  (options.flat ? '' : schematics_1.strings.dasherize(options.name) + '/') +
59
60
  schematics_1.strings.dasherize(options.name) +
60
- (options.type ? '.' : '') +
61
- schematics_1.strings.dasherize(options.type);
61
+ (options.type ? typeSeparator + schematics_1.strings.dasherize(options.type) : '');
62
62
  const importPath = (0, find_module_1.buildRelativePath)(modulePath, filePath);
63
- const classifiedName = schematics_1.strings.classify(options.name) + schematics_1.strings.classify(options.type);
63
+ const classifiedName = schematics_1.strings.classify(options.name) + (options.type ? schematics_1.strings.classify(options.type) : '');
64
64
  const changes = (0, ast_utils_1.addDeclarationToModule)(source, modulePath, classifiedName, importPath);
65
65
  if (options.export) {
66
66
  changes.push(...(0, ast_utils_1.addSymbolToNgModuleMetadata)(source, modulePath, 'exports', classifiedName));
@@ -60,7 +60,7 @@ export declare function findNode(node: ts.Node, kind: ts.SyntaxKind, text: strin
60
60
  */
61
61
  export declare function insertAfterLastOccurrence(nodes: ts.Node[] | ts.NodeArray<ts.Node>, toInsert: string, file: string, fallbackPos: number, syntaxKind?: ts.SyntaxKind): Change;
62
62
  export declare function getDecoratorMetadata(source: ts.SourceFile, identifier: string, module: string): ts.Node[];
63
- export declare function getMetadataField(node: ts.ObjectLiteralExpression, metadataField: string): ts.ObjectLiteralElement[];
63
+ export declare function getMetadataField(node: ts.ObjectLiteralExpression, metadataField: string): ts.PropertyAssignment[];
64
64
  export declare function addSymbolToNgModuleMetadata(source: ts.SourceFile, ngModulePath: string, metadataField: string, symbolName: string, importPath?: string | null): Change[];
65
65
  /**
66
66
  * Custom function to insert a declaration (component, pipe, directive)
@@ -442,11 +442,8 @@ function getRouterModuleDeclaration(source) {
442
442
  return undefined;
443
443
  }
444
444
  const matchingProperties = getMetadataField(node, 'imports');
445
- if (!matchingProperties) {
446
- return;
447
- }
448
445
  const assignment = matchingProperties[0];
449
- if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
446
+ if (!assignment || assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
450
447
  return;
451
448
  }
452
449
  const arrLiteral = assignment.initializer;
@@ -17,6 +17,8 @@ function generateFromFiles(options, extraTemplateValues = {}) {
17
17
  options.path ??= await (0, workspace_1.createDefaultPath)(host, options.project);
18
18
  options.prefix ??= '';
19
19
  options.flat ??= true;
20
+ // Schematic templates require a defined type value
21
+ options.type ??= '';
20
22
  const parsedPath = (0, parse_name_1.parseName)(options.path, options.name);
21
23
  options.name = parsedPath.name;
22
24
  options.path = parsedPath.path;
@@ -16,7 +16,7 @@ exports.latestVersions = {
16
16
  // As Angular CLI works with same minor versions of Angular Framework, a tilde match for the current
17
17
  Angular: '^20.0.0-next.0',
18
18
  NgPackagr: '^20.0.0-next.0',
19
- DevkitBuildAngular: '^20.0.0-next.4',
20
- AngularBuild: '^20.0.0-next.4',
21
- AngularSSR: '^20.0.0-next.4',
19
+ DevkitBuildAngular: '^20.0.0-next.5',
20
+ AngularBuild: '^20.0.0-next.5',
21
+ AngularSSR: '^20.0.0-next.5',
22
22
  };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import { Tree } from '@angular-devkit/schematics';
9
+ /** Data resolved for a bootstrapped component. */
10
+ interface BootstrappedComponentData {
11
+ /** Original name of the component class. */
12
+ componentName: string;
13
+ /** Path under which the component was imported in the main entrypoint. */
14
+ componentImportPathInSameFile: string;
15
+ /** Original name of the NgModule being bootstrapped, null if the app isn't module-based. */
16
+ moduleName: string | null;
17
+ /**
18
+ * Path under which the module was imported in the main entrypoint,
19
+ * null if the app isn't module-based.
20
+ */
21
+ moduleImportPathInSameFile: string | null;
22
+ }
23
+ /**
24
+ * Finds the original name and path relative to the `main.ts` of the bootrstrapped app component.
25
+ * @param tree File tree in which to look for the component.
26
+ * @param mainFilePath Path of the `main` file.
27
+ */
28
+ export declare function resolveBootstrappedComponentData(tree: Tree, mainFilePath: string): BootstrappedComponentData | null;
29
+ export {};
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.resolveBootstrappedComponentData = resolveBootstrappedComponentData;
14
+ const schematics_1 = require("@angular-devkit/schematics");
15
+ const typescript_1 = __importDefault(require("../../third_party/github.com/Microsoft/TypeScript/lib/typescript"));
16
+ const ast_utils_1 = require("../ast-utils");
17
+ const ng_ast_utils_1 = require("../ng-ast-utils");
18
+ const util_1 = require("./util");
19
+ /**
20
+ * Finds the original name and path relative to the `main.ts` of the bootrstrapped app component.
21
+ * @param tree File tree in which to look for the component.
22
+ * @param mainFilePath Path of the `main` file.
23
+ */
24
+ function resolveBootstrappedComponentData(tree, mainFilePath) {
25
+ // First try to resolve for a standalone app.
26
+ try {
27
+ const call = (0, util_1.findBootstrapApplicationCall)(tree, mainFilePath);
28
+ if (call.arguments.length > 0 && typescript_1.default.isIdentifier(call.arguments[0])) {
29
+ const resolved = resolveIdentifier(call.arguments[0]);
30
+ if (resolved) {
31
+ return {
32
+ componentName: resolved.name,
33
+ componentImportPathInSameFile: resolved.path,
34
+ moduleName: null,
35
+ moduleImportPathInSameFile: null,
36
+ };
37
+ }
38
+ }
39
+ }
40
+ catch (e) {
41
+ // `findBootstrapApplicationCall` will throw if it can't find the `bootrstrapApplication` call.
42
+ // Catch so we can continue to the fallback logic.
43
+ if (!(e instanceof schematics_1.SchematicsException)) {
44
+ throw e;
45
+ }
46
+ }
47
+ // Otherwise fall back to resolving an NgModule-based app.
48
+ return resolveNgModuleBasedData(tree, mainFilePath);
49
+ }
50
+ /** Resolves the bootstrap data for a NgModule-based app. */
51
+ function resolveNgModuleBasedData(tree, mainFilePath) {
52
+ const appModulePath = (0, ng_ast_utils_1.getAppModulePath)(tree, mainFilePath);
53
+ const appModuleFile = (0, util_1.getSourceFile)(tree, appModulePath);
54
+ const metadataNodes = (0, ast_utils_1.getDecoratorMetadata)(appModuleFile, 'NgModule', '@angular/core');
55
+ for (const node of metadataNodes) {
56
+ if (!typescript_1.default.isObjectLiteralExpression(node)) {
57
+ continue;
58
+ }
59
+ const bootstrapProp = (0, ast_utils_1.getMetadataField)(node, 'bootstrap').find((prop) => {
60
+ return (typescript_1.default.isArrayLiteralExpression(prop.initializer) &&
61
+ prop.initializer.elements.length > 0 &&
62
+ typescript_1.default.isIdentifier(prop.initializer.elements[0]));
63
+ });
64
+ const componentIdentifier = (bootstrapProp?.initializer)
65
+ .elements[0];
66
+ const componentResult = componentIdentifier ? resolveIdentifier(componentIdentifier) : null;
67
+ const bootstrapCall = (0, ng_ast_utils_1.findBootstrapModuleCall)(tree, mainFilePath);
68
+ if (componentResult &&
69
+ bootstrapCall &&
70
+ bootstrapCall.arguments.length > 0 &&
71
+ typescript_1.default.isIdentifier(bootstrapCall.arguments[0])) {
72
+ const moduleResult = resolveIdentifier(bootstrapCall.arguments[0]);
73
+ if (moduleResult) {
74
+ return {
75
+ componentName: componentResult.name,
76
+ componentImportPathInSameFile: componentResult.path,
77
+ moduleName: moduleResult.name,
78
+ moduleImportPathInSameFile: moduleResult.path,
79
+ };
80
+ }
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ /** Resolves an identifier to its original name and path that it was imported from. */
86
+ function resolveIdentifier(identifier) {
87
+ const sourceFile = identifier.getSourceFile();
88
+ // Try to resolve the import path by looking at the top-level named imports of the file.
89
+ for (const node of sourceFile.statements) {
90
+ if (!typescript_1.default.isImportDeclaration(node) ||
91
+ !typescript_1.default.isStringLiteral(node.moduleSpecifier) ||
92
+ !node.importClause ||
93
+ !node.importClause.namedBindings ||
94
+ !typescript_1.default.isNamedImports(node.importClause.namedBindings)) {
95
+ continue;
96
+ }
97
+ for (const element of node.importClause.namedBindings.elements) {
98
+ if (element.name.text === identifier.text) {
99
+ return {
100
+ // Note that we use `propertyName` if available, because it contains
101
+ // the real name in the case where the import is aliased.
102
+ name: (element.propertyName || element.name).text,
103
+ path: node.moduleSpecifier.text,
104
+ };
105
+ }
106
+ }
107
+ }
108
+ return null;
109
+ }