@openrewrite/recipes-angular 1.8.0 → 1.9.0-20260804-162757

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 (71) hide show
  1. package/dist/index.d.ts +13 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +54 -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 +90 -0
  8. package/dist/migration/add-eager-change-detection.js.map +1 -0
  9. package/dist/migration/add-no-incremental-hydration.d.ts +8 -0
  10. package/dist/migration/add-no-incremental-hydration.d.ts.map +1 -0
  11. package/dist/migration/add-no-incremental-hydration.js +69 -0
  12. package/dist/migration/add-no-incremental-hydration.js.map +1 -0
  13. package/dist/migration/add-strict-templates-false.d.ts +8 -0
  14. package/dist/migration/add-strict-templates-false.d.ts.map +1 -0
  15. package/dist/migration/add-strict-templates-false.js +129 -0
  16. package/dist/migration/add-strict-templates-false.js.map +1 -0
  17. package/dist/migration/remove-with-fetch.d.ts +8 -0
  18. package/dist/migration/remove-with-fetch.d.ts.map +1 -0
  19. package/dist/migration/remove-with-fetch.js +73 -0
  20. package/dist/migration/remove-with-fetch.js.map +1 -0
  21. package/dist/migration/rename-angular-js-globals.d.ts +8 -0
  22. package/dist/migration/rename-angular-js-globals.d.ts.map +1 -0
  23. package/dist/migration/rename-angular-js-globals.js +56 -0
  24. package/dist/migration/rename-angular-js-globals.js.map +1 -0
  25. package/dist/migration/rename-default-change-detection.d.ts +8 -0
  26. package/dist/migration/rename-default-change-detection.d.ts.map +1 -0
  27. package/dist/migration/rename-default-change-detection.js +52 -0
  28. package/dist/migration/rename-default-change-detection.js.map +1 -0
  29. package/dist/migration/replace-create-ng-module-ref.d.ts +8 -0
  30. package/dist/migration/replace-create-ng-module-ref.d.ts.map +1 -0
  31. package/dist/migration/replace-create-ng-module-ref.js +51 -0
  32. package/dist/migration/replace-create-ng-module-ref.js.map +1 -0
  33. package/dist/migration/upgrade-to-angular-22.d.ts +14 -0
  34. package/dist/migration/upgrade-to-angular-22.d.ts.map +1 -0
  35. package/dist/migration/upgrade-to-angular-22.js +96 -0
  36. package/dist/migration/upgrade-to-angular-22.js.map +1 -0
  37. package/dist/search/find-can-match-usage.d.ts +8 -0
  38. package/dist/search/find-can-match-usage.d.ts.map +1 -0
  39. package/dist/search/find-can-match-usage.js +65 -0
  40. package/dist/search/find-can-match-usage.js.map +1 -0
  41. package/dist/search/find-check-no-changes-usage.d.ts +8 -0
  42. package/dist/search/find-check-no-changes-usage.d.ts.map +1 -0
  43. package/dist/search/find-check-no-changes-usage.js +50 -0
  44. package/dist/search/find-check-no-changes-usage.js.map +1 -0
  45. package/dist/search/find-http-xhr-backend-usage.d.ts +8 -0
  46. package/dist/search/find-http-xhr-backend-usage.d.ts.map +1 -0
  47. package/dist/search/find-http-xhr-backend-usage.js +65 -0
  48. package/dist/search/find-http-xhr-backend-usage.js.map +1 -0
  49. package/dist/search/find-implicit-params-inheritance.d.ts +8 -0
  50. package/dist/search/find-implicit-params-inheritance.d.ts.map +1 -0
  51. package/dist/search/find-implicit-params-inheritance.js +74 -0
  52. package/dist/search/find-implicit-params-inheritance.js.map +1 -0
  53. package/dist/search/find-provide-routes-usage.d.ts +8 -0
  54. package/dist/search/find-provide-routes-usage.d.ts.map +1 -0
  55. package/dist/search/find-provide-routes-usage.js +64 -0
  56. package/dist/search/find-provide-routes-usage.js.map +1 -0
  57. package/package.json +1 -1
  58. package/src/index.ts +39 -0
  59. package/src/migration/add-eager-change-detection.ts +72 -0
  60. package/src/migration/add-no-incremental-hydration.ts +49 -0
  61. package/src/migration/add-strict-templates-false.ts +113 -0
  62. package/src/migration/remove-with-fetch.ts +53 -0
  63. package/src/migration/rename-angular-js-globals.ts +37 -0
  64. package/src/migration/rename-default-change-detection.ts +33 -0
  65. package/src/migration/replace-create-ng-module-ref.ts +32 -0
  66. package/src/migration/upgrade-to-angular-22.ts +76 -0
  67. package/src/search/find-can-match-usage.ts +45 -0
  68. package/src/search/find-check-no-changes-usage.ts +29 -0
  69. package/src/search/find-http-xhr-backend-usage.ts +44 -0
  70. package/src/search/find-implicit-params-inheritance.ts +49 -0
  71. package/src/search/find-provide-routes-usage.ts +43 -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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrewrite/recipes-angular",
3
- "version": "1.8.0",
3
+ "version": "1.9.0-20260804-162757",
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,18 @@ 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 {RemoveWithFetch} from './migration/remove-with-fetch';
121
+ import {ReplaceCreateNgModuleRef} from './migration/replace-create-ng-module-ref';
122
+ import {RenameAngularJsGlobals} from './migration/rename-angular-js-globals';
110
123
  import {UpgradeToPrimeNG18} from './libraries/primeng/18/upgrade-to-primeng-18';
111
124
  import {UpgradeComponentsTo18} from './libraries/primeng/18/upgrade-components-to-18';
112
125
  import {MigratePrimeNgConfigToPrimeNG} from './libraries/primeng/18/migrate-primeng-config';
@@ -189,6 +202,7 @@ export async function activate(marketplace: RecipeMarketplace) {
189
202
  await marketplace.install(UpgradeToAngular19, AngularMigration);
190
203
  await marketplace.install(UpgradeToAngular20, AngularMigration);
191
204
  await marketplace.install(UpgradeToAngular21, AngularMigration);
205
+ await marketplace.install(UpgradeToAngular22, AngularMigration);
192
206
  await marketplace.install(FindAngularDecorator, AngularSearch);
193
207
  await marketplace.install(FindZoneJsUsage, AngularSearch);
194
208
  await marketplace.install(FindHammerJsUsage, AngularSearch);
@@ -237,6 +251,18 @@ export async function activate(marketplace: RecipeMarketplace) {
237
251
  await marketplace.install(MigrateOutputToSignal, AngularMigration);
238
252
  await marketplace.install(MigrateQueryToSignal, AngularMigration);
239
253
  await marketplace.install(AddLocalizePolyfill, AngularMigration);
254
+ await marketplace.install(FindProvideRoutesUsage, AngularSearch);
255
+ await marketplace.install(FindCheckNoChangesUsage, AngularSearch);
256
+ await marketplace.install(FindCanMatchUsage, AngularSearch);
257
+ await marketplace.install(FindHttpXhrBackendUsage, AngularSearch);
258
+ await marketplace.install(FindImplicitParamsInheritance, AngularSearch);
259
+ await marketplace.install(AddEagerChangeDetection, AngularMigration);
260
+ await marketplace.install(RenameDefaultChangeDetection, AngularMigration);
261
+ await marketplace.install(AddNoIncrementalHydration, AngularMigration);
262
+ await marketplace.install(AddStrictTemplatesFalse, AngularMigration);
263
+ await marketplace.install(RemoveWithFetch, AngularMigration);
264
+ await marketplace.install(ReplaceCreateNgModuleRef, AngularMigration);
265
+ await marketplace.install(RenameAngularJsGlobals, AngularMigration);
240
266
  // PrimeNG
241
267
  await marketplace.install(UpgradeToPrimeNG18, PrimeNG);
242
268
  await marketplace.install(UpgradeComponentsTo18, PrimeNG);
@@ -314,6 +340,7 @@ export {UpgradeToAngular18} from './migration/upgrade-to-angular-18';
314
340
  export {UpgradeToAngular19} from './migration/upgrade-to-angular-19';
315
341
  export {UpgradeToAngular20} from './migration/upgrade-to-angular-20';
316
342
  export {UpgradeToAngular21} from './migration/upgrade-to-angular-21';
343
+ export {UpgradeToAngular22} from './migration/upgrade-to-angular-22';
317
344
  export {FindAngularDecorator} from './search/find-angular-decorator';
318
345
  export {FindZoneJsUsage} from './search/find-zone-js-usage';
319
346
  export {FindHammerJsUsage} from './search/find-hammer-js-usage';
@@ -362,6 +389,18 @@ export {MigrateInputToSignal} from './migration/migrate-input-to-signal';
362
389
  export {MigrateOutputToSignal} from './migration/migrate-output-to-signal';
363
390
  export {MigrateQueryToSignal} from './migration/migrate-query-to-signal';
364
391
  export {AddLocalizePolyfill} from './migration/add-localize-polyfill';
392
+ export {FindProvideRoutesUsage} from './search/find-provide-routes-usage';
393
+ export {FindCheckNoChangesUsage} from './search/find-check-no-changes-usage';
394
+ export {FindCanMatchUsage} from './search/find-can-match-usage';
395
+ export {FindHttpXhrBackendUsage} from './search/find-http-xhr-backend-usage';
396
+ export {FindImplicitParamsInheritance} from './search/find-implicit-params-inheritance';
397
+ export {AddEagerChangeDetection} from './migration/add-eager-change-detection';
398
+ export {RenameDefaultChangeDetection} from './migration/rename-default-change-detection';
399
+ export {AddNoIncrementalHydration} from './migration/add-no-incremental-hydration';
400
+ export {AddStrictTemplatesFalse} from './migration/add-strict-templates-false';
401
+ export {RemoveWithFetch} from './migration/remove-with-fetch';
402
+ export {ReplaceCreateNgModuleRef} from './migration/replace-create-ng-module-ref';
403
+ export {RenameAngularJsGlobals} from './migration/rename-angular-js-globals';
365
404
  // PrimeNG
366
405
  export {UpgradeToPrimeNG18} from './libraries/primeng/18/upgrade-to-primeng-18';
367
406
  export {UpgradeComponentsTo18} from './libraries/primeng/18/upgrade-components-to-18';
@@ -0,0 +1,72 @@
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, 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
+ override async visitAnnotation(annotation: J.Annotation, p: ExecutionContext): Promise<J | undefined> {
22
+ const a = await super.visitAnnotation(annotation, p) as J.Annotation;
23
+ if (!a) return a;
24
+
25
+ const annotType = a.annotationType;
26
+ if (!isIdentifier(annotType) || annotType.simpleName !== 'Component') return a;
27
+
28
+ if (!a.arguments?.elements?.length) return a;
29
+ const firstArg = a.arguments.elements[0].element;
30
+ if (firstArg.kind !== J.Kind.NewClass) return a;
31
+
32
+ const newClass = firstArg as J.NewClass;
33
+ if (!newClass.body) return a;
34
+
35
+ const statements = newClass.body.statements;
36
+ if (statements.length === 0) return a;
37
+
38
+ for (const stmt of statements) {
39
+ const element = stmt.element;
40
+ if (element.kind !== JS.Kind.PropertyAssignment) continue;
41
+ const propertyName = (element as JS.PropertyAssignment).name.element;
42
+ if (isIdentifier(propertyName) && propertyName.simpleName === 'changeDetection') return a;
43
+ }
44
+
45
+ maybeAddImport(this, {
46
+ module: '@angular/core',
47
+ member: 'ChangeDetectionStrategy',
48
+ onlyIfReferenced: false
49
+ });
50
+
51
+ const lastProp = statements[statements.length - 1].element as JS.PropertyAssignment;
52
+ const obj = await template`({ changeDetection: ChangeDetectionStrategy.Eager })`.apply(a, this.cursor) as any;
53
+ const changeDetectionProp = {
54
+ ...obj.tree.element.body.statements[0].element,
55
+ prefix: lastProp.prefix
56
+ };
57
+
58
+ const wrappedProp: J.RightPadded<any> = {
59
+ kind: J.Kind.RightPadded,
60
+ element: changeDetectionProp,
61
+ after: emptySpace,
62
+ markers: emptyMarkers,
63
+ };
64
+
65
+ return create(a, draft => {
66
+ const body = (draft.arguments!.elements[0].element as any).body!;
67
+ body.statements = [...body.statements, wrappedProp];
68
+ }) as J.Annotation;
69
+ }
70
+ }();
71
+ }
72
+ }
@@ -0,0 +1,49 @@
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 {ExecutionContext, Recipe, TreeVisitor} from "@openrewrite/rewrite";
8
+ import {JavaScriptVisitor, maybeAddImport, Template} from "@openrewrite/rewrite/javascript";
9
+ import {isIdentifier, J} from "@openrewrite/rewrite/java";
10
+
11
+ const HYDRATION_FEATURES = ['withIncrementalHydration', 'withNoIncrementalHydration'];
12
+
13
+ export class AddNoIncrementalHydration extends Recipe {
14
+ readonly name = "org.openrewrite.angular.migration.add-no-incremental-hydration";
15
+ readonly displayName: string = "Add `withNoIncrementalHydration()` to `provideClientHydration()`";
16
+ readonly description: string = "Adds the `withNoIncrementalHydration()` opt out to `provideClientHydration()` calls that do not " +
17
+ "already configure incremental hydration. Angular 22 enables incremental hydration by default; the opt out retains the previous behavior.";
18
+
19
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
20
+ return new class extends JavaScriptVisitor<ExecutionContext> {
21
+ override async visitMethodInvocation(method: J.MethodInvocation, p: ExecutionContext): Promise<J | undefined> {
22
+ const m = await super.visitMethodInvocation(method, p) as J.MethodInvocation;
23
+ if (!m || m.name.simpleName !== 'provideClientHydration') return m;
24
+
25
+ const elements = (m.arguments?.elements ?? []).filter(rp => rp.element.kind !== J.Kind.Empty);
26
+ if (elements.some(rp => isHydrationFeatureCall(rp.element))) return m;
27
+
28
+ maybeAddImport(this, {
29
+ module: '@angular/platform-browser',
30
+ member: 'withNoIncrementalHydration',
31
+ onlyIfReferenced: false
32
+ });
33
+
34
+ let builder = Template.builder().code('provideClientHydration(');
35
+ for (const element of elements) {
36
+ builder = builder.param(element.element).code(', ');
37
+ }
38
+ const t = builder.code('withNoIncrementalHydration())').build();
39
+ return await t.apply(m, this.cursor);
40
+ }
41
+ }();
42
+ }
43
+ }
44
+
45
+ function isHydrationFeatureCall(expression: J): boolean {
46
+ if (expression.kind !== J.Kind.MethodInvocation) return false;
47
+ const invocation = expression as J.MethodInvocation;
48
+ return isIdentifier(invocation.name) && HYDRATION_FEATURES.includes(invocation.name.simpleName);
49
+ }
@@ -0,0 +1,113 @@
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, randomId, Recipe, TreeVisitor} from "@openrewrite/rewrite";
8
+ import {detectIndent, getMemberKeyName, isMember, isObject, Json, JsonVisitor, rightPadded, space} from "@openrewrite/rewrite/json";
9
+
10
+ export class AddStrictTemplatesFalse extends Recipe {
11
+ readonly name = "org.openrewrite.angular.migration.add-strict-templates-false";
12
+ readonly displayName: string = "Add `strictTemplates: false` to `tsconfig.json`";
13
+ readonly description: string = "Adds `\"strictTemplates\": false` to `angularCompilerOptions` in `tsconfig.json` when the option is not set. " +
14
+ "Angular 22 turned strict template type checking on by default; setting it explicitly avoids new compilation errors.";
15
+
16
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
17
+ return new class extends JsonVisitor<ExecutionContext> {
18
+ private baseIndent = ' ';
19
+
20
+ protected async visitDocument(doc: Json.Document, p: ExecutionContext): Promise<Json | undefined> {
21
+ if (!doc.sourcePath.endsWith('tsconfig.json')) return doc;
22
+ this.baseIndent = detectIndent(doc);
23
+
24
+ const root = doc.value;
25
+ if (!isObject(root)) return doc;
26
+
27
+ const hasAngularCompilerOptions = root.members.some(rp =>
28
+ isMember(rp.element) && getMemberKeyName(rp.element as Json.Member) === 'angularCompilerOptions');
29
+ if (hasAngularCompilerOptions) return super.visitDocument(doc, p);
30
+
31
+ const indent = memberIndent(root, this.baseIndent);
32
+ const newMember = buildMember('angularCompilerOptions', {
33
+ kind: Json.Kind.Object,
34
+ id: randomId(),
35
+ prefix: space(' '),
36
+ markers: emptyMarkers,
37
+ members: [rightPadded(
38
+ buildMember('strictTemplates', literal('false', false), '\n' + indent + this.baseIndent),
39
+ space('\n' + indent))],
40
+ } as Json.Object, '\n' + indent);
41
+
42
+ return {...doc, value: appendMember(root, newMember, '\n')} as Json.Document;
43
+ }
44
+
45
+ protected async visitMember(member: Json.Member, p: ExecutionContext): Promise<Json | undefined> {
46
+ const m = await super.visitMember(member, p) as Json.Member;
47
+ if (!m) return m;
48
+
49
+ if (getMemberKeyName(m) !== 'angularCompilerOptions' || !isObject(m.value)) return m;
50
+
51
+ const options = m.value as Json.Object;
52
+ const hasStrictTemplates = options.members.some(rp =>
53
+ isMember(rp.element) && getMemberKeyName(rp.element as Json.Member) === 'strictTemplates');
54
+ if (hasStrictTemplates) return m;
55
+
56
+ const indent = memberIndent(options, this.baseIndent.repeat(2));
57
+ const newMember = buildMember('strictTemplates', literal('false', false), '\n' + indent);
58
+
59
+ return {...m, value: appendMember(options, newMember, '\n' + this.baseIndent)} as Json.Member;
60
+ }
61
+ }();
62
+ }
63
+ }
64
+
65
+ function literal(source: string, value: unknown): Json.Literal {
66
+ return {
67
+ kind: Json.Kind.Literal,
68
+ id: randomId(),
69
+ prefix: space(' '),
70
+ markers: emptyMarkers,
71
+ source,
72
+ value
73
+ };
74
+ }
75
+
76
+ function buildMember(key: string, value: Json.Value, keyPrefix = ''): Json.Member {
77
+ return {
78
+ kind: Json.Kind.Member,
79
+ id: randomId(),
80
+ prefix: space(''),
81
+ markers: emptyMarkers,
82
+ key: rightPadded({
83
+ kind: Json.Kind.Literal,
84
+ id: randomId(),
85
+ prefix: space(keyPrefix),
86
+ markers: emptyMarkers,
87
+ source: `"${key}"`,
88
+ value: key
89
+ } as Json.Literal, space('')),
90
+ value
91
+ };
92
+ }
93
+
94
+ function memberIndent(obj: Json.Object, fallback: string): string {
95
+ if (obj.members.length === 0) return fallback;
96
+ const firstPrefix = (obj.members[0].element as Json.Member).key.element.prefix.whitespace;
97
+ const match = firstPrefix.match(/\n([ \t]+)/);
98
+ return match ? match[1] : fallback;
99
+ }
100
+
101
+ function appendMember(obj: Json.Object, newMember: Json.Member, fallbackClosingWhitespace: string): Json.Object {
102
+ const members = [...obj.members];
103
+ const closingWhitespace = members.length > 0
104
+ ? members[members.length - 1].after.whitespace
105
+ : fallbackClosingWhitespace;
106
+
107
+ if (members.length > 0) {
108
+ members[members.length - 1] = {...members[members.length - 1], after: space('')};
109
+ }
110
+ members.push(rightPadded(newMember, space(closingWhitespace)));
111
+
112
+ return {...obj, members};
113
+ }
@@ -0,0 +1,53 @@
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, randomId, Recipe, TreeVisitor} from "@openrewrite/rewrite";
8
+ import {JavaScriptVisitor, maybeRemoveImport} from "@openrewrite/rewrite/javascript";
9
+ import {emptySpace, isIdentifier, J} from "@openrewrite/rewrite/java";
10
+
11
+ export class RemoveWithFetch extends Recipe {
12
+ readonly name = "org.openrewrite.angular.migration.remove-with-fetch";
13
+ readonly displayName: string = "Remove `withFetch()` from `provideHttpClient()`";
14
+ readonly description: string = "Removes the `withFetch()` feature from `provideHttpClient()` calls. " +
15
+ "Angular 22 uses the Fetch API by default and deprecated `withFetch()`, which can be safely removed.";
16
+
17
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
18
+ return new class extends JavaScriptVisitor<ExecutionContext> {
19
+ override async visitMethodInvocation(method: J.MethodInvocation, p: ExecutionContext): Promise<J | undefined> {
20
+ const m = await super.visitMethodInvocation(method, p) as J.MethodInvocation;
21
+ if (!m || m.name.simpleName !== 'provideHttpClient') return m;
22
+
23
+ const elements = m.arguments?.elements ?? [];
24
+ const retained = elements.filter(rp => !isWithFetchCall(rp.element));
25
+ if (retained.length === elements.length) return m;
26
+
27
+ maybeRemoveImport(this, '@angular/common/http', 'withFetch');
28
+
29
+ const newElements = retained.length > 0
30
+ ? [{...retained[0], element: {...retained[0].element, prefix: elements[0].element.prefix}}, ...retained.slice(1)]
31
+ : [{
32
+ kind: J.Kind.RightPadded,
33
+ element: {
34
+ kind: J.Kind.Empty,
35
+ id: randomId(),
36
+ prefix: emptySpace,
37
+ markers: emptyMarkers,
38
+ } as J.Empty,
39
+ after: emptySpace,
40
+ markers: emptyMarkers,
41
+ } as J.RightPadded<any>];
42
+
43
+ return {...m, arguments: {...m.arguments!, elements: newElements}} as J.MethodInvocation;
44
+ }
45
+ }();
46
+ }
47
+ }
48
+
49
+ function isWithFetchCall(expression: J): boolean {
50
+ if (expression.kind !== J.Kind.MethodInvocation) return false;
51
+ const invocation = expression as J.MethodInvocation;
52
+ return !invocation.select && isIdentifier(invocation.name) && invocation.name.simpleName === 'withFetch';
53
+ }