@openrewrite/recipes-nodejs 0.51.1 → 0.51.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +14 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/migrate/find-aead-decipheriv-without-auth-tag-length.d.ts +9 -0
  5. package/dist/migrate/find-aead-decipheriv-without-auth-tag-length.d.ts.map +1 -0
  6. package/dist/migrate/find-aead-decipheriv-without-auth-tag-length.js +89 -0
  7. package/dist/migrate/find-aead-decipheriv-without-auth-tag-length.js.map +1 -0
  8. package/dist/migrate/find-crypto-default-encoding.d.ts +9 -0
  9. package/dist/migrate/find-crypto-default-encoding.d.ts.map +1 -0
  10. package/dist/migrate/find-crypto-default-encoding.js +61 -0
  11. package/dist/migrate/find-crypto-default-encoding.js.map +1 -0
  12. package/dist/migrate/find-duplex-to-web-type.d.ts +9 -0
  13. package/dist/migrate/find-duplex-to-web-type.d.ts.map +1 -0
  14. package/dist/migrate/find-duplex-to-web-type.js +86 -0
  15. package/dist/migrate/find-duplex-to-web-type.js.map +1 -0
  16. package/dist/migrate/find-module-register.d.ts +9 -0
  17. package/dist/migrate/find-module-register.d.ts.map +1 -0
  18. package/dist/migrate/find-module-register.js +50 -0
  19. package/dist/migrate/find-module-register.js.map +1 -0
  20. package/dist/migrate/server-response-write-header.d.ts +9 -0
  21. package/dist/migrate/server-response-write-header.d.ts.map +1 -0
  22. package/dist/migrate/server-response-write-header.js +62 -0
  23. package/dist/migrate/server-response-write-header.js.map +1 -0
  24. package/dist/migrate/upgrade-node-20.d.ts +8 -0
  25. package/dist/migrate/upgrade-node-20.d.ts.map +1 -0
  26. package/dist/migrate/upgrade-node-20.js +42 -0
  27. package/dist/migrate/upgrade-node-20.js.map +1 -0
  28. package/dist/migrate/upgrade-node-22.d.ts +1 -1
  29. package/dist/migrate/upgrade-node-22.d.ts.map +1 -1
  30. package/dist/migrate/upgrade-node-22.js +5 -5
  31. package/dist/migrate/upgrade-node-22.js.map +1 -1
  32. package/dist/migrate/upgrade-node-26.d.ts +8 -0
  33. package/dist/migrate/upgrade-node-26.d.ts.map +1 -0
  34. package/dist/migrate/upgrade-node-26.js +53 -0
  35. package/dist/migrate/upgrade-node-26.js.map +1 -0
  36. package/package.json +2 -2
  37. package/src/index.ts +14 -0
  38. package/src/migrate/find-aead-decipheriv-without-auth-tag-length.ts +76 -0
  39. package/src/migrate/find-crypto-default-encoding.ts +47 -0
  40. package/src/migrate/find-duplex-to-web-type.ts +71 -0
  41. package/src/migrate/find-module-register.ts +36 -0
  42. package/src/migrate/server-response-write-header.ts +51 -0
  43. package/src/migrate/upgrade-node-20.ts +32 -0
  44. package/src/migrate/upgrade-node-22.ts +6 -6
  45. package/src/migrate/upgrade-node-26.ts +45 -0
@@ -19,7 +19,7 @@ const process_features_tls_1 = require("./process-features-tls");
19
19
  const util_type_checking_1 = require("./util-type-checking");
20
20
  const util_extend_1 = require("./util-extend");
21
21
  const buffer_slice_1 = require("./buffer-slice");
22
- const process_exit_code_1 = require("./process-exit-code");
22
+ const upgrade_node_20_1 = require("./upgrade-node-20");
23
23
  const increase_node_engine_version_1 = require("./increase-node-engine-version");
24
24
  const increase_node_engine_version_in_github_actions_1 = require("./increase-node-engine-version-in-github-actions");
25
25
  const find_punycode_usage_1 = require("./find-punycode-usage");
@@ -34,8 +34,7 @@ const node22RuntimeDeprecations = [
34
34
  new util_extend_1.ReplaceUtilExtend(),
35
35
  new crypto_constructors_1.ReplaceCryptoConstructors(),
36
36
  ];
37
- const olderDeprecations = [
38
- new process_exit_code_1.AvoidImplicitCoercionOfExitCode(),
37
+ const docOnlyDeprecations = [
39
38
  new buffer_slice_1.ReplaceDeprecatedBufferSlice(),
40
39
  new process_features_tls_1.RemoveUsageOfProcessFeaturesTlsConstants(),
41
40
  new promisify_on_promise_1.RemovePromisifyOnPromise(),
@@ -45,7 +44,7 @@ class UpgradeNode22 extends rewrite_1.Recipe {
45
44
  super(...arguments);
46
45
  this.name = "org.openrewrite.node.migrate.upgrade-node-22";
47
46
  this.displayName = "Upgrade to Node.js 22";
48
- this.description = "Migrate deprecated APIs for Node.js 22 compatibility. Addresses Node 22 runtime deprecations and deprecations from earlier versions.";
47
+ this.description = "Migrate deprecated APIs for Node.js 22 compatibility. Includes all migrations from Node.js 20, plus Node 21 and Node 22 deprecations.";
49
48
  }
50
49
  recipeList() {
51
50
  return __awaiter(this, void 0, void 0, function* () {
@@ -53,9 +52,10 @@ class UpgradeNode22 extends rewrite_1.Recipe {
53
52
  new increase_node_engine_version_1.IncreaseNodeEngineVersion({ version: "22" }),
54
53
  new increase_node_engine_version_in_github_actions_1.IncreaseNodeEngineVersionInGithubActions({ version: "22" }),
55
54
  yield (0, rpc_1.prepareJavaRecipe)("org.openrewrite.nodejs.migrate.UpgradeNodeVersion", { version: "22" }),
55
+ new upgrade_node_20_1.UpgradeNode20(),
56
56
  ...node22EolDeprecations,
57
57
  ...node22RuntimeDeprecations,
58
- ...olderDeprecations,
58
+ ...docOnlyDeprecations,
59
59
  ];
60
60
  });
61
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade-node-22.js","sourceRoot":"","sources":["../../src/migrate/upgrade-node-22.ts"],"names":[],"mappings":";;;;;;;;;;;;AAKA,kDAA4C;AAC5C,kDAA2D;AAC3D,iEAAgE;AAChE,uDAAqE;AACrE,+DAAgE;AAChE,iEAAgF;AAChF,6DAAkE;AAClE,+CAAgD;AAChD,iDAA4D;AAC5D,2DAAoE;AACpE,iFAAyE;AACzE,qHAA0G;AAC1G,+DAAwD;AACxD,2EAAmE;AAGnE,MAAM,qBAAqB,GAAa;IACpC,IAAI,oDAAkC,EAAE;IACxC,IAAI,uCAAiB,EAAE;IACvB,IAAI,kDAAsB,EAAE;CAC/B,CAAC;AAGF,MAAM,yBAAyB,GAAa;IACxC,IAAI,iDAA4B,EAAE;IAClC,IAAI,+BAAiB,EAAE;IACvB,IAAI,+CAAyB,EAAE;CAClC,CAAC;AAGF,MAAM,iBAAiB,GAAa;IAChC,IAAI,mDAA+B,EAAE;IACrC,IAAI,2CAA4B,EAAE;IAClC,IAAI,+DAAwC,EAAE;IAC9C,IAAI,+CAAwB,EAAE;CACjC,CAAC;AAEF,MAAa,aAAc,SAAQ,gBAAM;IAAzC;;QACa,SAAI,GAAG,8CAA8C,CAAC;QACtD,gBAAW,GAAG,uBAAuB,CAAC;QACtC,gBAAW,GAAG,sIAAsI,CAAC;IAYlK,CAAC;IAVS,UAAU;;YACZ,OAAO;gBACH,IAAI,wDAAyB,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC9C,IAAI,yFAAwC,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC7D,MAAM,IAAA,uBAAiB,EAAC,mDAAmD,EAAE,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC7F,GAAG,qBAAqB;gBACxB,GAAG,yBAAyB;gBAC5B,GAAG,iBAAiB;aACvB,CAAC;QACN,CAAC;KAAA;CACJ;AAfD,sCAeC"}
1
+ {"version":3,"file":"upgrade-node-22.js","sourceRoot":"","sources":["../../src/migrate/upgrade-node-22.ts"],"names":[],"mappings":";;;;;;;;;;;;AAKA,kDAA4C;AAC5C,kDAA2D;AAC3D,iEAAgE;AAChE,uDAAqE;AACrE,+DAAgE;AAChE,iEAAgF;AAChF,6DAAkE;AAClE,+CAAgD;AAChD,iDAA4D;AAC5D,uDAAgD;AAChD,iFAAyE;AACzE,qHAA0G;AAC1G,+DAAwD;AACxD,2EAAmE;AAGnE,MAAM,qBAAqB,GAAa;IACpC,IAAI,oDAAkC,EAAE;IACxC,IAAI,uCAAiB,EAAE;IACvB,IAAI,kDAAsB,EAAE;CAC/B,CAAC;AAGF,MAAM,yBAAyB,GAAa;IACxC,IAAI,iDAA4B,EAAE;IAClC,IAAI,+BAAiB,EAAE;IACvB,IAAI,+CAAyB,EAAE;CAClC,CAAC;AAGF,MAAM,mBAAmB,GAAa;IAClC,IAAI,2CAA4B,EAAE;IAClC,IAAI,+DAAwC,EAAE;IAC9C,IAAI,+CAAwB,EAAE;CACjC,CAAC;AAEF,MAAa,aAAc,SAAQ,gBAAM;IAAzC;;QACa,SAAI,GAAG,8CAA8C,CAAC;QACtD,gBAAW,GAAG,uBAAuB,CAAC;QACtC,gBAAW,GAAG,uIAAuI,CAAC;IAanK,CAAC;IAXS,UAAU;;YACZ,OAAO;gBACH,IAAI,wDAAyB,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC9C,IAAI,yFAAwC,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC7D,MAAM,IAAA,uBAAiB,EAAC,mDAAmD,EAAE,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC7F,IAAI,+BAAa,EAAE;gBACnB,GAAG,qBAAqB;gBACxB,GAAG,yBAAyB;gBAC5B,GAAG,mBAAmB;aACzB,CAAC;QACN,CAAC;KAAA;CACJ;AAhBD,sCAgBC"}
@@ -0,0 +1,8 @@
1
+ import { Recipe } from "@openrewrite/rewrite";
2
+ export declare class UpgradeNode26 extends Recipe {
3
+ readonly name = "org.openrewrite.node.migrate.upgrade-node-26";
4
+ readonly displayName = "Upgrade to Node.js 26";
5
+ readonly description = "Migrate deprecated APIs for Node.js 26 compatibility. Includes all migrations from Node.js 24, plus Node 25 and Node 26 deprecations.";
6
+ recipeList(): Promise<Recipe[]>;
7
+ }
8
+ //# sourceMappingURL=upgrade-node-26.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade-node-26.d.ts","sourceRoot":"","sources":["../../src/migrate/upgrade-node-26.ts"],"names":[],"mappings":"AAKA,OAAO,EAAC,MAAM,EAAC,MAAM,sBAAsB,CAAC;AAwB5C,qBAAa,aAAc,SAAQ,MAAM;IACrC,QAAQ,CAAC,IAAI,kDAAkD;IAC/D,QAAQ,CAAC,WAAW,2BAA2B;IAC/C,QAAQ,CAAC,WAAW,2IAA2I;IAEzJ,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAUxC"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.UpgradeNode26 = void 0;
13
+ const rewrite_1 = require("@openrewrite/rewrite");
14
+ const rpc_1 = require("@openrewrite/rewrite/rpc");
15
+ const upgrade_node_24_1 = require("./upgrade-node-24");
16
+ const increase_node_engine_version_1 = require("./increase-node-engine-version");
17
+ const increase_node_engine_version_in_github_actions_1 = require("./increase-node-engine-version-in-github-actions");
18
+ const stream_internal_modules_1 = require("./stream-internal-modules");
19
+ const server_response_write_header_1 = require("./server-response-write-header");
20
+ const find_aead_decipheriv_without_auth_tag_length_1 = require("./find-aead-decipheriv-without-auth-tag-length");
21
+ const find_duplex_to_web_type_1 = require("./find-duplex-to-web-type");
22
+ const find_module_register_1 = require("./find-module-register");
23
+ const node26EolDeprecations = [
24
+ new server_response_write_header_1.ReplaceServerResponseWriteHeader(),
25
+ new stream_internal_modules_1.ReplaceStreamInternalModules(),
26
+ new find_aead_decipheriv_without_auth_tag_length_1.FindAeadDecipherivWithoutAuthTagLength(),
27
+ ];
28
+ const node26RuntimeDeprecations = [
29
+ new find_duplex_to_web_type_1.FindDuplexToWebType(),
30
+ new find_module_register_1.FindModuleRegister(),
31
+ ];
32
+ class UpgradeNode26 extends rewrite_1.Recipe {
33
+ constructor() {
34
+ super(...arguments);
35
+ this.name = "org.openrewrite.node.migrate.upgrade-node-26";
36
+ this.displayName = "Upgrade to Node.js 26";
37
+ this.description = "Migrate deprecated APIs for Node.js 26 compatibility. Includes all migrations from Node.js 24, plus Node 25 and Node 26 deprecations.";
38
+ }
39
+ recipeList() {
40
+ return __awaiter(this, void 0, void 0, function* () {
41
+ return [
42
+ new increase_node_engine_version_1.IncreaseNodeEngineVersion({ version: "26" }),
43
+ new increase_node_engine_version_in_github_actions_1.IncreaseNodeEngineVersionInGithubActions({ version: "26" }),
44
+ yield (0, rpc_1.prepareJavaRecipe)("org.openrewrite.nodejs.migrate.UpgradeNodeVersion", { version: "26" }),
45
+ new upgrade_node_24_1.UpgradeNode24(),
46
+ ...node26EolDeprecations,
47
+ ...node26RuntimeDeprecations,
48
+ ];
49
+ });
50
+ }
51
+ }
52
+ exports.UpgradeNode26 = UpgradeNode26;
53
+ //# sourceMappingURL=upgrade-node-26.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade-node-26.js","sourceRoot":"","sources":["../../src/migrate/upgrade-node-26.ts"],"names":[],"mappings":";;;;;;;;;;;;AAKA,kDAA4C;AAC5C,kDAA2D;AAC3D,uDAAgD;AAChD,iFAAyE;AACzE,qHAA0G;AAC1G,uEAAuE;AACvE,iFAAgF;AAChF,iHAAsG;AACtG,uEAA8D;AAC9D,iEAA0D;AAG1D,MAAM,qBAAqB,GAAa;IACpC,IAAI,+DAAgC,EAAE;IACtC,IAAI,sDAA4B,EAAE;IAClC,IAAI,qFAAsC,EAAE;CAC/C,CAAC;AAGF,MAAM,yBAAyB,GAAa;IACxC,IAAI,6CAAmB,EAAE;IACzB,IAAI,yCAAkB,EAAE;CAC3B,CAAC;AAEF,MAAa,aAAc,SAAQ,gBAAM;IAAzC;;QACa,SAAI,GAAG,8CAA8C,CAAC;QACtD,gBAAW,GAAG,uBAAuB,CAAC;QACtC,gBAAW,GAAG,uIAAuI,CAAC;IAYnK,CAAC;IAVS,UAAU;;YACZ,OAAO;gBACH,IAAI,wDAAyB,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC9C,IAAI,yFAAwC,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC7D,MAAM,IAAA,uBAAiB,EAAC,mDAAmD,EAAE,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;gBAC7F,IAAI,+BAAa,EAAE;gBACnB,GAAG,qBAAqB;gBACxB,GAAG,yBAAyB;aAC/B,CAAC;QACN,CAAC;KAAA;CACJ;AAfD,sCAeC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrewrite/recipes-nodejs",
3
- "version": "0.51.1",
3
+ "version": "0.51.2",
4
4
  "license": "Moderne Proprietary",
5
5
  "description": "OpenRewrite recipes for Node.js library migrations.",
6
6
  "homepage": "https://github.com/moderneinc/rewrite-node",
@@ -25,7 +25,7 @@
25
25
  "ci:test": "jest"
26
26
  },
27
27
  "dependencies": {
28
- "@openrewrite/rewrite": "^8.93.0-20261008-033144",
28
+ "@openrewrite/rewrite": "^8.92.18",
29
29
  "mutative": "^1.1.0",
30
30
  "semver": "^7.7.3"
31
31
  },
package/src/index.ts CHANGED
@@ -24,15 +24,22 @@ import {ReplaceOutgoingMessageHeaders} from "./migrate/outgoing-message-headers"
24
24
  import {ReplaceTlsInternalModules} from "./migrate/tls-internal-modules";
25
25
  import {ReplaceStreamInternalModules} from "./migrate/stream-internal-modules";
26
26
  import {RemoveNetSetSimultaneousAccepts} from "./migrate/net-set-simultaneous-accepts";
27
+ import {UpgradeNode20} from "./migrate/upgrade-node-20";
27
28
  import {UpgradeNode22} from "./migrate/upgrade-node-22";
28
29
  import {UpgradeNode24} from "./migrate/upgrade-node-24";
30
+ import {UpgradeNode26} from "./migrate/upgrade-node-26";
29
31
  import {IncreaseNodeEngineVersion} from "./migrate/increase-node-engine-version";
30
32
  import {IncreaseNodeEngineVersionInGithubActions} from "./migrate/increase-node-engine-version-in-github-actions";
31
33
  import {FindCryptoCreateCipher} from "./migrate/find-crypto-create-cipher";
34
+ import {FindCryptoDefaultEncoding} from "./migrate/find-crypto-default-encoding";
32
35
  import {FindPunycodeUsage} from "./migrate/find-punycode-usage";
33
36
  import {FindProcessAssert} from "./migrate/find-process-assert";
34
37
  import {FindTlsSecurePair} from "./migrate/find-tls-secure-pair";
35
38
  import {FindTimersActive} from "./migrate/find-timers-active";
39
+ import {ReplaceServerResponseWriteHeader} from "./migrate/server-response-write-header";
40
+ import {FindAeadDecipherivWithoutAuthTagLength} from "./migrate/find-aead-decipheriv-without-auth-tag-length";
41
+ import {FindDuplexToWebType} from "./migrate/find-duplex-to-web-type";
42
+ import {FindModuleRegister} from "./migrate/find-module-register";
36
43
  import {MigrateFsExtraToNodeFs} from "./migrate/fs-extra/migrate-fs-extra-to-node-fs";
37
44
  import {ReplaceFsExtraRemove} from "./migrate/fs-extra/replace-fs-extra-remove";
38
45
  import {ReplaceFsExtraMkdirs} from "./migrate/fs-extra/replace-fs-extra-mkdirs";
@@ -100,15 +107,22 @@ export async function activate(marketplace: RecipeMarketplace): Promise<void> {
100
107
  await marketplace.install(ReplaceTlsInternalModules, Migrate);
101
108
  await marketplace.install(ReplaceStreamInternalModules, Migrate);
102
109
  await marketplace.install(RemoveNetSetSimultaneousAccepts, Migrate);
110
+ await marketplace.install(UpgradeNode20, Migrate);
103
111
  await marketplace.install(UpgradeNode22, Migrate);
104
112
  await marketplace.install(UpgradeNode24, Migrate);
113
+ await marketplace.install(UpgradeNode26, Migrate);
105
114
  await marketplace.install(IncreaseNodeEngineVersion, Migrate);
106
115
  await marketplace.install(IncreaseNodeEngineVersionInGithubActions, Migrate);
107
116
  await marketplace.install(FindCryptoCreateCipher, Migrate);
117
+ await marketplace.install(FindCryptoDefaultEncoding, Migrate);
108
118
  await marketplace.install(FindPunycodeUsage, Migrate);
109
119
  await marketplace.install(FindProcessAssert, Migrate);
110
120
  await marketplace.install(FindTlsSecurePair, Migrate);
111
121
  await marketplace.install(FindTimersActive, Migrate);
122
+ await marketplace.install(ReplaceServerResponseWriteHeader, Migrate);
123
+ await marketplace.install(FindAeadDecipherivWithoutAuthTagLength, Migrate);
124
+ await marketplace.install(FindDuplexToWebType, Migrate);
125
+ await marketplace.install(FindModuleRegister, Migrate);
112
126
  await marketplace.install(MigrateFsExtraToNodeFs, FsExtra);
113
127
  await marketplace.install(ReplaceFsExtraRemove, FsExtra);
114
128
  await marketplace.install(ReplaceFsExtraMkdirs, FsExtra);
@@ -0,0 +1,76 @@
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
+ import {check, ExecutionContext, foundSearchResult, or, Recipe, TreeVisitor} from "@openrewrite/rewrite";
7
+ import {JavaScriptVisitor, JS, MethodMatcher, usesMethod} from "@openrewrite/rewrite/javascript";
8
+ import {J} from "@openrewrite/rewrite/java";
9
+
10
+ export class FindAeadDecipherivWithoutAuthTagLength extends Recipe {
11
+ readonly name = "org.openrewrite.node.migrate.crypto.find-aead-decipheriv-without-auth-tag-length"
12
+ readonly displayName: string = "Find `crypto.createDecipheriv()` for AEAD ciphers without `authTagLength`";
13
+ readonly description: string = "Accepting shorter-than-default authentication tags for AEAD ciphers " +
14
+ "(`aes-*-gcm`, `aes-*-ccm`, `aes-*-ocb`, `chacha20-poly1305`) in `crypto.createDecipheriv()` is end-of-life in " +
15
+ "Node.js 26 (DEP0182). Pass an explicit `authTagLength` in the options so the tag length is validated at decipher " +
16
+ "construction instead of at `setAuthTag()` time.";
17
+ readonly tags: string[] = ["DEP0182"];
18
+
19
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
20
+ return check(or(
21
+ usesMethod("crypto createDecipheriv(..)"),
22
+ usesMethod("node:crypto createDecipheriv(..)"),
23
+ ), new class extends JavaScriptVisitor<ExecutionContext> {
24
+ matcher = new MethodMatcher("crypto createDecipheriv(..)");
25
+ nodeMatcher = new MethodMatcher("node:crypto createDecipheriv(..)");
26
+
27
+ private readonly aead = /^(aes-\d+-(gcm|ccm|ocb)|chacha20-poly1305)$/i;
28
+
29
+ private isAeadAlgorithm(expr: J): boolean {
30
+ if (expr.kind !== J.Kind.Literal) {
31
+ return false;
32
+ }
33
+ const value = (expr as J.Literal).value;
34
+ return typeof value === "string" && this.aead.test(value);
35
+ }
36
+
37
+ private hasAuthTagLength(expr: J): boolean {
38
+ if (expr.kind !== J.Kind.NewClass || (expr as J.NewClass).class) {
39
+ return false;
40
+ }
41
+ const obj = expr as J.NewClass;
42
+ const statements = obj.body?.statements ?? [];
43
+ for (const stmt of statements) {
44
+ const element = stmt.element;
45
+ if (element.kind !== JS.Kind.PropertyAssignment) {
46
+ continue;
47
+ }
48
+ const name = (element as JS.PropertyAssignment).name.element;
49
+ if (name.kind === J.Kind.Identifier && (name as J.Identifier).simpleName === "authTagLength") {
50
+ return true;
51
+ }
52
+ if (name.kind === J.Kind.Literal && (name as J.Literal).value === "authTagLength") {
53
+ return true;
54
+ }
55
+ }
56
+ return false;
57
+ }
58
+
59
+ protected async visitMethodInvocation(m: J.MethodInvocation, p: ExecutionContext): Promise<J | undefined> {
60
+ const method = await super.visitMethodInvocation(m, p) as J.MethodInvocation;
61
+ if (!this.matcher.matches(method.methodType) && !this.nodeMatcher.matches(method.methodType)) {
62
+ return method;
63
+ }
64
+ const args = method.arguments.elements;
65
+ if (args.length === 0 || !this.isAeadAlgorithm(args[0].element)) {
66
+ return method;
67
+ }
68
+ if (args.length >= 4 && this.hasAuthTagLength(args[3].element)) {
69
+ return method;
70
+ }
71
+ return foundSearchResult(method,
72
+ "crypto.createDecipheriv() for AEAD ciphers without authTagLength is end-of-life (DEP0182). Pass authTagLength in options.");
73
+ }
74
+ });
75
+ }
76
+ }
@@ -0,0 +1,47 @@
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
+ import {ExecutionContext, foundSearchResult, Recipe, TreeVisitor} from "@openrewrite/rewrite";
7
+ import {JavaScriptVisitor} from "@openrewrite/rewrite/javascript";
8
+ import {J, Type} from "@openrewrite/rewrite/java";
9
+
10
+ const FQNS = new Set(["crypto", "node:crypto"]);
11
+
12
+ export class FindCryptoDefaultEncoding extends Recipe {
13
+ readonly name = "org.openrewrite.node.migrate.find-crypto-default-encoding"
14
+ readonly displayName: string = "Find deprecated `crypto.DEFAULT_ENCODING` usage";
15
+ readonly description: string = "The `crypto.DEFAULT_ENCODING` property was deprecated in Node.js 10 (DEP0091) " +
16
+ "and removed in Node.js 20. Each API that accepted an encoding (hash, hmac, cipher, etc.) now requires " +
17
+ "the encoding to be passed explicitly. Occurrences must be reviewed and fixed by hand.";
18
+ readonly tags: string[] = ["DEP0091"];
19
+
20
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
21
+ return new class extends JavaScriptVisitor<ExecutionContext> {
22
+ private isCryptoReceiver(target: J | undefined): boolean {
23
+ if (!target || target.kind !== J.Kind.Identifier) {
24
+ return false;
25
+ }
26
+ const type = (target as J.Identifier).type;
27
+ if (!type) {
28
+ return (target as J.Identifier).simpleName === "crypto";
29
+ }
30
+ if (type.kind === Type.Kind.Class) {
31
+ const fqn = (type as Type.Class).fullyQualifiedName;
32
+ return FQNS.has(fqn);
33
+ }
34
+ return false;
35
+ }
36
+
37
+ protected async visitFieldAccess(fieldAccess: J.FieldAccess, p: ExecutionContext): Promise<J | undefined> {
38
+ const fa = await super.visitFieldAccess(fieldAccess, p) as J.FieldAccess;
39
+ if (fa.name.element.simpleName === "DEFAULT_ENCODING" && this.isCryptoReceiver(fa.target)) {
40
+ return foundSearchResult(fa,
41
+ "crypto.DEFAULT_ENCODING was removed in Node.js 20 (DEP0091). Pass the desired encoding explicitly to each crypto API instead.");
42
+ }
43
+ return fa;
44
+ }
45
+ };
46
+ }
47
+ }
@@ -0,0 +1,71 @@
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
+ import {check, ExecutionContext, foundSearchResult, Recipe, TreeVisitor} from "@openrewrite/rewrite";
7
+ import {JavaScriptVisitor, JS, usesType} from "@openrewrite/rewrite/javascript";
8
+ import {J, Type} from "@openrewrite/rewrite/java";
9
+
10
+ export class FindDuplexToWebType extends Recipe {
11
+ readonly name = "org.openrewrite.node.migrate.stream.find-duplex-to-web-type"
12
+ readonly displayName: string = "Find `Duplex.toWeb()` calls passing `options.type`";
13
+ readonly description: string = "Passing `options.type` to `stream.Duplex.toWeb()` is runtime-deprecated in Node.js 26 (DEP0201). " +
14
+ "The `type` field controls the readable-side ReadableStream chunk type; drop it from the options and set the type on the " +
15
+ "resulting `ReadableStream` instead.";
16
+ readonly tags: string[] = ["DEP0201"];
17
+
18
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
19
+ return check(usesType("Stream.Duplex"), new class extends JavaScriptVisitor<ExecutionContext> {
20
+
21
+ private isDuplexReceiver(select: J | undefined): boolean {
22
+ if (!select || select.kind !== J.Kind.Identifier) {
23
+ return false;
24
+ }
25
+ const type = (select as J.Identifier).type;
26
+ if (type?.kind !== Type.Kind.Class) {
27
+ return false;
28
+ }
29
+ return (type as Type.Class).fullyQualifiedName === "Stream.Duplex";
30
+ }
31
+
32
+ private hasTypeKey(expr: J): boolean {
33
+ if (expr.kind !== J.Kind.NewClass || (expr as J.NewClass).class) {
34
+ return false;
35
+ }
36
+ const obj = expr as J.NewClass;
37
+ const statements = obj.body?.statements ?? [];
38
+ for (const stmt of statements) {
39
+ const element = stmt.element;
40
+ if (element.kind !== JS.Kind.PropertyAssignment) {
41
+ continue;
42
+ }
43
+ const name = (element as JS.PropertyAssignment).name.element;
44
+ if (name.kind === J.Kind.Identifier && (name as J.Identifier).simpleName === "type") {
45
+ return true;
46
+ }
47
+ if (name.kind === J.Kind.Literal && (name as J.Literal).value === "type") {
48
+ return true;
49
+ }
50
+ }
51
+ return false;
52
+ }
53
+
54
+ protected async visitMethodInvocation(m: J.MethodInvocation, p: ExecutionContext): Promise<J | undefined> {
55
+ const method = await super.visitMethodInvocation(m, p) as J.MethodInvocation;
56
+ if (method.name.simpleName !== "toWeb" || !this.isDuplexReceiver(method.select?.element)) {
57
+ return method;
58
+ }
59
+ const args = method.arguments.elements;
60
+ if (args.length < 2) {
61
+ return method;
62
+ }
63
+ if (!this.hasTypeKey(args[1].element)) {
64
+ return method;
65
+ }
66
+ return foundSearchResult(method,
67
+ "Duplex.toWeb() options.type is runtime-deprecated (DEP0201). Drop it from the options and set the type on the resulting ReadableStream instead.");
68
+ }
69
+ });
70
+ }
71
+ }
@@ -0,0 +1,36 @@
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
+ import {check, ExecutionContext, foundSearchResult, or, Recipe, TreeVisitor} from "@openrewrite/rewrite";
7
+ import {JavaScriptVisitor, MethodMatcher, usesMethod} from "@openrewrite/rewrite/javascript";
8
+ import {J} from "@openrewrite/rewrite/java";
9
+
10
+ export class FindModuleRegister extends Recipe {
11
+ readonly name = "org.openrewrite.node.migrate.find-module-register"
12
+ readonly displayName: string = "Find `module.register()` usage";
13
+ readonly description: string = "`module.register()` for loading ESM loader hooks is runtime-deprecated in Node.js 26 (DEP0205). " +
14
+ "Use the `--import` CLI flag with a loader module instead.";
15
+ readonly tags: string[] = ["DEP0205"];
16
+
17
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
18
+ return check(or(
19
+ usesMethod("module register(..)"),
20
+ usesMethod("node:module register(..)"),
21
+ ), new class extends JavaScriptVisitor<ExecutionContext> {
22
+ moduleRegisterMatcher = new MethodMatcher("module register(..)");
23
+ nodeModuleRegisterMatcher = new MethodMatcher("node:module register(..)");
24
+
25
+ protected async visitMethodInvocation(m: J.MethodInvocation, p: ExecutionContext): Promise<J | undefined> {
26
+ const method = await super.visitMethodInvocation(m, p) as J.MethodInvocation;
27
+ if (this.moduleRegisterMatcher.matches(method.methodType) ||
28
+ this.nodeModuleRegisterMatcher.matches(method.methodType)) {
29
+ return foundSearchResult(method,
30
+ "module.register() is runtime-deprecated (DEP0205). Use the --import CLI flag with a loader module instead.");
31
+ }
32
+ return method;
33
+ }
34
+ });
35
+ }
36
+ }
@@ -0,0 +1,51 @@
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
+ import {check, ExecutionContext, or, Recipe, TreeVisitor} from "@openrewrite/rewrite";
7
+ import {JavaScriptVisitor, template, usesType} from "@openrewrite/rewrite/javascript";
8
+ import {J, Type} from "@openrewrite/rewrite/java";
9
+
10
+ export class ReplaceServerResponseWriteHeader extends Recipe {
11
+ readonly name = "org.openrewrite.node.migrate.http.replace-server-response-write-header"
12
+ readonly displayName: string = "Replace `ServerResponse.writeHeader()` with `writeHead()`";
13
+ readonly description: string = "`ServerResponse.prototype.writeHeader()` was an undocumented alias for `writeHead()` " +
14
+ "that was deprecated in Node.js 11 (DEP0063) and removed in Node.js 26. The signatures are identical.";
15
+ readonly tags: string[] = ["DEP0063"];
16
+
17
+ async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
18
+ return check(or(
19
+ usesType("http.ServerResponse"),
20
+ usesType("node:http.ServerResponse"),
21
+ ), new class extends JavaScriptVisitor<ExecutionContext> {
22
+
23
+ private isServerResponseType(type: Type | undefined): boolean {
24
+ if (!type) return false;
25
+ if (type.kind === Type.Kind.Class) {
26
+ const fqn = (type as Type.Class).fullyQualifiedName;
27
+ return fqn === "http.ServerResponse" || fqn === "node:http.ServerResponse";
28
+ }
29
+ if (type.kind === Type.Kind.Parameterized) {
30
+ return this.isServerResponseType((type as Type.Parameterized).type);
31
+ }
32
+ return false;
33
+ }
34
+
35
+ protected async visitMethodInvocation(method: J.MethodInvocation, p: ExecutionContext): Promise<J | undefined> {
36
+ const m = await super.visitMethodInvocation(method, p) as J.MethodInvocation;
37
+
38
+ if (m.name.simpleName !== "writeHeader" || !m.select) {
39
+ return m;
40
+ }
41
+ if (!this.isServerResponseType(m.select.element.type)) {
42
+ return m;
43
+ }
44
+
45
+ const target = m.select.element;
46
+ const args = m.arguments.elements;
47
+ return await template`${target}.writeHead(${args})`.apply(m, this.cursor);
48
+ }
49
+ });
50
+ }
51
+ }
@@ -0,0 +1,32 @@
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
+ import {Recipe} from "@openrewrite/rewrite";
7
+ import {prepareJavaRecipe} from "@openrewrite/rewrite/rpc";
8
+ import {AvoidImplicitCoercionOfExitCode} from "./process-exit-code";
9
+ import {FindCryptoDefaultEncoding} from "./find-crypto-default-encoding";
10
+ import {IncreaseNodeEngineVersion} from "./increase-node-engine-version";
11
+ import {IncreaseNodeEngineVersionInGithubActions} from "./increase-node-engine-version-in-github-actions";
12
+
13
+ // Node 20 End-of-Life deprecations
14
+ const node20EolDeprecations: Recipe[] = [
15
+ new AvoidImplicitCoercionOfExitCode(), // DEP0164: process.exit(code) (v20 EOL)
16
+ new FindCryptoDefaultEncoding(), // DEP0091: crypto.DEFAULT_ENCODING (v20 EOL)
17
+ ];
18
+
19
+ export class UpgradeNode20 extends Recipe {
20
+ readonly name = "org.openrewrite.node.migrate.upgrade-node-20";
21
+ readonly displayName = "Upgrade to Node.js 20";
22
+ readonly description = "Migrate deprecated APIs for Node.js 20 compatibility. Addresses deprecations that are end-of-life in Node 20.";
23
+
24
+ async recipeList(): Promise<Recipe[]> {
25
+ return [
26
+ new IncreaseNodeEngineVersion({version: "20"}),
27
+ new IncreaseNodeEngineVersionInGithubActions({version: "20"}),
28
+ await prepareJavaRecipe("org.openrewrite.nodejs.migrate.UpgradeNodeVersion", {version: "20"}),
29
+ ...node20EolDeprecations,
30
+ ];
31
+ }
32
+ }
@@ -12,7 +12,7 @@ import {RemoveUsageOfProcessFeaturesTlsConstants} from "./process-features-tls";
12
12
  import {UseNativeTypeCheckingMethods} from "./util-type-checking";
13
13
  import {ReplaceUtilExtend} from "./util-extend";
14
14
  import {ReplaceDeprecatedBufferSlice} from "./buffer-slice";
15
- import {AvoidImplicitCoercionOfExitCode} from "./process-exit-code";
15
+ import {UpgradeNode20} from "./upgrade-node-20";
16
16
  import {IncreaseNodeEngineVersion} from "./increase-node-engine-version";
17
17
  import {IncreaseNodeEngineVersionInGithubActions} from "./increase-node-engine-version-in-github-actions";
18
18
  import {FindPunycodeUsage} from "./find-punycode-usage";
@@ -32,9 +32,8 @@ const node22RuntimeDeprecations: Recipe[] = [
32
32
  new ReplaceCryptoConstructors(), // DEP0179, DEP0181: Hash/Hmac constructors
33
33
  ];
34
34
 
35
- // Deprecations from earlier versions (incl. EOL before v22)
36
- const olderDeprecations: Recipe[] = [
37
- new AvoidImplicitCoercionOfExitCode(), // DEP0164: process.exit(code) (v20 EOL)
35
+ // Doc-only deprecations (no fixed EOL version) bundled with the v22 upgrade
36
+ const docOnlyDeprecations: Recipe[] = [
38
37
  new ReplaceDeprecatedBufferSlice(), // DEP0158: buffer.slice (doc-only)
39
38
  new RemoveUsageOfProcessFeaturesTlsConstants(), // DEP0189: process.features.tls_* (doc-only)
40
39
  new RemovePromisifyOnPromise(), // DEP0174: promisify on promise (doc-only)
@@ -43,16 +42,17 @@ const olderDeprecations: Recipe[] = [
43
42
  export class UpgradeNode22 extends Recipe {
44
43
  readonly name = "org.openrewrite.node.migrate.upgrade-node-22";
45
44
  readonly displayName = "Upgrade to Node.js 22";
46
- readonly description = "Migrate deprecated APIs for Node.js 22 compatibility. Addresses Node 22 runtime deprecations and deprecations from earlier versions.";
45
+ readonly description = "Migrate deprecated APIs for Node.js 22 compatibility. Includes all migrations from Node.js 20, plus Node 21 and Node 22 deprecations.";
47
46
 
48
47
  async recipeList(): Promise<Recipe[]> {
49
48
  return [
50
49
  new IncreaseNodeEngineVersion({version: "22"}),
51
50
  new IncreaseNodeEngineVersionInGithubActions({version: "22"}),
52
51
  await prepareJavaRecipe("org.openrewrite.nodejs.migrate.UpgradeNodeVersion", {version: "22"}),
52
+ new UpgradeNode20(),
53
53
  ...node22EolDeprecations,
54
54
  ...node22RuntimeDeprecations,
55
- ...olderDeprecations,
55
+ ...docOnlyDeprecations,
56
56
  ];
57
57
  }
58
58
  }
@@ -0,0 +1,45 @@
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
+ import {Recipe} from "@openrewrite/rewrite";
7
+ import {prepareJavaRecipe} from "@openrewrite/rewrite/rpc";
8
+ import {UpgradeNode24} from "./upgrade-node-24";
9
+ import {IncreaseNodeEngineVersion} from "./increase-node-engine-version";
10
+ import {IncreaseNodeEngineVersionInGithubActions} from "./increase-node-engine-version-in-github-actions";
11
+ import {ReplaceStreamInternalModules} from "./stream-internal-modules";
12
+ import {ReplaceServerResponseWriteHeader} from "./server-response-write-header";
13
+ import {FindAeadDecipherivWithoutAuthTagLength} from "./find-aead-decipheriv-without-auth-tag-length";
14
+ import {FindDuplexToWebType} from "./find-duplex-to-web-type";
15
+ import {FindModuleRegister} from "./find-module-register";
16
+
17
+ // Node 26 End-of-Life deprecations (fully removed in v26)
18
+ const node26EolDeprecations: Recipe[] = [
19
+ new ReplaceServerResponseWriteHeader(), // DEP0063: ServerResponse.writeHeader() (v26 EOL)
20
+ new ReplaceStreamInternalModules(), // DEP0125: legacy _stream_* modules (v26 EOL)
21
+ new FindAeadDecipherivWithoutAuthTagLength(), // DEP0182: short GCM auth tags (v26 EOL)
22
+ ];
23
+
24
+ // Node 26 Runtime deprecations
25
+ const node26RuntimeDeprecations: Recipe[] = [
26
+ new FindDuplexToWebType(), // DEP0201: Duplex.toWeb options.type
27
+ new FindModuleRegister(), // DEP0205: module.register()
28
+ ];
29
+
30
+ export class UpgradeNode26 extends Recipe {
31
+ readonly name = "org.openrewrite.node.migrate.upgrade-node-26";
32
+ readonly displayName = "Upgrade to Node.js 26";
33
+ readonly description = "Migrate deprecated APIs for Node.js 26 compatibility. Includes all migrations from Node.js 24, plus Node 25 and Node 26 deprecations.";
34
+
35
+ async recipeList(): Promise<Recipe[]> {
36
+ return [
37
+ new IncreaseNodeEngineVersion({version: "26"}),
38
+ new IncreaseNodeEngineVersionInGithubActions({version: "26"}),
39
+ await prepareJavaRecipe("org.openrewrite.nodejs.migrate.UpgradeNodeVersion", {version: "26"}),
40
+ new UpgradeNode24(),
41
+ ...node26EolDeprecations,
42
+ ...node26RuntimeDeprecations,
43
+ ];
44
+ }
45
+ }