Skip to content

Commit bf0f0f6

Browse files
timtebeekknutwannhedenjkschneider
authored
Mark an initial set of recipes as usable for JavaScript (#6485)
* Mark an initial set of recipes as usable for npm ecosystem * Change the category, but keep the `maven` ecosystem * Integrate `RecipeMarketplace` into `PrepareRecipe` (#6490) Adds an integration test, which tests that a JS recipe can call a Java recipe loaded via marketplace. --------- Co-authored-by: Knut Wannheden <knut@moderne.io> Co-authored-by: Jonathan Schnéider <jkschneider@gmail.com>
1 parent 29b305c commit bf0f0f6

10 files changed

Lines changed: 154 additions & 32 deletions

File tree

rewrite-core/src/main/java/org/openrewrite/rpc/RewriteRpc.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import org.jspecify.annotations.Nullable;
2626
import org.openrewrite.*;
2727
import org.openrewrite.config.OptionDescriptor;
28+
import org.openrewrite.internal.RecipeLoader;
2829
import org.openrewrite.marketplace.RecipeBundle;
30+
import org.openrewrite.marketplace.RecipeListing;
2931
import org.openrewrite.marketplace.RecipeMarketplace;
3032
import org.openrewrite.rpc.internal.PreparedRecipeCache;
3133
import org.openrewrite.rpc.request.*;
@@ -67,7 +69,6 @@ public class RewriteRpc {
6769
private final AtomicReference<@Nullable PrintStream> log = new AtomicReference<>();
6870
private final AtomicReference<TraceGetObject> traceGetObject = new AtomicReference<>(
6971
new TraceGetObject(false, false));
70-
private final AtomicReference<PrepareRecipe.@Nullable Loader> recipeLoader = new AtomicReference<>();
7172

7273
final PreparedRecipeCache preparedRecipes = new PreparedRecipeCache();
7374

@@ -143,7 +144,14 @@ protected Object handle(Void noParams) {
143144
}
144145
}
145146
});
146-
jsonRpc.rpc("PrepareRecipe", new PrepareRecipe.Handler(preparedRecipes, recipeLoader));
147+
jsonRpc.rpc("PrepareRecipe", new PrepareRecipe.Handler(preparedRecipes, (id, opts) -> {
148+
RecipeListing listing = marketplace.findRecipe(id);
149+
if (listing != null) {
150+
return listing.prepare(opts);
151+
}
152+
// Fall back to loading by class name if not found in marketplace
153+
return new RecipeLoader(null).load(id, opts);
154+
}));
147155
jsonRpc.rpc("Print", new Print.Handler(this::getObject));
148156

149157
jsonRpc.bind();
@@ -169,11 +177,6 @@ public RewriteRpc log(@Nullable PrintStream logFile) {
169177
return this;
170178
}
171179

172-
public RewriteRpc recipeLoader(PrepareRecipe.@Nullable Loader recipeLoader) {
173-
this.recipeLoader.set(recipeLoader);
174-
return this;
175-
}
176-
177180
public void shutdown() {
178181
PrintStream logOut = log.get();
179182
if (logOut != null) {

rewrite-core/src/main/java/org/openrewrite/rpc/request/PrepareRecipe.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,11 @@
1919
import io.moderne.jsonrpc.internal.SnowflakeId;
2020
import lombok.RequiredArgsConstructor;
2121
import lombok.Value;
22-
import org.jspecify.annotations.Nullable;
2322
import org.openrewrite.Recipe;
2423
import org.openrewrite.ScanningRecipe;
25-
import org.openrewrite.internal.RecipeLoader;
2624
import org.openrewrite.rpc.internal.PreparedRecipeCache;
2725

2826
import java.util.Map;
29-
import java.util.concurrent.atomic.AtomicReference;
3027

3128
import static java.util.Collections.emptyList;
3229

@@ -42,15 +39,11 @@ public interface Loader {
4239
@RequiredArgsConstructor
4340
public static class Handler extends JsonRpcMethod<PrepareRecipe> {
4441
private final PreparedRecipeCache preparedRecipes;
45-
private final AtomicReference<@Nullable Loader> recipeLoader;
42+
private final Loader recipeLoader;
4643

4744
@Override
4845
protected Object handle(PrepareRecipe request) throws Exception {
49-
Loader loader = recipeLoader.get();
50-
if (loader == null) {
51-
loader = (recipeId, options) -> new RecipeLoader(null).load(recipeId, options);
52-
}
53-
Recipe recipe = loader.load(request.id, request.getOptions());
46+
Recipe recipe = recipeLoader.load(request.id, request.getOptions());
5447
String instanceId = SnowflakeId.generateId();
5548
preparedRecipes.getInstantiated().put(instanceId, recipe);
5649
return new PrepareRecipeResponse(

rewrite-core/src/test/java/org/openrewrite/rpc/RewriteRpcTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.openrewrite.*;
2626
import org.openrewrite.config.Environment;
2727
import org.openrewrite.config.RecipeDescriptor;
28+
import org.openrewrite.internal.RecipeLoader;
2829
import org.openrewrite.marketplace.*;
2930
import org.openrewrite.table.TextMatches;
3031
import org.openrewrite.test.RewriteTest;
@@ -240,7 +241,8 @@ public RecipeDescriptor describe(RecipeListing listing) {
240241

241242
@Override
242243
public Recipe prepare(RecipeListing listing, Map<String, Object> options) {
243-
return requireNonNull(marketplace.findRecipe(listing.getName())).prepare(options);
244+
// Use RecipeLoader to instantiate directly, avoiding recursive marketplace lookup
245+
return new RecipeLoader(null).load(listing.getName(), options);
244246
}
245247
};
246248
}

rewrite-java/src/main/resources/META-INF/rewrite/recipes.csv

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,10 @@ maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindDistinctMetho
9797
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindTypeMappings,Find type mappings,Study the frequency of `J` types and their `JavaType` type attribution.,1,Search,Java,,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.java.table.TypeMappings"",""displayName"":""Type mapping"",""description"":""The types mapped to `J` trees."",""columns"":[{""name"":""compilationUnitName"",""type"":""String"",""displayName"":""Compilation unit class name"",""description"":""The root compilation unit class name containing the mapping.""},{""name"":""treeName"",""type"":""String"",""displayName"":""Tree class name"",""description"":""The simple class name of the `J` element.""},{""name"":""typeName"",""type"":""String"",""displayName"":""Java type class name"",""description"":""The simple class name of the `JavaType`.""},{""name"":""count"",""type"":""Integer"",""displayName"":""Count"",""description"":""The number of times this tree and type pair occurred in a repository.""},{""name"":""nearestNonNullTreeName"",""type"":""String"",""displayName"":""Nearest non-null tree class name"",""description"":""The simple class name of the nearest non-null `J` element when `typeName` is null.""}]}]"
9898
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.HasMinimumJavaVersion,Find the oldest Java version in use,"The oldest Java version in use is the lowest Java version in use in any source set of any subproject of a repository. It is possible that, for example, the main source set of a project uses Java 8, but a test source set uses Java 17. In this case, the oldest Java version in use is Java 8.",1,Search,Java,,Basic building blocks for transforming Java code.,"[{""name"":""version"",""type"":""String"",""displayName"":""Java version"",""description"":""An exact version number or node-style semver selector used to select the version number."",""example"":""17.X"",""required"":true},{""name"":""checkTargetCompatibility"",""type"":""Boolean"",""displayName"":""Version check against target compatibility"",""description"":""The source and target compatibility versions can be different. This option allows you to check against the target compatibility version instead of the source compatibility version."",""example"":""17.X""}]",
9999
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindSecrets,Find plain text secrets,Find secrets stored in plain text in code.,3,Search,Java,,Basic building blocks for transforming Java code.,,
100+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ChangeMethodName,Change method name,Rename a method.,1,,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""org.mockito.Matchers anyVararg()"",""required"":true},{""name"":""newMethodName"",""type"":""String"",""displayName"":""New method name"",""description"":""The method name that will replace the existing name."",""example"":""any"",""required"":true},{""name"":""matchOverrides"",""type"":""Boolean"",""displayName"":""Match on overrides"",""description"":""When enabled, find methods that are overrides of the method pattern.""},{""name"":""ignoreDefinition"",""type"":""Boolean"",""displayName"":""Ignore type definition"",""description"":""When set to `true` the definition of the old type will be left untouched. This is useful when you're replacing usage of a class but don't want to rename it.""}]",
101+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ChangePackage,Rename package name,"A recipe that will rename a package name in package statements, imports, and fully-qualified types.",1,,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""oldPackageName"",""type"":""String"",""displayName"":""Old package name"",""description"":""The package name to replace."",""example"":""com.yourorg.foo"",""required"":true},{""name"":""newPackageName"",""type"":""String"",""displayName"":""New package name"",""description"":""New package name to replace the old package name with."",""example"":""com.yourorg.bar"",""required"":true},{""name"":""recursive"",""type"":""Boolean"",""displayName"":""Recursive"",""description"":""Recursively change subpackage names""}]",
102+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ChangeType,Change type,Change a given type to another.,1,,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""oldFullyQualifiedTypeName"",""type"":""String"",""displayName"":""Old fully-qualified type name"",""description"":""Fully-qualified class name of the original type."",""example"":""org.junit.Assume"",""required"":true},{""name"":""newFullyQualifiedTypeName"",""type"":""String"",""displayName"":""New fully-qualified type name"",""description"":""Fully-qualified class name of the replacement type, or the name of a primitive such as \""int\"". The `OuterClassName$NestedClassName` naming convention should be used for nested classes."",""example"":""org.junit.jupiter.api.Assumptions"",""required"":true},{""name"":""ignoreDefinition"",""type"":""Boolean"",""displayName"":""Ignore type definition"",""description"":""When set to `true` the definition of the old type will be left untouched. This is useful when you're replacing usage of a class but don't want to rename it.""}]",
103+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.DeleteMethodArgument,Delete method argument,Delete an argument from method invocations.,1,,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""com.yourorg.A foo(int, int)"",""required"":true},{""name"":""argumentIndex"",""type"":""int"",""displayName"":""Argument index"",""description"":""A zero-based index that indicates which argument will be removed from the method invocation."",""example"":""0"",""required"":true,""value"":0}]",
104+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ReorderMethodArguments,Reorder method arguments,Reorder method arguments into the specified order.,1,,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""com.yourorg.A foo(String, Integer, Integer)"",""required"":true},{""name"":""newParameterNames"",""type"":""String[]"",""displayName"":""New parameter names"",""description"":""An array of parameter names that indicates the new order in which those arguments should be arranged."",""example"":""[foo, bar, baz]"",""required"":true},{""name"":""oldParameterNames"",""type"":""String[]"",""displayName"":""Old parameter names"",""description"":""If the original method signature is not type-attributed, this is an optional list that indicates the original order in which the arguments were arranged."",""example"":""[baz, bar, foo]""},{""name"":""ignoreDefinition"",""type"":""Boolean"",""displayName"":""Ignore type definition"",""description"":""When set to `true` the definition of the old type will be left untouched. This is useful when you're replacing usage of a class but don't want to rename it.""},{""name"":""matchOverrides"",""type"":""Boolean"",""displayName"":""Match on overrides"",""description"":""When enabled, find methods that are overrides of the method pattern.""}]",
105+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindMethods,Find method usages,Find method calls by pattern.,1,Search,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""java.util.List add(..)"",""required"":true},{""name"":""matchOverrides"",""type"":""Boolean"",""displayName"":""Match on overrides"",""description"":""When enabled, find methods that are overrides of the method pattern.""}]","[{""name"":""org.openrewrite.java.table.MethodCalls"",""displayName"":""Method calls"",""description"":""The text of matching method invocations."",""columns"":[{""name"":""sourceFile"",""type"":""String"",""displayName"":""Source file"",""description"":""The source file that the method call occurred in.""},{""name"":""method"",""type"":""String"",""displayName"":""Method call"",""description"":""The text of the method call.""},{""name"":""className"",""type"":""String"",""displayName"":""Class name"",""description"":""The class name of the method call.""},{""name"":""methodName"",""type"":""String"",""displayName"":""Method name"",""description"":""The method name of the method call.""},{""name"":""argumentTypes"",""type"":""String"",""displayName"":""Argument types"",""description"":""The argument types of the method call.""}]}]"
106+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindTypes,Find types,Find type references by name.,1,Search,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""fullyQualifiedTypeName"",""type"":""String"",""displayName"":""Fully-qualified type name"",""description"":""A fully-qualified type name, that is used to find matching type references. Supports glob expressions. `java..*` finds every type from every subpackage of the `java` package."",""example"":""java.util.List"",""required"":true},{""name"":""checkAssignability"",""type"":""Boolean"",""displayName"":""Check for assignability"",""description"":""When enabled, find type references that are assignable to the provided type.""}]","[{""name"":""org.openrewrite.java.table.TypeUses"",""displayName"":""Type uses"",""description"":""The source code of matching type uses."",""columns"":[{""name"":""sourceFile"",""type"":""String"",""displayName"":""Source file"",""description"":""The source file that the method call occurred in.""},{""name"":""code"",""type"":""String"",""displayName"":""Source"",""description"":""The source code of the type use.""},{""name"":""concreteType"",""type"":""String"",""displayName"":""Concrete type"",""description"":""The concrete type in use, which may be a subtype of a searched type.""}]}]"

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {MarkPrimitiveTypes} from "./mark-primitive-types";
2727
import {MarkClassTypes} from "./mark-class-types";
2828
import {ScanningEditor} from "./scanning-editor";
2929
import {ReplaceAssignment} from "./replace-assignment";
30+
import {JavaChangeMethodName} from "./java-change-method-name";
3031

3132
export async function activate(marketplace: RecipeMarketplace): Promise<void> {
3233
await marketplace.install(ChangeText, JavaScript);
@@ -42,4 +43,5 @@ export async function activate(marketplace: RecipeMarketplace): Promise<void> {
4243
await marketplace.install(MarkClassTypes, JavaScript);
4344
await marketplace.install(ScanningEditor, JavaScript);
4445
await marketplace.install(ReplaceAssignment, JavaScript);
46+
await marketplace.install(JavaChangeMethodName, JavaScript);
4547
}

0 commit comments

Comments
 (0)