@openrewrite/rewrite 8.91.3 → 8.91.5

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 (47) hide show
  1. package/dist/javascript/index.d.ts +1 -1
  2. package/dist/javascript/index.d.ts.map +1 -1
  3. package/dist/javascript/index.js +2 -1
  4. package/dist/javascript/index.js.map +1 -1
  5. package/dist/javascript/parser.d.ts +3 -2
  6. package/dist/javascript/parser.d.ts.map +1 -1
  7. package/dist/javascript/parser.js +154 -114
  8. package/dist/javascript/parser.js.map +1 -1
  9. package/dist/javascript/remove-import.d.ts +0 -18
  10. package/dist/javascript/remove-import.d.ts.map +1 -1
  11. package/dist/javascript/remove-import.js +50 -438
  12. package/dist/javascript/remove-import.js.map +1 -1
  13. package/dist/javascript/scope.d.ts +6 -0
  14. package/dist/javascript/scope.d.ts.map +1 -1
  15. package/dist/javascript/scope.js +87 -7
  16. package/dist/javascript/scope.js.map +1 -1
  17. package/dist/javascript/tsconfig.d.ts +32 -0
  18. package/dist/javascript/tsconfig.d.ts.map +1 -0
  19. package/dist/javascript/tsconfig.js +205 -0
  20. package/dist/javascript/tsconfig.js.map +1 -0
  21. package/dist/rewrite-javascript-version.txt +1 -1
  22. package/dist/rpc/index.d.ts +1 -1
  23. package/dist/rpc/index.d.ts.map +1 -1
  24. package/dist/rpc/index.js +2 -1
  25. package/dist/rpc/index.js.map +1 -1
  26. package/dist/rpc/java-recipe.d.ts +32 -22
  27. package/dist/rpc/java-recipe.d.ts.map +1 -1
  28. package/dist/rpc/java-recipe.js +91 -30
  29. package/dist/rpc/java-recipe.js.map +1 -1
  30. package/dist/rpc/request/prepare-recipe.d.ts +10 -14
  31. package/dist/rpc/request/prepare-recipe.d.ts.map +1 -1
  32. package/dist/rpc/request/prepare-recipe.js +53 -88
  33. package/dist/rpc/request/prepare-recipe.js.map +1 -1
  34. package/dist/rpc/rewrite-rpc.d.ts +5 -12
  35. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  36. package/dist/rpc/rewrite-rpc.js +5 -12
  37. package/dist/rpc/rewrite-rpc.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/javascript/index.ts +1 -1
  40. package/src/javascript/parser.ts +104 -58
  41. package/src/javascript/remove-import.ts +50 -424
  42. package/src/javascript/scope.ts +103 -7
  43. package/src/javascript/tsconfig.ts +177 -0
  44. package/src/rpc/index.ts +1 -1
  45. package/src/rpc/java-recipe.ts +107 -33
  46. package/src/rpc/request/prepare-recipe.ts +55 -90
  47. package/src/rpc/rewrite-rpc.ts +5 -12
@@ -19,6 +19,7 @@ import {Recipe, RecipeDescriptor, ScanningRecipe} from "../../recipe";
19
19
  import {SnowflakeId} from "@akashrajpurohit/snowflake-id";
20
20
  import {Check, CheckArg, CompositePrecondition, RecipeRef} from "../../preconditions";
21
21
  import {RpcRecipe} from "../recipe";
22
+ import {DelegatingRecipe} from "../java-recipe";
22
23
  import {TreeVisitor} from "../../visitor";
23
24
  import {ExecutionContext} from "../../execution";
24
25
  import {withMetrics} from "./metrics";
@@ -42,22 +43,10 @@ export class PrepareRecipe {
42
43
  context.target = request.id;
43
44
  const recipeCtor = marketplace.findRecipe(request.id);
44
45
  if (!recipeCtor) {
45
- // A miss means the host owns this recipe (e.g. a sub-recipe that delegates to
46
- // a Java recipe, produced by prepareJavaRecipe — an RpcRecipe hosted on the peer
47
- // with no no-arg constructor to register here). Tell the host to resolve the id
48
- // locally via delegatesTo rather than failing with "Could not find recipe ...".
49
- const id = snowflake.generate();
50
- return {
51
- id: id,
52
- descriptor: PrepareRecipe.delegateDescriptor(request.id),
53
- editVisitor: `edit:${id}`,
54
- editPreconditions: [],
55
- scanPreconditions: [],
56
- delegatesTo: {
57
- recipeName: request.id,
58
- options: request.options ?? {}
59
- }
60
- };
46
+ // A miss means the host owns this recipe (e.g. a Java-delegate child a host
47
+ // re-prepares by name), so answer with a delegatesTo stand-in for it to resolve.
48
+ return await PrepareRecipe.prepareInstance(new DelegatingRecipe(request.id, request.options ?? {}),
49
+ snowflake, preparedRecipes, marketplace);
61
50
  }
62
51
  if (!recipeCtor[1]) {
63
52
  throw new Error(`Recipe ${request.id} was installed without a constructor`);
@@ -70,44 +59,30 @@ export class PrepareRecipe {
70
59
  }
71
60
 
72
61
  /**
73
- * Minimal stand-in descriptor for a recipe the host will resolve locally via
74
- * {@link PrepareRecipeResponse.delegatesTo}. The host reads only `delegatesTo` for
75
- * delegated recipes and ignores this descriptor, but the response type requires one.
76
- */
77
- private static delegateDescriptor(name: string): RecipeDescriptor {
78
- return {
79
- name: name,
80
- displayName: name,
81
- instanceName: name,
82
- description: "",
83
- tags: [],
84
- estimatedEffortPerOccurrence: 5,
85
- options: [],
86
- preconditions: [],
87
- recipeList: [],
88
- dataTables: [],
89
- maintainers: [],
90
- contributors: [],
91
- examples: []
92
- };
93
- }
94
-
95
- /**
96
- * Prepares a recipe instance and recursively its whole child tree, registering every node in
97
- * {@code preparedRecipes} and returning the prepared response with {@code recipeList} populated,
98
- * so the host builds the tree locally instead of a PrepareRecipe round-trip per child.
99
- *
100
- * Mirrors the C# server's PrepareInstance. Required options are validated as each node is
101
- * prepared (covering the whole tree). A child hosted on the peer (an {@link RpcRecipe}, e.g. from
102
- * prepareJavaRecipe) carries only {@code delegatesTo} for the host to resolve locally; every other
103
- * child carries its own {@code recipeList}. Delegating recipes forward validation to the recipe
104
- * they delegate to, so they are not validated here.
62
+ * Prepares a recipe and, recursively, its whole child tree, registering every node in
63
+ * {@code preparedRecipes}; the response carries {@code recipeList} so the host builds the tree
64
+ * without a PrepareRecipe round-trip per child (mirrors the C# server's PrepareInstance).
65
+ * Required options are validated per node. A stand-in for a host recipe (see {@link #delegatesTo})
66
+ * carries only {@code delegatesTo} and leaves validation to the host.
105
67
  */
106
68
  private static async prepareInstance(recipe: Recipe,
107
69
  snowflake: ReturnType<typeof SnowflakeId>,
108
70
  preparedRecipes: Map<String, Recipe>,
109
71
  marketplace: RecipeMarketplace): Promise<PrepareRecipeResponse> {
110
72
  const id = snowflake.generate();
73
+ const delegatesTo = PrepareRecipe.delegatesTo(recipe);
74
+ if (delegatesTo) {
75
+ // Registered so a peer that visits `edit:<id>` instead of honouring delegatesTo reaches it.
76
+ preparedRecipes.set(id, recipe);
77
+ return {
78
+ id: id,
79
+ descriptor: await recipe.descriptor(),
80
+ editVisitor: `edit:${id}`,
81
+ editPreconditions: [],
82
+ scanPreconditions: [],
83
+ delegatesTo
84
+ };
85
+ }
111
86
 
112
87
  const editPreconditions: Precondition[] = [];
113
88
  recipe = await PrepareRecipe.optimizePreconditions(recipe, "edit", editPreconditions);
@@ -117,13 +92,9 @@ export class PrepareRecipe {
117
92
  preparedRecipes.set(id, recipe);
118
93
 
119
94
  const descriptor = await recipe.descriptor();
120
- const isDelegating = 'javaRecipeName' in recipe;
121
-
122
- if (!isDelegating) {
123
- for (const option of descriptor.options) {
124
- if ((option.required ?? true) && option.value == null) {
125
- throw new Error(`Missing required option \`${option.name}\` for recipe \`${descriptor.name}\`.`);
126
- }
95
+ for (const option of descriptor.options) {
96
+ if ((option.required ?? true) && option.value == null) {
97
+ throw new Error(`Missing required option \`${option.name}\` for recipe \`${descriptor.name}\`.`);
127
98
  }
128
99
  }
129
100
 
@@ -136,49 +107,43 @@ export class PrepareRecipe {
136
107
  scanPreconditions: scanPreconditions
137
108
  };
138
109
 
139
- if (isDelegating) {
140
- response.delegatesTo = {
141
- recipeName: (recipe as any).javaRecipeName,
142
- options: (recipe as any).delegatesToOptions ?? {}
143
- };
144
- return response;
145
- }
146
-
147
110
  const childResponses: PrepareRecipeResponse[] = [];
148
111
  for (const child of await recipe.recipeList()) {
149
112
  if (child instanceof RpcRecipe) {
150
- // Hosted on the peer: emit delegatesTo (its name + the options its parent set) so the
151
- // host resolves it locally, matching how the by-name fallback used to resolve it.
152
- const childId = snowflake.generate();
153
- const childDescriptor = await child.descriptor();
113
+ // Already prepared on the host: hand it back by name with the options its parent set.
154
114
  const options: Record<string, any> = {};
155
- for (const option of childDescriptor.options) {
115
+ for (const option of (await child.descriptor()).options) {
156
116
  if (option.value != null) {
157
117
  options[option.name] = option.value;
158
118
  }
159
119
  }
160
- childResponses.push({
161
- id: childId,
162
- descriptor: PrepareRecipe.delegateDescriptor(child.name),
163
- editVisitor: `edit:${childId}`,
164
- editPreconditions: [],
165
- scanPreconditions: [],
166
- delegatesTo: {recipeName: child.name, options}
167
- });
168
- } else {
169
- // Register a child that was instantiated in recipeList() but never installed, so a peer
170
- // that re-prepares children by name (rather than consuming recipeList) can still find it.
171
- if (!marketplace.findRecipe(child.name)) {
172
- await marketplace.install(child.constructor as any, []);
173
- }
174
- childResponses.push(await PrepareRecipe.prepareInstance(child, snowflake, preparedRecipes, marketplace));
120
+ childResponses.push(await PrepareRecipe.prepareInstance(new DelegatingRecipe(child.name, options),
121
+ snowflake, preparedRecipes, marketplace));
122
+ continue;
175
123
  }
124
+ // Register a child that was instantiated in recipeList() but never installed, so a peer
125
+ // that re-prepares children by name (rather than consuming recipeList) can still find it.
126
+ if (!PrepareRecipe.delegatesTo(child) && !marketplace.findRecipe(child.name)) {
127
+ await marketplace.install(child.constructor as any, []);
128
+ }
129
+ childResponses.push(await PrepareRecipe.prepareInstance(child, snowflake, preparedRecipes, marketplace));
176
130
  }
177
131
  response.recipeList = childResponses;
178
132
 
179
133
  return response;
180
134
  }
181
135
 
136
+ /**
137
+ * The Java recipe {@code recipe} stands in for, or {@code undefined} for a recipe this process
138
+ * runs itself. Duck-typed on the fields {@link DelegatingRecipe} declares, so a recipe package
139
+ * that loaded its own copy of this module is recognised too.
140
+ */
141
+ private static delegatesTo(recipe: any): {recipeName: string, options: Record<string, any>} | undefined {
142
+ return recipe != null && typeof recipe.javaRecipeName === "string" ?
143
+ {recipeName: recipe.javaRecipeName, options: {...(recipe.delegatesToOptions ?? {})}} :
144
+ undefined;
145
+ }
146
+
182
147
  /**
183
148
  * For preconditions that can be evaluated on the remote peer, let the remote peer
184
149
  * evaluate them and know that we will only have to do the visit work if the
@@ -246,13 +211,13 @@ export class PrepareRecipe {
246
211
  }
247
212
  return {op: condition.op, operands};
248
213
  }
249
- // Common case: helpers like usesMethod / usesType return a lightweight
250
- // RecipeRef so the recipe author can declare a precondition without
251
- // firing an RPC at editor() time. The Java host's
252
- // PreparedRecipeCache.instantiateVisitor constructs the named recipe
253
- // via Jackson and uses its visitor.
254
- if (condition instanceof RecipeRef) {
255
- return {visitorName: condition.recipeName, visitorOptions: {...condition.options}};
214
+ // Common case: usesMethod / usesType return a lightweight RecipeRef, and
215
+ // prepareJavaRecipe a DelegatingRecipe, so a precondition is declared without
216
+ // firing an RPC at editor() time. The Java host's PreparedRecipeCache
217
+ // .instantiateVisitor constructs the named recipe via Jackson and uses its visitor.
218
+ const ref = condition instanceof RecipeRef ? condition : PrepareRecipe.delegatesTo(condition);
219
+ if (ref) {
220
+ return {visitorName: ref.recipeName, visitorOptions: {...ref.options}};
256
221
  }
257
222
  if (condition instanceof RpcRecipe) {
258
223
  return {visitorName: phase === "edit" ? condition.editVisitor : condition.scanVisitor!};
@@ -50,18 +50,11 @@ import {GetLanguages} from "./request/get-languages";
50
50
 
51
51
  export class RewriteRpc {
52
52
  /**
53
- * Key for the active {@link RewriteRpc} connection on {@link globalThis}.
54
- *
55
- * It deliberately lives on `globalThis` rather than as a `static` field:
56
- * a recipe package that bundles `@openrewrite/rewrite` (or resolves it from
57
- * its own `node_modules`) loads a *separate copy* of this module, with its
58
- * own class object and therefore its own statics. A `static` field set by
59
- * the host would be invisible to such a copy, so `RewriteRpc.get()` (e.g.
60
- * via `prepareJavaRecipe`) would return `undefined` and throw "no active
61
- * RewriteRpc connection" — surfacing during `InstallRecipes` as the
62
- * misleading "Ensure the constructor can be called without any arguments".
63
- * {@link Symbol.for} resolves to the same symbol across every module copy,
64
- * so all copies share the one active connection. See gh-7968.
53
+ * Key for the active {@link RewriteRpc} connection on {@link globalThis}. A recipe package that
54
+ * bundles `@openrewrite/rewrite` or resolves it from its own `node_modules` loads a separate copy
55
+ * of this module with its own class object and statics, so a `static` field set by the host would
56
+ * be invisible to it; {@link Symbol.for} resolves to the same symbol across every module copy, so
57
+ * all copies share the one active connection. See gh-7968.
65
58
  */
66
59
  private static readonly GLOBAL_KEY: symbol = Symbol.for("org.openrewrite.rpc.RewriteRpc.global");
67
60