Skip to content

Commit a237de3

Browse files
authored
Allow specifying environment variables to JavaScriptRewriteRpc.Builder (#6441)
* Allow specifying environment variables on `JavaScriptRewriteRpc.Builder`
1 parent 87ba1d2 commit a237de3

File tree

5 files changed

+96
-5
lines changed

5 files changed

+96
-5
lines changed

rewrite-javascript/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ testing {
195195
implementation(project(":rewrite-test"))
196196
implementation(project(":rewrite-json"))
197197
implementation(project(":rewrite-java-tck"))
198+
implementation(project(":rewrite-yaml"))
198199
implementation("org.assertj:assertj-core:latest.release")
199200
implementation("org.junit.platform:junit-platform-suite-api")
200201
runtimeOnly("org.junit.platform:junit-platform-suite-engine")

rewrite-javascript/rewrite/fixtures/example-recipe.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {MarkTypes} from "./mark-types";
2626
import {MarkPrimitiveTypes} from "./mark-primitive-types";
2727
import {MarkClassTypes} from "./mark-class-types";
2828
import {ScanningEditor} from "./scanning-editor";
29+
import {ReplaceAssignment} from "./replace-assignment";
2930

3031
export function activate(registry: RecipeRegistry) {
3132
registry.register(ChangeText);
@@ -40,4 +41,5 @@ export function activate(registry: RecipeRegistry) {
4041
registry.register(MarkPrimitiveTypes);
4142
registry.register(MarkClassTypes);
4243
registry.register(ScanningEditor);
44+
registry.register(ReplaceAssignment);
4345
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import {createDraft, finishDraft} from "immer";
17+
import {emptyMarkers, ExecutionContext, Option, randomId, Recipe, TreeVisitor} from "@openrewrite/rewrite";
18+
import {JavaScriptVisitor} from "@openrewrite/rewrite/javascript";
19+
import {J, singleSpace, Type} from "@openrewrite/rewrite/java";
20+
21+
export class ReplaceAssignment extends Recipe {
22+
name = "org.openrewrite.example.javascript.replace-assignment"
23+
displayName = "Replace assignment with env var value";
24+
description = "Replaces the assignment of a variable with the value of an environment variable.";
25+
26+
@Option({
27+
displayName: "Environment Variable Name",
28+
description: "The name of the environment variable whose value will replace the variable assignment.",
29+
})
30+
variable!: string;
31+
32+
constructor(options: { variable: string }) {
33+
super(options);
34+
}
35+
36+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
37+
const envVar = this.variable;
38+
const envVarValue = "'" + process.env[envVar] + "'";
39+
return new class extends JavaScriptVisitor<ExecutionContext> {
40+
protected async visitVariable(variable: J.VariableDeclarations.NamedVariable, c: ExecutionContext): Promise<J | undefined> {
41+
if ((variable.initializer!.element as J.Literal).valueSource === envVarValue) {
42+
return super.visitVariable(variable, c);
43+
}
44+
let draft = createDraft(variable);
45+
draft.initializer = {
46+
kind: J.Kind.LeftPadded,
47+
markers: emptyMarkers,
48+
element: {
49+
kind: J.Kind.Literal,
50+
id: randomId(),
51+
prefix: singleSpace,
52+
markers: emptyMarkers,
53+
valueSource: envVarValue,
54+
type: Type.Primitive.String,
55+
},
56+
before: singleSpace,
57+
};
58+
return finishDraft(draft);
59+
}
60+
}
61+
}
62+
}

rewrite-javascript/src/integTest/java/org/openrewrite/javascript/rpc/JavaScriptRewriteRpcTest.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
import static org.openrewrite.java.Assertions.java;
4646
import static org.openrewrite.javascript.Assertions.*;
4747
import static org.openrewrite.json.Assertions.json;
48-
import static org.openrewrite.yaml.Assertions.yaml;
4948
import static org.openrewrite.test.RewriteTest.toRecipe;
5049
import static org.openrewrite.test.SourceSpecs.text;
50+
import static org.openrewrite.yaml.Assertions.yaml;
5151

5252
class JavaScriptRewriteRpcTest implements RewriteTest {
5353
@TempDir
@@ -56,10 +56,10 @@ class JavaScriptRewriteRpcTest implements RewriteTest {
5656
@BeforeEach
5757
void before() {
5858
JavaScriptRewriteRpc.setFactory(JavaScriptRewriteRpc.builder()
59-
.recipeInstallDir(tempDir)
60-
.metricsCsv(tempDir.resolve("rpc.csv"))
61-
.log(tempDir.resolve("rpc.log"))
62-
.traceRpcMessages()
59+
.recipeInstallDir(tempDir)
60+
.metricsCsv(tempDir.resolve("rpc.csv"))
61+
.log(tempDir.resolve("rpc.log"))
62+
.traceRpcMessages()
6363
// .inspectBrk(Path.of("rewrite"))
6464
// .timeout(Duration.ofHours(1))
6565
);
@@ -446,6 +446,24 @@ void runScanningRecipeThatEdits() {
446446
);
447447
}
448448

449+
@Test
450+
void environmentVariableIsSetRemotely() {
451+
JavaScriptRewriteRpc.setFactory(JavaScriptRewriteRpc.builder()
452+
.recipeInstallDir(tempDir)
453+
.environment(Map.of("HTTPS_PROXY", "http://unused:3128"))
454+
);
455+
installRecipes();
456+
457+
rewriteRun(spec -> spec
458+
.recipe(client().prepareRecipe("org.openrewrite.example.javascript.replace-assignment",
459+
Map.of("variable", "HTTPS_PROXY"))),
460+
javascript(
461+
"const v = 'value'",
462+
"const v = 'http://unused:3128'"
463+
)
464+
);
465+
}
466+
449467
private void installRecipes() {
450468
File exampleRecipes = new File("rewrite/dist-fixtures/example-recipe.js");
451469
installRecipes(exampleRecipes);

rewrite-javascript/src/main/java/org/openrewrite/javascript/rpc/JavaScriptRewriteRpc.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.nio.file.Paths;
3535
import java.nio.file.StandardOpenOption;
3636
import java.time.Duration;
37+
import java.util.HashMap;
3738
import java.util.Map;
3839
import java.util.Objects;
3940
import java.util.function.Supplier;
@@ -109,6 +110,7 @@ public static Builder builder() {
109110

110111
@RequiredArgsConstructor
111112
public static class Builder implements Supplier<JavaScriptRewriteRpc> {
113+
private final Map<String, String> environment = new HashMap<>();
112114
private Environment marketplace = Environment.builder().build();
113115
private Path npxPath = Paths.get("npx");
114116
private @Nullable Path log;
@@ -163,6 +165,11 @@ public Builder metricsCsv(@Nullable Path metricsCsv) {
163165
return this;
164166
}
165167

168+
public Builder environment(Map<String, String> environment) {
169+
this.environment.putAll(environment);
170+
return this;
171+
}
172+
166173
/**
167174
* Enables info and debug level logging.
168175
*
@@ -283,6 +290,7 @@ public JavaScriptRewriteRpc get() {
283290
nodeOptions.append(" --max-old-space-size=").append(maxHeapSize);
284291
}
285292
}
293+
process.environment().putAll(environment);
286294
process.environment().put("NODE_OPTIONS", nodeOptions.toString());
287295
if (npxPath.getParent() != null) {
288296
// `npx` is typically a shebang script alongside the `node` executable

0 commit comments

Comments
 (0)