@openrewrite/rewrite 8.67.0-20251106-140126 → 8.67.0-20251107-071946

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 (39) 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 +34 -8
  10. package/dist/javascript/templating/engine.d.ts.map +1 -1
  11. package/dist/javascript/templating/engine.js +317 -60
  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/template.d.ts +9 -2
  20. package/dist/javascript/templating/template.d.ts.map +1 -1
  21. package/dist/javascript/templating/template.js +37 -0
  22. package/dist/javascript/templating/template.js.map +1 -1
  23. package/dist/javascript/templating/types.d.ts +26 -11
  24. package/dist/javascript/templating/types.d.ts.map +1 -1
  25. package/dist/javascript/templating/utils.d.ts +41 -22
  26. package/dist/javascript/templating/utils.d.ts.map +1 -1
  27. package/dist/javascript/templating/utils.js +111 -76
  28. package/dist/javascript/templating/utils.js.map +1 -1
  29. package/dist/version.txt +1 -1
  30. package/package.json +3 -1
  31. package/src/java/tree.ts +2 -0
  32. package/src/javascript/assertions.ts +1 -1
  33. package/src/javascript/format.ts +1 -1
  34. package/src/javascript/templating/engine.ts +376 -54
  35. package/src/javascript/templating/pattern.ts +55 -322
  36. package/src/javascript/templating/placeholder-replacement.ts +1 -1
  37. package/src/javascript/templating/template.ts +57 -3
  38. package/src/javascript/templating/types.ts +27 -11
  39. package/src/javascript/templating/utils.ts +113 -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,85 @@ 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(',');
49
+ export class LRUCache<K, V> {
50
+ private cache = new Map<K, V>();
75
51
 
76
- // Context statements
77
- const contextKey = contextStatements.join(';');
52
+ constructor(private maxSize: number) {}
78
53
 
79
- // Dependencies
80
- const depsKey = JSON.stringify(dependencies || {});
81
-
82
- return `${templateKey}::${capturesKey}::${contextKey}::${depsKey}`;
54
+ get(key: K): V | undefined {
55
+ const value = this.cache.get(key);
56
+ if (value !== undefined) {
57
+ // Move to end (most recently used)
58
+ this.cache.delete(key);
59
+ this.cache.set(key, value);
60
+ }
61
+ return value;
83
62
  }
84
63
 
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);
64
+ set(key: K, value: V): void {
65
+ // Remove if exists (to update position)
66
+ this.cache.delete(key);
95
67
 
96
- let cu = this.cache.get(key);
97
- if (cu) {
98
- return cu;
99
- }
68
+ // Add to end
69
+ this.cache.set(key, value);
100
70
 
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);
71
+ // Evict oldest if over capacity
72
+ if (this.cache.size > this.maxSize) {
73
+ const iterator = this.cache.keys();
74
+ const firstEntry = iterator.next();
75
+ if (!firstEntry.done) {
76
+ this.cache.delete(firstEntry.value);
77
+ }
107
78
  }
108
-
109
- // Prepend context statements for type attribution context
110
- const fullTemplateString = contextStatements.length > 0
111
- ? contextStatements.join('\n') + '\n' + templateString
112
- : templateString;
113
-
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;
122
-
123
- this.cache.set(key, cu);
124
- return cu;
125
79
  }
126
80
 
127
- /**
128
- * Clears the cache.
129
- */
130
81
  clear(): void {
131
82
  this.cache.clear();
132
83
  }
133
84
  }
134
85
 
135
- // Global cache instance
136
- export const templateCache = new TemplateCache();
86
+ /**
87
+ * Shared global LRU cache for both pattern and template ASTs.
88
+ * When pattern and template code is identical, they share the same cached AST.
89
+ * This mirrors JavaTemplate's unified approach in the Java implementation.
90
+ * Bounded to 100 entries using LRU eviction.
91
+ */
92
+ export const globalAstCache = new LRUCache<string, J>(100);
93
+
94
+ /**
95
+ * Generates a cache key for template/pattern processing.
96
+ * Used by both Pattern and Template for consistent cache key generation.
97
+ *
98
+ * @param templateParts The template string parts
99
+ * @param itemsKey String representing the captures/parameters (comma-separated)
100
+ * @param contextStatements Context declarations
101
+ * @param dependencies NPM dependencies
102
+ * @returns A cache key string
103
+ */
104
+ export function generateCacheKey(
105
+ templateParts: string[] | TemplateStringsArray,
106
+ itemsKey: string,
107
+ contextStatements: string[],
108
+ dependencies: Record<string, string>
109
+ ): string {
110
+ return [
111
+ Array.from(templateParts).join('|'),
112
+ itemsKey,
113
+ contextStatements.join(';'),
114
+ JSON.stringify(dependencies || {})
115
+ ].join('::');
116
+ }
137
117
 
138
118
  /**
139
119
  * Marker that stores capture metadata on pattern AST nodes.
@@ -262,4 +242,56 @@ export class PlaceholderUtils {
262
242
  }
263
243
  return undefined;
264
244
  }
245
+
246
+ /**
247
+ * Extracts the relevant AST node from a wrapper function.
248
+ * Used by both pattern and template processors to intelligently extract
249
+ * code from `function __WRAPPER__() { code }` wrappers.
250
+ *
251
+ * @param lastStatement The last statement from the compilation unit
252
+ * @param contextName Context name for error messages (e.g., 'Pattern', 'Template')
253
+ * @returns The extracted AST node
254
+ */
255
+ static extractFromWrapper(lastStatement: J, contextName: string): J {
256
+ let extracted: J;
257
+
258
+ // Since we always wrap in function __WRAPPER__() { code }, look for it
259
+ if (lastStatement.kind === J.Kind.MethodDeclaration) {
260
+ const method = lastStatement as J.MethodDeclaration;
261
+ if (method.name?.simpleName === WRAPPER_FUNCTION_NAME && method.body) {
262
+ const body = method.body;
263
+
264
+ // Intelligently extract based on what's in the function body
265
+ if (body.statements.length === 0) {
266
+ throw new Error(`${contextName} function body is empty`);
267
+ } else if (body.statements.length === 1) {
268
+ const stmt = body.statements[0].element;
269
+
270
+ // Single expression statement → extract the expression
271
+ if (stmt.kind === JS.Kind.ExpressionStatement) {
272
+ extracted = (stmt as JS.ExpressionStatement).expression;
273
+ }
274
+ // Single block statement → keep the block
275
+ else if (stmt.kind === J.Kind.Block) {
276
+ extracted = stmt;
277
+ }
278
+ // Other single statement → keep it
279
+ else {
280
+ extracted = stmt;
281
+ }
282
+ } else {
283
+ // Multiple statements → keep the block
284
+ extracted = body;
285
+ }
286
+ } else {
287
+ // Not our wrapper function
288
+ extracted = lastStatement;
289
+ }
290
+ } else {
291
+ // Shouldn't happen with our wrapping strategy, but handle it
292
+ extracted = lastStatement;
293
+ }
294
+
295
+ return extracted;
296
+ }
265
297
  }