@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.
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -2
- package/dist/index.js.map +1 -1
- package/dist/migration/add-eager-change-detection.d.ts +8 -0
- package/dist/migration/add-eager-change-detection.d.ts.map +1 -0
- package/dist/migration/add-eager-change-detection.js +90 -0
- package/dist/migration/add-eager-change-detection.js.map +1 -0
- package/dist/migration/add-no-incremental-hydration.d.ts +8 -0
- package/dist/migration/add-no-incremental-hydration.d.ts.map +1 -0
- package/dist/migration/add-no-incremental-hydration.js +69 -0
- package/dist/migration/add-no-incremental-hydration.js.map +1 -0
- package/dist/migration/add-strict-templates-false.d.ts +8 -0
- package/dist/migration/add-strict-templates-false.d.ts.map +1 -0
- package/dist/migration/add-strict-templates-false.js +129 -0
- package/dist/migration/add-strict-templates-false.js.map +1 -0
- package/dist/migration/remove-with-fetch.d.ts +8 -0
- package/dist/migration/remove-with-fetch.d.ts.map +1 -0
- package/dist/migration/remove-with-fetch.js +73 -0
- package/dist/migration/remove-with-fetch.js.map +1 -0
- package/dist/migration/rename-angular-js-globals.d.ts +8 -0
- package/dist/migration/rename-angular-js-globals.d.ts.map +1 -0
- package/dist/migration/rename-angular-js-globals.js +56 -0
- package/dist/migration/rename-angular-js-globals.js.map +1 -0
- package/dist/migration/rename-default-change-detection.d.ts +8 -0
- package/dist/migration/rename-default-change-detection.d.ts.map +1 -0
- package/dist/migration/rename-default-change-detection.js +52 -0
- package/dist/migration/rename-default-change-detection.js.map +1 -0
- package/dist/migration/replace-create-ng-module-ref.d.ts +8 -0
- package/dist/migration/replace-create-ng-module-ref.d.ts.map +1 -0
- package/dist/migration/replace-create-ng-module-ref.js +51 -0
- package/dist/migration/replace-create-ng-module-ref.js.map +1 -0
- package/dist/migration/upgrade-to-angular-22.d.ts +14 -0
- package/dist/migration/upgrade-to-angular-22.d.ts.map +1 -0
- package/dist/migration/upgrade-to-angular-22.js +96 -0
- package/dist/migration/upgrade-to-angular-22.js.map +1 -0
- package/dist/search/find-can-match-usage.d.ts +8 -0
- package/dist/search/find-can-match-usage.d.ts.map +1 -0
- package/dist/search/find-can-match-usage.js +65 -0
- package/dist/search/find-can-match-usage.js.map +1 -0
- package/dist/search/find-check-no-changes-usage.d.ts +8 -0
- package/dist/search/find-check-no-changes-usage.d.ts.map +1 -0
- package/dist/search/find-check-no-changes-usage.js +50 -0
- package/dist/search/find-check-no-changes-usage.js.map +1 -0
- package/dist/search/find-http-xhr-backend-usage.d.ts +8 -0
- package/dist/search/find-http-xhr-backend-usage.d.ts.map +1 -0
- package/dist/search/find-http-xhr-backend-usage.js +65 -0
- package/dist/search/find-http-xhr-backend-usage.js.map +1 -0
- package/dist/search/find-implicit-params-inheritance.d.ts +8 -0
- package/dist/search/find-implicit-params-inheritance.d.ts.map +1 -0
- package/dist/search/find-implicit-params-inheritance.js +74 -0
- package/dist/search/find-implicit-params-inheritance.js.map +1 -0
- package/dist/search/find-provide-routes-usage.d.ts +8 -0
- package/dist/search/find-provide-routes-usage.d.ts.map +1 -0
- package/dist/search/find-provide-routes-usage.js +64 -0
- package/dist/search/find-provide-routes-usage.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +39 -0
- package/src/migration/add-eager-change-detection.ts +72 -0
- package/src/migration/add-no-incremental-hydration.ts +49 -0
- package/src/migration/add-strict-templates-false.ts +113 -0
- package/src/migration/remove-with-fetch.ts +53 -0
- package/src/migration/rename-angular-js-globals.ts +37 -0
- package/src/migration/rename-default-change-detection.ts +33 -0
- package/src/migration/replace-create-ng-module-ref.ts +32 -0
- package/src/migration/upgrade-to-angular-22.ts +76 -0
- package/src/search/find-can-match-usage.ts +45 -0
- package/src/search/find-check-no-changes-usage.ts +29 -0
- package/src/search/find-http-xhr-backend-usage.ts +44 -0
- package/src/search/find-implicit-params-inheritance.ts +49 -0
- 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
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
|
+
}
|