@openrewrite/recipes-nodejs 0.47.0 → 0.47.3
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/migrate/increase-node-engine-version.d.ts +2 -2
- package/dist/migrate/increase-node-engine-version.d.ts.map +1 -1
- package/dist/migrate/increase-node-engine-version.js +53 -47
- package/dist/migrate/increase-node-engine-version.js.map +1 -1
- package/dist/resources/advisories-npm.csv +502 -54
- package/package.json +4 -5
- package/src/migrate/increase-node-engine-version.ts +59 -54
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openrewrite/recipes-nodejs",
|
|
3
|
-
"version": "0.47.
|
|
3
|
+
"version": "0.47.3",
|
|
4
4
|
"license": "Moderne Proprietary",
|
|
5
5
|
"description": "OpenRewrite recipes for Node.js library migrations.",
|
|
6
6
|
"homepage": "https://github.com/moderneinc/rewrite-node",
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"prebuild": "
|
|
20
|
+
"prebuild": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
21
21
|
"build": "tsc --build tsconfig.build.json",
|
|
22
|
-
"postbuild": "
|
|
22
|
+
"postbuild": "node -e \"const fse=require('fs-extra'); fse.existsSync('resources') && fse.copySync('resources','dist/resources',{dereference:true})\"",
|
|
23
23
|
"dev": "tsc --watch -p tsconfig.json",
|
|
24
24
|
"test": "npm run build && jest",
|
|
25
25
|
"ci:test": "jest"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@openrewrite/rewrite": "^8.
|
|
28
|
+
"@openrewrite/rewrite": "^8.88.0-20260729-151827",
|
|
29
29
|
"mutative": "^1.1.0",
|
|
30
30
|
"semver": "^7.7.3"
|
|
31
31
|
},
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
"@types/jest": "^29.5.13",
|
|
34
34
|
"@types/node": "^22.5.4",
|
|
35
35
|
"@types/semver": "^7.7.1",
|
|
36
|
-
"bun": "^1.3.5",
|
|
37
36
|
"fs-extra": "^11.3.3",
|
|
38
37
|
"jest": "^29.7.0",
|
|
39
38
|
"jest-junit": "^17.0.0",
|
|
@@ -11,7 +11,10 @@ import * as semver from "semver";
|
|
|
11
11
|
export class IncreaseNodeEngineVersion extends Recipe {
|
|
12
12
|
readonly name = "org.openrewrite.node.migrate.increase-node-engine-version";
|
|
13
13
|
readonly displayName = "Increase Node.js engine version";
|
|
14
|
-
readonly description = "
|
|
14
|
+
readonly description = "Raises the lower bound of the `engines.node` version range in package.json to the specified " +
|
|
15
|
+
"Node.js version, performing a hard cutover that drops support for older, end-of-life versions " +
|
|
16
|
+
"(`22.x` → `24.x`, `>= 22` → `>= 24`). The original constraint style is preserved where possible and the " +
|
|
17
|
+
"version is never lowered.";
|
|
15
18
|
|
|
16
19
|
@Option({
|
|
17
20
|
displayName: "Version",
|
|
@@ -48,13 +51,18 @@ export class IncreaseNodeEngineVersion extends Recipe {
|
|
|
48
51
|
return doc;
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
const updatedRange =
|
|
54
|
+
const updatedRange = raiseMinimumVersion(nodeRange, targetVersion);
|
|
52
55
|
if (updatedRange === nodeRange) {
|
|
53
56
|
return doc;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
// Targeted replacement preserves the file's original formatting (indentation,
|
|
60
|
+
// trailing newline, key order) instead of round-tripping through JSON.stringify.
|
|
61
|
+
const nodeMemberRegex = /("engines"\s*:\s*\{[\s\S]*?"node"\s*:\s*)"([^"]+)"/;
|
|
62
|
+
const modifiedContent = content.replace(nodeMemberRegex, `$1${JSON.stringify(updatedRange)}`);
|
|
63
|
+
if (modifiedContent === content) {
|
|
64
|
+
return doc;
|
|
65
|
+
}
|
|
58
66
|
|
|
59
67
|
const parsed = await new JsonParser({}).parseOne({
|
|
60
68
|
text: modifiedContent,
|
|
@@ -71,67 +79,64 @@ export class IncreaseNodeEngineVersion extends Recipe {
|
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
|
|
74
|
-
export function
|
|
82
|
+
export function raiseMinimumVersion(range: string, targetVersion: number): string {
|
|
83
|
+
const trimmed = range.trim();
|
|
84
|
+
|
|
85
|
+
let minVersion: semver.SemVer | null = null;
|
|
75
86
|
try {
|
|
76
|
-
|
|
77
|
-
return range;
|
|
78
|
-
}
|
|
87
|
+
minVersion = semver.minVersion(trimmed);
|
|
79
88
|
} catch {
|
|
80
|
-
// fall through to string manipulation
|
|
89
|
+
// unparseable range: fall through to string manipulation
|
|
81
90
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const hyphenMatch = range.match(/^(.+\s+-\s+)(\d+)(\.0\.0)?$/);
|
|
85
|
-
if (hyphenMatch) {
|
|
86
|
-
return `${hyphenMatch[1]}${targetVersion}${hyphenMatch[3] || ''}`;
|
|
91
|
+
if (minVersion && minVersion.major >= targetVersion) {
|
|
92
|
+
return range;
|
|
87
93
|
}
|
|
88
94
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
newPart = `${targetVersion}`;
|
|
95
|
+
// OR union with no comparators: drop terms below the target, floor the rest.
|
|
96
|
+
// "20.x || 22.x" -> "24.x"; "^26 || ^22" -> "^26" (preserves out-of-order terms already ≥ target).
|
|
97
|
+
if (trimmed.includes('||') && !/[<>]/.test(trimmed)) {
|
|
98
|
+
const parts = trimmed.split(/\s*\|\|\s*/).map(p => p.trim());
|
|
99
|
+
const kept = parts.filter(p => {
|
|
100
|
+
try {
|
|
101
|
+
const m = semver.minVersion(p);
|
|
102
|
+
return m !== null && m.major >= targetVersion;
|
|
103
|
+
} catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
if (kept.length > 0) {
|
|
108
|
+
return kept.join(' || ');
|
|
104
109
|
}
|
|
105
|
-
|
|
106
|
-
return `${range}${separator}${newPart}`;
|
|
110
|
+
return floorTerm(parts[parts.length - 1], targetVersion);
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
//
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Single caret: "^18" or "^18.0.0"
|
|
120
|
-
const caretMatch = range.match(/^\^(\d+)(\.0\.0)?$/);
|
|
121
|
-
if (caretMatch) {
|
|
122
|
-
return `${range} || ^${targetVersion}${caretMatch[2] || ''}`;
|
|
113
|
+
// Hyphen range: "18 - 20" or "18.0.0 - 20.0.0".
|
|
114
|
+
const hyphenMatch = trimmed.match(/^(.+?)\s+-\s+(.+)$/);
|
|
115
|
+
if (hyphenMatch) {
|
|
116
|
+
const upper = hyphenMatch[2].trim();
|
|
117
|
+
const upperMajor = parseInt(upper, 10);
|
|
118
|
+
if (!Number.isNaN(upperMajor) && upperMajor < targetVersion) {
|
|
119
|
+
return `>=${targetVersion}`;
|
|
120
|
+
}
|
|
121
|
+
return `${targetVersion} - ${upper}`;
|
|
123
122
|
}
|
|
124
123
|
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
// Two-sided comparator range: raise the lower bound, keep the upper bound only if it still admits the target.
|
|
125
|
+
if (/</.test(trimmed)) {
|
|
126
|
+
const upperMatch = trimmed.match(/(<=?)\s*(\d+)(?:\.\d+)*/);
|
|
127
|
+
let upper = '';
|
|
128
|
+
if (upperMatch) {
|
|
129
|
+
const op = upperMatch[1];
|
|
130
|
+
const upperMajor = parseInt(upperMatch[2], 10);
|
|
131
|
+
const admitsTarget = op === '<=' ? upperMajor >= targetVersion : upperMajor > targetVersion;
|
|
132
|
+
upper = admitsTarget ? ` ${op}${upperMatch[2]}` : '';
|
|
133
|
+
}
|
|
134
|
+
return `>=${targetVersion}${upper}`;
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return `${range} || ${targetVersion}.x`;
|
|
134
|
-
}
|
|
137
|
+
return floorTerm(trimmed, targetVersion);
|
|
138
|
+
}
|
|
135
139
|
|
|
136
|
-
|
|
140
|
+
function floorTerm(part: string, targetVersion: number): string {
|
|
141
|
+
return part.replace(/\d+/, String(targetVersion));
|
|
137
142
|
}
|