@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,952 @@
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.MatchResult = exports.Pattern = exports.PatternBuilder = void 0;
13
+ exports.pattern = pattern;
14
+ /*
15
+ * Copyright 2025 the original author or authors.
16
+ * <p>
17
+ * Licensed under the Moderne Source Available License (the "License");
18
+ * you may not use this file except in compliance with the License.
19
+ * You may obtain a copy of the License at
20
+ * <p>
21
+ * https://docs.moderne.io/licensing/moderne-source-available-license
22
+ * <p>
23
+ * Unless required by applicable law or agreed to in writing, software
24
+ * distributed under the License is distributed on an "AS IS" BASIS,
25
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26
+ * See the License for the specific language governing permissions and
27
+ * limitations under the License.
28
+ */
29
+ const __1 = require("../..");
30
+ const capture_1 = require("./capture");
31
+ const comparator_1 = require("./comparator");
32
+ const utils_1 = require("./utils");
33
+ const engine_1 = require("./engine");
34
+ const print_1 = require("../../print");
35
+ const index_1 = require("../index");
36
+ /**
37
+ * Builder for creating patterns programmatically.
38
+ * Use when pattern structure is not known at compile time.
39
+ *
40
+ * @example
41
+ * // Loop-based pattern generation
42
+ * const builder = Pattern.builder().code('myFunction(');
43
+ * for (let i = 0; i < argCount; i++) {
44
+ * if (i > 0) builder.code(', ');
45
+ * builder.capture(capture(`arg${i}`));
46
+ * }
47
+ * builder.code(')');
48
+ * const pat = builder.build();
49
+ *
50
+ * @example
51
+ * // Conditional pattern construction
52
+ * const builder = Pattern.builder().code('foo(');
53
+ * builder.capture(capture('first'));
54
+ * if (needsSecondArg) {
55
+ * builder.code(', ').capture(capture('second'));
56
+ * }
57
+ * builder.code(')');
58
+ * const pat = builder.build();
59
+ */
60
+ class PatternBuilder {
61
+ constructor() {
62
+ this.parts = [];
63
+ this.captures = [];
64
+ }
65
+ /**
66
+ * Adds a static string part to the pattern.
67
+ *
68
+ * @param str The string to add
69
+ * @returns This builder for chaining
70
+ */
71
+ code(str) {
72
+ // If there are already captures, we need to add an empty string before this
73
+ if (this.captures.length > this.parts.length) {
74
+ this.parts.push('');
75
+ }
76
+ // Append to the last part or start a new one
77
+ if (this.parts.length === 0) {
78
+ this.parts.push(str);
79
+ }
80
+ else {
81
+ this.parts[this.parts.length - 1] += str;
82
+ }
83
+ return this;
84
+ }
85
+ /**
86
+ * Adds a capture to the pattern.
87
+ *
88
+ * @param value The capture object (Capture, Any, or RawCode) or string name
89
+ * @returns This builder for chaining
90
+ */
91
+ capture(value) {
92
+ // Ensure we have a part for after this capture
93
+ if (this.parts.length === 0) {
94
+ this.parts.push('');
95
+ }
96
+ // Convert string to Capture if needed, or use value as-is for RawCode
97
+ const captureObj = typeof value === 'string' ? new capture_1.CaptureImpl(value) : value;
98
+ this.captures.push(captureObj);
99
+ // Add an empty string for the next part
100
+ this.parts.push('');
101
+ return this;
102
+ }
103
+ /**
104
+ * Builds the pattern from accumulated parts and captures.
105
+ *
106
+ * @returns A Pattern instance
107
+ */
108
+ build() {
109
+ // Ensure parts array is one longer than captures array
110
+ while (this.parts.length <= this.captures.length) {
111
+ this.parts.push('');
112
+ }
113
+ // Create a synthetic TemplateStringsArray
114
+ const templateStrings = this.parts.slice();
115
+ templateStrings.raw = this.parts.slice();
116
+ Object.defineProperty(templateStrings, 'raw', {
117
+ value: this.parts.slice(),
118
+ writable: false
119
+ });
120
+ // Delegate to the pattern() function
121
+ return pattern(templateStrings, ...this.captures);
122
+ }
123
+ }
124
+ exports.PatternBuilder = PatternBuilder;
125
+ /**
126
+ * Represents a pattern that can be matched against AST nodes.
127
+ */
128
+ class Pattern {
129
+ /**
130
+ * Gets the configuration options for this pattern.
131
+ * @readonly
132
+ */
133
+ get options() {
134
+ return this._options;
135
+ }
136
+ /**
137
+ * Creates a new builder for constructing patterns programmatically.
138
+ *
139
+ * @returns A new PatternBuilder instance
140
+ *
141
+ * @example
142
+ * const pat = Pattern.builder()
143
+ * .code('function ')
144
+ * .capture(capture('name'))
145
+ * .code('() { return ')
146
+ * .capture(capture('value'))
147
+ * .code('; }')
148
+ * .build();
149
+ */
150
+ static builder() {
151
+ return new PatternBuilder();
152
+ }
153
+ /**
154
+ * Creates a new pattern from template parts and captures.
155
+ *
156
+ * @param templateParts The string parts of the template
157
+ * @param captures The captures between the string parts (can be Capture, Any, or RawCode)
158
+ */
159
+ constructor(templateParts, captures) {
160
+ this.templateParts = templateParts;
161
+ this.captures = captures;
162
+ this._options = {};
163
+ this.unnamedCaptureMapping = new Map();
164
+ this.patternId = Pattern.nextPatternId++;
165
+ // Build mapping for unnamed captures (unnamed_N -> _X)
166
+ let unnamedIndex = 1;
167
+ for (const cap of captures) {
168
+ if (cap && typeof cap === 'object' && 'getName' in cap) {
169
+ const name = cap.getName();
170
+ if (name && name.startsWith('unnamed_')) {
171
+ this.unnamedCaptureMapping.set(name, `_${unnamedIndex}`);
172
+ unnamedIndex++;
173
+ }
174
+ }
175
+ }
176
+ }
177
+ /**
178
+ * Configures this pattern with additional options.
179
+ *
180
+ * @param options Configuration options
181
+ * @returns This pattern for method chaining
182
+ *
183
+ * @example
184
+ * pattern`forwardRef((${props}, ${ref}) => ${body})`
185
+ * .configure({
186
+ * context: ['import { forwardRef } from "react"'],
187
+ * dependencies: {'@types/react': '^18.0.0'}
188
+ * })
189
+ */
190
+ configure(options) {
191
+ this._options = Object.assign(Object.assign({}, this._options), options);
192
+ // Invalidate cache when configuration changes
193
+ this._cachedAstPattern = undefined;
194
+ return this;
195
+ }
196
+ /**
197
+ * Gets the AST pattern for this pattern, using two-level caching:
198
+ * 1. Instance-level cache (fastest - this pattern instance)
199
+ * 2. Global LRU cache (fast - shared across pattern instances with same code)
200
+ * 3. Compute via TemplateProcessor (slow - parse and process)
201
+ *
202
+ * @returns The cached or newly computed pattern AST
203
+ * @internal
204
+ */
205
+ getAstPattern() {
206
+ return __awaiter(this, void 0, void 0, function* () {
207
+ // Level 1: Instance cache (fastest path)
208
+ if (this._cachedAstPattern) {
209
+ return this._cachedAstPattern;
210
+ }
211
+ // Generate cache key for global lookup
212
+ // Include raw code values in the key since they affect the generated AST
213
+ const contextStatements = this._options.context || this._options.imports || [];
214
+ const capturesKey = this.captures.map(c => {
215
+ if (c instanceof capture_1.RawCode || (c && typeof c === 'object' && c[capture_1.RAW_CODE_SYMBOL])) {
216
+ return `raw:${c.code}`;
217
+ }
218
+ return c.getName();
219
+ }).join(',');
220
+ const cacheKey = (0, utils_1.generateCacheKey)(this.templateParts, capturesKey, contextStatements, this._options.dependencies || {});
221
+ // Level 2: Global cache (fast path - shared with Template)
222
+ const cached = utils_1.globalAstCache.get(cacheKey);
223
+ if (cached) {
224
+ this._cachedAstPattern = cached;
225
+ return cached;
226
+ }
227
+ // Level 3: Compute via TemplateEngine (slow path)
228
+ const result = yield engine_1.TemplateEngine.getPatternTree(this.templateParts, this.captures, contextStatements, this._options.dependencies || {});
229
+ // Cache in both levels
230
+ utils_1.globalAstCache.set(cacheKey, result);
231
+ this._cachedAstPattern = result;
232
+ return result;
233
+ });
234
+ }
235
+ /**
236
+ * Creates a matcher for this pattern against a specific AST node.
237
+ *
238
+ * @param ast The AST node to match against
239
+ * @param cursor Optional cursor at the node's position in a larger tree. Used for context-aware
240
+ * capture constraints to navigate to parent nodes. If omitted, a cursor will be
241
+ * created at the ast root, allowing constraints to navigate within the matched subtree.
242
+ * @param options Optional match options (e.g., debug flag)
243
+ * @returns A MatchResult if the pattern matches, undefined otherwise
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * // Normal match
248
+ * const match = await pattern.match(node);
249
+ *
250
+ * // Debug this specific call
251
+ * const match = await pattern.match(node, cursor, { debug: true });
252
+ * ```
253
+ */
254
+ match(ast, cursor, options) {
255
+ return __awaiter(this, void 0, void 0, function* () {
256
+ // Three-level precedence: call > pattern > global
257
+ const debugEnabled = (options === null || options === void 0 ? void 0 : options.debug) !== undefined
258
+ ? options.debug // 1. Explicit call-level (true OR false)
259
+ : (this._options.debug !== undefined
260
+ ? this._options.debug // 2. Explicit pattern-level
261
+ : process.env.PATTERN_DEBUG === 'true'); // 3. Global
262
+ if (debugEnabled) {
263
+ // Use matchWithExplanation and log the result
264
+ const result = yield this.matchWithExplanation(ast, cursor);
265
+ yield this.logMatchResult(ast, cursor, result);
266
+ if (result.matched) {
267
+ // result.result is the MatchResult class instance
268
+ return result.result;
269
+ }
270
+ else {
271
+ return undefined;
272
+ }
273
+ }
274
+ // Fast path - no debug
275
+ const matcher = new Matcher(this, ast, cursor);
276
+ const success = yield matcher.matches();
277
+ if (!success) {
278
+ return undefined;
279
+ }
280
+ // Create MatchResult with unified storage
281
+ const storage = matcher.storage;
282
+ return new MatchResult(new Map(storage));
283
+ });
284
+ }
285
+ /**
286
+ * Formats and logs the match result to stderr.
287
+ * @private
288
+ */
289
+ logMatchResult(ast, cursor, result) {
290
+ return __awaiter(this, void 0, void 0, function* () {
291
+ const patternSource = this.getPatternSource();
292
+ const patternId = `Pattern #${this.patternId}`;
293
+ const nodeKind = ast.kind || 'unknown';
294
+ // Format kind: extract short name (e.g., "org.openrewrite.java.tree.J$Binary" -> "J$Binary")
295
+ const shortKind = typeof nodeKind === 'string'
296
+ ? nodeKind.split('.').pop() || nodeKind
297
+ : nodeKind;
298
+ // First, log the pattern source
299
+ console.error(`[${patternId}] ${patternSource}`);
300
+ // Build the complete match result message
301
+ const lines = [];
302
+ // Print the target tree being matched
303
+ let treeStr;
304
+ try {
305
+ const printer = print_1.TreePrinters.printer(index_1.JS.Kind.CompilationUnit);
306
+ treeStr = yield printer.print(ast);
307
+ }
308
+ catch (e) {
309
+ treeStr = '(tree printing unavailable)';
310
+ }
311
+ if (result.matched) {
312
+ // Success case - result first, then tree, then captures
313
+ lines.push(`[${patternId}] ✅ SUCCESS matching against ${shortKind}:`);
314
+ treeStr.split('\n').forEach(line => lines.push(`[${patternId}] ${line}`));
315
+ // Log captured values
316
+ if (result.result) {
317
+ const storage = result.result.storage;
318
+ if (storage && storage.size > 0) {
319
+ for (const [name, value] of storage) {
320
+ const extractedValue = result.result.extractElements(value);
321
+ const valueStr = this.formatCapturedValue(extractedValue);
322
+ const displayName = this.unnamedCaptureMapping.get(name) || name;
323
+ lines.push(`[${patternId}] Captured '${displayName}': ${valueStr}`);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ else {
329
+ // Failure case - result first, then tree, then explanation
330
+ lines.push(`[${patternId}] ❌ FAILED matching against ${shortKind}:`);
331
+ treeStr.split('\n').forEach(line => lines.push(`[${patternId}] ${line}`));
332
+ const explanation = result.explanation;
333
+ if (explanation) {
334
+ // Always show path, even if empty, to make it clear where the mismatch occurred
335
+ const compactedPath = this.compactPath(explanation.path);
336
+ const pathStr = compactedPath.length > 0 ? compactedPath.join(' → ') : '';
337
+ lines.push(`[${patternId}] At path: [${pathStr}]`);
338
+ lines.push(`[${patternId}] Reason: ${explanation.reason}`);
339
+ lines.push(`[${patternId}] Expected: ${explanation.expected}`);
340
+ lines.push(`[${patternId}] Actual: ${explanation.actual}`);
341
+ }
342
+ }
343
+ // Single console.error call with all lines joined
344
+ console.error(lines.join('\n'));
345
+ });
346
+ }
347
+ /**
348
+ * Compacts array index navigations into the previous path element.
349
+ * For example: ['J$VariableDeclarations#variables', '0'] → ['J$VariableDeclarations#variables[0]']
350
+ * @private
351
+ */
352
+ compactPath(path) {
353
+ const compacted = [];
354
+ let i = 0;
355
+ while (i < path.length) {
356
+ const current = path[i];
357
+ // Check if current element is itself a numeric index
358
+ if (/^\d+$/.test(current)) {
359
+ // This is a bare numeric index - shouldn't normally happen
360
+ // If we have a previous element, append to it
361
+ if (compacted.length > 0) {
362
+ compacted[compacted.length - 1] += `[${current}]`;
363
+ }
364
+ else {
365
+ // No previous element to attach to - this is an error in path construction
366
+ // Skip it to avoid bare [0] in output
367
+ console.warn(`Warning: Path starts with numeric index '${current}' - skipping`);
368
+ }
369
+ i++;
370
+ continue;
371
+ }
372
+ // Look ahead to collect consecutive numeric indices
373
+ let j = i + 1;
374
+ const indices = [];
375
+ while (j < path.length && /^\d+$/.test(path[j])) {
376
+ indices.push(path[j]);
377
+ j++;
378
+ }
379
+ // If we found numeric indices, append them to current element
380
+ if (indices.length > 0) {
381
+ compacted.push(current + indices.map(idx => `[${idx}]`).join(''));
382
+ i = j; // Skip the indices we just processed
383
+ }
384
+ else {
385
+ compacted.push(current);
386
+ i++;
387
+ }
388
+ }
389
+ return compacted;
390
+ }
391
+ /**
392
+ * Gets the source code representation of this pattern for logging.
393
+ * @private
394
+ */
395
+ getPatternSource() {
396
+ // Reconstruct pattern source from template parts
397
+ let source = '';
398
+ for (let i = 0; i < this.templateParts.length; i++) {
399
+ source += this.templateParts[i];
400
+ if (i < this.captures.length) {
401
+ const cap = this.captures[i];
402
+ // Skip raw code
403
+ if (cap instanceof capture_1.RawCode || (cap && typeof cap === 'object' && cap[capture_1.RAW_CODE_SYMBOL])) {
404
+ source += '${raw(...)}';
405
+ continue;
406
+ }
407
+ // Show capture name or placeholder
408
+ const name = cap[capture_1.CAPTURE_NAME_SYMBOL];
409
+ if (cap && typeof cap === 'object' && name) {
410
+ // Use mapped name for unnamed captures, or original name
411
+ const displayName = this.unnamedCaptureMapping.get(name) || name;
412
+ source += `\${${displayName}}`;
413
+ }
414
+ else {
415
+ source += '${...}';
416
+ }
417
+ }
418
+ }
419
+ return source;
420
+ }
421
+ /**
422
+ * Formats a captured value for logging.
423
+ * @private
424
+ */
425
+ formatCapturedValue(value) {
426
+ if (value === null)
427
+ return 'null';
428
+ if (value === undefined)
429
+ return 'undefined';
430
+ // Check if it's an array (variadic capture)
431
+ if (Array.isArray(value)) {
432
+ if (value.length === 0)
433
+ return '[]';
434
+ const items = value.slice(0, 3).map(v => this.formatSingleValue(v));
435
+ const suffix = value.length > 3 ? `, ... (${value.length} total)` : '';
436
+ return `[${items.join(', ')}${suffix}]`;
437
+ }
438
+ return this.formatSingleValue(value);
439
+ }
440
+ /**
441
+ * Formats a single AST node for logging.
442
+ * @private
443
+ */
444
+ formatSingleValue(value) {
445
+ if (!value || typeof value !== 'object') {
446
+ return String(value);
447
+ }
448
+ const kind = value.kind;
449
+ if (!kind)
450
+ return String(value);
451
+ // Extract simple kind name (last segment)
452
+ const kindStr = kind.split('.').pop();
453
+ // For literals, show the value
454
+ if (kindStr === 'Literal' && value.value !== undefined) {
455
+ const litValue = typeof value.value === 'string'
456
+ ? `"${value.value}"`
457
+ : String(value.value);
458
+ return `${kindStr}(${litValue})`;
459
+ }
460
+ // For identifiers, show the name
461
+ if (kindStr === 'Identifier' && value.simpleName) {
462
+ return `${kindStr}(${value.simpleName})`;
463
+ }
464
+ // Default: just the kind
465
+ return kindStr;
466
+ }
467
+ /**
468
+ * Matches a pattern against an AST node with detailed debug information.
469
+ * Part of Layer 2 (Public API).
470
+ *
471
+ * This method always enables debug logging and returns detailed information about
472
+ * the match attempt, including:
473
+ * - Whether the pattern matched
474
+ * - Captured nodes (if matched)
475
+ * - Explanation of failure (if not matched)
476
+ * - Debug log entries showing the matching process
477
+ *
478
+ * @param ast The AST node to match against
479
+ * @param cursor Optional cursor at the node's position in a larger tree
480
+ * @param debugOptions Optional debug options (defaults to all logging enabled)
481
+ * @returns Detailed result with debug information
482
+ *
483
+ * @example
484
+ * const x = capture('x');
485
+ * const pat = pattern`console.log(${x})`;
486
+ * const attempt = await pat.matchWithExplanation(node);
487
+ * if (attempt.matched) {
488
+ * console.log('Matched!');
489
+ * console.log('Captured x:', attempt.result.get('x'));
490
+ * } else {
491
+ * console.log('Failed:', attempt.explanation);
492
+ * console.log('Debug log:', attempt.debugLog);
493
+ * }
494
+ */
495
+ matchWithExplanation(ast, cursor, debugOptions) {
496
+ return __awaiter(this, void 0, void 0, function* () {
497
+ // Default to full debug logging if not specified
498
+ const options = Object.assign({ enabled: true, logComparison: true, logConstraints: true }, debugOptions);
499
+ const matcher = new Matcher(this, ast, cursor, options);
500
+ const success = yield matcher.matches();
501
+ if (success) {
502
+ // Match succeeded - return MatchResult with debug info
503
+ const storage = matcher.storage;
504
+ const matchResult = new MatchResult(new Map(storage));
505
+ return {
506
+ matched: true,
507
+ result: matchResult,
508
+ debugLog: matcher.getDebugLog()
509
+ };
510
+ }
511
+ else {
512
+ // Match failed - return explanation
513
+ return {
514
+ matched: false,
515
+ explanation: matcher.getExplanation(),
516
+ debugLog: matcher.getDebugLog()
517
+ };
518
+ }
519
+ });
520
+ }
521
+ }
522
+ exports.Pattern = Pattern;
523
+ Pattern.nextPatternId = 1;
524
+ /**
525
+ * Result of a successful pattern match containing captured values.
526
+ *
527
+ * Provides access to captured AST nodes from pattern matching operations.
528
+ * Use the `get()` method to retrieve captured values by name or by Capture object.
529
+ *
530
+ * @example
531
+ * const x = capture('x');
532
+ * const pat = pattern`foo(${x})`;
533
+ * const match = await pat.match(someNode);
534
+ * if (match) {
535
+ * const captured = match.get('x'); // Get by name
536
+ * // or
537
+ * const captured = match.get(x); // Get by Capture object
538
+ * }
539
+ *
540
+ * @example
541
+ * // Variadic captures return arrays
542
+ * const args = capture({ variadic: true });
543
+ * const pat = pattern`foo(${args})`;
544
+ * const match = await pat.match(methodInvocation);
545
+ * if (match) {
546
+ * const capturedArgs = match.get(args); // Returns J[] for variadic captures
547
+ * }
548
+ */
549
+ class MatchResult {
550
+ constructor(storage = new Map()) {
551
+ this.storage = storage;
552
+ }
553
+ // Implementation
554
+ get(capture) {
555
+ // Use symbol to get internal name without triggering Proxy
556
+ const name = typeof capture === "string" ? capture : (capture[capture_1.CAPTURE_NAME_SYMBOL] || capture.getName());
557
+ const value = this.storage.get(name);
558
+ if (value === undefined) {
559
+ return undefined;
560
+ }
561
+ return this.extractElements(value);
562
+ }
563
+ /**
564
+ * Extracts semantic elements from storage value.
565
+ * For wrappers, extracts the .element; for arrays, returns array of elements.
566
+ *
567
+ * @param value The storage value
568
+ * @returns The semantic element(s)
569
+ */
570
+ extractElements(value) {
571
+ if (Array.isArray(value)) {
572
+ // Check if it's an array of wrappers
573
+ if (value.length > 0 && value[0].element !== undefined) {
574
+ // Array of J.RightPadded - extract elements
575
+ return value.map(w => w.element);
576
+ }
577
+ // Already an array of elements
578
+ return value;
579
+ }
580
+ // Check if it's a scalar wrapper
581
+ if (value.element !== undefined) {
582
+ return value.element;
583
+ }
584
+ // Scalar element
585
+ return value;
586
+ }
587
+ /**
588
+ * Internal method to get wrappers (used by template expansion).
589
+ * Returns both scalar and variadic wrappers.
590
+ * @internal
591
+ */
592
+ [utils_1.WRAPPERS_MAP_SYMBOL]() {
593
+ const result = new Map();
594
+ for (const [name, value] of this.storage) {
595
+ if (Array.isArray(value) && value.length > 0 && value[0].element !== undefined) {
596
+ // This is an array of wrappers (variadic)
597
+ result.set(name, value);
598
+ }
599
+ else if (!Array.isArray(value) && value.element !== undefined) {
600
+ // This is a scalar wrapper
601
+ result.set(name, value);
602
+ }
603
+ }
604
+ return result;
605
+ }
606
+ }
607
+ exports.MatchResult = MatchResult;
608
+ /**
609
+ * Matcher for checking if a pattern matches an AST node and extracting captured nodes.
610
+ */
611
+ class Matcher {
612
+ /**
613
+ * Creates a new matcher for a pattern against an AST node.
614
+ *
615
+ * @param pattern The pattern to match
616
+ * @param ast The AST node to match against
617
+ * @param cursor Optional cursor at the AST node's position
618
+ * @param debugOptions Optional debug options for instrumentation
619
+ */
620
+ constructor(pattern, ast, cursor, debugOptions) {
621
+ this.pattern = pattern;
622
+ this.ast = ast;
623
+ // Unified storage: holds J for scalar captures, J.RightPadded<J>[] or J[] for variadic captures
624
+ this.storage = new Map();
625
+ this.debugLog = [];
626
+ this.currentPath = [];
627
+ // If no cursor provided, create one at the ast root so constraints can navigate up
628
+ this.cursor = cursor !== null && cursor !== void 0 ? cursor : new __1.Cursor(ast, undefined);
629
+ this.debugOptions = debugOptions !== null && debugOptions !== void 0 ? debugOptions : {};
630
+ }
631
+ /**
632
+ * Checks if the pattern matches the AST node.
633
+ *
634
+ * @returns true if the pattern matches, false otherwise
635
+ */
636
+ matches() {
637
+ return __awaiter(this, void 0, void 0, function* () {
638
+ if (!this.patternAst) {
639
+ this.patternAst = yield this.pattern.getAstPattern();
640
+ }
641
+ return this.matchNode(this.patternAst, this.ast);
642
+ });
643
+ }
644
+ /**
645
+ * Gets all captured nodes (projected view: extracts elements from wrappers).
646
+ *
647
+ * @returns A map of capture names to captured nodes
648
+ */
649
+ getAll() {
650
+ const result = new Map();
651
+ for (const [name, value] of this.storage) {
652
+ result.set(name, this.extractElements(value));
653
+ }
654
+ return result;
655
+ }
656
+ /**
657
+ * Extracts semantic elements from storage value.
658
+ * For wrappers, extracts the .element; for arrays, returns array of elements.
659
+ *
660
+ * @param value The storage value
661
+ * @returns The semantic element(s)
662
+ */
663
+ extractElements(value) {
664
+ if (Array.isArray(value)) {
665
+ // Check if it's an array of wrappers
666
+ if (value.length > 0 && value[0].element !== undefined) {
667
+ // Array of J.RightPadded - extract elements
668
+ return value.map(w => w.element);
669
+ }
670
+ // Already an array of elements
671
+ return value;
672
+ }
673
+ // Check if it's a scalar wrapper
674
+ if (value.element !== undefined) {
675
+ return value.element;
676
+ }
677
+ // Scalar element
678
+ return value;
679
+ }
680
+ /**
681
+ * Logs a debug message if debugging is enabled.
682
+ * Part of Layer 1 (Core Instrumentation).
683
+ *
684
+ * @param level The severity level
685
+ * @param scope The scope/category
686
+ * @param message The message to log
687
+ * @param data Optional data to include
688
+ */
689
+ log(level, scope, message, data) {
690
+ if (!this.debugOptions.enabled)
691
+ return;
692
+ // Filter by scope if specific logging is requested
693
+ if (scope === 'comparison' && !this.debugOptions.logComparison)
694
+ return;
695
+ if (scope === 'constraint' && !this.debugOptions.logConstraints)
696
+ return;
697
+ this.debugLog.push({
698
+ level,
699
+ scope,
700
+ path: [...this.currentPath],
701
+ message,
702
+ data
703
+ });
704
+ }
705
+ /**
706
+ * Sets the explanation for why the pattern match failed.
707
+ * Only sets the first failure (most relevant).
708
+ * Part of Layer 1 (Core Instrumentation).
709
+ *
710
+ * @param reason The reason for failure
711
+ * @param expected Human-readable description of what was expected
712
+ * @param actual Human-readable description of what was found
713
+ * @param details Optional additional context
714
+ */
715
+ setExplanation(reason, expected, actual, details) {
716
+ // Only set the first failure (most relevant)
717
+ if (this.explanation)
718
+ return;
719
+ this.explanation = {
720
+ reason,
721
+ path: [...this.currentPath],
722
+ expected,
723
+ actual,
724
+ details
725
+ };
726
+ }
727
+ /**
728
+ * Pushes a path component onto the current path.
729
+ * Used to track where in the AST tree we are during matching.
730
+ * Part of Layer 1 (Core Instrumentation).
731
+ *
732
+ * @param name The path component to push
733
+ */
734
+ pushPath(name) {
735
+ this.currentPath.push(name);
736
+ }
737
+ /**
738
+ * Pops the last path component from the current path.
739
+ * Part of Layer 1 (Core Instrumentation).
740
+ */
741
+ popPath() {
742
+ this.currentPath.pop();
743
+ }
744
+ /**
745
+ * Matches a pattern node against a target node.
746
+ *
747
+ * @param pattern The pattern node
748
+ * @param target The target node
749
+ * @returns true if the pattern matches the target, false otherwise
750
+ */
751
+ matchNode(pattern, target) {
752
+ return __awaiter(this, void 0, void 0, function* () {
753
+ var _a, _b, _c;
754
+ // Always delegate to the comparator visitor, which handles:
755
+ // - Capture detection and constraint evaluation
756
+ // - Kind checking
757
+ // - Deep structural comparison
758
+ // This centralizes all matching logic in one place
759
+ const lenientTypeMatching = (_a = this.pattern.options.lenientTypeMatching) !== null && _a !== void 0 ? _a : true;
760
+ // Factory pattern: instantiate debug or production comparator
761
+ // Zero cost in production - DebugPatternMatchingComparator is never instantiated
762
+ const matcherCallbacks = {
763
+ handleCapture: (capture, t, w) => this.handleCapture(capture, t, w),
764
+ handleVariadicCapture: (capture, ts, ws) => this.handleVariadicCapture(capture, ts, ws),
765
+ saveState: () => this.saveState(),
766
+ restoreState: (state) => this.restoreState(state),
767
+ // Debug callbacks (Layer 1) - grouped together, always present or absent
768
+ debug: this.debugOptions.enabled ? {
769
+ log: (level, scope, message, data) => this.log(level, scope, message, data),
770
+ setExplanation: (reason, expected, actual, details) => this.setExplanation(reason, expected, actual, details),
771
+ getExplanation: () => this.explanation,
772
+ restoreExplanation: (explanation) => { this.explanation = explanation; },
773
+ clearExplanation: () => { this.explanation = undefined; },
774
+ pushPath: (name) => this.pushPath(name),
775
+ popPath: () => this.popPath()
776
+ } : undefined
777
+ };
778
+ const comparator = this.debugOptions.enabled
779
+ ? new comparator_1.DebugPatternMatchingComparator(matcherCallbacks, lenientTypeMatching)
780
+ : new comparator_1.PatternMatchingComparator(matcherCallbacks, lenientTypeMatching);
781
+ // Pass cursors to allow constraints to navigate to root
782
+ // Pattern cursor is undefined (pattern is the root), target cursor is provided by user
783
+ const result = yield comparator.compare(pattern, target, undefined, this.cursor);
784
+ // If match failed and no explanation was set, provide a generic one
785
+ if (!result && this.debugOptions.enabled && !this.explanation) {
786
+ const patternKind = ((_b = pattern.kind) === null || _b === void 0 ? void 0 : _b.split('.').pop()) || 'unknown';
787
+ const targetKind = ((_c = target.kind) === null || _c === void 0 ? void 0 : _c.split('.').pop()) || 'unknown';
788
+ this.setExplanation('structural-mismatch', `Pattern node of type ${patternKind}`, `Target node of type ${targetKind}`, 'Nodes did not match structurally');
789
+ }
790
+ return result;
791
+ });
792
+ }
793
+ /**
794
+ * Saves the current state for backtracking.
795
+ * Includes both capture storage AND debug state (explanation, log, path).
796
+ *
797
+ * @returns A snapshot of the current state
798
+ */
799
+ saveState() {
800
+ return {
801
+ storage: new Map(this.storage),
802
+ debugState: this.debugOptions.enabled ? {
803
+ explanation: this.explanation,
804
+ logLength: this.debugLog.length,
805
+ path: [...this.currentPath]
806
+ } : undefined
807
+ };
808
+ }
809
+ /**
810
+ * Restores a previously saved state for backtracking.
811
+ * Restores both capture storage AND debug state.
812
+ *
813
+ * @param state The state to restore
814
+ */
815
+ restoreState(state) {
816
+ // Restore capture storage
817
+ this.storage.clear();
818
+ state.storage.forEach((value, key) => this.storage.set(key, value));
819
+ // Restore debug state if it was saved
820
+ if (state.debugState) {
821
+ // Restore explanation to the saved state
822
+ // This clears any explanations set during failed exploratory attempts (like pivot detection)
823
+ this.explanation = state.debugState.explanation;
824
+ // Truncate debug log to saved length (remove entries added during failed attempt)
825
+ this.debugLog.length = state.debugState.logLength;
826
+ // Restore path
827
+ this.currentPath.length = 0;
828
+ this.currentPath.push(...state.debugState.path);
829
+ }
830
+ }
831
+ /**
832
+ * Handles a capture placeholder.
833
+ *
834
+ * @param capture The pattern node capture
835
+ * @param target The target node
836
+ * @param wrapper Optional wrapper containing the target (for preserving markers)
837
+ * @returns true if the capture is successful, false otherwise
838
+ */
839
+ handleCapture(capture, target, wrapper) {
840
+ var _a;
841
+ const captureName = capture.captureName;
842
+ if (!captureName) {
843
+ return false;
844
+ }
845
+ // Find the original capture object to get capturing flag
846
+ // Note: Constraints are now evaluated in PatternMatchingComparator where cursor is correctly positioned
847
+ // Filter out RawCode since it doesn't have getName()
848
+ const captureObj = this.pattern.captures.find(c => !(c instanceof capture_1.RawCode || (c && typeof c === 'object' && c[capture_1.RAW_CODE_SYMBOL])) &&
849
+ c.getName() === captureName);
850
+ // Only store the binding if this is a capturing placeholder
851
+ const capturing = (_a = captureObj === null || captureObj === void 0 ? void 0 : captureObj[capture_1.CAPTURE_CAPTURING_SYMBOL]) !== null && _a !== void 0 ? _a : true;
852
+ if (capturing) {
853
+ // Store wrapper if available (preserves markers), otherwise store element
854
+ this.storage.set(captureName, wrapper !== null && wrapper !== void 0 ? wrapper : target);
855
+ }
856
+ return true;
857
+ }
858
+ /**
859
+ * Handles a variadic capture placeholder.
860
+ *
861
+ * @param capture The pattern node capture (the variadic capture)
862
+ * @param targets The target nodes that were matched
863
+ * @param wrappers Optional wrappers to preserve markers
864
+ * @returns true if the capture is successful, false otherwise
865
+ */
866
+ handleVariadicCapture(capture, targets, wrappers) {
867
+ var _a;
868
+ const captureName = capture.captureName;
869
+ if (!captureName) {
870
+ return false;
871
+ }
872
+ // Find the original capture object to get capturing flag
873
+ // Note: Constraints are now evaluated in PatternMatchingComparator where cursor is correctly positioned
874
+ // Filter out RawCode since it doesn't have getName()
875
+ const captureObj = this.pattern.captures.find(c => !(c instanceof capture_1.RawCode || (c && typeof c === 'object' && c[capture_1.RAW_CODE_SYMBOL])) &&
876
+ c.getName() === captureName);
877
+ // Only store the binding if this is a capturing placeholder
878
+ const capturing = (_a = captureObj === null || captureObj === void 0 ? void 0 : captureObj[capture_1.CAPTURE_CAPTURING_SYMBOL]) !== null && _a !== void 0 ? _a : true;
879
+ if (capturing) {
880
+ // Store the richest representation: wrappers if available, otherwise elements
881
+ if (wrappers && wrappers.length > 0) {
882
+ this.storage.set(captureName, wrappers);
883
+ }
884
+ else {
885
+ this.storage.set(captureName, targets);
886
+ }
887
+ }
888
+ return true;
889
+ }
890
+ /**
891
+ * Gets the debug log entries collected during matching.
892
+ * Part of Layer 2 (Public API).
893
+ *
894
+ * @returns The debug log entries, or undefined if debug wasn't enabled
895
+ */
896
+ getDebugLog() {
897
+ return this.debugOptions.enabled ? [...this.debugLog] : undefined;
898
+ }
899
+ /**
900
+ * Gets the explanation for why the match failed.
901
+ * Part of Layer 2 (Public API).
902
+ *
903
+ * @returns The match explanation, or undefined if match succeeded or no explanation available
904
+ */
905
+ getExplanation() {
906
+ return this.explanation;
907
+ }
908
+ }
909
+ // Implementation
910
+ function pattern(stringsOrOptions, ...captures) {
911
+ // Check if first arg is TemplateStringsArray (direct usage)
912
+ if (Array.isArray(stringsOrOptions) && 'raw' in stringsOrOptions) {
913
+ // Direct usage: pattern`...`
914
+ return createPattern(stringsOrOptions, captures, {});
915
+ }
916
+ // Options usage: pattern({ ... })`...`
917
+ const options = stringsOrOptions;
918
+ return (strings, ...caps) => {
919
+ return createPattern(strings, caps, options);
920
+ };
921
+ }
922
+ /**
923
+ * Internal helper to create a Pattern instance.
924
+ * @private
925
+ */
926
+ function createPattern(strings, captures, options) {
927
+ const capturesByName = captures.reduce((map, c) => {
928
+ // Skip raw code - it's not a capture
929
+ if (c instanceof capture_1.RawCode || (typeof c === 'object' && c && c[capture_1.RAW_CODE_SYMBOL])) {
930
+ return map;
931
+ }
932
+ const capture = typeof c === "string" ? new capture_1.CaptureImpl(c) : c;
933
+ // Use symbol to get internal name without triggering Proxy
934
+ const name = capture[capture_1.CAPTURE_NAME_SYMBOL] || capture.getName();
935
+ return map.set(name, capture);
936
+ }, new Map());
937
+ const pat = new Pattern(strings, captures.map(c => {
938
+ // Return raw code as-is
939
+ if (c instanceof capture_1.RawCode || (typeof c === 'object' && c && c[capture_1.RAW_CODE_SYMBOL])) {
940
+ return c;
941
+ }
942
+ // Use symbol to get internal name without triggering Proxy
943
+ const name = typeof c === "string" ? c : (c[capture_1.CAPTURE_NAME_SYMBOL] || c.getName());
944
+ return capturesByName.get(name);
945
+ }));
946
+ // Apply options if provided
947
+ if (options && Object.keys(options).length > 0) {
948
+ pat.configure(options);
949
+ }
950
+ return pat;
951
+ }
952
+ //# sourceMappingURL=pattern.js.map