@openrewrite/rewrite 8.67.0-20251104-084009 → 8.67.0-20251104-114312
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 +3 -2
- 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/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 +2 -1
- package/src/javascript/index.ts +1 -1
- 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,326 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 the original author or authors.
|
|
3
|
+
* <p>
|
|
4
|
+
* Licensed under the Moderne Source Available License (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* <p>
|
|
8
|
+
* https://docs.moderne.io/licensing/moderne-source-available-license
|
|
9
|
+
* <p>
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import {Cursor, Tree} from '../..';
|
|
17
|
+
import {J} from '../../java';
|
|
18
|
+
import {TemplateOptions, TemplateParameter, Capture} from './types';
|
|
19
|
+
import {MatchResult} from './pattern';
|
|
20
|
+
import {WRAPPERS_MAP_SYMBOL} from './utils';
|
|
21
|
+
import {CAPTURE_NAME_SYMBOL} from './capture';
|
|
22
|
+
import {TemplateEngine, Parameter} from './engine';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Coordinates for template application.
|
|
26
|
+
*/
|
|
27
|
+
type JavaCoordinates = {
|
|
28
|
+
tree?: Tree;
|
|
29
|
+
loc?: JavaCoordinates.Location;
|
|
30
|
+
mode?: JavaCoordinates.Mode;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
namespace JavaCoordinates {
|
|
34
|
+
// FIXME need to come up with the equivalent of `Space.Location` support
|
|
35
|
+
export type Location = 'EXPRESSION_PREFIX' | 'STATEMENT_PREFIX' | 'BLOCK_END';
|
|
36
|
+
|
|
37
|
+
export enum Mode {
|
|
38
|
+
Before,
|
|
39
|
+
After,
|
|
40
|
+
Replace,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Builder for creating templates programmatically.
|
|
46
|
+
* Use when template structure is not known at compile time.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Conditional construction
|
|
50
|
+
* const builder = Template.builder().code('function foo(x) {');
|
|
51
|
+
* if (needsValidation) {
|
|
52
|
+
* builder.code('if (typeof x !== "number") throw new Error("Invalid");');
|
|
53
|
+
* }
|
|
54
|
+
* builder.code('return x * 2; }');
|
|
55
|
+
* const tmpl = builder.build();
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // Composition from fragments
|
|
59
|
+
* function createWrapper(innerBody: Capture): Template {
|
|
60
|
+
* return Template.builder()
|
|
61
|
+
* .code('function wrapper() { try { ')
|
|
62
|
+
* .param(innerBody)
|
|
63
|
+
* .code(' } catch(e) { console.error(e); } }')
|
|
64
|
+
* .build();
|
|
65
|
+
* }
|
|
66
|
+
*/
|
|
67
|
+
export class TemplateBuilder {
|
|
68
|
+
private parts: string[] = [];
|
|
69
|
+
private params: TemplateParameter[] = [];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Adds a static string part to the template.
|
|
73
|
+
*
|
|
74
|
+
* @param str The string to add
|
|
75
|
+
* @returns This builder for chaining
|
|
76
|
+
*/
|
|
77
|
+
code(str: string): this {
|
|
78
|
+
// If there are already params, we need to add an empty string before this
|
|
79
|
+
if (this.params.length > this.parts.length) {
|
|
80
|
+
this.parts.push('');
|
|
81
|
+
}
|
|
82
|
+
// Append to the last part or start a new one
|
|
83
|
+
if (this.parts.length === 0) {
|
|
84
|
+
this.parts.push(str);
|
|
85
|
+
} else {
|
|
86
|
+
this.parts[this.parts.length - 1] += str;
|
|
87
|
+
}
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Adds a parameter to the template.
|
|
93
|
+
*
|
|
94
|
+
* @param value The parameter value (Capture, Tree, or primitive)
|
|
95
|
+
* @returns This builder for chaining
|
|
96
|
+
*/
|
|
97
|
+
param(value: TemplateParameter): this {
|
|
98
|
+
// Ensure we have a part for after this parameter
|
|
99
|
+
if (this.parts.length === 0) {
|
|
100
|
+
this.parts.push('');
|
|
101
|
+
}
|
|
102
|
+
this.params.push(value);
|
|
103
|
+
// Add an empty string for the next part
|
|
104
|
+
this.parts.push('');
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Builds the template from accumulated parts and parameters.
|
|
110
|
+
*
|
|
111
|
+
* @returns A Template instance
|
|
112
|
+
*/
|
|
113
|
+
build(): Template {
|
|
114
|
+
// Ensure parts array is one longer than params array
|
|
115
|
+
while (this.parts.length <= this.params.length) {
|
|
116
|
+
this.parts.push('');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Create a synthetic TemplateStringsArray
|
|
120
|
+
const templateStrings = this.parts.slice() as any;
|
|
121
|
+
templateStrings.raw = this.parts.slice();
|
|
122
|
+
Object.defineProperty(templateStrings, 'raw', {
|
|
123
|
+
value: this.parts.slice(),
|
|
124
|
+
writable: false
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Delegate to the template() function
|
|
128
|
+
return template(templateStrings, ...this.params);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Template for creating AST nodes.
|
|
134
|
+
*
|
|
135
|
+
* This class provides the public API for template generation.
|
|
136
|
+
* The actual templating logic is handled by the internal TemplateEngine.
|
|
137
|
+
*
|
|
138
|
+
* Templates can reference captures from patterns, and you can access properties
|
|
139
|
+
* of captured nodes using dot notation. This allows you to extract and insert
|
|
140
|
+
* specific subtrees from matched AST nodes.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* // Generate a literal AST node
|
|
144
|
+
* const result = template`2`.apply(cursor, coordinates);
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* // Generate an AST node with a parameter
|
|
148
|
+
* const result = template`${capture()}`.apply(cursor, coordinates);
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* // Access properties of captured nodes in templates
|
|
152
|
+
* const method = capture<J.MethodInvocation>('method');
|
|
153
|
+
* const pat = pattern`foo(${method})`;
|
|
154
|
+
* const tmpl = template`bar(${method.name})`; // Access the 'name' property
|
|
155
|
+
*
|
|
156
|
+
* const match = await pat.match(someNode);
|
|
157
|
+
* if (match) {
|
|
158
|
+
* // The template will insert just the 'name' subtree from the captured method
|
|
159
|
+
* const result = await tmpl.apply(cursor, someNode, match);
|
|
160
|
+
* }
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* // Deep property access chains
|
|
164
|
+
* const method = capture<J.MethodInvocation>('method');
|
|
165
|
+
* template`console.log(${method.name.simpleName})` // Navigate multiple properties
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* // Array element access
|
|
169
|
+
* const invocation = capture<J.MethodInvocation>('invocation');
|
|
170
|
+
* template`bar(${invocation.arguments.elements[0].element})` // Access array elements
|
|
171
|
+
*/
|
|
172
|
+
export class Template {
|
|
173
|
+
private options: TemplateOptions = {};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Creates a new builder for constructing templates programmatically.
|
|
177
|
+
*
|
|
178
|
+
* @returns A new TemplateBuilder instance
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* const tmpl = Template.builder()
|
|
182
|
+
* .code('function foo() {')
|
|
183
|
+
* .code('return ')
|
|
184
|
+
* .param(capture('value'))
|
|
185
|
+
* .code('; }')
|
|
186
|
+
* .build();
|
|
187
|
+
*/
|
|
188
|
+
static builder(): TemplateBuilder {
|
|
189
|
+
return new TemplateBuilder();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Creates a new template.
|
|
194
|
+
*
|
|
195
|
+
* @param templateParts The string parts of the template
|
|
196
|
+
* @param parameters The parameters between the string parts
|
|
197
|
+
*/
|
|
198
|
+
constructor(
|
|
199
|
+
private readonly templateParts: TemplateStringsArray,
|
|
200
|
+
private readonly parameters: Parameter[]
|
|
201
|
+
) {
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Configures this template with additional options.
|
|
206
|
+
*
|
|
207
|
+
* @param options Configuration options
|
|
208
|
+
* @returns This template for method chaining
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* template`isDate(${capture('date')})`
|
|
212
|
+
* .configure({
|
|
213
|
+
* imports: ['import { isDate } from "util"'],
|
|
214
|
+
* dependencies: { 'util': '^1.0.0' }
|
|
215
|
+
* })
|
|
216
|
+
*/
|
|
217
|
+
configure(options: TemplateOptions): Template {
|
|
218
|
+
this.options = { ...this.options, ...options };
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Applies this template and returns the resulting tree.
|
|
224
|
+
*
|
|
225
|
+
* @param cursor The cursor pointing to the current location in the AST
|
|
226
|
+
* @param tree Input tree
|
|
227
|
+
* @param values values for parameters in template
|
|
228
|
+
* @returns A Promise resolving to the generated AST node
|
|
229
|
+
*/
|
|
230
|
+
async apply(cursor: Cursor, tree: J, values?: Map<Capture | string, J> | Pick<Map<string, J>, 'get'>): Promise<J | undefined> {
|
|
231
|
+
// Normalize the values map: convert any Capture keys to string keys
|
|
232
|
+
let normalizedValues: Pick<Map<string, J>, 'get'> | undefined;
|
|
233
|
+
let wrappersMap: Map<string, J.RightPadded<J> | J.RightPadded<J>[]> = new Map();
|
|
234
|
+
|
|
235
|
+
if (values instanceof Map) {
|
|
236
|
+
const normalized = new Map<string, J>();
|
|
237
|
+
for (const [key, value] of values.entries()) {
|
|
238
|
+
const stringKey = typeof key === 'string'
|
|
239
|
+
? key
|
|
240
|
+
: ((key as any)[CAPTURE_NAME_SYMBOL] || key.getName());
|
|
241
|
+
normalized.set(stringKey, value);
|
|
242
|
+
}
|
|
243
|
+
normalizedValues = normalized;
|
|
244
|
+
} else if (values instanceof MatchResult) {
|
|
245
|
+
// MatchResult - extract both bindings and wrappersMap
|
|
246
|
+
normalizedValues = values;
|
|
247
|
+
wrappersMap = (values as any)[WRAPPERS_MAP_SYMBOL]();
|
|
248
|
+
} else {
|
|
249
|
+
// Other Pick<Map> implementation
|
|
250
|
+
normalizedValues = values;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Prefer 'context' over deprecated 'imports'
|
|
254
|
+
const contextStatements = this.options.context || this.options.imports || [];
|
|
255
|
+
return TemplateEngine.applyTemplate(
|
|
256
|
+
this.templateParts,
|
|
257
|
+
this.parameters,
|
|
258
|
+
cursor,
|
|
259
|
+
{
|
|
260
|
+
tree,
|
|
261
|
+
mode: JavaCoordinates.Mode.Replace
|
|
262
|
+
},
|
|
263
|
+
normalizedValues,
|
|
264
|
+
wrappersMap,
|
|
265
|
+
contextStatements,
|
|
266
|
+
this.options.dependencies || {}
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Tagged template function for creating templates that generate AST nodes.
|
|
273
|
+
*
|
|
274
|
+
* Templates support property access on captures from patterns, allowing you to
|
|
275
|
+
* extract and insert specific subtrees from matched AST nodes. Use dot notation
|
|
276
|
+
* to navigate properties (e.g., `method.name`) or array bracket notation to
|
|
277
|
+
* access array elements (e.g., `args.elements[0].element`).
|
|
278
|
+
*
|
|
279
|
+
* @param strings The string parts of the template
|
|
280
|
+
* @param parameters The parameters between the string parts (Capture, Tree, or primitives)
|
|
281
|
+
* @returns A Template object that can be applied to generate AST nodes
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* // Simple template with literal
|
|
285
|
+
* const tmpl = template`console.log("hello")`;
|
|
286
|
+
* const result = await tmpl.apply(cursor, node);
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* // Template with capture - matches captured value from pattern
|
|
290
|
+
* const expr = capture('expr');
|
|
291
|
+
* const pat = pattern`foo(${expr})`;
|
|
292
|
+
* const tmpl = template`bar(${expr})`;
|
|
293
|
+
*
|
|
294
|
+
* const match = await pat.match(node);
|
|
295
|
+
* if (match) {
|
|
296
|
+
* const result = await tmpl.apply(cursor, node, match);
|
|
297
|
+
* }
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* // Property access on captures - extract subtrees
|
|
301
|
+
* const method = capture<J.MethodInvocation>('method');
|
|
302
|
+
* const pat = pattern`foo(${method})`;
|
|
303
|
+
* // Access the 'name' property of the captured method invocation
|
|
304
|
+
* const tmpl = template`bar(${method.name})`;
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* // Deep property chains
|
|
308
|
+
* const method = capture<J.MethodInvocation>('method');
|
|
309
|
+
* template`console.log(${method.name.simpleName})`
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* // Array element access
|
|
313
|
+
* const invocation = capture<J.MethodInvocation>('invocation');
|
|
314
|
+
* template`bar(${invocation.arguments.elements[0].element})`
|
|
315
|
+
*/
|
|
316
|
+
export function template(strings: TemplateStringsArray, ...parameters: TemplateParameter[]): Template {
|
|
317
|
+
// Convert parameters to Parameter objects (no longer need to check for mutable tree property)
|
|
318
|
+
const processedParameters = parameters.map(param => {
|
|
319
|
+
// Just wrap each parameter value in a Parameter object
|
|
320
|
+
return {value: param};
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
return new Template(strings, processedParameters);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export type {JavaCoordinates};
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 the original author or authors.
|
|
3
|
+
* <p>
|
|
4
|
+
* Licensed under the Moderne Source Available License (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* <p>
|
|
8
|
+
* https://docs.moderne.io/licensing/moderne-source-available-license
|
|
9
|
+
* <p>
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import {Cursor, Tree} from '../..';
|
|
17
|
+
import {J} from '../../java';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Options for variadic captures that match zero or more nodes in a sequence.
|
|
21
|
+
*/
|
|
22
|
+
export interface VariadicOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Minimum number of nodes that must be matched (default: 0).
|
|
25
|
+
*/
|
|
26
|
+
min?: number;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Maximum number of nodes that can be matched (default: unlimited).
|
|
30
|
+
*/
|
|
31
|
+
max?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Options for the capture function.
|
|
36
|
+
*
|
|
37
|
+
* The constraint function receives different parameter types depending on whether
|
|
38
|
+
* the capture is variadic:
|
|
39
|
+
* - For regular captures: constraint receives a single node of type T
|
|
40
|
+
* - For variadic captures: constraint receives an array of nodes of type T[]
|
|
41
|
+
*/
|
|
42
|
+
export interface CaptureOptions<T = any> {
|
|
43
|
+
name?: string;
|
|
44
|
+
variadic?: boolean | VariadicOptions;
|
|
45
|
+
constraint?: (node: T) => boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Capture specification for pattern matching.
|
|
50
|
+
* Represents a placeholder in a template pattern that can capture a part of the AST.
|
|
51
|
+
*
|
|
52
|
+
* @template T The expected type of the captured AST node (for TypeScript autocomplete)
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* **Important: Type Parameter is for IDE Support Only**
|
|
56
|
+
*
|
|
57
|
+
* The generic type parameter `<T>` provides IDE autocomplete and type checking in your code,
|
|
58
|
+
* but does NOT enforce any runtime constraints on what the capture will match.
|
|
59
|
+
*
|
|
60
|
+
* **Pattern Matching Behavior:**
|
|
61
|
+
* - A bare `pattern`${capture('x')}`` will structurally match ANY expression
|
|
62
|
+
* - Pattern structure determines matching: `pattern`foo(${capture('x')})`` only matches `foo()` calls
|
|
63
|
+
* - Use structural patterns to narrow matching scope before applying semantic validation
|
|
64
|
+
*
|
|
65
|
+
* **Variadic Captures:**
|
|
66
|
+
* Use `{ variadic: true }` to match zero or more nodes in a sequence:
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const args = capture('args', { variadic: true });
|
|
69
|
+
* pattern`foo(${args})` // Matches: foo(), foo(a), foo(a, b, c)
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export interface Capture<T = any> {
|
|
73
|
+
/**
|
|
74
|
+
* Gets the string name of this capture.
|
|
75
|
+
*/
|
|
76
|
+
getName(): string;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns true if this is a variadic capture (matching zero or more nodes).
|
|
80
|
+
*/
|
|
81
|
+
isVariadic(): boolean;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Returns the variadic options if this is a variadic capture, undefined otherwise.
|
|
85
|
+
*/
|
|
86
|
+
getVariadicOptions(): VariadicOptions | undefined;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Gets the constraint function if this capture has one.
|
|
90
|
+
* For regular captures (T = Expression), constraint receives a single node.
|
|
91
|
+
* For variadic captures (T = Expression[]), constraint receives an array of nodes.
|
|
92
|
+
*/
|
|
93
|
+
getConstraint?(): ((node: T) => boolean) | undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Non-capturing pattern match specification.
|
|
98
|
+
* Represents a placeholder in a pattern that matches AST nodes without binding them to a name.
|
|
99
|
+
*
|
|
100
|
+
* Use `any()` when you need to match structure without caring about the specific values.
|
|
101
|
+
* The key difference from `Capture` is that `Any` cannot be used in templates - the TypeScript
|
|
102
|
+
* type system prevents this at compile time.
|
|
103
|
+
*
|
|
104
|
+
* @template T The expected type of the matched AST node (for TypeScript autocomplete and constraints)
|
|
105
|
+
*
|
|
106
|
+
* @remarks
|
|
107
|
+
* **Why Any<T> is Separate from Capture<T>:**
|
|
108
|
+
*
|
|
109
|
+
* Using a separate type provides compile-time safety:
|
|
110
|
+
* - `pattern`foo(${any()})`` - ✅ OK in patterns
|
|
111
|
+
* - `template`bar(${any()})`` - ❌ TypeScript error (Any<T> not assignable to template parameters)
|
|
112
|
+
*
|
|
113
|
+
* This prevents logical errors where you try to use a non-capturing match in a template.
|
|
114
|
+
*
|
|
115
|
+
* **Semantic Parallel with TypeScript's `any`:**
|
|
116
|
+
*
|
|
117
|
+
* Just as TypeScript's `any` type means "be permissive about types here",
|
|
118
|
+
* pattern matching's `any()` means "be permissive about values here":
|
|
119
|
+
* - TypeScript `any`: Accept any type, don't check it
|
|
120
|
+
* - Pattern `any()`: Match any value, don't bind it
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* // Match without capturing
|
|
124
|
+
* const pat = pattern`foo(${any()})`
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* // Variadic any - match zero or more without capturing
|
|
128
|
+
* const rest = any({ variadic: true });
|
|
129
|
+
* const pat = pattern`bar(${capture('first')}, ${rest})`
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* // With constraints - validate but don't capture
|
|
133
|
+
* const numericArg = any<J.Literal>({
|
|
134
|
+
* constraint: (node) => typeof node.value === 'number'
|
|
135
|
+
* });
|
|
136
|
+
* const pat = pattern`process(${numericArg})`
|
|
137
|
+
*/
|
|
138
|
+
export interface Any<T = any> {
|
|
139
|
+
/**
|
|
140
|
+
* Gets the internal identifier for this any pattern.
|
|
141
|
+
*/
|
|
142
|
+
getName(): string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Returns true if this is a variadic any (matching zero or more nodes).
|
|
146
|
+
*/
|
|
147
|
+
isVariadic(): boolean;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Returns the variadic options if this is a variadic any, undefined otherwise.
|
|
151
|
+
*/
|
|
152
|
+
getVariadicOptions(): VariadicOptions | undefined;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Gets the constraint function if this any pattern has one.
|
|
156
|
+
* For regular any (T = Expression), constraint receives a single node.
|
|
157
|
+
* For variadic any (T = Expression[]), constraint receives an array of nodes.
|
|
158
|
+
*/
|
|
159
|
+
getConstraint?(): ((node: T) => boolean) | undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Template parameter specification for template-only parameter substitution.
|
|
164
|
+
* Unlike Capture, TemplateParam does not support property access and is simpler.
|
|
165
|
+
*
|
|
166
|
+
* @template T The expected type of the parameter value (for TypeScript autocomplete only)
|
|
167
|
+
*/
|
|
168
|
+
export interface TemplateParam<T = any> {
|
|
169
|
+
/**
|
|
170
|
+
* The name of the parameter, used to look up the value in the values map.
|
|
171
|
+
*/
|
|
172
|
+
readonly name: string;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Gets the string name of this parameter.
|
|
176
|
+
*/
|
|
177
|
+
getName(): string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Configuration options for patterns.
|
|
182
|
+
*/
|
|
183
|
+
export interface PatternOptions {
|
|
184
|
+
/**
|
|
185
|
+
* Declarations to provide type attribution context for the pattern.
|
|
186
|
+
* These can include import statements, type declarations, function declarations, or any
|
|
187
|
+
* other declarations needed for proper type information. They are prepended to the pattern
|
|
188
|
+
* when parsing to ensure proper type attribution.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* pattern`forwardRef(${capture('comp')})`
|
|
193
|
+
* .configure({
|
|
194
|
+
* context: [
|
|
195
|
+
* `import { forwardRef } from 'react'`,
|
|
196
|
+
* `type MyType = { value: number }`
|
|
197
|
+
* ]
|
|
198
|
+
* })
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
context?: string[];
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @deprecated Use `context` instead. This alias will be removed in a future version.
|
|
205
|
+
*
|
|
206
|
+
* Import statements to provide type attribution context.
|
|
207
|
+
* These are prepended to the pattern when parsing to ensure proper type information.
|
|
208
|
+
*/
|
|
209
|
+
imports?: string[];
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* NPM dependencies required for import resolution and type attribution.
|
|
213
|
+
* Maps package names to version specifiers (e.g., { 'util': '^1.0.0' }).
|
|
214
|
+
* The template engine will create a package.json with these dependencies.
|
|
215
|
+
*/
|
|
216
|
+
dependencies?: Record<string, string>;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* When true, allows patterns without type annotations to match code with type annotations.
|
|
220
|
+
* This enables more flexible pattern matching during development or when full type attribution
|
|
221
|
+
* is not needed. When false, enforces strict type matching where both pattern and target must
|
|
222
|
+
* have matching type annotations.
|
|
223
|
+
*
|
|
224
|
+
* @default true (lenient matching enabled for backward compatibility)
|
|
225
|
+
*/
|
|
226
|
+
lenientTypeMatching?: boolean;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Valid parameter types for template literals.
|
|
231
|
+
* - Capture: For pattern matching and reuse
|
|
232
|
+
* - CaptureValue: Result of property access or array operations on captures (e.g., capture.prop, capture[0], capture.slice(1))
|
|
233
|
+
* - Tree: AST nodes to be inserted directly
|
|
234
|
+
* - Tree[]: Arrays of AST nodes (from variadic capture operations like slice)
|
|
235
|
+
* - Primitives: Values to be converted to literals
|
|
236
|
+
*/
|
|
237
|
+
export type TemplateParameter = Capture | any | TemplateParam | Tree | Tree[] | string | number | boolean;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Configuration options for templates.
|
|
241
|
+
*/
|
|
242
|
+
export interface TemplateOptions {
|
|
243
|
+
/**
|
|
244
|
+
* Declarations to provide type attribution context for the template.
|
|
245
|
+
* These can include import statements, type declarations, function declarations, or any
|
|
246
|
+
* other declarations needed for proper type information. They are prepended to the template
|
|
247
|
+
* when parsing to ensure proper type attribution.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* template`console.log(${capture('value')})`
|
|
252
|
+
* .configure({
|
|
253
|
+
* context: [
|
|
254
|
+
* `type MyType = { value: number }`,
|
|
255
|
+
* `const console = { log: (x: any) => void 0 }`
|
|
256
|
+
* ]
|
|
257
|
+
* })
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
context?: string[];
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @deprecated Use `context` instead. This alias will be removed in a future version.
|
|
264
|
+
*
|
|
265
|
+
* Import statements to provide type attribution context.
|
|
266
|
+
* These are prepended to the template when parsing to ensure proper type information.
|
|
267
|
+
*/
|
|
268
|
+
imports?: string[];
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* NPM dependencies required for import resolution and type attribution.
|
|
272
|
+
* Maps package names to version specifiers (e.g., { 'util': '^1.0.0' }).
|
|
273
|
+
* The template engine will create a package.json with these dependencies.
|
|
274
|
+
*/
|
|
275
|
+
dependencies?: Record<string, string>;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Represents a replacement rule that can match a pattern and apply a template.
|
|
280
|
+
*/
|
|
281
|
+
export interface RewriteRule {
|
|
282
|
+
/**
|
|
283
|
+
* Attempts to apply this rewrite rule to the given AST node.
|
|
284
|
+
*
|
|
285
|
+
* @param cursor The cursor context at the current position in the AST
|
|
286
|
+
* @param node The AST node to try matching and transforming
|
|
287
|
+
* @returns The transformed node if a pattern matched, or `undefined` if no pattern matched.
|
|
288
|
+
* When using in a visitor, always use the `|| node` pattern to return the original
|
|
289
|
+
* node when there's no match: `return await rule.tryOn(this.cursor, node) || node;`
|
|
290
|
+
*/
|
|
291
|
+
tryOn(cursor: Cursor, node: J): Promise<J | undefined>;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Configuration for a replacement rule.
|
|
296
|
+
*/
|
|
297
|
+
export interface RewriteConfig {
|
|
298
|
+
before: any; // Pattern | Pattern[], but we'll import Pattern in rewrite.ts
|
|
299
|
+
after: any; // Template, but we'll import Template in rewrite.ts
|
|
300
|
+
}
|