@openrewrite/rewrite 8.88.0 → 8.88.2

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/node-resolution-result.js +18 -16
  2. package/dist/javascript/node-resolution-result.js.map +1 -1
  3. package/dist/javascript/package-exported-types.d.ts +13 -0
  4. package/dist/javascript/package-exported-types.d.ts.map +1 -0
  5. package/dist/javascript/package-exported-types.js +374 -0
  6. package/dist/javascript/package-exported-types.js.map +1 -0
  7. package/dist/javascript/type-mapping.d.ts +6 -0
  8. package/dist/javascript/type-mapping.d.ts.map +1 -1
  9. package/dist/javascript/type-mapping.js +16 -0
  10. package/dist/javascript/type-mapping.js.map +1 -1
  11. package/dist/rewrite-javascript-version.txt +1 -1
  12. package/dist/rpc/queue.d.ts +12 -0
  13. package/dist/rpc/queue.d.ts.map +1 -1
  14. package/dist/rpc/queue.js +35 -4
  15. package/dist/rpc/queue.js.map +1 -1
  16. package/dist/rpc/request/dependency-types.d.ts +15 -0
  17. package/dist/rpc/request/dependency-types.d.ts.map +1 -0
  18. package/dist/rpc/request/dependency-types.js +143 -0
  19. package/dist/rpc/request/dependency-types.js.map +1 -0
  20. package/dist/rpc/request/index.d.ts +1 -0
  21. package/dist/rpc/request/index.d.ts.map +1 -1
  22. package/dist/rpc/request/index.js +1 -0
  23. package/dist/rpc/request/index.js.map +1 -1
  24. package/dist/rpc/request/last-parsed-project.d.ts +3 -0
  25. package/dist/rpc/request/last-parsed-project.d.ts.map +1 -0
  26. package/dist/rpc/request/last-parsed-project.js +29 -0
  27. package/dist/rpc/request/last-parsed-project.js.map +1 -0
  28. package/dist/rpc/request/parse-project.d.ts.map +1 -1
  29. package/dist/rpc/request/parse-project.js +2 -0
  30. package/dist/rpc/request/parse-project.js.map +1 -1
  31. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  32. package/dist/rpc/rewrite-rpc.js +1 -0
  33. package/dist/rpc/rewrite-rpc.js.map +1 -1
  34. package/dist/run.d.ts.map +1 -1
  35. package/dist/run.js +39 -27
  36. package/dist/run.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/javascript/node-resolution-result.ts +18 -16
  39. package/src/javascript/package-exported-types.ts +351 -0
  40. package/src/javascript/type-mapping.ts +16 -0
  41. package/src/rpc/queue.ts +38 -4
  42. package/src/rpc/request/dependency-types.ts +128 -0
  43. package/src/rpc/request/index.ts +1 -0
  44. package/src/rpc/request/last-parsed-project.ts +27 -0
  45. package/src/rpc/request/parse-project.ts +2 -0
  46. package/src/rpc/rewrite-rpc.ts +2 -0
  47. package/src/run.ts +44 -23
package/src/run.ts CHANGED
@@ -40,28 +40,47 @@ export class Result {
40
40
  }
41
41
  }
42
42
 
43
- async function hasScanningRecipe(recipe: Recipe): Promise<boolean> {
43
+ /**
44
+ * Recipes commonly instantiate their sub-recipes inside `recipeList()`, so calling it more
45
+ * than once yields different instances. A scanning sub-recipe would then hold a different
46
+ * accumulator in every phase and for every source file. Resolving each recipe list once per
47
+ * run keeps sub-recipe identity, and therefore accumulator identity, stable.
48
+ */
49
+ type RecipeLists = WeakMap<Recipe, Recipe[]>;
50
+
51
+ async function subRecipes(recipe: Recipe, recipeLists: RecipeLists): Promise<Recipe[]> {
52
+ let resolved = recipeLists.get(recipe);
53
+ if (resolved === undefined) {
54
+ resolved = await recipe.recipeList();
55
+ recipeLists.set(recipe, resolved);
56
+ }
57
+ return resolved;
58
+ }
59
+
60
+ async function hasScanningRecipe(recipe: Recipe, recipeLists: RecipeLists): Promise<boolean> {
44
61
  if (recipe instanceof ScanningRecipe) return true;
45
- for (const item of (await recipe.recipeList())) {
46
- if (await hasScanningRecipe(item)) return true;
62
+ for (const item of (await subRecipes(recipe, recipeLists))) {
63
+ if (await hasScanningRecipe(item, recipeLists)) return true;
47
64
  }
48
65
  return false;
49
66
  }
50
67
 
51
- async function recurseRecipeList<T>(recipe: Recipe, initial: T, fn: (recipe: Recipe, t: T) => Promise<T | undefined>): Promise<T | undefined> {
68
+ async function recurseRecipeList<T>(recipe: Recipe, initial: T, recipeLists: RecipeLists, fn: (recipe: Recipe, t: T) => Promise<T | undefined>): Promise<T | undefined> {
52
69
  let t: T | undefined = await fn(recipe, initial);
53
- for (const subRecipe of await recipe.recipeList()) {
70
+ for (const subRecipe of await subRecipes(recipe, recipeLists)) {
54
71
  if (t === undefined) {
55
72
  return undefined;
56
73
  }
57
- t = await recurseRecipeList(subRecipe, t, fn);
74
+ t = await recurseRecipeList(subRecipe, t, recipeLists, fn);
58
75
  }
59
76
  return t;
60
77
  }
61
78
 
62
- async function recursiveOnComplete(recipe: Recipe, ctx: ExecutionContext): Promise<void> {
79
+ async function recursiveOnComplete(recipe: Recipe, ctx: ExecutionContext, recipeLists: RecipeLists): Promise<void> {
63
80
  await recipe.onComplete(ctx);
64
- (await recipe.recipeList()).forEach(r => recursiveOnComplete(r, ctx));
81
+ for (const subRecipe of await subRecipes(recipe, recipeLists)) {
82
+ await recursiveOnComplete(subRecipe, ctx, recipeLists);
83
+ }
65
84
  }
66
85
 
67
86
  export async function scheduleRun(recipe: Recipe, before: SourceFile[], ctx: ExecutionContext): Promise<RecipeRun> {
@@ -94,34 +113,38 @@ export async function* scheduleRunStreaming(
94
113
  onProgress?: ProgressCallback
95
114
  ): AsyncGenerator<Result, void, undefined> {
96
115
  const cursor = rootCursor();
97
- const isScanning = await hasScanningRecipe(recipe);
116
+ const recipeLists: RecipeLists = new WeakMap();
117
+ const isScanning = await hasScanningRecipe(recipe, recipeLists);
118
+ const knownTotal = Array.isArray(before) ? before.length : -1; // -1 = unknown total
98
119
 
99
120
  if (isScanning) {
100
121
  // For scanning recipes, pull files from the generator and scan them immediately.
101
122
  // Files are stored for the later edit phase.
102
123
  const files: SourceFile[] = [];
103
- const iterable = Array.isArray(before) ? before : before;
104
- const knownTotal = Array.isArray(before) ? before.length : -1; // -1 = unknown total
105
124
 
106
125
  // Phase 1: Pull files from generator and scan each immediately
107
126
  let scanCount = 0;
108
- for await (const b of iterable) {
127
+ for await (const b of before) {
109
128
  files.push(b);
110
129
  scanCount++;
111
130
  onProgress?.('scanning', scanCount, knownTotal, b.sourcePath);
112
131
 
113
- // Scan this file immediately
114
- await recurseRecipeList(recipe, b, async (recipe, b2) => {
132
+ // Scan this file immediately. Scanning must not modify the tree, so the visit
133
+ // result is discarded and the original file is passed on to the next recipe in
134
+ // the list. Returning `undefined` here would abort traversal of the remainder
135
+ // of the recipe list, leaving nested scanners unrun.
136
+ await recurseRecipeList(recipe, b, recipeLists, async (recipe, b2) => {
115
137
  if (recipe instanceof ScanningRecipe) {
116
- return (await recipe.scanner(recipe.accumulator(cursor, ctx))).visit(b2, ctx, cursor)
138
+ await (await recipe.scanner(recipe.accumulator(cursor, ctx))).visit(b2, ctx, cursor);
117
139
  }
140
+ return b2;
118
141
  });
119
142
  }
120
143
 
121
144
  const totalFiles = files.length;
122
145
 
123
146
  // Phase 2: Collect generated files
124
- const generated = (await recurseRecipeList(recipe, [] as SourceFile[], async (recipe, generated) => {
147
+ const generated = (await recurseRecipeList(recipe, [] as SourceFile[], recipeLists, async (recipe, generated) => {
125
148
  if (recipe instanceof ScanningRecipe) {
126
149
  generated.push(...await recipe.generate(recipe.accumulator(cursor, ctx), ctx));
127
150
  }
@@ -132,7 +155,7 @@ export async function* scheduleRunStreaming(
132
155
  for (let i = 0; i < files.length; i++) {
133
156
  const b = files[i];
134
157
  onProgress?.('processing', i + 1, totalFiles, b.sourcePath);
135
- const editedB = await recurseRecipeList(recipe, b, async (recipe, b2) => (await recipe.editor()).visit(b2, ctx, cursor));
158
+ const editedB = await recurseRecipeList(recipe, b, recipeLists, async (recipe, b2) => (await recipe.editor()).visit(b2, ctx, cursor));
136
159
  // Always yield a result so the caller knows when each file is processed
137
160
  yield new Result(b, editedB !== b ? editedB : b);
138
161
  // Clear array entry to allow GC to free memory for this file
@@ -141,24 +164,22 @@ export async function* scheduleRunStreaming(
141
164
 
142
165
  // Phase 4: Edit generated files and yield results
143
166
  for (const g of generated) {
144
- const editedG = await recurseRecipeList(recipe, g, async (recipe, g2) => (await recipe.editor()).visit(g2, ctx, cursor));
167
+ const editedG = await recurseRecipeList(recipe, g, recipeLists, async (recipe, g2) => (await recipe.editor()).visit(g2, ctx, cursor));
145
168
  if (editedG) {
146
169
  yield new Result(undefined, editedG);
147
170
  }
148
171
  }
149
172
  } else {
150
173
  // For non-scanning recipes, process files immediately as they come in
151
- const iterable = Array.isArray(before) ? before : before;
152
- const knownTotal = Array.isArray(before) ? before.length : -1; // -1 = unknown total
153
174
  let processCount = 0;
154
- for await (const b of iterable) {
175
+ for await (const b of before) {
155
176
  processCount++;
156
177
  onProgress?.('processing', processCount, knownTotal, b.sourcePath);
157
- const editedB = await recurseRecipeList(recipe, b, async (recipe, b2) => (await recipe.editor()).visit(b2, ctx, cursor));
178
+ const editedB = await recurseRecipeList(recipe, b, recipeLists, async (recipe, b2) => (await recipe.editor()).visit(b2, ctx, cursor));
158
179
  // Always yield a result so the caller knows when each file is processed
159
180
  yield new Result(b, editedB !== b ? editedB : b);
160
181
  }
161
182
  }
162
183
 
163
- await recursiveOnComplete(recipe, ctx);
184
+ await recursiveOnComplete(recipe, ctx, recipeLists);
164
185
  }