@openrewrite/rewrite 8.66.0 → 8.66.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 (113) hide show
  1. package/dist/java/tree.d.ts +10 -1
  2. package/dist/java/tree.d.ts.map +1 -1
  3. package/dist/java/tree.js +21 -5
  4. package/dist/java/tree.js.map +1 -1
  5. package/dist/java/type-visitor.d.ts +1 -1
  6. package/dist/java/type-visitor.d.ts.map +1 -1
  7. package/dist/java/visitor.d.ts +2 -2
  8. package/dist/java/visitor.d.ts.map +1 -1
  9. package/dist/java/visitor.js +8 -2
  10. package/dist/java/visitor.js.map +1 -1
  11. package/dist/javascript/assertions.d.ts +6 -0
  12. package/dist/javascript/assertions.d.ts.map +1 -1
  13. package/dist/javascript/assertions.js +14 -6
  14. package/dist/javascript/assertions.js.map +1 -1
  15. package/dist/javascript/comparator.d.ts +217 -7
  16. package/dist/javascript/comparator.d.ts.map +1 -1
  17. package/dist/javascript/comparator.js +1020 -2848
  18. package/dist/javascript/comparator.js.map +1 -1
  19. package/dist/javascript/format.d.ts +5 -3
  20. package/dist/javascript/format.d.ts.map +1 -1
  21. package/dist/javascript/format.js +87 -44
  22. package/dist/javascript/format.js.map +1 -1
  23. package/dist/javascript/index.d.ts +2 -1
  24. package/dist/javascript/index.d.ts.map +1 -1
  25. package/dist/javascript/index.js +2 -1
  26. package/dist/javascript/index.js.map +1 -1
  27. package/dist/javascript/parser.d.ts +2 -1
  28. package/dist/javascript/parser.d.ts.map +1 -1
  29. package/dist/javascript/parser.js +54 -43
  30. package/dist/javascript/parser.js.map +1 -1
  31. package/dist/javascript/templating/capture.d.ts +293 -0
  32. package/dist/javascript/templating/capture.d.ts.map +1 -0
  33. package/dist/javascript/templating/capture.js +461 -0
  34. package/dist/javascript/templating/capture.js.map +1 -0
  35. package/dist/javascript/templating/comparator.d.ts +171 -0
  36. package/dist/javascript/templating/comparator.d.ts.map +1 -0
  37. package/dist/javascript/templating/comparator.js +1221 -0
  38. package/dist/javascript/templating/comparator.js.map +1 -0
  39. package/dist/javascript/templating/engine.d.ts +108 -0
  40. package/dist/javascript/templating/engine.d.ts.map +1 -0
  41. package/dist/javascript/templating/engine.js +661 -0
  42. package/dist/javascript/templating/engine.js.map +1 -0
  43. package/dist/javascript/templating/index.d.ts +6 -0
  44. package/dist/javascript/templating/index.d.ts.map +1 -0
  45. package/dist/javascript/templating/index.js +44 -0
  46. package/dist/javascript/templating/index.js.map +1 -0
  47. package/dist/javascript/templating/pattern.d.ts +276 -0
  48. package/dist/javascript/templating/pattern.d.ts.map +1 -0
  49. package/dist/javascript/templating/pattern.js +952 -0
  50. package/dist/javascript/templating/pattern.js.map +1 -0
  51. package/dist/javascript/templating/placeholder-replacement.d.ts +83 -0
  52. package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -0
  53. package/dist/javascript/templating/placeholder-replacement.js +467 -0
  54. package/dist/javascript/templating/placeholder-replacement.js.map +1 -0
  55. package/dist/javascript/templating/rewrite.d.ts +84 -0
  56. package/dist/javascript/templating/rewrite.d.ts.map +1 -0
  57. package/dist/javascript/templating/rewrite.js +208 -0
  58. package/dist/javascript/templating/rewrite.js.map +1 -0
  59. package/dist/javascript/templating/template.d.ts +230 -0
  60. package/dist/javascript/templating/template.d.ts.map +1 -0
  61. package/dist/javascript/templating/template.js +367 -0
  62. package/dist/javascript/templating/template.js.map +1 -0
  63. package/dist/javascript/templating/types.d.ts +610 -0
  64. package/dist/javascript/templating/types.d.ts.map +1 -0
  65. package/dist/javascript/templating/types.js +3 -0
  66. package/dist/javascript/templating/types.js.map +1 -0
  67. package/dist/javascript/templating/utils.d.ts +135 -0
  68. package/dist/javascript/templating/utils.d.ts.map +1 -0
  69. package/dist/javascript/templating/utils.js +251 -0
  70. package/dist/javascript/templating/utils.js.map +1 -0
  71. package/dist/javascript/type-mapping.d.ts.map +1 -1
  72. package/dist/javascript/type-mapping.js +21 -11
  73. package/dist/javascript/type-mapping.js.map +1 -1
  74. package/dist/json/rpc.js +2 -2
  75. package/dist/json/rpc.js.map +1 -1
  76. package/dist/recipe/order-imports.js.map +1 -1
  77. package/dist/test/rewrite-test.d.ts.map +1 -1
  78. package/dist/test/rewrite-test.js +10 -6
  79. package/dist/test/rewrite-test.js.map +1 -1
  80. package/dist/version.txt +1 -1
  81. package/dist/visitor.d.ts +4 -4
  82. package/dist/visitor.d.ts.map +1 -1
  83. package/dist/visitor.js +8 -3
  84. package/dist/visitor.js.map +1 -1
  85. package/package.json +5 -2
  86. package/src/java/tree.ts +10 -3
  87. package/src/java/type-visitor.ts +1 -1
  88. package/src/java/visitor.ts +11 -5
  89. package/src/javascript/assertions.ts +9 -3
  90. package/src/javascript/comparator.ts +1095 -3373
  91. package/src/javascript/format.ts +72 -33
  92. package/src/javascript/index.ts +2 -1
  93. package/src/javascript/parser.ts +67 -45
  94. package/src/javascript/templating/capture.ts +595 -0
  95. package/src/javascript/templating/comparator.ts +1383 -0
  96. package/src/javascript/templating/engine.ts +750 -0
  97. package/src/javascript/templating/index.ts +67 -0
  98. package/src/javascript/templating/pattern.ts +1101 -0
  99. package/src/javascript/templating/placeholder-replacement.ts +475 -0
  100. package/src/javascript/templating/rewrite.ts +229 -0
  101. package/src/javascript/templating/template.ts +414 -0
  102. package/src/javascript/templating/types.ts +674 -0
  103. package/src/javascript/templating/utils.ts +298 -0
  104. package/src/javascript/type-mapping.ts +20 -11
  105. package/src/json/rpc.ts +2 -2
  106. package/src/recipe/order-imports.ts +1 -1
  107. package/src/test/rewrite-test.ts +12 -7
  108. package/src/visitor.ts +14 -6
  109. package/dist/javascript/templating.d.ts +0 -265
  110. package/dist/javascript/templating.d.ts.map +0 -1
  111. package/dist/javascript/templating.js +0 -1027
  112. package/dist/javascript/templating.js.map +0 -1
  113. package/src/javascript/templating.ts +0 -1226
@@ -0,0 +1,661 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TemplateApplier = exports.TemplateEngine = void 0;
13
+ exports.setTemplateSourceFileCache = setTemplateSourceFileCache;
14
+ exports.clearTemplateCache = clearTemplateCache;
15
+ /*
16
+ * Copyright 2025 the original author or authors.
17
+ * <p>
18
+ * Licensed under the Moderne Source Available License (the "License");
19
+ * you may not use this file except in compliance with the License.
20
+ * You may obtain a copy of the License at
21
+ * <p>
22
+ * https://docs.moderne.io/licensing/moderne-source-available-license
23
+ * <p>
24
+ * Unless required by applicable law or agreed to in writing, software
25
+ * distributed under the License is distributed on an "AS IS" BASIS,
26
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27
+ * See the License for the specific language governing permissions and
28
+ * limitations under the License.
29
+ */
30
+ const __1 = require("../..");
31
+ const java_1 = require("../../java");
32
+ const __2 = require("..");
33
+ const immer_1 = require("immer");
34
+ const utils_1 = require("./utils");
35
+ const capture_1 = require("./capture");
36
+ const placeholder_replacement_1 = require("./placeholder-replacement");
37
+ const format_1 = require("../format");
38
+ const parser_utils_1 = require("../parser-utils");
39
+ const uuid_1 = require("../../uuid");
40
+ const dependency_workspace_1 = require("../dependency-workspace");
41
+ /**
42
+ * Simple LRU (Least Recently Used) cache implementation.
43
+ * Used for template/pattern compilation caching with bounded memory usage.
44
+ */
45
+ class LRUCache {
46
+ constructor(maxSize) {
47
+ this.maxSize = maxSize;
48
+ this.cache = new Map();
49
+ }
50
+ get(key) {
51
+ const value = this.cache.get(key);
52
+ if (value !== undefined) {
53
+ // Move to end (most recently used)
54
+ this.cache.delete(key);
55
+ this.cache.set(key, value);
56
+ }
57
+ return value;
58
+ }
59
+ set(key, value) {
60
+ // Remove if exists (to update position)
61
+ this.cache.delete(key);
62
+ // Add to end
63
+ this.cache.set(key, value);
64
+ // Evict oldest if over capacity
65
+ if (this.cache.size > this.maxSize) {
66
+ const iterator = this.cache.keys();
67
+ const firstEntry = iterator.next();
68
+ if (!firstEntry.done) {
69
+ this.cache.delete(firstEntry.value);
70
+ }
71
+ }
72
+ }
73
+ clear() {
74
+ this.cache.clear();
75
+ }
76
+ }
77
+ /**
78
+ * Module-level TypeScript sourceFileCache for template parsing.
79
+ */
80
+ let templateSourceFileCache;
81
+ /**
82
+ * Configure the sourceFileCache used for template parsing.
83
+ *
84
+ * @param cache The sourceFileCache to use, or undefined to disable caching
85
+ */
86
+ function setTemplateSourceFileCache(cache) {
87
+ templateSourceFileCache = cache;
88
+ }
89
+ /**
90
+ * Cache for compiled templates and patterns.
91
+ * Stores parsed ASTs to avoid expensive re-parsing and dependency resolution.
92
+ * Bounded to 100 entries using LRU eviction to prevent unbounded memory growth.
93
+ */
94
+ class TemplateCache {
95
+ constructor() {
96
+ this.cache = new LRUCache(100);
97
+ }
98
+ /**
99
+ * Generates a cache key from template string, captures, and options.
100
+ */
101
+ generateKey(templateString, captures, contextStatements, dependencies) {
102
+ // Use the actual template string (with placeholders) as the primary key
103
+ const templateKey = templateString;
104
+ // Capture names
105
+ const capturesKey = captures.map(c => c.getName()).join(',');
106
+ // Context statements
107
+ const contextKey = contextStatements.join(';');
108
+ // Dependencies
109
+ const depsKey = JSON.stringify(dependencies || {});
110
+ return `${templateKey}::${capturesKey}::${contextKey}::${depsKey}`;
111
+ }
112
+ /**
113
+ * Gets a cached compilation unit or creates and caches a new one.
114
+ */
115
+ getOrParse(templateString, captures, contextStatements, dependencies) {
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ const key = this.generateKey(templateString, captures, contextStatements, dependencies);
118
+ let cu = this.cache.get(key);
119
+ if (cu) {
120
+ return cu;
121
+ }
122
+ // Create workspace if dependencies are provided
123
+ // DependencyWorkspace has its own cache, so multiple templates with
124
+ // the same dependencies will automatically share the same workspace
125
+ let workspaceDir;
126
+ if (dependencies && Object.keys(dependencies).length > 0) {
127
+ workspaceDir = yield dependency_workspace_1.DependencyWorkspace.getOrCreateWorkspace(dependencies);
128
+ }
129
+ // Prepend context statements for type attribution context
130
+ const fullTemplateString = contextStatements.length > 0
131
+ ? contextStatements.join('\n') + '\n' + templateString
132
+ : templateString;
133
+ // Parse and cache (workspace only needed during parsing)
134
+ // Use templateSourceFileCache if configured for ~3.2x speedup on dependency file parsing
135
+ const parser = new __2.JavaScriptParser({
136
+ relativeTo: workspaceDir,
137
+ sourceFileCache: templateSourceFileCache
138
+ });
139
+ const parseGenerator = parser.parse({ text: fullTemplateString, sourcePath: 'template.ts' });
140
+ cu = (yield parseGenerator.next()).value;
141
+ this.cache.set(key, cu);
142
+ return cu;
143
+ });
144
+ }
145
+ /**
146
+ * Clears the cache.
147
+ */
148
+ clear() {
149
+ this.cache.clear();
150
+ }
151
+ }
152
+ /**
153
+ * Cache for compiled templates and patterns.
154
+ * Private to the engine module - encapsulates caching implementation.
155
+ */
156
+ const templateCache = new TemplateCache();
157
+ /**
158
+ * Clears the template cache. Only exported for testing and benchmarking purposes.
159
+ * Normal application code should not need to call this.
160
+ */
161
+ function clearTemplateCache() {
162
+ templateCache.clear();
163
+ }
164
+ /**
165
+ * Internal template engine - handles the core templating logic.
166
+ * Not exported from index, so only visible within the templating module.
167
+ */
168
+ class TemplateEngine {
169
+ /**
170
+ * Gets the parsed and extracted template tree (before value substitution).
171
+ * This is the cacheable part of template processing.
172
+ *
173
+ * @param templateParts The string parts of the template
174
+ * @param parameters The parameters between the string parts
175
+ * @param contextStatements Context declarations (imports, types, etc.) to prepend for type attribution
176
+ * @param dependencies NPM dependencies for type attribution
177
+ * @returns A Promise resolving to the extracted template AST
178
+ */
179
+ static getTemplateTree(templateParts_1, parameters_1) {
180
+ return __awaiter(this, arguments, void 0, function* (templateParts, parameters, contextStatements = [], dependencies = {}) {
181
+ // Generate type preamble for captures/parameters with types
182
+ const preamble = TemplateEngine.generateTypePreamble(parameters);
183
+ // Build the template string with parameter placeholders
184
+ const templateString = TemplateEngine.buildTemplateString(templateParts, parameters);
185
+ // Add preamble to context statements (so they're skipped during extraction)
186
+ const contextWithPreamble = preamble.length > 0
187
+ ? [...contextStatements, ...preamble]
188
+ : contextStatements;
189
+ // Use cache to get or parse the compilation unit
190
+ const cu = yield templateCache.getOrParse(templateString, [], contextWithPreamble, dependencies);
191
+ // Check if there are any statements
192
+ if (!cu.statements || cu.statements.length === 0) {
193
+ throw new Error(`Failed to parse template code (no statements):\n${templateString}`);
194
+ }
195
+ // The template code is always the last statement (after context + preamble)
196
+ const lastStatement = cu.statements[cu.statements.length - 1].element;
197
+ // Extract from wrapper using shared utility
198
+ const extracted = utils_1.PlaceholderUtils.extractFromWrapper(lastStatement, 'Template');
199
+ return (0, immer_1.produce)(extracted, _ => { });
200
+ });
201
+ }
202
+ /**
203
+ * Applies a template from a pre-parsed AST and returns the resulting AST.
204
+ * This method is used by Template.apply() after getting the cached template tree.
205
+ *
206
+ * @param ast The pre-parsed template AST
207
+ * @param parameters The parameters between the string parts
208
+ * @param cursor The cursor pointing to the current location in the AST
209
+ * @param coordinates The coordinates specifying where and how to insert the generated AST
210
+ * @param values Map of capture names to values to replace the parameters with
211
+ * @param wrappersMap Map of capture names to J.RightPadded wrappers (for preserving markers)
212
+ * @returns A Promise resolving to the generated AST node
213
+ */
214
+ static applyTemplateFromAst(ast_1, parameters_1, cursor_1, coordinates_1) {
215
+ return __awaiter(this, arguments, void 0, function* (ast, parameters, cursor, coordinates, values = new Map(), wrappersMap = new Map()) {
216
+ // Create substitutions map for placeholders
217
+ const substitutions = new Map();
218
+ for (let i = 0; i < parameters.length; i++) {
219
+ const placeholder = `${utils_1.PlaceholderUtils.PLACEHOLDER_PREFIX}${i}__`;
220
+ substitutions.set(placeholder, parameters[i]);
221
+ }
222
+ // Unsubstitute placeholders with actual parameter values and match results
223
+ const visitor = new placeholder_replacement_1.PlaceholderReplacementVisitor(substitutions, values, wrappersMap);
224
+ const unsubstitutedAst = (yield visitor.visit(ast, null));
225
+ // Apply the template to the current AST
226
+ return new TemplateApplier(cursor, coordinates, unsubstitutedAst).apply();
227
+ });
228
+ }
229
+ /**
230
+ * Generates type preamble declarations for captures/parameters with type annotations.
231
+ *
232
+ * @param parameters The parameters
233
+ * @returns Array of preamble statements
234
+ */
235
+ static generateTypePreamble(parameters) {
236
+ const preamble = [];
237
+ for (let i = 0; i < parameters.length; i++) {
238
+ const param = parameters[i].value;
239
+ const placeholder = `${utils_1.PlaceholderUtils.PLACEHOLDER_PREFIX}${i}__`;
240
+ // Check for Capture (could be a Proxy, so check for symbol property)
241
+ const isCapture = param instanceof capture_1.CaptureImpl ||
242
+ (param && typeof param === 'object' && param[capture_1.CAPTURE_NAME_SYMBOL]);
243
+ const isCaptureValue = param instanceof capture_1.CaptureValue;
244
+ const isTreeArray = Array.isArray(param) && param.length > 0 && (0, __1.isTree)(param[0]);
245
+ if (isCapture) {
246
+ const captureType = param[capture_1.CAPTURE_TYPE_SYMBOL];
247
+ if (captureType) {
248
+ const typeString = typeof captureType === 'string'
249
+ ? captureType
250
+ : this.typeToString(captureType);
251
+ // Only add preamble if we have a concrete type (not 'any')
252
+ if (typeString !== 'any') {
253
+ preamble.push(`let ${placeholder}: ${typeString};`);
254
+ }
255
+ }
256
+ }
257
+ else if (isCaptureValue) {
258
+ // For CaptureValue, check if the root capture has a type
259
+ const rootCapture = param.rootCapture;
260
+ if (rootCapture) {
261
+ const captureType = rootCapture[capture_1.CAPTURE_TYPE_SYMBOL];
262
+ if (captureType) {
263
+ const typeString = typeof captureType === 'string'
264
+ ? captureType
265
+ : this.typeToString(captureType);
266
+ // Only add preamble if we have a concrete type (not 'any')
267
+ if (typeString !== 'any') {
268
+ preamble.push(`let ${placeholder}: ${typeString};`);
269
+ }
270
+ }
271
+ }
272
+ }
273
+ else if ((0, __1.isTree)(param) && !isTreeArray) {
274
+ // For J elements, derive type from the element's type property if it exists
275
+ const jElement = param;
276
+ if (jElement.type) {
277
+ const typeString = this.typeToString(jElement.type);
278
+ // Only add preamble if we have a concrete type (not 'any')
279
+ if (typeString !== 'any') {
280
+ preamble.push(`let ${placeholder}: ${typeString};`);
281
+ }
282
+ }
283
+ }
284
+ }
285
+ return preamble;
286
+ }
287
+ /**
288
+ * Builds a template string with parameter placeholders.
289
+ * RawCode parameters are spliced directly into the template at construction time.
290
+ * Other parameters use placeholders that are replaced during application.
291
+ *
292
+ * @param templateParts The string parts of the template
293
+ * @param parameters The parameters between the string parts
294
+ * @returns The template string
295
+ */
296
+ static buildTemplateString(templateParts, parameters) {
297
+ let result = '';
298
+ for (let i = 0; i < templateParts.length; i++) {
299
+ result += templateParts[i];
300
+ if (i < parameters.length) {
301
+ const param = parameters[i].value;
302
+ // Check if this is a RawCode instance - splice directly
303
+ if (param instanceof capture_1.RawCode || (param && typeof param === 'object' && param[capture_1.RAW_CODE_SYMBOL])) {
304
+ result += param.code;
305
+ }
306
+ else {
307
+ // All other parameters use placeholders
308
+ // This ensures templates with the same structure always produce the same AST
309
+ const placeholder = `${utils_1.PlaceholderUtils.PLACEHOLDER_PREFIX}${i}__`;
310
+ result += placeholder;
311
+ }
312
+ }
313
+ }
314
+ // Always wrap in function body - let the parser decide what it is,
315
+ // then we'll extract intelligently based on what was parsed
316
+ return `function ${utils_1.WRAPPER_FUNCTION_NAME}() { ${result} }`;
317
+ }
318
+ /**
319
+ * Converts a Type instance to a TypeScript type string.
320
+ *
321
+ * @param type The Type instance
322
+ * @returns A TypeScript type string
323
+ */
324
+ static typeToString(type) {
325
+ // Handle Type.Class and Type.ShallowClass - return their fully qualified names
326
+ if (type.kind === java_1.Type.Kind.Class || type.kind === java_1.Type.Kind.ShallowClass) {
327
+ const classType = type;
328
+ return classType.fullyQualifiedName;
329
+ }
330
+ // Handle Type.Primitive - map to TypeScript primitive types
331
+ if (type.kind === java_1.Type.Kind.Primitive) {
332
+ const primitiveType = type;
333
+ switch (primitiveType.keyword) {
334
+ case 'String':
335
+ return 'string';
336
+ case 'boolean':
337
+ return 'boolean';
338
+ case 'double':
339
+ case 'float':
340
+ case 'int':
341
+ case 'long':
342
+ case 'short':
343
+ case 'byte':
344
+ return 'number';
345
+ case 'void':
346
+ return 'void';
347
+ default:
348
+ return 'any';
349
+ }
350
+ }
351
+ // Handle Type.Array - render component type plus []
352
+ if (type.kind === java_1.Type.Kind.Array) {
353
+ const arrayType = type;
354
+ const componentTypeString = this.typeToString(arrayType.elemType);
355
+ return `${componentTypeString}[]`;
356
+ }
357
+ // For other types, return 'any' as a fallback
358
+ // TODO: Implement proper Type to string conversion for other Type.Kind values
359
+ return 'any';
360
+ }
361
+ /**
362
+ * Gets the parsed and extracted pattern tree with capture markers attached.
363
+ * This is the entry point for pattern processing, providing pattern-specific
364
+ * functionality on top of the shared template tree generation.
365
+ *
366
+ * @param templateParts The string parts of the template
367
+ * @param captures The captures between the string parts (can include RawCode)
368
+ * @param contextStatements Context declarations (imports, types, etc.) to prepend for type attribution
369
+ * @param dependencies NPM dependencies for type attribution
370
+ * @returns A Promise resolving to the extracted pattern AST with capture markers
371
+ */
372
+ static getPatternTree(templateParts_1, captures_1) {
373
+ return __awaiter(this, arguments, void 0, function* (templateParts, captures, contextStatements = [], dependencies = {}) {
374
+ // Generate type preamble for captures with types (skip RawCode)
375
+ const preamble = [];
376
+ for (const capture of captures) {
377
+ // Skip raw code - it's not a capture
378
+ if (capture instanceof capture_1.RawCode || (capture && typeof capture === 'object' && capture[capture_1.RAW_CODE_SYMBOL])) {
379
+ continue;
380
+ }
381
+ const captureName = capture[capture_1.CAPTURE_NAME_SYMBOL] || capture.getName();
382
+ const captureType = capture[capture_1.CAPTURE_TYPE_SYMBOL];
383
+ if (captureType) {
384
+ // Convert Type to string if needed
385
+ const typeString = typeof captureType === 'string'
386
+ ? captureType
387
+ : this.typeToString(captureType);
388
+ // Only add preamble if we have a concrete type (not 'any')
389
+ if (typeString !== 'any') {
390
+ const placeholder = utils_1.PlaceholderUtils.createCapture(captureName, undefined);
391
+ preamble.push(`let ${placeholder}: ${typeString};`);
392
+ }
393
+ }
394
+ // Don't add preamble declarations without types - they don't provide type attribution
395
+ }
396
+ // Build the template string with placeholders for captures and raw code
397
+ let result = '';
398
+ for (let i = 0; i < templateParts.length; i++) {
399
+ result += templateParts[i];
400
+ if (i < captures.length) {
401
+ const capture = captures[i];
402
+ // Check if this is a RawCode instance - splice directly
403
+ if (capture instanceof capture_1.RawCode || (capture && typeof capture === 'object' && capture[capture_1.RAW_CODE_SYMBOL])) {
404
+ result += capture.code;
405
+ }
406
+ else {
407
+ // Use symbol to access capture name without triggering Proxy
408
+ const captureName = capture[capture_1.CAPTURE_NAME_SYMBOL] || capture.getName();
409
+ result += utils_1.PlaceholderUtils.createCapture(captureName, undefined);
410
+ }
411
+ }
412
+ }
413
+ // Always wrap in function body - let the parser decide what it is,
414
+ // then we'll extract intelligently based on what was parsed
415
+ const templateString = `function ${utils_1.WRAPPER_FUNCTION_NAME}() { ${result} }`;
416
+ // Add preamble to context statements (so they're skipped during extraction)
417
+ const contextWithPreamble = preamble.length > 0
418
+ ? [...contextStatements, ...preamble]
419
+ : contextStatements;
420
+ // Filter out RawCode from captures for cache and marker attachment
421
+ const actualCaptures = captures.filter(c => !(c instanceof capture_1.RawCode || (c && typeof c === 'object' && c[capture_1.RAW_CODE_SYMBOL])));
422
+ // Use cache to get or parse the compilation unit
423
+ const cu = yield templateCache.getOrParse(templateString, actualCaptures, contextWithPreamble, dependencies);
424
+ // Check if there are any statements
425
+ if (!cu.statements || cu.statements.length === 0) {
426
+ throw new Error(`Failed to parse pattern code (no statements):\n${templateString}`);
427
+ }
428
+ // The pattern code is always the last statement (after context + preamble)
429
+ const lastStatement = cu.statements[cu.statements.length - 1].element;
430
+ // Extract from wrapper using shared utility
431
+ const extracted = utils_1.PlaceholderUtils.extractFromWrapper(lastStatement, 'Pattern');
432
+ // Attach CaptureMarkers to capture identifiers (only for actual captures, not raw code)
433
+ const visitor = new MarkerAttachmentVisitor(actualCaptures);
434
+ return (yield visitor.visit(extracted, undefined));
435
+ });
436
+ }
437
+ }
438
+ exports.TemplateEngine = TemplateEngine;
439
+ /**
440
+ * Visitor that attaches CaptureMarkers to capture identifiers in pattern ASTs.
441
+ * This allows efficient capture detection without string parsing during matching.
442
+ * Used by TemplateEngine.getPatternTree() for pattern-specific processing.
443
+ */
444
+ class MarkerAttachmentVisitor extends __2.JavaScriptVisitor {
445
+ constructor(captures) {
446
+ super();
447
+ this.captures = captures;
448
+ }
449
+ /**
450
+ * Attaches CaptureMarker to capture identifiers.
451
+ */
452
+ visitIdentifier(ident, p) {
453
+ const _super = Object.create(null, {
454
+ visitIdentifier: { get: () => super.visitIdentifier }
455
+ });
456
+ return __awaiter(this, void 0, void 0, function* () {
457
+ var _a, _b;
458
+ // First call parent to handle standard visitation
459
+ const visited = yield _super.visitIdentifier.call(this, ident, p);
460
+ if (!visited || visited.kind !== java_1.J.Kind.Identifier) {
461
+ return visited;
462
+ }
463
+ ident = visited;
464
+ // Check if this is a capture placeholder
465
+ if ((_a = ident.simpleName) === null || _a === void 0 ? void 0 : _a.startsWith(utils_1.PlaceholderUtils.CAPTURE_PREFIX)) {
466
+ const captureInfo = utils_1.PlaceholderUtils.parseCapture(ident.simpleName);
467
+ if (captureInfo) {
468
+ // Find the original capture object to get variadic options and constraint
469
+ const captureObj = this.captures.find(c => c.getName() === captureInfo.name);
470
+ const variadicOptions = captureObj === null || captureObj === void 0 ? void 0 : captureObj.getVariadicOptions();
471
+ const constraint = (_b = captureObj === null || captureObj === void 0 ? void 0 : captureObj.getConstraint) === null || _b === void 0 ? void 0 : _b.call(captureObj);
472
+ // Add CaptureMarker to the Identifier with constraint
473
+ const marker = new utils_1.CaptureMarker(captureInfo.name, variadicOptions, constraint);
474
+ return (0, __1.updateIfChanged)(ident, {
475
+ markers: Object.assign(Object.assign({}, ident.markers), { markers: [...ident.markers.markers, marker] })
476
+ });
477
+ }
478
+ }
479
+ return ident;
480
+ });
481
+ }
482
+ /**
483
+ * Propagates markers from element to RightPadded wrapper.
484
+ */
485
+ visitRightPadded(right, p) {
486
+ return __awaiter(this, void 0, void 0, function* () {
487
+ if (!(0, __1.isTree)(right.element)) {
488
+ return right;
489
+ }
490
+ const visitedElement = yield this.visit(right.element, p);
491
+ if (visitedElement && visitedElement !== right.element) {
492
+ const result = yield (0, __1.produceAsync)(right, (draft) => __awaiter(this, void 0, void 0, function* () {
493
+ // Visit element first
494
+ if (right.element && right.element.kind) {
495
+ // Check if element has a CaptureMarker
496
+ const elementMarker = utils_1.PlaceholderUtils.getCaptureMarker(visitedElement);
497
+ if (elementMarker) {
498
+ draft.markers.markers.push(elementMarker);
499
+ }
500
+ else {
501
+ draft.element = visitedElement;
502
+ }
503
+ }
504
+ }));
505
+ return result;
506
+ }
507
+ return right;
508
+ });
509
+ }
510
+ /**
511
+ * Propagates markers from expression to ExpressionStatement.
512
+ */
513
+ visitExpressionStatement(expressionStatement, p) {
514
+ return __awaiter(this, void 0, void 0, function* () {
515
+ // Visit the expression
516
+ const visitedExpression = yield this.visit(expressionStatement.expression, p);
517
+ // Check if expression has a CaptureMarker
518
+ const expressionMarker = utils_1.PlaceholderUtils.getCaptureMarker(visitedExpression);
519
+ if (expressionMarker) {
520
+ return (0, __1.updateIfChanged)(expressionStatement, {
521
+ markers: Object.assign(Object.assign({}, expressionStatement.markers), { markers: [...expressionStatement.markers.markers, expressionMarker] }),
522
+ });
523
+ }
524
+ // No marker to move, just update with visited expression
525
+ return (0, __1.updateIfChanged)(expressionStatement, {
526
+ expression: visitedExpression
527
+ });
528
+ });
529
+ }
530
+ /**
531
+ * Propagates markers from name identifier to BindingElement.
532
+ * This handles destructuring patterns like {${props}} where the capture marker
533
+ * is on the identifier but needs to be on the BindingElement for container matching.
534
+ */
535
+ visitBindingElement(bindingElement, p) {
536
+ return __awaiter(this, void 0, void 0, function* () {
537
+ // Visit the name
538
+ const visitedName = yield this.visit(bindingElement.name, p);
539
+ // Check if name has a CaptureMarker
540
+ const nameMarker = utils_1.PlaceholderUtils.getCaptureMarker(visitedName);
541
+ if (nameMarker) {
542
+ return (0, __1.updateIfChanged)(bindingElement, {
543
+ name: visitedName,
544
+ markers: Object.assign(Object.assign({}, bindingElement.markers), { markers: [...bindingElement.markers.markers, nameMarker] }),
545
+ });
546
+ }
547
+ // No marker to move, just update with visited name
548
+ return (0, __1.updateIfChanged)(bindingElement, {
549
+ name: visitedName
550
+ });
551
+ });
552
+ }
553
+ }
554
+ /**
555
+ * Helper class for applying a template to an AST.
556
+ */
557
+ class TemplateApplier {
558
+ constructor(cursor, coordinates, ast) {
559
+ this.cursor = cursor;
560
+ this.coordinates = coordinates;
561
+ this.ast = ast;
562
+ }
563
+ /**
564
+ * Applies the template to the current AST.
565
+ *
566
+ * @returns A Promise resolving to the modified AST
567
+ */
568
+ apply() {
569
+ return __awaiter(this, void 0, void 0, function* () {
570
+ const { loc } = this.coordinates;
571
+ // Apply the template based on the location and mode
572
+ switch (loc || 'EXPRESSION_PREFIX') {
573
+ case 'EXPRESSION_PREFIX':
574
+ case 'STATEMENT_PREFIX':
575
+ case 'BLOCK_END':
576
+ return this.applyInternal();
577
+ default:
578
+ throw new Error(`Unsupported location: ${loc}`);
579
+ }
580
+ });
581
+ }
582
+ /**
583
+ * Applies the template to an expression.
584
+ *
585
+ * @returns A Promise resolving to the modified AST
586
+ */
587
+ applyInternal() {
588
+ return __awaiter(this, void 0, void 0, function* () {
589
+ const { tree } = this.coordinates;
590
+ if (!tree) {
591
+ return this.ast;
592
+ }
593
+ const originalTree = tree;
594
+ const resultToUse = this.wrapTree(originalTree, this.ast);
595
+ return this.format(resultToUse, originalTree);
596
+ });
597
+ }
598
+ format(resultToUse, originalTree) {
599
+ return __awaiter(this, void 0, void 0, function* () {
600
+ var _a;
601
+ // Create a copy of the AST with the prefix from the target
602
+ const result = Object.assign(Object.assign({}, resultToUse), {
603
+ // We temporarily set the ID so that the formatter can identify the tree
604
+ id: originalTree.id, prefix: originalTree.prefix });
605
+ // Apply auto-formatting to the result
606
+ const formatted = yield (0, format_1.maybeAutoFormat)(originalTree, result, null, undefined, (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.parent);
607
+ // Restore the original ID
608
+ return Object.assign(Object.assign({}, formatted), { id: resultToUse.id });
609
+ });
610
+ }
611
+ wrapTree(originalTree, resultToUse) {
612
+ var _a, _b;
613
+ const parentTree = (_b = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.parentTree()) === null || _b === void 0 ? void 0 : _b.value;
614
+ // Only apply wrapping logic if we have parent context
615
+ if (parentTree) {
616
+ // FIXME: This is a heuristic to determine if the parent expects a statement child
617
+ const parentExpectsStatement = parentTree.kind === java_1.J.Kind.Block ||
618
+ parentTree.kind === java_1.J.Kind.Case ||
619
+ parentTree.kind === java_1.J.Kind.DoWhileLoop ||
620
+ parentTree.kind === java_1.J.Kind.ForEachLoop ||
621
+ parentTree.kind === java_1.J.Kind.ForLoop ||
622
+ parentTree.kind === java_1.J.Kind.If ||
623
+ parentTree.kind === java_1.J.Kind.IfElse ||
624
+ parentTree.kind === java_1.J.Kind.WhileLoop ||
625
+ parentTree.kind === __2.JS.Kind.CompilationUnit ||
626
+ parentTree.kind === __2.JS.Kind.ForInLoop;
627
+ const originalIsStatement = (0, parser_utils_1.isStatement)(originalTree);
628
+ const resultIsStatement = (0, parser_utils_1.isStatement)(resultToUse);
629
+ const resultIsExpression = (0, parser_utils_1.isExpression)(resultToUse);
630
+ // Determine context and wrap if needed
631
+ if (parentExpectsStatement && originalIsStatement) {
632
+ // Statement context: wrap in ExpressionStatement if result is not a statement
633
+ if (!resultIsStatement && resultIsExpression) {
634
+ resultToUse = {
635
+ kind: __2.JS.Kind.ExpressionStatement,
636
+ id: (0, uuid_1.randomId)(),
637
+ prefix: resultToUse.prefix,
638
+ markers: resultToUse.markers,
639
+ expression: Object.assign(Object.assign({}, resultToUse), { prefix: java_1.emptySpace })
640
+ };
641
+ }
642
+ }
643
+ else if (!parentExpectsStatement) {
644
+ // Expression context: wrap in StatementExpression if result is statement-only
645
+ if (resultIsStatement && !resultIsExpression) {
646
+ const stmt = resultToUse;
647
+ resultToUse = {
648
+ kind: __2.JS.Kind.StatementExpression,
649
+ id: (0, uuid_1.randomId)(),
650
+ prefix: stmt.prefix,
651
+ markers: stmt.markers,
652
+ statement: Object.assign(Object.assign({}, stmt), { prefix: java_1.emptySpace })
653
+ };
654
+ }
655
+ }
656
+ }
657
+ return resultToUse;
658
+ }
659
+ }
660
+ exports.TemplateApplier = TemplateApplier;
661
+ //# sourceMappingURL=engine.js.map