@openrewrite/rewrite 8.66.0 → 8.66.1
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.
- package/dist/javascript/comparator.d.ts +67 -4
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +523 -2794
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/format.d.ts.map +1 -1
- package/dist/javascript/format.js +4 -3
- package/dist/javascript/format.js.map +1 -1
- package/dist/javascript/index.d.ts +1 -1
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +1 -1
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +18 -16
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/templating/capture.d.ts +226 -0
- package/dist/javascript/templating/capture.d.ts.map +1 -0
- package/dist/javascript/templating/capture.js +371 -0
- package/dist/javascript/templating/capture.js.map +1 -0
- package/dist/javascript/templating/comparator.d.ts +61 -0
- package/dist/javascript/templating/comparator.d.ts.map +1 -0
- package/dist/javascript/templating/comparator.js +393 -0
- package/dist/javascript/templating/comparator.js.map +1 -0
- package/dist/javascript/templating/engine.d.ts +75 -0
- package/dist/javascript/templating/engine.d.ts.map +1 -0
- package/dist/javascript/templating/engine.js +228 -0
- package/dist/javascript/templating/engine.js.map +1 -0
- package/dist/javascript/templating/index.d.ts +6 -0
- package/dist/javascript/templating/index.d.ts.map +1 -0
- package/dist/javascript/templating/index.js +42 -0
- package/dist/javascript/templating/index.js.map +1 -0
- package/dist/javascript/templating/pattern.d.ts +171 -0
- package/dist/javascript/templating/pattern.d.ts.map +1 -0
- package/dist/javascript/templating/pattern.js +681 -0
- package/dist/javascript/templating/pattern.js.map +1 -0
- package/dist/javascript/templating/placeholder-replacement.d.ts +58 -0
- package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -0
- package/dist/javascript/templating/placeholder-replacement.js +365 -0
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -0
- package/dist/javascript/templating/rewrite.d.ts +39 -0
- package/dist/javascript/templating/rewrite.d.ts.map +1 -0
- package/dist/javascript/templating/rewrite.js +81 -0
- package/dist/javascript/templating/rewrite.js.map +1 -0
- package/dist/javascript/templating/template.d.ts +204 -0
- package/dist/javascript/templating/template.d.ts.map +1 -0
- package/dist/javascript/templating/template.js +293 -0
- package/dist/javascript/templating/template.js.map +1 -0
- package/dist/javascript/templating/types.d.ts +263 -0
- package/dist/javascript/templating/types.d.ts.map +1 -0
- package/dist/javascript/templating/types.js +3 -0
- package/dist/javascript/templating/types.js.map +1 -0
- package/dist/javascript/templating/utils.d.ts +118 -0
- package/dist/javascript/templating/utils.d.ts.map +1 -0
- package/dist/javascript/templating/utils.js +253 -0
- package/dist/javascript/templating/utils.js.map +1 -0
- package/dist/version.txt +1 -1
- package/package.json +2 -1
- package/src/javascript/comparator.ts +554 -3323
- package/src/javascript/format.ts +3 -2
- package/src/javascript/index.ts +1 -1
- package/src/javascript/parser.ts +19 -17
- package/src/javascript/templating/capture.ts +503 -0
- package/src/javascript/templating/comparator.ts +430 -0
- package/src/javascript/templating/engine.ts +252 -0
- package/src/javascript/templating/index.ts +60 -0
- package/src/javascript/templating/pattern.ts +727 -0
- package/src/javascript/templating/placeholder-replacement.ts +372 -0
- package/src/javascript/templating/rewrite.ts +95 -0
- package/src/javascript/templating/template.ts +326 -0
- package/src/javascript/templating/types.ts +300 -0
- package/src/javascript/templating/utils.ts +284 -0
- package/dist/javascript/templating.d.ts +0 -265
- package/dist/javascript/templating.d.ts.map +0 -1
- package/dist/javascript/templating.js +0 -1027
- package/dist/javascript/templating.js.map +0 -1
- package/src/javascript/templating.ts +0 -1226
|
@@ -0,0 +1,681 @@
|
|
|
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 immer_1 = require("immer");
|
|
30
|
+
const java_1 = require("../../java");
|
|
31
|
+
const index_1 = require("../index");
|
|
32
|
+
const uuid_1 = require("../../uuid");
|
|
33
|
+
const capture_1 = require("./capture");
|
|
34
|
+
const comparator_1 = require("./comparator");
|
|
35
|
+
const utils_1 = require("./utils");
|
|
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 or Any) 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
|
|
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 or Any)
|
|
158
|
+
*/
|
|
159
|
+
constructor(templateParts, captures) {
|
|
160
|
+
this.templateParts = templateParts;
|
|
161
|
+
this.captures = captures;
|
|
162
|
+
this._options = {};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Configures this pattern with additional options.
|
|
166
|
+
*
|
|
167
|
+
* @param options Configuration options
|
|
168
|
+
* @returns This pattern for method chaining
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* pattern`isDate(${capture('date')})`
|
|
172
|
+
* .configure({
|
|
173
|
+
* imports: ['import { isDate } from \"util\"'],
|
|
174
|
+
* dependencies: { 'util': '^1.0.0' }
|
|
175
|
+
* })
|
|
176
|
+
*/
|
|
177
|
+
configure(options) {
|
|
178
|
+
this._options = Object.assign(Object.assign({}, this._options), options);
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Creates a matcher for this pattern against a specific AST node.
|
|
183
|
+
*
|
|
184
|
+
* @param ast The AST node to match against
|
|
185
|
+
* @returns A Matcher object
|
|
186
|
+
*/
|
|
187
|
+
match(ast) {
|
|
188
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
189
|
+
const matcher = new Matcher(this, ast);
|
|
190
|
+
const success = yield matcher.matches();
|
|
191
|
+
if (!success) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
// Create MatchResult with unified storage
|
|
195
|
+
const storage = matcher.storage;
|
|
196
|
+
return new MatchResult(new Map(storage));
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
exports.Pattern = Pattern;
|
|
201
|
+
/**
|
|
202
|
+
* Result of a successful pattern match containing captured values.
|
|
203
|
+
*
|
|
204
|
+
* Provides access to captured AST nodes from pattern matching operations.
|
|
205
|
+
* Use the `get()` method to retrieve captured values by name or by Capture object.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* const x = capture('x');
|
|
209
|
+
* const pat = pattern`foo(${x})`;
|
|
210
|
+
* const match = await pat.match(someNode);
|
|
211
|
+
* if (match) {
|
|
212
|
+
* const captured = match.get('x'); // Get by name
|
|
213
|
+
* // or
|
|
214
|
+
* const captured = match.get(x); // Get by Capture object
|
|
215
|
+
* }
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* // Variadic captures return arrays
|
|
219
|
+
* const args = capture({ variadic: true });
|
|
220
|
+
* const pat = pattern`foo(${args})`;
|
|
221
|
+
* const match = await pat.match(methodInvocation);
|
|
222
|
+
* if (match) {
|
|
223
|
+
* const capturedArgs = match.get(args); // Returns J[] for variadic captures
|
|
224
|
+
* }
|
|
225
|
+
*/
|
|
226
|
+
class MatchResult {
|
|
227
|
+
constructor(storage = new Map()) {
|
|
228
|
+
this.storage = storage;
|
|
229
|
+
}
|
|
230
|
+
// Implementation
|
|
231
|
+
get(capture) {
|
|
232
|
+
// Use symbol to get internal name without triggering Proxy
|
|
233
|
+
const name = typeof capture === "string" ? capture : (capture[capture_1.CAPTURE_NAME_SYMBOL] || capture.getName());
|
|
234
|
+
const value = this.storage.get(name);
|
|
235
|
+
if (value === undefined) {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
return this.extractElements(value);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Extracts semantic elements from storage value.
|
|
242
|
+
* For wrappers, extracts the .element; for arrays, returns array of elements.
|
|
243
|
+
*
|
|
244
|
+
* @param value The storage value
|
|
245
|
+
* @returns The semantic element(s)
|
|
246
|
+
*/
|
|
247
|
+
extractElements(value) {
|
|
248
|
+
if (Array.isArray(value)) {
|
|
249
|
+
// Check if it's an array of wrappers
|
|
250
|
+
if (value.length > 0 && value[0].element !== undefined) {
|
|
251
|
+
// Array of J.RightPadded - extract elements
|
|
252
|
+
return value.map(w => w.element);
|
|
253
|
+
}
|
|
254
|
+
// Already an array of elements
|
|
255
|
+
return value;
|
|
256
|
+
}
|
|
257
|
+
// Check if it's a scalar wrapper
|
|
258
|
+
if (value.element !== undefined) {
|
|
259
|
+
return value.element;
|
|
260
|
+
}
|
|
261
|
+
// Scalar element
|
|
262
|
+
return value;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Internal method to get wrappers (used by template expansion).
|
|
266
|
+
* Returns both scalar and variadic wrappers.
|
|
267
|
+
* @internal
|
|
268
|
+
*/
|
|
269
|
+
[utils_1.WRAPPERS_MAP_SYMBOL]() {
|
|
270
|
+
const result = new Map();
|
|
271
|
+
for (const [name, value] of this.storage) {
|
|
272
|
+
if (Array.isArray(value) && value.length > 0 && value[0].element !== undefined) {
|
|
273
|
+
// This is an array of wrappers (variadic)
|
|
274
|
+
result.set(name, value);
|
|
275
|
+
}
|
|
276
|
+
else if (!Array.isArray(value) && value.element !== undefined) {
|
|
277
|
+
// This is a scalar wrapper
|
|
278
|
+
result.set(name, value);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
exports.MatchResult = MatchResult;
|
|
285
|
+
/**
|
|
286
|
+
* Matcher for checking if a pattern matches an AST node and extracting captured nodes.
|
|
287
|
+
*/
|
|
288
|
+
class Matcher {
|
|
289
|
+
/**
|
|
290
|
+
* Creates a new matcher for a pattern against an AST node.
|
|
291
|
+
*
|
|
292
|
+
* @param pattern The pattern to match
|
|
293
|
+
* @param ast The AST node to match against
|
|
294
|
+
*/
|
|
295
|
+
constructor(pattern, ast) {
|
|
296
|
+
this.pattern = pattern;
|
|
297
|
+
this.ast = ast;
|
|
298
|
+
// Unified storage: holds J for scalar captures, J.RightPadded<J>[] or J[] for variadic captures
|
|
299
|
+
this.storage = new Map();
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Checks if the pattern matches the AST node.
|
|
303
|
+
*
|
|
304
|
+
* @returns true if the pattern matches, false otherwise
|
|
305
|
+
*/
|
|
306
|
+
matches() {
|
|
307
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
308
|
+
if (!this.patternAst) {
|
|
309
|
+
// Prefer 'context' over deprecated 'imports'
|
|
310
|
+
const contextStatements = this.pattern.options.context || this.pattern.options.imports || [];
|
|
311
|
+
const templateProcessor = new TemplateProcessor(this.pattern.templateParts, this.pattern.captures, contextStatements, this.pattern.options.dependencies || {});
|
|
312
|
+
this.patternAst = yield templateProcessor.toAstPattern();
|
|
313
|
+
}
|
|
314
|
+
return this.matchNode(this.patternAst, this.ast);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Gets all captured nodes (projected view: extracts elements from wrappers).
|
|
319
|
+
*
|
|
320
|
+
* @returns A map of capture names to captured nodes
|
|
321
|
+
*/
|
|
322
|
+
getAll() {
|
|
323
|
+
const result = new Map();
|
|
324
|
+
for (const [name, value] of this.storage) {
|
|
325
|
+
result.set(name, this.extractElements(value));
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Extracts semantic elements from storage value.
|
|
331
|
+
* For wrappers, extracts the .element; for arrays, returns array of elements.
|
|
332
|
+
*
|
|
333
|
+
* @param value The storage value
|
|
334
|
+
* @returns The semantic element(s)
|
|
335
|
+
*/
|
|
336
|
+
extractElements(value) {
|
|
337
|
+
if (Array.isArray(value)) {
|
|
338
|
+
// Check if it's an array of wrappers
|
|
339
|
+
if (value.length > 0 && value[0].element !== undefined) {
|
|
340
|
+
// Array of J.RightPadded - extract elements
|
|
341
|
+
return value.map(w => w.element);
|
|
342
|
+
}
|
|
343
|
+
// Already an array of elements
|
|
344
|
+
return value;
|
|
345
|
+
}
|
|
346
|
+
// Check if it's a scalar wrapper
|
|
347
|
+
if (value.element !== undefined) {
|
|
348
|
+
return value.element;
|
|
349
|
+
}
|
|
350
|
+
// Scalar element
|
|
351
|
+
return value;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Matches a pattern node against a target node.
|
|
355
|
+
*
|
|
356
|
+
* @param pattern The pattern node
|
|
357
|
+
* @param target The target node
|
|
358
|
+
* @returns true if the pattern matches the target, false otherwise
|
|
359
|
+
*/
|
|
360
|
+
matchNode(pattern, target) {
|
|
361
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
362
|
+
var _a;
|
|
363
|
+
// Check if pattern is a capture placeholder
|
|
364
|
+
if (utils_1.PlaceholderUtils.isCapture(pattern)) {
|
|
365
|
+
return this.handleCapture(pattern, target);
|
|
366
|
+
}
|
|
367
|
+
// Check if nodes have the same kind
|
|
368
|
+
if (pattern.kind !== target.kind) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
// Use the pattern matching comparator with configured lenient type matching
|
|
372
|
+
// Default to true for backward compatibility with existing patterns
|
|
373
|
+
const lenientTypeMatching = (_a = this.pattern.options.lenientTypeMatching) !== null && _a !== void 0 ? _a : true;
|
|
374
|
+
const comparator = new comparator_1.PatternMatchingComparator({
|
|
375
|
+
handleCapture: (p, t) => this.handleCapture(p, t),
|
|
376
|
+
handleVariadicCapture: (p, ts, ws) => this.handleVariadicCapture(p, ts, ws),
|
|
377
|
+
saveState: () => this.saveState(),
|
|
378
|
+
restoreState: (state) => this.restoreState(state)
|
|
379
|
+
}, lenientTypeMatching);
|
|
380
|
+
return yield comparator.compare(pattern, target);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Saves the current state of storage for backtracking.
|
|
385
|
+
*
|
|
386
|
+
* @returns A snapshot of the current state
|
|
387
|
+
*/
|
|
388
|
+
saveState() {
|
|
389
|
+
return new Map(this.storage);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Restores a previously saved state for backtracking.
|
|
393
|
+
*
|
|
394
|
+
* @param state The state to restore
|
|
395
|
+
*/
|
|
396
|
+
restoreState(state) {
|
|
397
|
+
this.storage.clear();
|
|
398
|
+
state.forEach((value, key) => this.storage.set(key, value));
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Handles a capture placeholder.
|
|
402
|
+
*
|
|
403
|
+
* @param pattern The pattern node
|
|
404
|
+
* @param target The target node
|
|
405
|
+
* @param wrapper Optional wrapper containing the target (for preserving markers)
|
|
406
|
+
* @returns true if the capture is successful, false otherwise
|
|
407
|
+
*/
|
|
408
|
+
handleCapture(pattern, target, wrapper) {
|
|
409
|
+
var _a, _b;
|
|
410
|
+
const captureName = utils_1.PlaceholderUtils.getCaptureName(pattern);
|
|
411
|
+
if (!captureName) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
// Find the original capture object to get constraint and capturing flag
|
|
415
|
+
const captureObj = this.pattern.captures.find(c => c.getName() === captureName);
|
|
416
|
+
const constraint = (_a = captureObj === null || captureObj === void 0 ? void 0 : captureObj.getConstraint) === null || _a === void 0 ? void 0 : _a.call(captureObj);
|
|
417
|
+
// Apply constraint if present
|
|
418
|
+
if (constraint && !constraint(target)) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
// Only store the binding if this is a capturing placeholder
|
|
422
|
+
const capturing = (_b = captureObj === null || captureObj === void 0 ? void 0 : captureObj[capture_1.CAPTURE_CAPTURING_SYMBOL]) !== null && _b !== void 0 ? _b : true;
|
|
423
|
+
if (capturing) {
|
|
424
|
+
// Store wrapper if available (preserves markers), otherwise store element
|
|
425
|
+
this.storage.set(captureName, wrapper !== null && wrapper !== void 0 ? wrapper : target);
|
|
426
|
+
}
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Handles a variadic capture placeholder.
|
|
431
|
+
*
|
|
432
|
+
* @param pattern The pattern node (the variadic capture)
|
|
433
|
+
* @param targets The target nodes that were matched
|
|
434
|
+
* @param wrappers Optional wrappers to preserve markers
|
|
435
|
+
* @returns true if the capture is successful, false otherwise
|
|
436
|
+
*/
|
|
437
|
+
handleVariadicCapture(pattern, targets, wrappers) {
|
|
438
|
+
var _a, _b;
|
|
439
|
+
const captureName = utils_1.PlaceholderUtils.getCaptureName(pattern);
|
|
440
|
+
if (!captureName) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
// Find the original capture object to get constraint and capturing flag
|
|
444
|
+
const captureObj = this.pattern.captures.find(c => c.getName() === captureName);
|
|
445
|
+
const constraint = (_a = captureObj === null || captureObj === void 0 ? void 0 : captureObj.getConstraint) === null || _a === void 0 ? void 0 : _a.call(captureObj);
|
|
446
|
+
// Apply constraint if present - for variadic captures, constraint receives the array of elements
|
|
447
|
+
if (constraint && !constraint(targets)) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
// Only store the binding if this is a capturing placeholder
|
|
451
|
+
const capturing = (_b = captureObj === null || captureObj === void 0 ? void 0 : captureObj[capture_1.CAPTURE_CAPTURING_SYMBOL]) !== null && _b !== void 0 ? _b : true;
|
|
452
|
+
if (capturing) {
|
|
453
|
+
// Store the richest representation: wrappers if available, otherwise elements
|
|
454
|
+
if (wrappers && wrappers.length > 0) {
|
|
455
|
+
this.storage.set(captureName, wrappers);
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
this.storage.set(captureName, targets);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Processor for template strings.
|
|
466
|
+
* Converts a template string with captures into an AST pattern.
|
|
467
|
+
*/
|
|
468
|
+
class TemplateProcessor {
|
|
469
|
+
/**
|
|
470
|
+
* Creates a new template processor.
|
|
471
|
+
*
|
|
472
|
+
* @param templateParts The string parts of the template
|
|
473
|
+
* @param captures The captures between the string parts (can be Capture or Any)
|
|
474
|
+
* @param contextStatements Context declarations (imports, types, etc.) to prepend for type attribution
|
|
475
|
+
* @param dependencies NPM dependencies for type attribution
|
|
476
|
+
*/
|
|
477
|
+
constructor(templateParts, captures, contextStatements = [], dependencies = {}) {
|
|
478
|
+
this.templateParts = templateParts;
|
|
479
|
+
this.captures = captures;
|
|
480
|
+
this.contextStatements = contextStatements;
|
|
481
|
+
this.dependencies = dependencies;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Converts the template to an AST pattern.
|
|
485
|
+
*
|
|
486
|
+
* @returns A Promise resolving to the AST pattern
|
|
487
|
+
*/
|
|
488
|
+
toAstPattern() {
|
|
489
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
490
|
+
// Combine template parts and placeholders
|
|
491
|
+
const templateString = this.buildTemplateString();
|
|
492
|
+
// Use cache to get or parse the compilation unit
|
|
493
|
+
const cu = yield utils_1.templateCache.getOrParse(templateString, this.captures, this.contextStatements, this.dependencies);
|
|
494
|
+
// Extract the relevant part of the AST
|
|
495
|
+
return this.extractPatternFromAst(cu);
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Builds a template string with placeholders for captures.
|
|
500
|
+
* If the template looks like a block pattern, wraps it in a function.
|
|
501
|
+
*
|
|
502
|
+
* @returns The template string
|
|
503
|
+
*/
|
|
504
|
+
buildTemplateString() {
|
|
505
|
+
let result = '';
|
|
506
|
+
for (let i = 0; i < this.templateParts.length; i++) {
|
|
507
|
+
result += this.templateParts[i];
|
|
508
|
+
if (i < this.captures.length) {
|
|
509
|
+
const capture = this.captures[i];
|
|
510
|
+
// Use symbol to access capture name without triggering Proxy
|
|
511
|
+
const captureName = capture[capture_1.CAPTURE_NAME_SYMBOL] || capture.getName();
|
|
512
|
+
result += utils_1.PlaceholderUtils.createCapture(captureName, undefined);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// Check if this looks like a block pattern (starts with { and contains statement keywords)
|
|
516
|
+
const trimmed = result.trim();
|
|
517
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
518
|
+
// Check for statement keywords that indicate this is a block, not an object literal
|
|
519
|
+
const hasStatementKeywords = /\b(return|if|for|while|do|switch|try|throw|break|continue|const|let|var|function|class)\b/.test(result);
|
|
520
|
+
if (hasStatementKeywords) {
|
|
521
|
+
// Wrap in a function to ensure it parses as a block
|
|
522
|
+
return `function __PATTERN__() ${result}`;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return result;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Extracts the pattern from the parsed AST.
|
|
529
|
+
*
|
|
530
|
+
* @param cu The compilation unit
|
|
531
|
+
* @returns The extracted pattern
|
|
532
|
+
*/
|
|
533
|
+
extractPatternFromAst(cu) {
|
|
534
|
+
var _a, _b;
|
|
535
|
+
// Skip context statements to get to the actual pattern code
|
|
536
|
+
const patternStatementIndex = this.contextStatements.length;
|
|
537
|
+
// Check if we have any statements at the pattern index
|
|
538
|
+
if (!cu.statements || patternStatementIndex >= cu.statements.length) {
|
|
539
|
+
// If there's no statement at the index, but we have exactly one statement
|
|
540
|
+
// and it's a block, it might be the pattern itself (e.g., pattern`{ ... }`)
|
|
541
|
+
if (cu.statements && cu.statements.length === 1 && cu.statements[0].element.kind === java_1.J.Kind.Block) {
|
|
542
|
+
return this.attachCaptureMarkers(cu.statements[0].element);
|
|
543
|
+
}
|
|
544
|
+
throw new Error(`No statement found at index ${patternStatementIndex} in compilation unit with ${((_a = cu.statements) === null || _a === void 0 ? void 0 : _a.length) || 0} statements`);
|
|
545
|
+
}
|
|
546
|
+
// Extract the relevant part of the AST based on the template content
|
|
547
|
+
const firstStatement = cu.statements[patternStatementIndex].element;
|
|
548
|
+
let extracted;
|
|
549
|
+
// Check if this is our wrapper function for block patterns
|
|
550
|
+
if (firstStatement.kind === java_1.J.Kind.MethodDeclaration) {
|
|
551
|
+
const method = firstStatement;
|
|
552
|
+
if (((_b = method.name) === null || _b === void 0 ? void 0 : _b.simpleName) === '__PATTERN__' && method.body) {
|
|
553
|
+
// Extract the block from the wrapper function
|
|
554
|
+
extracted = method.body;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
extracted = firstStatement;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
else if (firstStatement.kind === index_1.JS.Kind.ExpressionStatement) {
|
|
561
|
+
// If the first statement is an expression statement, extract the expression
|
|
562
|
+
extracted = firstStatement.expression;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
// Otherwise, return the statement itself
|
|
566
|
+
extracted = firstStatement;
|
|
567
|
+
}
|
|
568
|
+
// Attach CaptureMarkers to capture identifiers
|
|
569
|
+
return this.attachCaptureMarkers(extracted);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Attaches CaptureMarkers to capture identifiers in the AST.
|
|
573
|
+
* This allows efficient capture detection without string parsing.
|
|
574
|
+
*
|
|
575
|
+
* @param ast The AST to process
|
|
576
|
+
* @returns The AST with CaptureMarkers attached
|
|
577
|
+
*/
|
|
578
|
+
attachCaptureMarkers(ast) {
|
|
579
|
+
const visited = new Set();
|
|
580
|
+
return (0, immer_1.produce)(ast, draft => {
|
|
581
|
+
this.visitAndAttachMarkers(draft, visited);
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Recursively visits AST nodes and attaches CaptureMarkers to capture identifiers.
|
|
586
|
+
* For statement-level captures (identifiers in ExpressionStatement), the marker
|
|
587
|
+
* is attached to the ExpressionStatement itself rather than the nested identifier.
|
|
588
|
+
*
|
|
589
|
+
* @param node The node to visit
|
|
590
|
+
* @param visited Set of already visited nodes to avoid cycles
|
|
591
|
+
*/
|
|
592
|
+
visitAndAttachMarkers(node, visited) {
|
|
593
|
+
var _a, _b, _c;
|
|
594
|
+
if (!node || typeof node !== 'object' || visited.has(node)) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
// Mark as visited to avoid cycles
|
|
598
|
+
visited.add(node);
|
|
599
|
+
// Check if this is an ExpressionStatement containing a capture identifier
|
|
600
|
+
// For statement-level captures, we attach the marker to the ExpressionStatement itself
|
|
601
|
+
if (node.kind === index_1.JS.Kind.ExpressionStatement &&
|
|
602
|
+
((_a = node.expression) === null || _a === void 0 ? void 0 : _a.kind) === java_1.J.Kind.Identifier &&
|
|
603
|
+
((_b = node.expression.simpleName) === null || _b === void 0 ? void 0 : _b.startsWith(utils_1.PlaceholderUtils.CAPTURE_PREFIX))) {
|
|
604
|
+
const captureInfo = utils_1.PlaceholderUtils.parseCapture(node.expression.simpleName);
|
|
605
|
+
if (captureInfo) {
|
|
606
|
+
// Initialize markers on the ExpressionStatement
|
|
607
|
+
if (!node.markers) {
|
|
608
|
+
node.markers = { kind: 'org.openrewrite.marker.Markers', id: (0, uuid_1.randomId)(), markers: [] };
|
|
609
|
+
}
|
|
610
|
+
if (!node.markers.markers) {
|
|
611
|
+
node.markers.markers = [];
|
|
612
|
+
}
|
|
613
|
+
// Find the original capture object to get variadic options
|
|
614
|
+
const captureObj = this.captures.find(c => c.getName() === captureInfo.name);
|
|
615
|
+
const variadicOptions = captureObj === null || captureObj === void 0 ? void 0 : captureObj.getVariadicOptions();
|
|
616
|
+
// Add CaptureMarker to the ExpressionStatement
|
|
617
|
+
node.markers.markers.push(new utils_1.CaptureMarker(captureInfo.name, variadicOptions));
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// For non-statement captures (expressions), attach marker to the identifier
|
|
621
|
+
else if (node.kind === java_1.J.Kind.Identifier && ((_c = node.simpleName) === null || _c === void 0 ? void 0 : _c.startsWith(utils_1.PlaceholderUtils.CAPTURE_PREFIX))) {
|
|
622
|
+
const captureInfo = utils_1.PlaceholderUtils.parseCapture(node.simpleName);
|
|
623
|
+
if (captureInfo) {
|
|
624
|
+
// Initialize markers if needed
|
|
625
|
+
if (!node.markers) {
|
|
626
|
+
node.markers = { kind: 'org.openrewrite.marker.Markers', id: (0, uuid_1.randomId)(), markers: [] };
|
|
627
|
+
}
|
|
628
|
+
if (!node.markers.markers) {
|
|
629
|
+
node.markers.markers = [];
|
|
630
|
+
}
|
|
631
|
+
// Find the original capture object to get variadic options
|
|
632
|
+
const captureObj = this.captures.find(c => c.getName() === captureInfo.name);
|
|
633
|
+
const variadicOptions = captureObj === null || captureObj === void 0 ? void 0 : captureObj.getVariadicOptions();
|
|
634
|
+
// Add CaptureMarker with variadic options if available
|
|
635
|
+
node.markers.markers.push(new utils_1.CaptureMarker(captureInfo.name, variadicOptions));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
// Recursively visit all properties
|
|
639
|
+
for (const key in node) {
|
|
640
|
+
if (node.hasOwnProperty(key)) {
|
|
641
|
+
const value = node[key];
|
|
642
|
+
if (Array.isArray(value)) {
|
|
643
|
+
value.forEach(item => this.visitAndAttachMarkers(item, visited));
|
|
644
|
+
}
|
|
645
|
+
else if (typeof value === 'object' && value !== null) {
|
|
646
|
+
this.visitAndAttachMarkers(value, visited);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Tagged template function for creating patterns.
|
|
654
|
+
*
|
|
655
|
+
* @param strings The string parts of the template
|
|
656
|
+
* @param captures The captures between the string parts (Capture, Any, or string names)
|
|
657
|
+
* @returns A Pattern object
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* // Using the same capture multiple times for repeated patterns
|
|
661
|
+
* const expr = capture('expr');
|
|
662
|
+
* const redundantOr = pattern`${expr} || ${expr}`;
|
|
663
|
+
*
|
|
664
|
+
* @example
|
|
665
|
+
* // Using any() for non-capturing matches
|
|
666
|
+
* const pat = pattern`foo(${any()})`;
|
|
667
|
+
*/
|
|
668
|
+
function pattern(strings, ...captures) {
|
|
669
|
+
const capturesByName = captures.reduce((map, c) => {
|
|
670
|
+
const capture = typeof c === "string" ? new capture_1.CaptureImpl(c) : c;
|
|
671
|
+
// Use symbol to get internal name without triggering Proxy
|
|
672
|
+
const name = capture[capture_1.CAPTURE_NAME_SYMBOL] || capture.getName();
|
|
673
|
+
return map.set(name, capture);
|
|
674
|
+
}, new Map());
|
|
675
|
+
return new Pattern(strings, captures.map(c => {
|
|
676
|
+
// Use symbol to get internal name without triggering Proxy
|
|
677
|
+
const name = typeof c === "string" ? c : (c[capture_1.CAPTURE_NAME_SYMBOL] || c.getName());
|
|
678
|
+
return capturesByName.get(name);
|
|
679
|
+
}));
|
|
680
|
+
}
|
|
681
|
+
//# sourceMappingURL=pattern.js.map
|