@openrewrite/rewrite 8.67.0-20251106-160325 → 8.67.0-20251107-103550

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 (44) hide show
  1. package/dist/java/tree.d.ts +2 -0
  2. package/dist/java/tree.d.ts.map +1 -1
  3. package/dist/java/tree.js +5 -1
  4. package/dist/java/tree.js.map +1 -1
  5. package/dist/javascript/assertions.js +2 -2
  6. package/dist/javascript/assertions.js.map +1 -1
  7. package/dist/javascript/format.js +1 -1
  8. package/dist/javascript/format.js.map +1 -1
  9. package/dist/javascript/templating/engine.d.ts +41 -25
  10. package/dist/javascript/templating/engine.d.ts.map +1 -1
  11. package/dist/javascript/templating/engine.js +378 -92
  12. package/dist/javascript/templating/engine.js.map +1 -1
  13. package/dist/javascript/templating/pattern.d.ts +11 -0
  14. package/dist/javascript/templating/pattern.d.ts.map +1 -1
  15. package/dist/javascript/templating/pattern.js +36 -295
  16. package/dist/javascript/templating/pattern.js.map +1 -1
  17. package/dist/javascript/templating/placeholder-replacement.d.ts +1 -1
  18. package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
  19. package/dist/javascript/templating/rewrite.d.ts +17 -11
  20. package/dist/javascript/templating/rewrite.d.ts.map +1 -1
  21. package/dist/javascript/templating/rewrite.js +17 -11
  22. package/dist/javascript/templating/rewrite.js.map +1 -1
  23. package/dist/javascript/templating/template.d.ts +17 -3
  24. package/dist/javascript/templating/template.d.ts.map +1 -1
  25. package/dist/javascript/templating/template.js +45 -5
  26. package/dist/javascript/templating/template.js.map +1 -1
  27. package/dist/javascript/templating/types.d.ts +36 -13
  28. package/dist/javascript/templating/types.d.ts.map +1 -1
  29. package/dist/javascript/templating/utils.d.ts +41 -22
  30. package/dist/javascript/templating/utils.d.ts.map +1 -1
  31. package/dist/javascript/templating/utils.js +111 -76
  32. package/dist/javascript/templating/utils.js.map +1 -1
  33. package/dist/version.txt +1 -1
  34. package/package.json +3 -1
  35. package/src/java/tree.ts +2 -0
  36. package/src/javascript/assertions.ts +1 -1
  37. package/src/javascript/format.ts +1 -1
  38. package/src/javascript/templating/engine.ts +439 -105
  39. package/src/javascript/templating/pattern.ts +55 -322
  40. package/src/javascript/templating/placeholder-replacement.ts +1 -1
  41. package/src/javascript/templating/rewrite.ts +17 -11
  42. package/src/javascript/templating/template.ts +66 -11
  43. package/src/javascript/templating/types.ts +37 -13
  44. package/src/javascript/templating/utils.ts +114 -81
@@ -13,15 +13,12 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import ts from 'typescript';
17
16
  import {Cursor} from '../..';
18
17
  import {J} from '../../java';
19
18
  import {JS} from '../index';
20
- import {JavaScriptParser} from '../parser';
21
- import {DependencyWorkspace} from '../dependency-workspace';
22
19
  import {Marker, Markers} from '../../markers';
23
20
  import {randomId} from '../../uuid';
24
- import {VariadicOptions, Capture, Any} from './types';
21
+ import {VariadicOptions} from './types';
25
22
 
26
23
  /**
27
24
  * Internal storage value type for pattern match captures.
@@ -38,102 +35,86 @@ export type CaptureStorageValue = J | J.RightPadded<J> | J[] | J.RightPadded<J>[
38
35
  export const WRAPPERS_MAP_SYMBOL = Symbol('wrappersMap');
39
36
 
40
37
  /**
41
- * Module-level TypeScript sourceFileCache for template parsing.
38
+ * Shared wrapper function name used by both patterns and templates.
39
+ * Using the same name allows cache sharing when pattern and template code is identical.
42
40
  */
43
- let templateSourceFileCache: Map<string, ts.SourceFile> | undefined;
41
+ export const WRAPPER_FUNCTION_NAME = '__WRAPPER__';
44
42
 
45
43
  /**
46
- * Configure the sourceFileCache used for template parsing.
44
+ * Simple LRU (Least Recently Used) cache implementation using Map's insertion order.
45
+ * JavaScript Map maintains insertion order, so the first entry is the oldest.
47
46
  *
48
- * @param cache The sourceFileCache to use, or undefined to disable caching
47
+ * Used by both Pattern and Template caching to provide bounded memory usage.
49
48
  */
50
- export function setTemplateSourceFileCache(cache?: Map<string, ts.SourceFile>): void {
51
- templateSourceFileCache = cache;
52
- }
53
-
54
- /**
55
- * Cache for compiled templates and patterns.
56
- * Stores parsed ASTs to avoid expensive re-parsing and dependency resolution.
57
- */
58
- export class TemplateCache {
59
- private cache = new Map<string, JS.CompilationUnit>();
60
-
61
- /**
62
- * Generates a cache key from template string, captures, and options.
63
- */
64
- private generateKey(
65
- templateString: string,
66
- captures: (Capture | Any<any>)[],
67
- contextStatements: string[],
68
- dependencies: Record<string, string>
69
- ): string {
70
- // Use the actual template string (with placeholders) as the primary key
71
- const templateKey = templateString;
72
-
73
- // Capture names
74
- const capturesKey = captures.map(c => c.getName()).join(',');
75
-
76
- // Context statements
77
- const contextKey = contextStatements.join(';');
78
-
79
- // Dependencies
80
- const depsKey = JSON.stringify(dependencies || {});
49
+ export class LRUCache<K, V> {
50
+ private cache = new Map<K, V>();
81
51
 
82
- return `${templateKey}::${capturesKey}::${contextKey}::${depsKey}`;
52
+ constructor(private maxSize: number) {
83
53
  }
84
54
 
85
- /**
86
- * Gets a cached compilation unit or creates and caches a new one.
87
- */
88
- async getOrParse(
89
- templateString: string,
90
- captures: (Capture | Any)[],
91
- contextStatements: string[],
92
- dependencies: Record<string, string>
93
- ): Promise<JS.CompilationUnit> {
94
- const key = this.generateKey(templateString, captures, contextStatements, dependencies);
95
-
96
- let cu = this.cache.get(key);
97
- if (cu) {
98
- return cu;
99
- }
100
-
101
- // Create workspace if dependencies are provided
102
- // DependencyWorkspace has its own cache, so multiple templates with
103
- // the same dependencies will automatically share the same workspace
104
- let workspaceDir: string | undefined;
105
- if (dependencies && Object.keys(dependencies).length > 0) {
106
- workspaceDir = await DependencyWorkspace.getOrCreateWorkspace(dependencies);
55
+ get(key: K): V | undefined {
56
+ const value = this.cache.get(key);
57
+ if (value !== undefined) {
58
+ // Move to end (most recently used)
59
+ this.cache.delete(key);
60
+ this.cache.set(key, value);
107
61
  }
62
+ return value;
63
+ }
108
64
 
109
- // Prepend context statements for type attribution context
110
- const fullTemplateString = contextStatements.length > 0
111
- ? contextStatements.join('\n') + '\n' + templateString
112
- : templateString;
65
+ set(key: K, value: V): void {
66
+ // Remove if exists (to update position)
67
+ this.cache.delete(key);
113
68
 
114
- // Parse and cache (workspace only needed during parsing)
115
- // Use templateSourceFileCache if configured for ~3.2x speedup on dependency file parsing
116
- const parser = new JavaScriptParser({
117
- relativeTo: workspaceDir,
118
- sourceFileCache: templateSourceFileCache
119
- });
120
- const parseGenerator = parser.parse({text: fullTemplateString, sourcePath: 'template.ts'});
121
- cu = (await parseGenerator.next()).value as JS.CompilationUnit;
69
+ // Add to end
70
+ this.cache.set(key, value);
122
71
 
123
- this.cache.set(key, cu);
124
- return cu;
72
+ // Evict oldest if over capacity
73
+ if (this.cache.size > this.maxSize) {
74
+ const iterator = this.cache.keys();
75
+ const firstEntry = iterator.next();
76
+ if (!firstEntry.done) {
77
+ this.cache.delete(firstEntry.value);
78
+ }
79
+ }
125
80
  }
126
81
 
127
- /**
128
- * Clears the cache.
129
- */
130
82
  clear(): void {
131
83
  this.cache.clear();
132
84
  }
133
85
  }
134
86
 
135
- // Global cache instance
136
- export const templateCache = new TemplateCache();
87
+ /**
88
+ * Shared global LRU cache for both pattern and template ASTs.
89
+ * When pattern and template code is identical, they share the same cached AST.
90
+ * This mirrors JavaTemplate's unified approach in the Java implementation.
91
+ * Bounded to 100 entries using LRU eviction.
92
+ */
93
+ export const globalAstCache = new LRUCache<string, J>(100);
94
+
95
+ /**
96
+ * Generates a cache key for template/pattern processing.
97
+ * Used by both Pattern and Template for consistent cache key generation.
98
+ *
99
+ * @param templateParts The template string parts
100
+ * @param itemsKey String representing the captures/parameters (comma-separated)
101
+ * @param contextStatements Context declarations
102
+ * @param dependencies NPM dependencies
103
+ * @returns A cache key string
104
+ */
105
+ export function generateCacheKey(
106
+ templateParts: string[] | TemplateStringsArray,
107
+ itemsKey: string,
108
+ contextStatements: string[],
109
+ dependencies: Record<string, string>
110
+ ): string {
111
+ return [
112
+ Array.from(templateParts).join('|'),
113
+ itemsKey,
114
+ contextStatements.join(';'),
115
+ JSON.stringify(dependencies)
116
+ ].join('::');
117
+ }
137
118
 
138
119
  /**
139
120
  * Marker that stores capture metadata on pattern AST nodes.
@@ -262,4 +243,56 @@ export class PlaceholderUtils {
262
243
  }
263
244
  return undefined;
264
245
  }
246
+
247
+ /**
248
+ * Extracts the relevant AST node from a wrapper function.
249
+ * Used by both pattern and template processors to intelligently extract
250
+ * code from `function __WRAPPER__() { code }` wrappers.
251
+ *
252
+ * @param lastStatement The last statement from the compilation unit
253
+ * @param contextName Context name for error messages (e.g., 'Pattern', 'Template')
254
+ * @returns The extracted AST node
255
+ */
256
+ static extractFromWrapper(lastStatement: J, contextName: string): J {
257
+ let extracted: J;
258
+
259
+ // Since we always wrap in function __WRAPPER__() { code }, look for it
260
+ if (lastStatement.kind === J.Kind.MethodDeclaration) {
261
+ const method = lastStatement as J.MethodDeclaration;
262
+ if (method.name?.simpleName === WRAPPER_FUNCTION_NAME && method.body) {
263
+ const body = method.body;
264
+
265
+ // Intelligently extract based on what's in the function body
266
+ if (body.statements.length === 0) {
267
+ throw new Error(`${contextName} function body is empty`);
268
+ } else if (body.statements.length === 1) {
269
+ const stmt = body.statements[0].element;
270
+
271
+ // Single expression statement → extract the expression
272
+ if (stmt.kind === JS.Kind.ExpressionStatement) {
273
+ extracted = (stmt as JS.ExpressionStatement).expression;
274
+ }
275
+ // Single block statement → keep the block
276
+ else if (stmt.kind === J.Kind.Block) {
277
+ extracted = stmt;
278
+ }
279
+ // Other single statement → keep it
280
+ else {
281
+ extracted = stmt;
282
+ }
283
+ } else {
284
+ // Multiple statements → keep the block
285
+ extracted = body;
286
+ }
287
+ } else {
288
+ // Not our wrapper function
289
+ extracted = lastStatement;
290
+ }
291
+ } else {
292
+ // Shouldn't happen with our wrapping strategy, but handle it
293
+ extracted = lastStatement;
294
+ }
295
+
296
+ return extracted;
297
+ }
265
298
  }