@openrewrite/recipes-angular 1.9.0 → 1.10.0-20260813-195315

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 (120) hide show
  1. package/dist/index.d.ts +20 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +84 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/migration/add-eager-change-detection.d.ts +8 -0
  6. package/dist/migration/add-eager-change-detection.d.ts.map +1 -0
  7. package/dist/migration/add-eager-change-detection.js +130 -0
  8. package/dist/migration/add-eager-change-detection.js.map +1 -0
  9. package/dist/migration/add-istanbul-instrumenter.d.ts +8 -0
  10. package/dist/migration/add-istanbul-instrumenter.d.ts.map +1 -0
  11. package/dist/migration/add-istanbul-instrumenter.js +80 -0
  12. package/dist/migration/add-istanbul-instrumenter.js.map +1 -0
  13. package/dist/migration/add-no-incremental-hydration.d.ts +8 -0
  14. package/dist/migration/add-no-incremental-hydration.d.ts.map +1 -0
  15. package/dist/migration/add-no-incremental-hydration.js +69 -0
  16. package/dist/migration/add-no-incremental-hydration.js.map +1 -0
  17. package/dist/migration/add-strict-templates-false.d.ts +8 -0
  18. package/dist/migration/add-strict-templates-false.d.ts.map +1 -0
  19. package/dist/migration/add-strict-templates-false.js +84 -0
  20. package/dist/migration/add-strict-templates-false.js.map +1 -0
  21. package/dist/migration/add-trust-proxy-headers.d.ts +8 -0
  22. package/dist/migration/add-trust-proxy-headers.d.ts.map +1 -0
  23. package/dist/migration/add-trust-proxy-headers.js +82 -0
  24. package/dist/migration/add-trust-proxy-headers.js.map +1 -0
  25. package/dist/migration/add-with-xhr.d.ts +8 -0
  26. package/dist/migration/add-with-xhr.d.ts.map +1 -0
  27. package/dist/migration/add-with-xhr.js +70 -0
  28. package/dist/migration/add-with-xhr.js.map +1 -0
  29. package/dist/migration/js-support.d.ts +6 -0
  30. package/dist/migration/js-support.d.ts.map +1 -0
  31. package/dist/migration/js-support.js +94 -0
  32. package/dist/migration/js-support.js.map +1 -0
  33. package/dist/migration/json-support.d.ts +19 -0
  34. package/dist/migration/json-support.d.ts.map +1 -0
  35. package/dist/migration/json-support.js +117 -0
  36. package/dist/migration/json-support.js.map +1 -0
  37. package/dist/migration/migrate-input-to-signal.d.ts.map +1 -1
  38. package/dist/migration/migrate-input-to-signal.js +3 -67
  39. package/dist/migration/migrate-input-to-signal.js.map +1 -1
  40. package/dist/migration/migrate-model-with-output.d.ts +8 -0
  41. package/dist/migration/migrate-model-with-output.d.ts.map +1 -0
  42. package/dist/migration/migrate-model-with-output.js +192 -0
  43. package/dist/migration/migrate-model-with-output.js.map +1 -0
  44. package/dist/migration/remove-with-fetch.d.ts +8 -0
  45. package/dist/migration/remove-with-fetch.d.ts.map +1 -0
  46. package/dist/migration/remove-with-fetch.js +73 -0
  47. package/dist/migration/remove-with-fetch.js.map +1 -0
  48. package/dist/migration/rename-angular-js-globals.d.ts +8 -0
  49. package/dist/migration/rename-angular-js-globals.d.ts.map +1 -0
  50. package/dist/migration/rename-angular-js-globals.js +56 -0
  51. package/dist/migration/rename-angular-js-globals.js.map +1 -0
  52. package/dist/migration/rename-default-change-detection.d.ts +8 -0
  53. package/dist/migration/rename-default-change-detection.d.ts.map +1 -0
  54. package/dist/migration/rename-default-change-detection.js +52 -0
  55. package/dist/migration/rename-default-change-detection.js.map +1 -0
  56. package/dist/migration/rename-ssr-experimental-platform.d.ts +8 -0
  57. package/dist/migration/rename-ssr-experimental-platform.d.ts.map +1 -0
  58. package/dist/migration/rename-ssr-experimental-platform.js +124 -0
  59. package/dist/migration/rename-ssr-experimental-platform.js.map +1 -0
  60. package/dist/migration/replace-create-ng-module-ref.d.ts +8 -0
  61. package/dist/migration/replace-create-ng-module-ref.d.ts.map +1 -0
  62. package/dist/migration/replace-create-ng-module-ref.js +51 -0
  63. package/dist/migration/replace-create-ng-module-ref.js.map +1 -0
  64. package/dist/migration/suppress-safe-navigation-diagnostics.d.ts +8 -0
  65. package/dist/migration/suppress-safe-navigation-diagnostics.d.ts.map +1 -0
  66. package/dist/migration/suppress-safe-navigation-diagnostics.js +55 -0
  67. package/dist/migration/suppress-safe-navigation-diagnostics.js.map +1 -0
  68. package/dist/migration/upgrade-to-angular-22.d.ts +14 -0
  69. package/dist/migration/upgrade-to-angular-22.d.ts.map +1 -0
  70. package/dist/migration/upgrade-to-angular-22.js +112 -0
  71. package/dist/migration/upgrade-to-angular-22.js.map +1 -0
  72. package/dist/search/find-can-match-usage.d.ts +8 -0
  73. package/dist/search/find-can-match-usage.d.ts.map +1 -0
  74. package/dist/search/find-can-match-usage.js +53 -0
  75. package/dist/search/find-can-match-usage.js.map +1 -0
  76. package/dist/search/find-check-no-changes-usage.d.ts +8 -0
  77. package/dist/search/find-check-no-changes-usage.d.ts.map +1 -0
  78. package/dist/search/find-check-no-changes-usage.js +50 -0
  79. package/dist/search/find-check-no-changes-usage.js.map +1 -0
  80. package/dist/search/find-http-xhr-backend-usage.d.ts +8 -0
  81. package/dist/search/find-http-xhr-backend-usage.d.ts.map +1 -0
  82. package/dist/search/find-http-xhr-backend-usage.js +65 -0
  83. package/dist/search/find-http-xhr-backend-usage.js.map +1 -0
  84. package/dist/search/find-implicit-params-inheritance.d.ts +8 -0
  85. package/dist/search/find-implicit-params-inheritance.d.ts.map +1 -0
  86. package/dist/search/find-implicit-params-inheritance.js +74 -0
  87. package/dist/search/find-implicit-params-inheritance.js.map +1 -0
  88. package/dist/search/find-provide-routes-usage.d.ts +8 -0
  89. package/dist/search/find-provide-routes-usage.d.ts.map +1 -0
  90. package/dist/search/find-provide-routes-usage.js +64 -0
  91. package/dist/search/find-provide-routes-usage.js.map +1 -0
  92. package/dist/search/find-template-safe-navigation.d.ts +8 -0
  93. package/dist/search/find-template-safe-navigation.d.ts.map +1 -0
  94. package/dist/search/find-template-safe-navigation.js +105 -0
  95. package/dist/search/find-template-safe-navigation.js.map +1 -0
  96. package/package.json +1 -1
  97. package/src/index.ts +60 -0
  98. package/src/migration/add-eager-change-detection.ts +109 -0
  99. package/src/migration/add-istanbul-instrumenter.ts +62 -0
  100. package/src/migration/add-no-incremental-hydration.ts +49 -0
  101. package/src/migration/add-strict-templates-false.ts +62 -0
  102. package/src/migration/add-trust-proxy-headers.ts +61 -0
  103. package/src/migration/add-with-xhr.ts +50 -0
  104. package/src/migration/js-support.ts +92 -0
  105. package/src/migration/json-support.ts +119 -0
  106. package/src/migration/migrate-input-to-signal.ts +1 -67
  107. package/src/migration/migrate-model-with-output.ts +201 -0
  108. package/src/migration/remove-with-fetch.ts +53 -0
  109. package/src/migration/rename-angular-js-globals.ts +37 -0
  110. package/src/migration/rename-default-change-detection.ts +33 -0
  111. package/src/migration/rename-ssr-experimental-platform.ts +108 -0
  112. package/src/migration/replace-create-ng-module-ref.ts +32 -0
  113. package/src/migration/suppress-safe-navigation-diagnostics.ts +42 -0
  114. package/src/migration/upgrade-to-angular-22.ts +92 -0
  115. package/src/search/find-can-match-usage.ts +32 -0
  116. package/src/search/find-check-no-changes-usage.ts +29 -0
  117. package/src/search/find-http-xhr-backend-usage.ts +44 -0
  118. package/src/search/find-implicit-params-inheritance.ts +49 -0
  119. package/src/search/find-provide-routes-usage.ts +43 -0
  120. package/src/search/find-template-safe-navigation.ts +72 -0
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 the original author or authors.
4
+ *
5
+ * Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
6
+ */
7
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
+ return new (P || (P = Promise))(function (resolve, reject) {
10
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
14
+ });
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FindHttpXhrBackendUsage = void 0;
18
+ const rewrite_1 = require("@openrewrite/rewrite");
19
+ const javascript_1 = require("@openrewrite/rewrite/javascript");
20
+ const java_1 = require("@openrewrite/rewrite/java");
21
+ class FindHttpXhrBackendUsage extends rewrite_1.Recipe {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.name = "org.openrewrite.angular.search.find-http-xhr-backend-usage";
25
+ this.displayName = "Find `HttpXhrBackend` usage";
26
+ this.description = "Finds imports of `HttpXhrBackend` from `@angular/common/http`. " +
27
+ "Angular 22 uses the Fetch API by default; add `withXhr()` to `provideHttpClient()` to keep using `XMLHttpRequest`, " +
28
+ "which is also required for upload progress reports.";
29
+ }
30
+ editor() {
31
+ return __awaiter(this, void 0, void 0, function* () {
32
+ return new class extends javascript_1.JavaScriptVisitor {
33
+ visitImportDeclaration(jsImport, p) {
34
+ const _super = Object.create(null, {
35
+ visitImportDeclaration: { get: () => super.visitImportDeclaration }
36
+ });
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ var _a;
39
+ const imp = yield _super.visitImportDeclaration.call(this, jsImport, p);
40
+ if (!imp.moduleSpecifier)
41
+ return imp;
42
+ const moduleSpec = imp.moduleSpecifier.element;
43
+ if (moduleSpec.kind !== java_1.J.Kind.Literal)
44
+ return imp;
45
+ if (moduleSpec.value !== '@angular/common/http')
46
+ return imp;
47
+ const namedBindings = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings;
48
+ if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === javascript_1.JS.Kind.NamedImports) {
49
+ const named = namedBindings;
50
+ for (const specifier of named.elements.elements) {
51
+ const spec = specifier.element;
52
+ if ((0, java_1.isIdentifier)(spec.specifier) && spec.specifier.simpleName === 'HttpXhrBackend') {
53
+ return (0, rewrite_1.foundSearchResult)(imp, "HttpXhrBackend is no longer the default in Angular 22. Use provideHttpClient(withXhr()) to keep XMLHttpRequest based requests. See https://angular.dev/api/common/http/withXhr");
54
+ }
55
+ }
56
+ }
57
+ return imp;
58
+ });
59
+ }
60
+ };
61
+ });
62
+ }
63
+ }
64
+ exports.FindHttpXhrBackendUsage = FindHttpXhrBackendUsage;
65
+ //# sourceMappingURL=find-http-xhr-backend-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-http-xhr-backend-usage.js","sourceRoot":"","sources":["../../src/search/find-http-xhr-backend-usage.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;AAEH,kDAA8F;AAC9F,gEAAsE;AACtE,oDAA0D;AAE1D,MAAa,uBAAwB,SAAQ,gBAAM;IAAnD;;QACa,SAAI,GAAG,4DAA4D,CAAC;QACpE,gBAAW,GAAW,6BAA6B,CAAC;QACpD,gBAAW,GAAW,iEAAiE;YAC5F,qHAAqH;YACrH,qDAAqD,CAAC;IA4B9D,CAAC;IA1BS,MAAM;;YACR,OAAO,IAAI,KAAM,SAAQ,8BAAmC;gBACxC,sBAAsB,CAAC,QAAmB,EAAE,CAAmB;;;;;;wBAC3E,MAAM,GAAG,GAAG,MAAM,OAAM,sBAAsB,YAAC,QAAQ,EAAE,CAAC,CAAc,CAAC;wBACzE,IAAI,CAAC,GAAG,CAAC,eAAe;4BAAE,OAAO,GAAG,CAAC;wBAErC,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC;wBAC/C,IAAI,UAAU,CAAC,IAAI,KAAK,QAAC,CAAC,IAAI,CAAC,OAAO;4BAAE,OAAO,GAAG,CAAC;wBACnD,IAAK,UAAwB,CAAC,KAAK,KAAK,sBAAsB;4BAAE,OAAO,GAAG,CAAC;wBAE3E,MAAM,aAAa,GAAG,MAAA,GAAG,CAAC,YAAY,0CAAE,aAAa,CAAC;wBACtD,IAAI,CAAA,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,IAAI,MAAK,eAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;4BAC/C,MAAM,KAAK,GAAG,aAAgC,CAAC;4BAC/C,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;gCAC9C,MAAM,IAAI,GAAG,SAAS,CAAC,OAA6B,CAAC;gCACrD,IAAI,IAAA,mBAAY,EAAC,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,KAAK,gBAAgB,EAAE,CAAC;oCACjF,OAAO,IAAA,2BAAiB,EAAC,GAAG,EACxB,gLAAgL,CAAC,CAAC;gCAC1L,CAAC;4BACL,CAAC;wBACL,CAAC;wBAED,OAAO,GAAG,CAAC;oBACf,CAAC;iBAAA;aACJ,CAAC;QACN,CAAC;KAAA;CACJ;AAjCD,0DAiCC"}
@@ -0,0 +1,8 @@
1
+ import { ExecutionContext, Recipe, TreeVisitor } from "@openrewrite/rewrite";
2
+ export declare class FindImplicitParamsInheritance extends Recipe {
3
+ readonly name = "org.openrewrite.angular.search.find-implicit-params-inheritance";
4
+ readonly displayName: string;
5
+ readonly description: string;
6
+ editor(): Promise<TreeVisitor<any, ExecutionContext>>;
7
+ }
8
+ //# sourceMappingURL=find-implicit-params-inheritance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-implicit-params-inheritance.d.ts","sourceRoot":"","sources":["../../src/search/find-implicit-params-inheritance.ts"],"names":[],"mappings":"AAMA,OAAO,EAAC,gBAAgB,EAAqB,MAAM,EAAE,WAAW,EAAC,MAAM,sBAAsB,CAAC;AAI9F,qBAAa,6BAA8B,SAAQ,MAAM;IACrD,QAAQ,CAAC,IAAI,qEAAqE;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAkF;IAC9G,QAAQ,CAAC,WAAW,EAAE,MAAM,CAC8G;IAEpI,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;CAkB9D"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 the original author or authors.
4
+ *
5
+ * Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
6
+ */
7
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
+ return new (P || (P = Promise))(function (resolve, reject) {
10
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
14
+ });
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FindImplicitParamsInheritance = void 0;
18
+ const rewrite_1 = require("@openrewrite/rewrite");
19
+ const javascript_1 = require("@openrewrite/rewrite/javascript");
20
+ const java_1 = require("@openrewrite/rewrite/java");
21
+ class FindImplicitParamsInheritance extends rewrite_1.Recipe {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.name = "org.openrewrite.angular.search.find-implicit-params-inheritance";
25
+ this.displayName = "Find router configuration relying on the default `paramsInheritanceStrategy`";
26
+ this.description = "Finds `provideRouter()` and `RouterModule.forRoot()` calls that do not set `paramsInheritanceStrategy`. " +
27
+ "Angular 22 changed the default from `emptyOnly` to `always`, so route parameters and data are now inherited from all parent routes.";
28
+ }
29
+ editor() {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ return new class extends javascript_1.JavaScriptVisitor {
32
+ visitMethodInvocation(method, p) {
33
+ const _super = Object.create(null, {
34
+ visitMethodInvocation: { get: () => super.visitMethodInvocation }
35
+ });
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ const m = yield _super.visitMethodInvocation.call(this, method, p);
38
+ if (!m || !(0, java_1.isIdentifier)(m.name))
39
+ return m;
40
+ const isProvideRouter = !m.select && m.name.simpleName === 'provideRouter';
41
+ const isForRoot = m.name.simpleName === 'forRoot' &&
42
+ !!m.select && (0, java_1.isIdentifier)(m.select.element) && m.select.element.simpleName === 'RouterModule';
43
+ if (!isProvideRouter && !isForRoot)
44
+ return m;
45
+ if (yield declaresParamsInheritanceStrategy(m, p))
46
+ return m;
47
+ return (0, rewrite_1.foundSearchResult)(m, "paramsInheritanceStrategy defaults to 'always' as of Angular 22. Set it to 'emptyOnly' to keep the previous behavior.");
48
+ });
49
+ }
50
+ };
51
+ });
52
+ }
53
+ }
54
+ exports.FindImplicitParamsInheritance = FindImplicitParamsInheritance;
55
+ function declaresParamsInheritanceStrategy(method, p) {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ var _a, _b;
58
+ let found = false;
59
+ const detector = new class extends javascript_1.JavaScriptVisitor {
60
+ visitIdentifier(identifier, ctx) {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ if (identifier.simpleName === 'paramsInheritanceStrategy')
63
+ found = true;
64
+ return identifier;
65
+ });
66
+ }
67
+ }();
68
+ for (const element of (_b = (_a = method.arguments) === null || _a === void 0 ? void 0 : _a.elements) !== null && _b !== void 0 ? _b : []) {
69
+ yield detector.visit(element.element, p);
70
+ }
71
+ return found;
72
+ });
73
+ }
74
+ //# sourceMappingURL=find-implicit-params-inheritance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-implicit-params-inheritance.js","sourceRoot":"","sources":["../../src/search/find-implicit-params-inheritance.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;AAEH,kDAA8F;AAC9F,gEAAkE;AAClE,oDAA0D;AAE1D,MAAa,6BAA8B,SAAQ,gBAAM;IAAzD;;QACa,SAAI,GAAG,iEAAiE,CAAC;QACzE,gBAAW,GAAW,8EAA8E,CAAC;QACrG,gBAAW,GAAW,0GAA0G;YACrI,qIAAqI,CAAC;IAoB9I,CAAC;IAlBS,MAAM;;YACR,OAAO,IAAI,KAAM,SAAQ,8BAAmC;gBACzC,qBAAqB,CAAC,MAA0B,EAAE,CAAmB;;;;;wBAChF,MAAM,CAAC,GAAG,MAAM,OAAM,qBAAqB,YAAC,MAAM,EAAE,CAAC,CAAuB,CAAC;wBAC7E,IAAI,CAAC,CAAC,IAAI,CAAC,IAAA,mBAAY,EAAC,CAAC,CAAC,IAAI,CAAC;4BAAE,OAAO,CAAC,CAAC;wBAE1C,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,KAAK,eAAe,CAAC;wBAC3E,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS;4BAC7C,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,IAAA,mBAAY,EAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,KAAK,cAAc,CAAC;wBACnG,IAAI,CAAC,eAAe,IAAI,CAAC,SAAS;4BAAE,OAAO,CAAC,CAAC;wBAE7C,IAAI,MAAM,iCAAiC,CAAC,CAAC,EAAE,CAAC,CAAC;4BAAE,OAAO,CAAC,CAAC;wBAE5D,OAAO,IAAA,2BAAiB,EAAC,CAAC,EACtB,uHAAuH,CAAC,CAAC;oBACjI,CAAC;iBAAA;aACJ,CAAC;QACN,CAAC;KAAA;CACJ;AAxBD,sEAwBC;AAED,SAAe,iCAAiC,CAAC,MAA0B,EAAE,CAAmB;;;QAC5F,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,MAAM,QAAQ,GAAG,IAAI,KAAM,SAAQ,8BAAmC;YACnD,eAAe,CAAC,UAAwB,EAAE,GAAqB;;oBAC1E,IAAI,UAAU,CAAC,UAAU,KAAK,2BAA2B;wBAAE,KAAK,GAAG,IAAI,CAAC;oBACxE,OAAO,UAAU,CAAC;gBACtB,CAAC;aAAA;SACJ,EAAE,CAAC;QACJ,KAAK,MAAM,OAAO,IAAI,MAAA,MAAA,MAAM,CAAC,SAAS,0CAAE,QAAQ,mCAAI,EAAE,EAAE,CAAC;YACrD,MAAM,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CAAA"}
@@ -0,0 +1,8 @@
1
+ import { ExecutionContext, Recipe, TreeVisitor } from "@openrewrite/rewrite";
2
+ export declare class FindProvideRoutesUsage extends Recipe {
3
+ readonly name = "org.openrewrite.angular.search.find-provide-routes-usage";
4
+ readonly displayName: string;
5
+ readonly description: string;
6
+ editor(): Promise<TreeVisitor<any, ExecutionContext>>;
7
+ }
8
+ //# sourceMappingURL=find-provide-routes-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-provide-routes-usage.d.ts","sourceRoot":"","sources":["../../src/search/find-provide-routes-usage.ts"],"names":[],"mappings":"AAMA,OAAO,EAAC,gBAAgB,EAAqB,MAAM,EAAE,WAAW,EAAC,MAAM,sBAAsB,CAAC;AAI9F,qBAAa,sBAAuB,SAAQ,MAAM;IAC9C,QAAQ,CAAC,IAAI,8DAA8D;IAC3E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAgC;IAC5D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAC+C;IAErE,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;CA0B9D"}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 the original author or authors.
4
+ *
5
+ * Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
6
+ */
7
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
+ return new (P || (P = Promise))(function (resolve, reject) {
10
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
14
+ });
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FindProvideRoutesUsage = void 0;
18
+ const rewrite_1 = require("@openrewrite/rewrite");
19
+ const javascript_1 = require("@openrewrite/rewrite/javascript");
20
+ const java_1 = require("@openrewrite/rewrite/java");
21
+ class FindProvideRoutesUsage extends rewrite_1.Recipe {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.name = "org.openrewrite.angular.search.find-provide-routes-usage";
25
+ this.displayName = "Find `provideRoutes` usage";
26
+ this.description = "Finds imports of `provideRoutes` from `@angular/router`, which was removed in Angular 22. " +
27
+ "Use `provideRouter()`, or provide the `ROUTES` multi token directly.";
28
+ }
29
+ editor() {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ return new class extends javascript_1.JavaScriptVisitor {
32
+ visitImportDeclaration(jsImport, p) {
33
+ const _super = Object.create(null, {
34
+ visitImportDeclaration: { get: () => super.visitImportDeclaration }
35
+ });
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ var _a;
38
+ const imp = yield _super.visitImportDeclaration.call(this, jsImport, p);
39
+ if (!imp.moduleSpecifier)
40
+ return imp;
41
+ const moduleSpec = imp.moduleSpecifier.element;
42
+ if (moduleSpec.kind !== java_1.J.Kind.Literal)
43
+ return imp;
44
+ if (moduleSpec.value !== '@angular/router')
45
+ return imp;
46
+ const namedBindings = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings;
47
+ if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === javascript_1.JS.Kind.NamedImports) {
48
+ const named = namedBindings;
49
+ for (const specifier of named.elements.elements) {
50
+ const spec = specifier.element;
51
+ if ((0, java_1.isIdentifier)(spec.specifier) && spec.specifier.simpleName === 'provideRoutes') {
52
+ return (0, rewrite_1.foundSearchResult)(imp, "provideRoutes() was removed in Angular 22. Use provideRouter(), or the ROUTES multi token if necessary.");
53
+ }
54
+ }
55
+ }
56
+ return imp;
57
+ });
58
+ }
59
+ };
60
+ });
61
+ }
62
+ }
63
+ exports.FindProvideRoutesUsage = FindProvideRoutesUsage;
64
+ //# sourceMappingURL=find-provide-routes-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-provide-routes-usage.js","sourceRoot":"","sources":["../../src/search/find-provide-routes-usage.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;AAEH,kDAA8F;AAC9F,gEAAsE;AACtE,oDAA0D;AAE1D,MAAa,sBAAuB,SAAQ,gBAAM;IAAlD;;QACa,SAAI,GAAG,0DAA0D,CAAC;QAClE,gBAAW,GAAW,4BAA4B,CAAC;QACnD,gBAAW,GAAW,4FAA4F;YACvH,sEAAsE,CAAC;IA4B/E,CAAC;IA1BS,MAAM;;YACR,OAAO,IAAI,KAAM,SAAQ,8BAAmC;gBACxC,sBAAsB,CAAC,QAAmB,EAAE,CAAmB;;;;;;wBAC3E,MAAM,GAAG,GAAG,MAAM,OAAM,sBAAsB,YAAC,QAAQ,EAAE,CAAC,CAAc,CAAC;wBACzE,IAAI,CAAC,GAAG,CAAC,eAAe;4BAAE,OAAO,GAAG,CAAC;wBAErC,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC;wBAC/C,IAAI,UAAU,CAAC,IAAI,KAAK,QAAC,CAAC,IAAI,CAAC,OAAO;4BAAE,OAAO,GAAG,CAAC;wBACnD,IAAK,UAAwB,CAAC,KAAK,KAAK,iBAAiB;4BAAE,OAAO,GAAG,CAAC;wBAEtE,MAAM,aAAa,GAAG,MAAA,GAAG,CAAC,YAAY,0CAAE,aAAa,CAAC;wBACtD,IAAI,CAAA,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,IAAI,MAAK,eAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;4BAC/C,MAAM,KAAK,GAAG,aAAgC,CAAC;4BAC/C,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;gCAC9C,MAAM,IAAI,GAAG,SAAS,CAAC,OAA6B,CAAC;gCACrD,IAAI,IAAA,mBAAY,EAAC,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,KAAK,eAAe,EAAE,CAAC;oCAChF,OAAO,IAAA,2BAAiB,EAAC,GAAG,EACxB,yGAAyG,CAAC,CAAC;gCACnH,CAAC;4BACL,CAAC;wBACL,CAAC;wBAED,OAAO,GAAG,CAAC;oBACf,CAAC;iBAAA;aACJ,CAAC;QACN,CAAC;KAAA;CACJ;AAhCD,wDAgCC"}
@@ -0,0 +1,8 @@
1
+ import { Recipe } from "@openrewrite/rewrite";
2
+ export declare class FindTemplateSafeNavigation extends Recipe {
3
+ readonly name = "org.openrewrite.angular.search.find-template-safe-navigation";
4
+ readonly displayName: string;
5
+ readonly description: string;
6
+ recipeList(): Promise<Recipe[]>;
7
+ }
8
+ //# sourceMappingURL=find-template-safe-navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-template-safe-navigation.d.ts","sourceRoot":"","sources":["../../src/search/find-template-safe-navigation.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsC,MAAM,EAAc,MAAM,sBAAsB,CAAC;AAY9F,qBAAa,0BAA2B,SAAQ,MAAM;IAClD,QAAQ,CAAC,IAAI,kEAAkE;IAC/E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAA+C;IAC3E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAGiB;IAEvC,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAGxC"}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 the original author or authors.
4
+ *
5
+ * Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
6
+ */
7
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
+ return new (P || (P = Promise))(function (resolve, reject) {
10
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
14
+ });
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FindTemplateSafeNavigation = void 0;
18
+ const rewrite_1 = require("@openrewrite/rewrite");
19
+ const text_1 = require("@openrewrite/rewrite/text");
20
+ const javascript_1 = require("@openrewrite/rewrite/javascript");
21
+ const java_1 = require("@openrewrite/rewrite/java");
22
+ const MESSAGE = "Safe navigation in an Angular template returns undefined rather than null as of Angular 22. " +
23
+ "Review bindings that distinguish the two, such as comparisons against null and pipes that treat null and " +
24
+ "undefined differently. See https://angular.dev/errors/safe-navigation-undefined";
25
+ /** `{{ a?.b }}`, `[x]="a?.b"`, and `@if (a?.b)`, but not `a ?? b` or the `?.` of a TypeScript expression. */
26
+ const SAFE_NAVIGATION = /\?\.(?![.\d])/;
27
+ class FindTemplateSafeNavigation extends rewrite_1.Recipe {
28
+ constructor() {
29
+ super(...arguments);
30
+ this.name = "org.openrewrite.angular.search.find-template-safe-navigation";
31
+ this.displayName = "Find safe navigation in Angular templates";
32
+ this.description = "Finds `?.` in HTML templates and in inline `template` metadata. Angular 22 changed " +
33
+ "safe navigation in templates to return `undefined` instead of `null`, which changes bindings that tell the two " +
34
+ "apart. Angular ships a `$safeNavigationMigration()` wrapper for this; this recipe reports the templates to review " +
35
+ "rather than rewriting the expressions.";
36
+ }
37
+ recipeList() {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ return [new FindHtmlSafeNavigation(), new FindInlineTemplateSafeNavigation()];
40
+ });
41
+ }
42
+ }
43
+ exports.FindTemplateSafeNavigation = FindTemplateSafeNavigation;
44
+ class FindHtmlSafeNavigation extends rewrite_1.Recipe {
45
+ constructor() {
46
+ super(...arguments);
47
+ this.name = "org.openrewrite.angular.search.find-template-safe-navigation.html";
48
+ this.displayName = "Find safe navigation in HTML templates";
49
+ this.description = "Finds `?.` in `.html` templates.";
50
+ }
51
+ editor() {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ return new class extends text_1.PlainTextVisitor {
54
+ visitText(text, p) {
55
+ const _super = Object.create(null, {
56
+ visitText: { get: () => super.visitText }
57
+ });
58
+ return __awaiter(this, void 0, void 0, function* () {
59
+ const t = yield _super.visitText.call(this, text, p);
60
+ if (!t || !t.sourcePath.endsWith('.html'))
61
+ return t;
62
+ if (!SAFE_NAVIGATION.test(t.text))
63
+ return t;
64
+ return (0, rewrite_1.foundSearchResult)(t, MESSAGE);
65
+ });
66
+ }
67
+ }();
68
+ });
69
+ }
70
+ }
71
+ class FindInlineTemplateSafeNavigation extends rewrite_1.Recipe {
72
+ constructor() {
73
+ super(...arguments);
74
+ this.name = "org.openrewrite.angular.search.find-template-safe-navigation.inline";
75
+ this.displayName = "Find safe navigation in inline templates";
76
+ this.description = "Finds `?.` in inline `template` metadata of `@Component` decorators.";
77
+ }
78
+ editor() {
79
+ return __awaiter(this, void 0, void 0, function* () {
80
+ return new class extends javascript_1.JavaScriptVisitor {
81
+ visitPropertyAssignment(property, p) {
82
+ const _super = Object.create(null, {
83
+ visitPropertyAssignment: { get: () => super.visitPropertyAssignment }
84
+ });
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ var _a, _b;
87
+ const prop = yield _super.visitPropertyAssignment.call(this, property, p);
88
+ if (!prop)
89
+ return prop;
90
+ const name = (_a = prop.name) === null || _a === void 0 ? void 0 : _a.element;
91
+ if (!(0, java_1.isIdentifier)(name) || name.simpleName !== 'template')
92
+ return prop;
93
+ const initializer = prop.initializer;
94
+ if (!initializer || !(0, java_1.isLiteral)(initializer))
95
+ return prop;
96
+ if (!SAFE_NAVIGATION.test((_b = initializer.valueSource) !== null && _b !== void 0 ? _b : ''))
97
+ return prop;
98
+ return (0, rewrite_1.foundSearchResult)(prop, MESSAGE);
99
+ });
100
+ }
101
+ }();
102
+ });
103
+ }
104
+ }
105
+ //# sourceMappingURL=find-template-safe-navigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-template-safe-navigation.js","sourceRoot":"","sources":["../../src/search/find-template-safe-navigation.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;AAEH,kDAA8F;AAC9F,oDAAsE;AACtE,gEAAkE;AAClE,oDAAqE;AAErE,MAAM,OAAO,GAAG,8FAA8F;IAC1G,2GAA2G;IAC3G,iFAAiF,CAAC;AAEtF,6GAA6G;AAC7G,MAAM,eAAe,GAAG,eAAe,CAAC;AAExC,MAAa,0BAA2B,SAAQ,gBAAM;IAAtD;;QACa,SAAI,GAAG,8DAA8D,CAAC;QACtE,gBAAW,GAAW,2CAA2C,CAAC;QAClE,gBAAW,GAAW,qFAAqF;YAChH,iHAAiH;YACjH,oHAAoH;YACpH,wCAAwC,CAAC;IAKjD,CAAC;IAHS,UAAU;;YACZ,OAAO,CAAC,IAAI,sBAAsB,EAAE,EAAE,IAAI,gCAAgC,EAAE,CAAC,CAAC;QAClF,CAAC;KAAA;CACJ;AAXD,gEAWC;AAED,MAAM,sBAAuB,SAAQ,gBAAM;IAA3C;;QACa,SAAI,GAAG,mEAAmE,CAAC;QAC3E,gBAAW,GAAW,wCAAwC,CAAC;QAC/D,gBAAW,GAAW,kCAAkC,CAAC;IAatE,CAAC;IAXS,MAAM;;YACR,OAAO,IAAI,KAAM,SAAQ,uBAAkC;gBACxC,SAAS,CAAC,IAAe,EAAE,CAAmB;;;;;wBACzD,MAAM,CAAC,GAAG,MAAM,OAAM,SAAS,YAAC,IAAI,EAAE,CAAC,CAAc,CAAC;wBACtD,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC;4BAAE,OAAO,CAAC,CAAC;wBACpD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;4BAAE,OAAO,CAAC,CAAC;wBAE5C,OAAO,IAAA,2BAAiB,EAAC,CAAC,EAAE,OAAO,CAAC,CAAC;oBACzC,CAAC;iBAAA;aACJ,EAAE,CAAC;QACR,CAAC;KAAA;CACJ;AAED,MAAM,gCAAiC,SAAQ,gBAAM;IAArD;;QACa,SAAI,GAAG,qEAAqE,CAAC;QAC7E,gBAAW,GAAW,0CAA0C,CAAC;QACjE,gBAAW,GAAW,sEAAsE,CAAC;IAmB1G,CAAC;IAjBS,MAAM;;YACR,OAAO,IAAI,KAAM,SAAQ,8BAAmC;gBACxC,uBAAuB,CAAC,QAAa,EAAE,CAAmB;;;;;;wBACtE,MAAM,IAAI,GAAG,MAAM,OAAM,uBAAuB,YAAC,QAAQ,EAAE,CAAC,CAAQ,CAAC;wBACrE,IAAI,CAAC,IAAI;4BAAE,OAAO,IAAI,CAAC;wBAEvB,MAAM,IAAI,GAAG,MAAA,IAAI,CAAC,IAAI,0CAAE,OAAO,CAAC;wBAChC,IAAI,CAAC,IAAA,mBAAY,EAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU;4BAAE,OAAO,IAAI,CAAC;wBAEvE,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;wBACrC,IAAI,CAAC,WAAW,IAAI,CAAC,IAAA,gBAAS,EAAC,WAAW,CAAC;4BAAE,OAAO,IAAI,CAAC;wBACzD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAA,WAAW,CAAC,WAAW,mCAAI,EAAE,CAAC;4BAAE,OAAO,IAAI,CAAC;wBAEtE,OAAO,IAAA,2BAAiB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;oBAC5C,CAAC;iBAAA;aACJ,EAAE,CAAC;QACR,CAAC;KAAA;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrewrite/recipes-angular",
3
- "version": "1.9.0",
3
+ "version": "1.10.0-20260813-195315",
4
4
  "license": "Moderne Proprietary",
5
5
  "description": "OpenRewrite recipes for Angular.",
6
6
  "homepage": "https://github.com/openrewrite/rewrite",
package/src/index.ts CHANGED
@@ -61,6 +61,7 @@ import {UpgradeToAngular18} from './migration/upgrade-to-angular-18';
61
61
  import {UpgradeToAngular19} from './migration/upgrade-to-angular-19';
62
62
  import {UpgradeToAngular20} from './migration/upgrade-to-angular-20';
63
63
  import {UpgradeToAngular21} from './migration/upgrade-to-angular-21';
64
+ import {UpgradeToAngular22} from './migration/upgrade-to-angular-22';
64
65
  import {FindAngularDecorator} from './search/find-angular-decorator';
65
66
  import {FindZoneJsUsage} from './search/find-zone-js-usage';
66
67
  import {FindHammerJsUsage} from './search/find-hammer-js-usage';
@@ -107,6 +108,25 @@ import {MigrateInputToSignal} from './migration/migrate-input-to-signal';
107
108
  import {MigrateOutputToSignal} from './migration/migrate-output-to-signal';
108
109
  import {MigrateQueryToSignal} from './migration/migrate-query-to-signal';
109
110
  import {AddLocalizePolyfill} from './migration/add-localize-polyfill';
111
+ import {FindProvideRoutesUsage} from './search/find-provide-routes-usage';
112
+ import {FindCheckNoChangesUsage} from './search/find-check-no-changes-usage';
113
+ import {FindCanMatchUsage} from './search/find-can-match-usage';
114
+ import {FindHttpXhrBackendUsage} from './search/find-http-xhr-backend-usage';
115
+ import {FindImplicitParamsInheritance} from './search/find-implicit-params-inheritance';
116
+ import {AddEagerChangeDetection} from './migration/add-eager-change-detection';
117
+ import {RenameDefaultChangeDetection} from './migration/rename-default-change-detection';
118
+ import {AddNoIncrementalHydration} from './migration/add-no-incremental-hydration';
119
+ import {AddStrictTemplatesFalse} from './migration/add-strict-templates-false';
120
+ import {SuppressSafeNavigationDiagnostics} from './migration/suppress-safe-navigation-diagnostics';
121
+ import {RemoveWithFetch} from './migration/remove-with-fetch';
122
+ import {AddWithXhr} from './migration/add-with-xhr';
123
+ import {AddTrustProxyHeaders} from './migration/add-trust-proxy-headers';
124
+ import {AddIstanbulInstrumenter} from './migration/add-istanbul-instrumenter';
125
+ import {MigrateModelWithOutput} from './migration/migrate-model-with-output';
126
+ import {FindTemplateSafeNavigation} from './search/find-template-safe-navigation';
127
+ import {ReplaceCreateNgModuleRef} from './migration/replace-create-ng-module-ref';
128
+ import {RenameAngularJsGlobals} from './migration/rename-angular-js-globals';
129
+ import {RenameSsrExperimentalPlatform} from './migration/rename-ssr-experimental-platform';
110
130
  import {UpgradeToPrimeNG18} from './libraries/primeng/18/upgrade-to-primeng-18';
111
131
  import {UpgradeComponentsTo18} from './libraries/primeng/18/upgrade-components-to-18';
112
132
  import {MigratePrimeNgConfigToPrimeNG} from './libraries/primeng/18/migrate-primeng-config';
@@ -189,6 +209,7 @@ export async function activate(marketplace: RecipeMarketplace) {
189
209
  await marketplace.install(UpgradeToAngular19, AngularMigration);
190
210
  await marketplace.install(UpgradeToAngular20, AngularMigration);
191
211
  await marketplace.install(UpgradeToAngular21, AngularMigration);
212
+ await marketplace.install(UpgradeToAngular22, AngularMigration);
192
213
  await marketplace.install(FindAngularDecorator, AngularSearch);
193
214
  await marketplace.install(FindZoneJsUsage, AngularSearch);
194
215
  await marketplace.install(FindHammerJsUsage, AngularSearch);
@@ -237,6 +258,25 @@ export async function activate(marketplace: RecipeMarketplace) {
237
258
  await marketplace.install(MigrateOutputToSignal, AngularMigration);
238
259
  await marketplace.install(MigrateQueryToSignal, AngularMigration);
239
260
  await marketplace.install(AddLocalizePolyfill, AngularMigration);
261
+ await marketplace.install(FindProvideRoutesUsage, AngularSearch);
262
+ await marketplace.install(FindCheckNoChangesUsage, AngularSearch);
263
+ await marketplace.install(FindCanMatchUsage, AngularSearch);
264
+ await marketplace.install(FindHttpXhrBackendUsage, AngularSearch);
265
+ await marketplace.install(FindImplicitParamsInheritance, AngularSearch);
266
+ await marketplace.install(AddEagerChangeDetection, AngularMigration);
267
+ await marketplace.install(RenameDefaultChangeDetection, AngularMigration);
268
+ await marketplace.install(AddNoIncrementalHydration, AngularMigration);
269
+ await marketplace.install(AddStrictTemplatesFalse, AngularMigration);
270
+ await marketplace.install(SuppressSafeNavigationDiagnostics, AngularMigration);
271
+ await marketplace.install(RemoveWithFetch, AngularMigration);
272
+ await marketplace.install(AddWithXhr, AngularMigration);
273
+ await marketplace.install(AddTrustProxyHeaders, AngularMigration);
274
+ await marketplace.install(AddIstanbulInstrumenter, AngularMigration);
275
+ await marketplace.install(MigrateModelWithOutput, AngularMigration);
276
+ await marketplace.install(FindTemplateSafeNavigation, AngularSearch);
277
+ await marketplace.install(ReplaceCreateNgModuleRef, AngularMigration);
278
+ await marketplace.install(RenameAngularJsGlobals, AngularMigration);
279
+ await marketplace.install(RenameSsrExperimentalPlatform, AngularMigration);
240
280
  // PrimeNG
241
281
  await marketplace.install(UpgradeToPrimeNG18, PrimeNG);
242
282
  await marketplace.install(UpgradeComponentsTo18, PrimeNG);
@@ -314,6 +354,7 @@ export {UpgradeToAngular18, UpgradeToAngular18CodeChanges} from './migration/upg
314
354
  export {UpgradeToAngular19, UpgradeToAngular19CodeChanges} from './migration/upgrade-to-angular-19';
315
355
  export {UpgradeToAngular20, UpgradeToAngular20CodeChanges} from './migration/upgrade-to-angular-20';
316
356
  export {UpgradeToAngular21, UpgradeToAngular21CodeChanges} from './migration/upgrade-to-angular-21';
357
+ export {UpgradeToAngular22, UpgradeToAngular22CodeChanges} from './migration/upgrade-to-angular-22';
317
358
  export {FindAngularDecorator} from './search/find-angular-decorator';
318
359
  export {FindZoneJsUsage} from './search/find-zone-js-usage';
319
360
  export {FindHammerJsUsage} from './search/find-hammer-js-usage';
@@ -362,6 +403,25 @@ export {MigrateInputToSignal} from './migration/migrate-input-to-signal';
362
403
  export {MigrateOutputToSignal} from './migration/migrate-output-to-signal';
363
404
  export {MigrateQueryToSignal} from './migration/migrate-query-to-signal';
364
405
  export {AddLocalizePolyfill} from './migration/add-localize-polyfill';
406
+ export {FindProvideRoutesUsage} from './search/find-provide-routes-usage';
407
+ export {FindCheckNoChangesUsage} from './search/find-check-no-changes-usage';
408
+ export {FindCanMatchUsage} from './search/find-can-match-usage';
409
+ export {FindHttpXhrBackendUsage} from './search/find-http-xhr-backend-usage';
410
+ export {FindImplicitParamsInheritance} from './search/find-implicit-params-inheritance';
411
+ export {AddEagerChangeDetection} from './migration/add-eager-change-detection';
412
+ export {RenameDefaultChangeDetection} from './migration/rename-default-change-detection';
413
+ export {AddNoIncrementalHydration} from './migration/add-no-incremental-hydration';
414
+ export {AddStrictTemplatesFalse} from './migration/add-strict-templates-false';
415
+ export {SuppressSafeNavigationDiagnostics} from './migration/suppress-safe-navigation-diagnostics';
416
+ export {RemoveWithFetch} from './migration/remove-with-fetch';
417
+ export {AddWithXhr} from './migration/add-with-xhr';
418
+ export {AddTrustProxyHeaders} from './migration/add-trust-proxy-headers';
419
+ export {AddIstanbulInstrumenter} from './migration/add-istanbul-instrumenter';
420
+ export {MigrateModelWithOutput} from './migration/migrate-model-with-output';
421
+ export {FindTemplateSafeNavigation} from './search/find-template-safe-navigation';
422
+ export {ReplaceCreateNgModuleRef} from './migration/replace-create-ng-module-ref';
423
+ export {RenameAngularJsGlobals} from './migration/rename-angular-js-globals';
424
+ export {RenameSsrExperimentalPlatform} from './migration/rename-ssr-experimental-platform';
365
425
  // PrimeNG
366
426
  export {UpgradeToPrimeNG18} from './libraries/primeng/18/upgrade-to-primeng-18';
367
427
  export {UpgradeComponentsTo18} from './libraries/primeng/18/upgrade-components-to-18';
@@ -0,0 +1,109 @@
1
+ /*
2
+ * Copyright 2026 the original author or authors.
3
+ *
4
+ * Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
5
+ */
6
+
7
+ import {emptyMarkers, ExecutionContext, markers, Marker, Recipe, TreeVisitor} from "@openrewrite/rewrite";
8
+ import {JavaScriptVisitor, JS, maybeAddImport, template} from "@openrewrite/rewrite/javascript";
9
+ import {emptySpace, isIdentifier, J} from "@openrewrite/rewrite/java";
10
+ import {create} from "mutative";
11
+
12
+ export class AddEagerChangeDetection extends Recipe {
13
+ readonly name = "org.openrewrite.angular.migration.add-eager-change-detection";
14
+ readonly displayName: string = "Add `ChangeDetectionStrategy.Eager` to components";
15
+ readonly description: string = "Adds `changeDetection: ChangeDetectionStrategy.Eager` to `@Component` decorators that do not " +
16
+ "specify a change detection strategy. Angular 22 changed the default for such components to " +
17
+ "`ChangeDetectionStrategy.OnPush`; making the previous check-always behavior explicit keeps them working unchanged.";
18
+
19
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
20
+ return new class extends JavaScriptVisitor<ExecutionContext> {
21
+ private componentFromAngularCore = false;
22
+
23
+ protected async visitImportDeclaration(jsImport: JS.Import, p: ExecutionContext): Promise<J | undefined> {
24
+ const imp = await super.visitImportDeclaration(jsImport, p) as JS.Import;
25
+ if (imp && importsComponentFromAngularCore(imp)) {
26
+ this.componentFromAngularCore = true;
27
+ }
28
+ return imp;
29
+ }
30
+
31
+ override async visitAnnotation(annotation: J.Annotation, p: ExecutionContext): Promise<J | undefined> {
32
+ const a = await super.visitAnnotation(annotation, p) as J.Annotation;
33
+ if (!a || !this.componentFromAngularCore) return a;
34
+
35
+ const annotType = a.annotationType;
36
+ if (!isIdentifier(annotType) || annotType.simpleName !== 'Component') return a;
37
+
38
+ if (!a.arguments?.elements?.length) return a;
39
+ const firstArg = a.arguments.elements[0].element;
40
+ if (firstArg.kind !== J.Kind.NewClass) return a;
41
+
42
+ const newClass = firstArg as J.NewClass;
43
+ if (!newClass.body) return a;
44
+
45
+ const statements = newClass.body.statements;
46
+ if (statements.length === 0) return a;
47
+
48
+ for (const stmt of statements) {
49
+ const element = stmt.element;
50
+ // A spread may already supply `changeDetection`, which we cannot see from here
51
+ if (element.kind === JS.Kind.Spread) return a;
52
+ if (element.kind !== JS.Kind.PropertyAssignment) continue;
53
+ const propertyName = (element as JS.PropertyAssignment).name.element;
54
+ if (isIdentifier(propertyName) && propertyName.simpleName === 'changeDetection') return a;
55
+ }
56
+
57
+ maybeAddImport(this, {
58
+ module: '@angular/core',
59
+ member: 'ChangeDetectionStrategy',
60
+ onlyIfReferenced: false
61
+ });
62
+
63
+ const lastStatement = statements[statements.length - 1];
64
+ const obj = await template`({ changeDetection: ChangeDetectionStrategy.Eager })`.apply(a, this.cursor) as any;
65
+ const changeDetectionProp = {
66
+ ...obj.tree.element.body.statements[0].element,
67
+ prefix: lastStatement.element.prefix
68
+ };
69
+
70
+ // The comma of a trailing comma object belongs to whichever property ends up last
71
+ const trailingComma = lastStatement.markers.markers
72
+ .find((m: Marker) => m.kind === J.Markers.TrailingComma);
73
+
74
+ const wrappedProp: J.RightPadded<any> = {
75
+ kind: J.Kind.RightPadded,
76
+ element: changeDetectionProp,
77
+ after: emptySpace,
78
+ markers: trailingComma ? markers(trailingComma) : emptyMarkers,
79
+ };
80
+
81
+ return create(a, draft => {
82
+ const body = (draft.arguments!.elements[0].element as any).body!;
83
+ const statements = [...body.statements];
84
+ if (trailingComma) {
85
+ const last = statements[statements.length - 1];
86
+ statements[statements.length - 1] = {
87
+ ...last,
88
+ markers: markers(...last.markers.markers.filter((m: Marker) => m.kind !== J.Markers.TrailingComma))
89
+ };
90
+ }
91
+ body.statements = [...statements, wrappedProp];
92
+ }) as J.Annotation;
93
+ }
94
+ }();
95
+ }
96
+ }
97
+
98
+ function importsComponentFromAngularCore(jsImport: JS.Import): boolean {
99
+ const moduleSpec = jsImport.moduleSpecifier?.element;
100
+ if (moduleSpec?.kind !== J.Kind.Literal || (moduleSpec as J.Literal).value !== '@angular/core') return false;
101
+
102
+ const namedBindings = jsImport.importClause?.namedBindings;
103
+ if (namedBindings?.kind !== JS.Kind.NamedImports) return false;
104
+
105
+ return (namedBindings as JS.NamedImports).elements.elements.some(specifier => {
106
+ const spec = specifier.element as JS.ImportSpecifier;
107
+ return isIdentifier(spec.specifier) && spec.specifier.simpleName === 'Component';
108
+ });
109
+ }