@openrewrite/recipes-nodejs 0.51.0-20260922-170911 → 0.51.1
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.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/migrate/angular-animations/remove-angular-animations-providers.js +7 -7
- package/dist/migrate/angular-animations/remove-angular-animations-providers.js.map +1 -1
- package/dist/migrate/express-request-id/migrate-express-request-id-to-native.d.ts.map +1 -1
- package/dist/migrate/express-request-id/migrate-express-request-id-to-native.js +3 -2
- package/dist/migrate/express-request-id/migrate-express-request-id-to-native.js.map +1 -1
- package/dist/migrate/express-request-id/remove-unused-express-request-id-dependency.d.ts +3 -6
- package/dist/migrate/express-request-id/remove-unused-express-request-id-dependency.d.ts.map +1 -1
- package/dist/migrate/express-request-id/remove-unused-express-request-id-dependency.js +5 -11
- package/dist/migrate/express-request-id/remove-unused-express-request-id-dependency.js.map +1 -1
- package/dist/migrate/find-process-assert.js +2 -2
- package/dist/migrate/find-process-assert.js.map +1 -1
- package/dist/migrate/fs-extra/migrate-fs-extra-to-node-fs.d.ts.map +1 -1
- package/dist/migrate/fs-extra/migrate-fs-extra-to-node-fs.js +4 -2
- package/dist/migrate/fs-extra/migrate-fs-extra-to-node-fs.js.map +1 -1
- package/dist/migrate/fs-extra/remove-unused-fs-extra-dependency.d.ts +4 -6
- package/dist/migrate/fs-extra/remove-unused-fs-extra-dependency.d.ts.map +1 -1
- package/dist/migrate/fs-extra/remove-unused-fs-extra-dependency.js +6 -14
- package/dist/migrate/fs-extra/remove-unused-fs-extra-dependency.js.map +1 -1
- package/dist/migrate/fs-extra/replace-fs-extra-native-methods.js +1 -1
- package/dist/migrate/fs-extra/replace-fs-extra-native-methods.js.map +1 -1
- package/dist/migrate/hpagent/migrate-hpagent-to-https-proxy-agent.d.ts.map +1 -1
- package/dist/migrate/hpagent/migrate-hpagent-to-https-proxy-agent.js +3 -7
- package/dist/migrate/hpagent/migrate-hpagent-to-https-proxy-agent.js.map +1 -1
- package/dist/migrate/hpagent/replace-hpagent.d.ts.map +1 -1
- package/dist/migrate/hpagent/replace-hpagent.js +68 -2
- package/dist/migrate/hpagent/replace-hpagent.js.map +1 -1
- package/dist/migrate/hpagent/swap-hpagent-dependency.d.ts +3 -15
- package/dist/migrate/hpagent/swap-hpagent-dependency.d.ts.map +1 -1
- package/dist/migrate/hpagent/swap-hpagent-dependency.js +11 -62
- package/dist/migrate/hpagent/swap-hpagent-dependency.js.map +1 -1
- package/dist/migrate/package-json-support.d.ts +6 -0
- package/dist/migrate/package-json-support.d.ts.map +1 -0
- package/dist/migrate/package-json-support.js +31 -0
- package/dist/migrate/package-json-support.js.map +1 -0
- package/dist/migrate/path/remove-path-dependency.js +8 -8
- package/dist/migrate/path/remove-path-dependency.js.map +1 -1
- package/dist/migrate/process-exit-code.js +3 -3
- package/dist/migrate/process-exit-code.js.map +1 -1
- package/dist/migrate/process-features-tls.js +1 -1
- package/dist/migrate/process-features-tls.js.map +1 -1
- package/dist/migrate/prop-types/remove-prop-types.js +8 -8
- package/dist/migrate/prop-types/remove-prop-types.js.map +1 -1
- package/dist/migrate/remove-dependencies.d.ts +6 -0
- package/dist/migrate/remove-dependencies.d.ts.map +1 -0
- package/dist/migrate/remove-dependencies.js +23 -0
- package/dist/migrate/remove-dependencies.js.map +1 -0
- package/dist/migrate/source-map-support/enable-source-maps-at-launch.d.ts +19 -0
- package/dist/migrate/source-map-support/enable-source-maps-at-launch.d.ts.map +1 -0
- package/dist/migrate/source-map-support/enable-source-maps-at-launch.js +311 -0
- package/dist/migrate/source-map-support/enable-source-maps-at-launch.js.map +1 -0
- package/dist/migrate/source-map-support/migrate-source-map-support-to-native.d.ts.map +1 -1
- package/dist/migrate/source-map-support/migrate-source-map-support-to-native.js +9 -4
- package/dist/migrate/source-map-support/migrate-source-map-support-to-native.js.map +1 -1
- package/dist/migrate/source-map-support/register-flag.d.ts +4 -0
- package/dist/migrate/source-map-support/register-flag.d.ts.map +1 -1
- package/dist/migrate/source-map-support/register-flag.js +35 -0
- package/dist/migrate/source-map-support/register-flag.js.map +1 -1
- package/dist/migrate/source-map-support/remove-unused-source-map-support-dependency.d.ts +3 -6
- package/dist/migrate/source-map-support/remove-unused-source-map-support-dependency.d.ts.map +1 -1
- package/dist/migrate/source-map-support/remove-unused-source-map-support-dependency.js +5 -12
- package/dist/migrate/source-map-support/remove-unused-source-map-support-dependency.js.map +1 -1
- package/dist/migrate/source-map-support/source-map-support-rewrites.d.ts.map +1 -1
- package/dist/migrate/source-map-support/source-map-support-rewrites.js +2 -0
- package/dist/migrate/source-map-support/source-map-support-rewrites.js.map +1 -1
- package/dist/migrate/upgrade-node-22.d.ts.map +1 -1
- package/dist/migrate/upgrade-node-22.js +2 -0
- package/dist/migrate/upgrade-node-22.js.map +1 -1
- package/dist/migrate/upgrade-node-24.d.ts.map +1 -1
- package/dist/migrate/upgrade-node-24.js +2 -0
- package/dist/migrate/upgrade-node-24.js.map +1 -1
- package/dist/resources/advisories-npm.csv +278 -20
- package/package.json +2 -2
- package/src/index.ts +2 -0
- package/src/migrate/angular-animations/remove-angular-animations-providers.ts +1 -1
- package/src/migrate/express-request-id/migrate-express-request-id-to-native.ts +3 -2
- package/src/migrate/express-request-id/remove-unused-express-request-id-dependency.ts +5 -13
- package/src/migrate/find-process-assert.ts +2 -2
- package/src/migrate/fs-extra/migrate-fs-extra-to-node-fs.ts +4 -2
- package/src/migrate/fs-extra/remove-unused-fs-extra-dependency.ts +6 -16
- package/src/migrate/fs-extra/replace-fs-extra-native-methods.ts +1 -1
- package/src/migrate/hpagent/migrate-hpagent-to-https-proxy-agent.ts +3 -7
- package/src/migrate/hpagent/replace-hpagent.ts +82 -3
- package/src/migrate/hpagent/swap-hpagent-dependency.ts +13 -68
- package/src/migrate/package-json-support.ts +34 -0
- package/src/migrate/path/remove-path-dependency.ts +1 -1
- package/src/migrate/process-exit-code.ts +3 -3
- package/src/migrate/process-features-tls.ts +1 -1
- package/src/migrate/prop-types/remove-prop-types.ts +1 -1
- package/src/migrate/remove-dependencies.ts +21 -0
- package/src/migrate/source-map-support/enable-source-maps-at-launch.ts +289 -0
- package/src/migrate/source-map-support/migrate-source-map-support-to-native.ts +9 -4
- package/src/migrate/source-map-support/register-flag.ts +48 -0
- package/src/migrate/source-map-support/remove-unused-source-map-support-dependency.ts +5 -14
- package/src/migrate/source-map-support/source-map-support-rewrites.ts +3 -0
- package/src/migrate/upgrade-node-22.ts +2 -0
- package/src/migrate/upgrade-node-24.ts +2 -0
- package/dist/migrate/remove-unused-dependency.d.ts +0 -43
- package/dist/migrate/remove-unused-dependency.d.ts.map +0 -1
- package/dist/migrate/remove-unused-dependency.js +0 -257
- package/dist/migrate/remove-unused-dependency.js.map +0 -1
- package/src/migrate/remove-unused-dependency.ts +0 -275
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openrewrite/recipes-nodejs",
|
|
3
|
-
"version": "0.51.
|
|
3
|
+
"version": "0.51.1",
|
|
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-
|
|
28
|
+
"@openrewrite/rewrite": "^8.93.0-20261008-033144",
|
|
29
29
|
"mutative": "^1.1.0",
|
|
30
30
|
"semver": "^7.7.3"
|
|
31
31
|
},
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,7 @@ import {FindHpagentManualMigrations} from "./migrate/hpagent/find-hpagent-manual
|
|
|
53
53
|
import {SwapHpagentDependency} from "./migrate/hpagent/swap-hpagent-dependency";
|
|
54
54
|
import {MigrateSourceMapSupportToNative} from "./migrate/source-map-support/migrate-source-map-support-to-native";
|
|
55
55
|
import {FindSourceMapSupportManualMigrations} from "./migrate/source-map-support/find-source-map-support-manual-migrations";
|
|
56
|
+
import {EnableSourceMapsAtLaunch} from "./migrate/source-map-support/enable-source-maps-at-launch";
|
|
56
57
|
import {ReplaceSourceMapSupportInstall} from "./migrate/source-map-support/replace-source-map-support-install";
|
|
57
58
|
import {ReplaceSourceMapSupportRegisterInPackageJson} from "./migrate/source-map-support/replace-source-map-support-register-in-package-json";
|
|
58
59
|
import {ReplaceSourceMapSupportRegisterInMochaConfig} from "./migrate/source-map-support/replace-source-map-support-register-in-mocha-config";
|
|
@@ -128,6 +129,7 @@ export async function activate(marketplace: RecipeMarketplace): Promise<void> {
|
|
|
128
129
|
await marketplace.install(SwapHpagentDependency, Hpagent);
|
|
129
130
|
await marketplace.install(MigrateSourceMapSupportToNative, SourceMapSupport);
|
|
130
131
|
await marketplace.install(ReplaceSourceMapSupportInstall, SourceMapSupport);
|
|
132
|
+
await marketplace.install(EnableSourceMapsAtLaunch, SourceMapSupport);
|
|
131
133
|
await marketplace.install(ReplaceSourceMapSupportRegisterInPackageJson, SourceMapSupport);
|
|
132
134
|
await marketplace.install(ReplaceSourceMapSupportRegisterInMochaConfig, SourceMapSupport);
|
|
133
135
|
await marketplace.install(FindSourceMapSupportManualMigrations, SourceMapSupport);
|
|
@@ -11,7 +11,7 @@ import {JavaScriptVisitor, JS, maybeUnbind, walk} from "@openrewrite/rewrite/jav
|
|
|
11
11
|
import {PlainText} from "@openrewrite/rewrite/text";
|
|
12
12
|
import * as semver from "semver";
|
|
13
13
|
import {bindingsOf, importModule, ModuleBindings} from "../module-bindings";
|
|
14
|
-
import {anyBeneath, dirOf, isPackageJson, removeMembers} from "../
|
|
14
|
+
import {anyBeneath, dirOf, isPackageJson, removeMembers} from "../package-json-support";
|
|
15
15
|
|
|
16
16
|
const PROVIDER_MODULES = new Set(['@angular/platform-browser/animations', '@angular/platform-browser/animations/async']);
|
|
17
17
|
const ANIMATIONS = '@angular/animations';
|
|
@@ -12,13 +12,14 @@ export class MigrateExpressRequestIdToNative extends Recipe {
|
|
|
12
12
|
readonly name = "org.openrewrite.node.migrate.express-request-id.migrate-express-request-id-to-native";
|
|
13
13
|
readonly displayName = "Migrate `express-request-id` to an inline middleware";
|
|
14
14
|
readonly description = "Replaces the `express-request-id` middleware with an equivalent inline middleware built on " +
|
|
15
|
-
"`randomUUID()` from `node:crypto`, then removes the dependency from each `package.json` that
|
|
15
|
+
"`randomUUID()` from `node:crypto`, then removes the dependency from each `package.json` that declares it, " +
|
|
16
|
+
"updating the lock file to match.";
|
|
16
17
|
readonly tags = ["express-request-id"];
|
|
17
18
|
|
|
18
19
|
async recipeList(): Promise<Recipe[]> {
|
|
19
20
|
return [
|
|
20
21
|
new ReplaceExpressRequestId(),
|
|
21
|
-
new RemoveUnusedExpressRequestIdDependency(
|
|
22
|
+
new RemoveUnusedExpressRequestIdDependency(),
|
|
22
23
|
];
|
|
23
24
|
}
|
|
24
25
|
}
|
|
@@ -4,22 +4,14 @@
|
|
|
4
4
|
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {RemoveUnusedDependency} from "../remove-unused-dependency";
|
|
9
|
-
import {ReplaceExpressRequestId} from "./replace-express-request-id";
|
|
7
|
+
import {RemoveDependencies} from "../remove-dependencies";
|
|
10
8
|
|
|
11
|
-
export class RemoveUnusedExpressRequestIdDependency extends
|
|
9
|
+
export class RemoveUnusedExpressRequestIdDependency extends RemoveDependencies {
|
|
12
10
|
readonly name = "org.openrewrite.node.migrate.express-request-id.remove-unused-express-request-id-dependency";
|
|
13
|
-
readonly displayName = "Remove
|
|
14
|
-
readonly description = "Removes `express-request-id` and `@types/express-request-id` from each `package.json`
|
|
15
|
-
"
|
|
16
|
-
"since the application may use it directly. Lock files are not regenerated; run the package manager's install afterwards.";
|
|
11
|
+
readonly displayName = "Remove the `express-request-id` dependency";
|
|
12
|
+
readonly description = "Removes `express-request-id` and `@types/express-request-id` from each `package.json` " +
|
|
13
|
+
"that declares them, and updates the lock file to match. `uuid` is kept, since the application may use it directly.";
|
|
17
14
|
readonly tags = ["express-request-id"];
|
|
18
15
|
|
|
19
|
-
protected readonly module = 'express-request-id';
|
|
20
16
|
protected readonly packages = ['express-request-id', '@types/express-request-id'];
|
|
21
|
-
|
|
22
|
-
protected migrationRecipes(): Recipe[] {
|
|
23
|
-
return [new ReplaceExpressRequestId()];
|
|
24
|
-
}
|
|
25
17
|
}
|
|
@@ -14,7 +14,7 @@ export class FindProcessAssert extends Recipe {
|
|
|
14
14
|
readonly tags: string[] = ["DEP0100"];
|
|
15
15
|
|
|
16
16
|
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
17
|
-
return check(usesType("
|
|
17
|
+
return check(usesType("NodeJS.Process"), new class extends JavaScriptVisitor<ExecutionContext> {
|
|
18
18
|
protected async visitMethodInvocation(m: J.MethodInvocation, p: ExecutionContext): Promise<J | undefined> {
|
|
19
19
|
const method = await super.visitMethodInvocation(m, p) as J.MethodInvocation;
|
|
20
20
|
if (method.name.simpleName !== "assert" || !method.select) {
|
|
@@ -25,7 +25,7 @@ export class FindProcessAssert extends Recipe {
|
|
|
25
25
|
if (select.kind === J.Kind.Identifier) {
|
|
26
26
|
const id = select as J.Identifier;
|
|
27
27
|
if (id.simpleName === "process" && id.type?.kind === Type.Kind.Class &&
|
|
28
|
-
(id.type as Type.Class).fullyQualifiedName === "
|
|
28
|
+
(id.type as Type.Class).fullyQualifiedName === "NodeJS.Process") {
|
|
29
29
|
return foundSearchResult(method,
|
|
30
30
|
"process.assert() is deprecated (DEP0100). Use the assert module instead.");
|
|
31
31
|
}
|
|
@@ -20,7 +20,9 @@ export class MigrateFsExtraToNodeFs extends Recipe {
|
|
|
20
20
|
"existing destination, or work across devices. Calls without a faithful native equivalent " +
|
|
21
21
|
"(`ensureSymlink`, `ensureLink`, ...) are left in place and listed in the `FsExtraManualMigrationSteps` " +
|
|
22
22
|
"data table. " +
|
|
23
|
-
"`fs-extra` and `@types/fs-extra` are removed from each `package.json`
|
|
23
|
+
"`fs-extra` and `@types/fs-extra` are removed from each `package.json` that declares them, with the lock file " +
|
|
24
|
+
"updated to match. They are removed even where a call listed in the data table still uses fs-extra, so " +
|
|
25
|
+
"migrate those before building. " +
|
|
24
26
|
"Tests that mock or spy on fs-extra itself (`vi.mock('fs-extra')`, `vi.spyOn(fs, 'outputFile')`, ...) keep " +
|
|
25
27
|
"watching a module the migrated code no longer calls, so they fail until they mock `node:fs` / " +
|
|
26
28
|
"`node:fs/promises` instead. Every such site is listed in the data table; migrate them together with the " +
|
|
@@ -31,7 +33,7 @@ export class MigrateFsExtraToNodeFs extends Recipe {
|
|
|
31
33
|
return [
|
|
32
34
|
...fsExtraRewriteRecipes(),
|
|
33
35
|
new FindFsExtraManualMigrations(),
|
|
34
|
-
new RemoveUnusedFsExtraDependency(
|
|
36
|
+
new RemoveUnusedFsExtraDependency(),
|
|
35
37
|
];
|
|
36
38
|
}
|
|
37
39
|
}
|
|
@@ -4,24 +4,14 @@
|
|
|
4
4
|
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {fsExtraRewriteRecipes} from "./fs-extra-rewrites";
|
|
9
|
-
import {RemoveUnusedDependency} from "../remove-unused-dependency";
|
|
7
|
+
import {RemoveDependencies} from "../remove-dependencies";
|
|
10
8
|
|
|
11
|
-
export class RemoveUnusedFsExtraDependency extends
|
|
9
|
+
export class RemoveUnusedFsExtraDependency extends RemoveDependencies {
|
|
12
10
|
readonly name = "org.openrewrite.node.migrate.fs-extra.remove-unused-fs-extra-dependency";
|
|
13
|
-
readonly displayName = "Remove
|
|
14
|
-
readonly description = "Removes `fs-extra` and `@types/fs-extra` from each `package.json`
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"`package.json`'s directory keeps its dependencies, since a monorepo root hoists them for its workspaces. " +
|
|
18
|
-
"A package is left alone when any source beneath it failed to parse, or none was parsed at all. " +
|
|
19
|
-
"Lock files are not regenerated; run the package manager's install afterwards.";
|
|
11
|
+
readonly displayName = "Remove the `fs-extra` dependency";
|
|
12
|
+
readonly description = "Removes `fs-extra` and `@types/fs-extra` from each `package.json` that declares them, " +
|
|
13
|
+
"and updates the lock file to match.";
|
|
14
|
+
readonly tags = ["fs-extra"];
|
|
20
15
|
|
|
21
|
-
protected readonly module = 'fs-extra';
|
|
22
16
|
protected readonly packages = ['fs-extra', '@types/fs-extra'];
|
|
23
|
-
|
|
24
|
-
protected migrationRecipes(): Recipe[] {
|
|
25
|
-
return fsExtraRewriteRecipes();
|
|
26
|
-
}
|
|
27
17
|
}
|
|
@@ -59,7 +59,7 @@ function uniformTarget(cu: JS.CompilationUnit, local: string, member: string): N
|
|
|
59
59
|
const body = cu.statements.filter(s => s.element?.kind !== JS.Kind.Import && !declarationsOf(s.element).some(d =>
|
|
60
60
|
d.variables.some(v => v.element.initializer?.element?.kind === J.Kind.MethodInvocation &&
|
|
61
61
|
(v.element.initializer.element as J.MethodInvocation).name.simpleName === 'require')));
|
|
62
|
-
walk(body, node => {
|
|
62
|
+
walk(body, (node: any) => {
|
|
63
63
|
if (node.kind === J.Kind.Identifier && node.simpleName === local) {
|
|
64
64
|
occurrences++;
|
|
65
65
|
} else if (node.kind === J.Kind.MethodInvocation && !node.select && node.name.simpleName === local) {
|
|
@@ -17,14 +17,14 @@ export class MigrateHpagentToHttpsProxyAgent extends Recipe {
|
|
|
17
17
|
"tunnel to TLS only for an `https:` target — so `HttpProxyAgent` and `HttpsProxyAgent` both become " +
|
|
18
18
|
"`https-proxy-agent`'s `HttpsProxyAgent`. The proxy URL moves from the `proxy` option to the constructor's " +
|
|
19
19
|
"first argument, `proxyRequestOptions` members move up into the options object, and the dependency is swapped " +
|
|
20
|
-
"in each `package.json` that declares it.\n\n" +
|
|
20
|
+
"in each `package.json` that declares it, with the lock file updated to match.\n\n" +
|
|
21
21
|
"Two things do not carry over, and both are reported in the `HpagentManualMigrationSteps` data table rather " +
|
|
22
22
|
"than rewritten. A TLS option set at the top level of hpagent's `HttpsProxyAgent` (`ca`, `rejectUnauthorized`, " +
|
|
23
23
|
"`cert`, ...) configured the handshake with the **destination**, because that class extends `https.Agent`; " +
|
|
24
24
|
"`https-proxy-agent` spends its constructor options on the connection to the **proxy** and takes the " +
|
|
25
25
|
"destination's TLS options from each request, so such an option has to move to the request or the client " +
|
|
26
26
|
"library. Agent options that aren't written as an object literal can't be split into the new two-argument " +
|
|
27
|
-
"form either.";
|
|
27
|
+
"form either. The dependency is swapped regardless, so migrate every reported site before building.";
|
|
28
28
|
readonly tags = ["hpagent", "https-proxy-agent"];
|
|
29
29
|
|
|
30
30
|
@Option({
|
|
@@ -53,11 +53,7 @@ export class MigrateHpagentToHttpsProxyAgent extends Recipe {
|
|
|
53
53
|
return [
|
|
54
54
|
new ReplaceHpagent({migrateSpreadOptions: this.migrateSpreadOptions}),
|
|
55
55
|
new FindHpagentManualMigrations(),
|
|
56
|
-
new SwapHpagentDependency({
|
|
57
|
-
version: this.version,
|
|
58
|
-
migrateSpreadOptions: this.migrateSpreadOptions,
|
|
59
|
-
accountForMigration: true
|
|
60
|
-
}),
|
|
56
|
+
new SwapHpagentDependency({version: this.version}),
|
|
61
57
|
];
|
|
62
58
|
}
|
|
63
59
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {ExecutionContext, Option, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
7
|
+
import {emptyMarkers, ExecutionContext, Option, randomId, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
8
8
|
import {isIdentifier, J, rightPadded, space} from "@openrewrite/rewrite/java";
|
|
9
9
|
import {compilationUnitOf, declarationsOf, JavaScriptVisitor, JS, maybeRebind, maybeUnbind, namesDeclaredIn} from "@openrewrite/rewrite/javascript";
|
|
10
10
|
import {requiredModule} from "../module-bindings";
|
|
@@ -107,6 +107,76 @@ function typeKey(type: J): string | undefined {
|
|
|
107
107
|
return isIdentifier(clazz) ? `${clazz.simpleName}<>` : undefined;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Whether `proxy` is a URL however the program got here. hpagent keeps a missing proxy until a request
|
|
112
|
+
* needs it, but `https-proxy-agent` reads it in the constructor, so anything else may now throw there.
|
|
113
|
+
*/
|
|
114
|
+
function alwaysDefined(proxy: J): boolean {
|
|
115
|
+
switch (proxy.kind) {
|
|
116
|
+
case J.Kind.Literal:
|
|
117
|
+
return typeof (proxy as J.Literal).value === 'string';
|
|
118
|
+
case JS.Kind.TemplateExpression:
|
|
119
|
+
case J.Kind.NewClass:
|
|
120
|
+
return true;
|
|
121
|
+
case J.Kind.Parentheses:
|
|
122
|
+
return alwaysDefined((proxy as J.Parentheses<J>).tree.element);
|
|
123
|
+
case J.Kind.Binary:
|
|
124
|
+
return (proxy as J.Binary).operator.element === J.Binary.Type.Or && alwaysDefined((proxy as J.Binary).right);
|
|
125
|
+
case JS.Kind.Binary:
|
|
126
|
+
return (proxy as JS.Binary).operator.element === JS.Binary.Type.QuestionQuestion && alwaysDefined((proxy as JS.Binary).right);
|
|
127
|
+
default:
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Whether evaluating `e` twice is the same as evaluating it once: names, member reads and fallbacks among them. */
|
|
133
|
+
function readsOnly(e: J): boolean {
|
|
134
|
+
switch (e.kind) {
|
|
135
|
+
case J.Kind.Identifier:
|
|
136
|
+
case J.Kind.Literal:
|
|
137
|
+
return true;
|
|
138
|
+
case J.Kind.FieldAccess:
|
|
139
|
+
return readsOnly((e as J.FieldAccess).target);
|
|
140
|
+
case J.Kind.ArrayAccess: {
|
|
141
|
+
const access = e as J.ArrayAccess;
|
|
142
|
+
return readsOnly(access.indexed) && access.dimension.index.element.kind === J.Kind.Literal;
|
|
143
|
+
}
|
|
144
|
+
case J.Kind.Parentheses:
|
|
145
|
+
return readsOnly((e as J.Parentheses<J>).tree.element);
|
|
146
|
+
case J.Kind.Binary:
|
|
147
|
+
return (e as J.Binary).operator.element === J.Binary.Type.Or &&
|
|
148
|
+
readsOnly((e as J.Binary).left) && readsOnly((e as J.Binary).right);
|
|
149
|
+
case JS.Kind.Binary:
|
|
150
|
+
return (e as JS.Binary).operator.element === JS.Binary.Type.QuestionQuestion &&
|
|
151
|
+
readsOnly((e as JS.Binary).left) && readsOnly((e as JS.Binary).right);
|
|
152
|
+
default:
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** A JavaScript file, where nothing checked that the proxy is defined; TypeScript required hpagent's `proxy` to be. */
|
|
158
|
+
function isJavaScript(sourcePath: string): boolean {
|
|
159
|
+
return /\.[cm]?jsx?$/.test(sourcePath);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** `proxy ? construction : undefined`, built directly so the construction keeps its layout. */
|
|
163
|
+
function whenDefined(proxy: J, construction: J.NewClass): J.Ternary {
|
|
164
|
+
const padded = <T extends J>(element: T): J.LeftPadded<T> =>
|
|
165
|
+
({kind: J.Kind.LeftPadded, before: space(' '), element, markers: emptyMarkers});
|
|
166
|
+
const undefinedValue: J.Identifier = {
|
|
167
|
+
kind: J.Kind.Identifier, id: randomId(), prefix: space(' '), markers: emptyMarkers, annotations: [], simpleName: 'undefined'
|
|
168
|
+
};
|
|
169
|
+
return {
|
|
170
|
+
kind: J.Kind.Ternary,
|
|
171
|
+
id: randomId(),
|
|
172
|
+
prefix: construction.prefix,
|
|
173
|
+
markers: emptyMarkers,
|
|
174
|
+
condition: {...proxy, id: randomId(), prefix: space('')} as J,
|
|
175
|
+
truePart: padded({...construction, prefix: space(' ')} as J.NewClass),
|
|
176
|
+
falsePart: padded(undefinedValue),
|
|
177
|
+
} as J.Ternary;
|
|
178
|
+
}
|
|
179
|
+
|
|
110
180
|
/** The members of an object literal, still padded, with the placeholder of an empty literal dropped. */
|
|
111
181
|
function paddedMembers(literal: J.NewClass): J.RightPadded<J>[] {
|
|
112
182
|
return (literal.body?.statements ?? []).filter(padded => padded.element.kind !== J.Kind.Empty) as J.RightPadded<J>[];
|
|
@@ -120,7 +190,10 @@ export class ReplaceHpagent extends Recipe {
|
|
|
120
190
|
"option: `new HttpsProxyAgent({proxy: url, keepAlive: true})` becomes `new HttpsProxyAgent(url, {keepAlive: true})`. " +
|
|
121
191
|
"Both hpagent classes tunnel with `CONNECT`, and `https-proxy-agent` upgrades the tunnel to TLS only for an " +
|
|
122
192
|
"`https:` target, so one class serves both. `proxyRequestOptions` members move up into the options object, " +
|
|
123
|
-
"where `https-proxy-agent` applies them to the connection to the proxy.
|
|
193
|
+
"where `https-proxy-agent` applies them to the connection to the proxy. hpagent accepted a missing proxy until " +
|
|
194
|
+
"a request needed it, but `https-proxy-agent` reads it in the constructor, so in JavaScript a proxy that may be " +
|
|
195
|
+
"missing, such as `process.env.HTTP_PROXY`, guards the construction: `proxy ? new HttpsProxyAgent(proxy) : undefined`. " +
|
|
196
|
+
"Calls whose options aren't an object " +
|
|
124
197
|
"literal, or that set a TLS option for the destination handshake (`ca`, `rejectUnauthorized`, `cert`, ...), " +
|
|
125
198
|
"are left alone: `https-proxy-agent` would apply those to the proxy hop instead. " +
|
|
126
199
|
"`FindHpagentManualMigrations` reports every usage left behind.";
|
|
@@ -275,7 +348,13 @@ export class ReplaceHpagent extends Recipe {
|
|
|
275
348
|
(rewrite ? this.migrated : this.stranded).add(site.local);
|
|
276
349
|
return n;
|
|
277
350
|
}
|
|
278
|
-
|
|
351
|
+
if (!rewrite || this.stranded.has(site.local)) return n;
|
|
352
|
+
const migrated = this.applyRewrite(n, site, rewrite);
|
|
353
|
+
// Constructing with a missing proxy used to succeed and now throws, as when a module builds its
|
|
354
|
+
// agent from an environment variable a test run doesn't set, so the construction is skipped then.
|
|
355
|
+
return isJavaScript(compilationUnitOf(this)!.sourcePath) && !alwaysDefined(rewrite.proxy) && readsOnly(rewrite.proxy)
|
|
356
|
+
? whenDefined(rewrite.proxy, migrated)
|
|
357
|
+
: migrated;
|
|
279
358
|
}
|
|
280
359
|
|
|
281
360
|
/**
|
|
@@ -4,35 +4,23 @@
|
|
|
4
4
|
* Moderne Proprietary. Only for use by Moderne customers under the terms of a commercial contract.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {anyBeneath, dirOf, isPackageJson, RemoveUnusedDependency} from "../remove-unused-dependency";
|
|
10
|
-
import {insertPaddedAfter, literal, member, memberIndex} from "../json-edits";
|
|
11
|
-
import {ReplaceHpagent} from "./replace-hpagent";
|
|
7
|
+
import {Option, Recipe} from "@openrewrite/rewrite";
|
|
8
|
+
import {changeDependency} from "@openrewrite/rewrite/javascript";
|
|
12
9
|
import {TARGET_MODULE} from "./hpagent-support";
|
|
13
10
|
|
|
14
|
-
const SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
|
|
15
|
-
|
|
16
11
|
/**
|
|
17
12
|
* `https-proxy-agent` 7 is the last major to keep hpagent's Node floor: hpagent supports Node 14 and
|
|
18
13
|
* up, as do `https-proxy-agent` 7 and 8, while 9 requires Node 20.
|
|
19
14
|
*/
|
|
20
15
|
const DEFAULT_VERSION = "^7.0.6";
|
|
21
16
|
|
|
22
|
-
export class SwapHpagentDependency extends
|
|
17
|
+
export class SwapHpagentDependency extends Recipe {
|
|
23
18
|
readonly name = "org.openrewrite.node.migrate.hpagent.swap-hpagent-dependency";
|
|
24
19
|
readonly displayName = "Swap the `hpagent` dependency for `https-proxy-agent`";
|
|
25
|
-
readonly description = "Replaces `hpagent` with `https-proxy-agent` in each `package.json` that declares it, in
|
|
26
|
-
"
|
|
27
|
-
"`package.json` still references hpagent — a usage the migration left in place, a mock, or a bundler " +
|
|
28
|
-
"configuration naming it — `https-proxy-agent` is added alongside and hpagent kept, since both are then " +
|
|
29
|
-
"needed. A `package.json` whose sources the migration didn't reach is left alone entirely, rather than " +
|
|
30
|
-
"gaining a dependency nothing uses. Lock files are not regenerated; run the package manager's install afterwards.";
|
|
20
|
+
readonly description = "Replaces `hpagent` with `https-proxy-agent` in each `package.json` that declares it, in " +
|
|
21
|
+
"whichever dependency section declares it, and updates the lock file to match.";
|
|
31
22
|
readonly tags = ["hpagent", "https-proxy-agent"];
|
|
32
23
|
|
|
33
|
-
protected readonly module = 'hpagent';
|
|
34
|
-
protected readonly packages = ['hpagent'];
|
|
35
|
-
|
|
36
24
|
@Option({
|
|
37
25
|
displayName: "`https-proxy-agent` version",
|
|
38
26
|
description: "The version range to declare for `https-proxy-agent`. The default keeps hpagent's Node 14 " +
|
|
@@ -42,58 +30,15 @@ export class SwapHpagentDependency extends RemoveUnusedDependency {
|
|
|
42
30
|
})
|
|
43
31
|
version?: string;
|
|
44
32
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
constructor(options?: { version?: string; accountForMigration?: boolean; migrateSpreadOptions?: boolean }) {
|
|
49
|
-
super({accountForMigration: options?.accountForMigration});
|
|
50
|
-
this.version = options?.version;
|
|
51
|
-
this.migrateSpreadOptions = options?.migrateSpreadOptions;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
protected migrationRecipes(): Recipe[] {
|
|
55
|
-
return [new ReplaceHpagent({migrateSpreadOptions: this.migrateSpreadOptions})];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
protected override replacementModules(): string[] {
|
|
59
|
-
return [TARGET_MODULE];
|
|
33
|
+
constructor(options?: { version?: string }) {
|
|
34
|
+
super(options);
|
|
60
35
|
}
|
|
61
36
|
|
|
62
|
-
async
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// Nothing beneath was parsed, so there is no telling whether the migration reached it.
|
|
69
|
-
if (!anyBeneath(dir, acc.parsedFiles)) return doc;
|
|
70
|
-
const stillUsed = anyBeneath(dir, acc.referencingFiles);
|
|
71
|
-
// Nothing beneath came to use the replacement, so declaring it would add an unused dependency.
|
|
72
|
-
if (!anyBeneath(dir, acc.replacementFiles)) return doc;
|
|
73
|
-
|
|
74
|
-
const root = doc.value as Json.Object;
|
|
75
|
-
let changed = false;
|
|
76
|
-
const members = root.members.map(padded => {
|
|
77
|
-
if (!isMember(padded.element)) return padded;
|
|
78
|
-
const section = padded.element as Json.Member;
|
|
79
|
-
if (!SECTIONS.includes(getMemberKeyName(section) ?? '') || !isObject(section.value)) return padded;
|
|
80
|
-
const obj = section.value as Json.Object;
|
|
81
|
-
const hpagent = memberIndex(obj, 'hpagent');
|
|
82
|
-
// Only a section declaring hpagent needs the replacement, and only where it isn't already there.
|
|
83
|
-
if (hpagent < 0 || memberIndex(obj, TARGET_MODULE) >= 0) return padded;
|
|
84
|
-
changed = true;
|
|
85
|
-
const replacement = member(TARGET_MODULE, literal(version));
|
|
86
|
-
const declaration = obj.members[hpagent];
|
|
87
|
-
const updated = stillUsed
|
|
88
|
-
? insertPaddedAfter(obj.members, hpagent, replacement as Json)
|
|
89
|
-
// Replacing it in place keeps the section's formatting and its declaration order.
|
|
90
|
-
: obj.members.map((m, i) => i === hpagent
|
|
91
|
-
? {...m, element: {...replacement, prefix: declaration.element.prefix} as Json}
|
|
92
|
-
: m);
|
|
93
|
-
return {...padded, element: {...section, value: {...obj, members: updated}} as Json.Member};
|
|
94
|
-
});
|
|
95
|
-
return changed ? {...doc, value: {...root, members}} as Json.Document : doc;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
37
|
+
async recipeList(): Promise<Recipe[]> {
|
|
38
|
+
return [await changeDependency({
|
|
39
|
+
oldPackageName: 'hpagent',
|
|
40
|
+
newPackageName: TARGET_MODULE,
|
|
41
|
+
newVersion: this.version ?? DEFAULT_VERSION
|
|
42
|
+
})];
|
|
98
43
|
}
|
|
99
44
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
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, randomId} from "@openrewrite/rewrite";
|
|
8
|
+
import {getMemberKeyName, isMember, Json, rightPadded, space} from "@openrewrite/rewrite/json";
|
|
9
|
+
|
|
10
|
+
export function dirOf(path: string): string {
|
|
11
|
+
const i = path.lastIndexOf('/');
|
|
12
|
+
return i < 0 ? '' : path.substring(0, i);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isPackageJson(path: string): boolean {
|
|
16
|
+
return (path === 'package.json' || path.endsWith('/package.json')) && !path.includes('node_modules/');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function anyBeneath(dir: string, files: string[]): boolean {
|
|
20
|
+
return files.some(file => dir === '' || file.startsWith(dir + '/'));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Removes members named in `keys`, handing the removed last member's trailing space to the one before it. */
|
|
24
|
+
export function removeMembers(obj: Json.Object, keys: Set<string>): Json.Object {
|
|
25
|
+
const kept = obj.members.filter(rp => !(isMember(rp.element) && keys.has(getMemberKeyName(rp.element as Json.Member) ?? '')));
|
|
26
|
+
if (kept.length === obj.members.length) return obj;
|
|
27
|
+
if (kept.length === 0) {
|
|
28
|
+
const empty: Json.Empty = {kind: Json.Kind.Empty, id: randomId(), prefix: space(''), markers: emptyMarkers};
|
|
29
|
+
return {...obj, members: [rightPadded(empty, space(''))]};
|
|
30
|
+
}
|
|
31
|
+
const closing = obj.members[obj.members.length - 1].after;
|
|
32
|
+
kept[kept.length - 1] = {...kept[kept.length - 1], after: closing};
|
|
33
|
+
return {...obj, members: kept};
|
|
34
|
+
}
|
|
@@ -9,7 +9,7 @@ import {getMemberKeyName, isObject, Json, JsonVisitor} from "@openrewrite/rewrit
|
|
|
9
9
|
import {JS, walk} from "@openrewrite/rewrite/javascript";
|
|
10
10
|
import {J} from "@openrewrite/rewrite/java";
|
|
11
11
|
import {PlainText} from "@openrewrite/rewrite/text";
|
|
12
|
-
import {anyBeneath, dirOf, isPackageJson, removeMembers} from "../
|
|
12
|
+
import {anyBeneath, dirOf, isPackageJson, removeMembers} from "../package-json-support";
|
|
13
13
|
import {requiredModule, stringLiteral} from "../module-bindings";
|
|
14
14
|
|
|
15
15
|
const SECTIONS = new Set(['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']);
|
|
@@ -19,8 +19,8 @@ export class AvoidImplicitCoercionOfExitCode extends Recipe {
|
|
|
19
19
|
readonly tags: string[] = ["DEP0164"];
|
|
20
20
|
|
|
21
21
|
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
22
|
-
return check(usesType("
|
|
23
|
-
exitMatcher = new MethodMatcher("Process exit(..)");
|
|
22
|
+
return check(usesType("NodeJS.Process"), new class extends JavaScriptVisitor<ExecutionContext> {
|
|
23
|
+
exitMatcher = new MethodMatcher("NodeJS.Process exit(..)");
|
|
24
24
|
|
|
25
25
|
private isProcessExitCodeAccess(fieldAccess: J.FieldAccess): boolean {
|
|
26
26
|
if (fieldAccess.name.element.simpleName !== "exitCode") {
|
|
@@ -33,7 +33,7 @@ export class AvoidImplicitCoercionOfExitCode extends Recipe {
|
|
|
33
33
|
|
|
34
34
|
if (idType?.kind === Type.Kind.Class) {
|
|
35
35
|
const classType = idType as Type.Class;
|
|
36
|
-
return classType.fullyQualifiedName === "
|
|
36
|
+
return classType.fullyQualifiedName === "NodeJS.Process";
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -17,7 +17,7 @@ export class RemoveUsageOfProcessFeaturesTlsConstants extends Recipe {
|
|
|
17
17
|
readonly tags: string[] = ["DEP0189"];
|
|
18
18
|
|
|
19
19
|
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
20
|
-
return check(usesType("
|
|
20
|
+
return check(usesType("NodeJS.Process"), new class extends JavaScriptVisitor<ExecutionContext> {
|
|
21
21
|
protected async visitExpression(expression: Expression, p: ExecutionContext): Promise<J | undefined> {
|
|
22
22
|
const ret = await super.visitExpression(expression, p) as Expression;
|
|
23
23
|
if (await pattern`process.features.tls_alpn`.match(ret, this.cursor)
|
|
@@ -11,7 +11,7 @@ import {declarationsOf, JavaScriptVisitor, JS, maybeUnbind, walk as walkTree} fr
|
|
|
11
11
|
import {PlainText} from "@openrewrite/rewrite/text";
|
|
12
12
|
import * as semver from "semver";
|
|
13
13
|
import {bindingsOf, importModule, requiredModule, stringLiteral} from "../module-bindings";
|
|
14
|
-
import {anyBeneath, dirOf, isPackageJson, removeMembers} from "../
|
|
14
|
+
import {anyBeneath, dirOf, isPackageJson, removeMembers} from "../package-json-support";
|
|
15
15
|
|
|
16
16
|
const MODULE = 'prop-types';
|
|
17
17
|
const JSX_RUNTIMES = new Set(['react', 'preact', 'preact/compat']);
|
|
@@ -0,0 +1,21 @@
|
|
|
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 {Recipe} from "@openrewrite/rewrite";
|
|
8
|
+
import {removeDependency} from "@openrewrite/rewrite/javascript";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Removes a library's packages from every `package.json` that declares them, updating the lock file
|
|
12
|
+
* to match, through the native dependency recipe.
|
|
13
|
+
*/
|
|
14
|
+
export abstract class RemoveDependencies extends Recipe {
|
|
15
|
+
/** The packages removed together, e.g. the library and its `@types`. */
|
|
16
|
+
protected abstract readonly packages: string[];
|
|
17
|
+
|
|
18
|
+
async recipeList(): Promise<Recipe[]> {
|
|
19
|
+
return Promise.all(this.packages.map(packageName => removeDependency({packageName})));
|
|
20
|
+
}
|
|
21
|
+
}
|