@openrewrite/rewrite 8.67.0-20251105-091131 → 8.67.0-20251105-105242
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.map +1 -1
- package/dist/javascript/comparator.js +7 -10
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +5 -5
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +41 -38
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts +3 -3
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +31 -10
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.js +20 -1
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/rewrite.d.ts.map +1 -1
- package/dist/javascript/templating/rewrite.js +24 -4
- package/dist/javascript/templating/rewrite.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +1 -1
- package/dist/javascript/templating/template.js +1 -1
- package/dist/javascript/templating/types.d.ts +30 -1
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/javascript/templating/utils.d.ts +10 -13
- package/dist/javascript/templating/utils.d.ts.map +1 -1
- package/dist/javascript/templating/utils.js +0 -40
- package/dist/javascript/templating/utils.js.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/javascript/comparator.ts +8 -10
- package/src/javascript/templating/comparator.ts +48 -39
- package/src/javascript/templating/pattern.ts +30 -6
- package/src/javascript/templating/placeholder-replacement.ts +22 -1
- package/src/javascript/templating/rewrite.ts +25 -5
- package/src/javascript/templating/template.ts +1 -1
- package/src/javascript/templating/types.ts +31 -1
- package/src/javascript/templating/utils.ts +4 -27
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {Cursor, Tree} from '../..';
|
|
16
|
+
import {Cursor, isTree, Tree} from '../..';
|
|
17
17
|
import {J} from '../../java';
|
|
18
18
|
import {JS} from '../index';
|
|
19
19
|
import {JavaScriptSemanticComparatorVisitor} from '../comparator';
|
|
@@ -38,26 +38,11 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
|
|
|
38
38
|
super(lenientTypeMatching);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* Extracts the wrapper from the cursor if the parent is a RightPadded.
|
|
43
|
-
*/
|
|
44
|
-
private getWrapperFromCursor(cursor: Cursor): J.RightPadded<J> | undefined {
|
|
45
|
-
if (!cursor.parent) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
const parent = cursor.parent.value;
|
|
49
|
-
// Check if parent is a RightPadded by checking its kind
|
|
50
|
-
if ((parent as any).kind === J.Kind.RightPadded) {
|
|
51
|
-
return parent as J.RightPadded<J>;
|
|
52
|
-
}
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
41
|
override async visit<R extends J>(j: Tree, p: J, parent?: Cursor): Promise<R | undefined> {
|
|
57
|
-
// Check if the pattern node is a capture - this handles captures
|
|
42
|
+
// Check if the pattern node is a capture - this handles unwrapped captures
|
|
43
|
+
// (Wrapped captures in J.RightPadded are handled by visitRightPadded override)
|
|
58
44
|
if (PlaceholderUtils.isCapture(j as J)) {
|
|
59
|
-
const
|
|
60
|
-
const success = this.matcher.handleCapture(j as J, p, wrapper);
|
|
45
|
+
const success = this.matcher.handleCapture(j as J, p, undefined);
|
|
61
46
|
if (!success) {
|
|
62
47
|
return this.abort(j) as R;
|
|
63
48
|
}
|
|
@@ -68,27 +53,58 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
|
|
|
68
53
|
return j as R;
|
|
69
54
|
}
|
|
70
55
|
|
|
71
|
-
return super.visit(j, p, parent);
|
|
56
|
+
return await super.visit(j, p, parent);
|
|
72
57
|
}
|
|
73
58
|
|
|
74
59
|
protected hasSameKind(j: J, other: J): boolean {
|
|
75
60
|
return super.hasSameKind(j, other) ||
|
|
76
|
-
|
|
61
|
+
(j.kind == J.Kind.Identifier && PlaceholderUtils.isCapture(j as J.Identifier));
|
|
77
62
|
}
|
|
78
63
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Override visitRightPadded to check if this wrapper has a CaptureMarker.
|
|
66
|
+
* If so, capture the entire wrapper (to preserve markers like semicolons).
|
|
67
|
+
*/
|
|
68
|
+
override async visitRightPadded<T extends J | boolean>(right: J.RightPadded<T>, p: J): Promise<J.RightPadded<T>> {
|
|
69
|
+
if (!this.match) {
|
|
70
|
+
return right;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check if this RightPadded or its element has a CaptureMarker (attached during pattern construction)
|
|
74
|
+
const element = right.element;
|
|
75
|
+
let captureMarker = undefined;
|
|
76
|
+
|
|
77
|
+
// Check RightPadded itself
|
|
78
|
+
if (right.markers) {
|
|
79
|
+
captureMarker = PlaceholderUtils.getCaptureMarker(right);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check element if it's a J node
|
|
83
|
+
if (!captureMarker && isTree(element)) {
|
|
84
|
+
captureMarker = PlaceholderUtils.getCaptureMarker(element);
|
|
85
|
+
}
|
|
86
|
+
if (captureMarker) {
|
|
87
|
+
// Extract the target wrapper if it's also a RightPadded
|
|
88
|
+
const isRightPadded = (p as any).kind === J.Kind.RightPadded;
|
|
89
|
+
const targetWrapper = isRightPadded ? (p as unknown) as J.RightPadded<T> : undefined;
|
|
90
|
+
const targetElement = isRightPadded ? targetWrapper!.element : p;
|
|
91
|
+
|
|
92
|
+
// Handle the capture with the wrapper - use the element for pattern matching
|
|
93
|
+
const success = this.matcher.handleCapture(element as J, targetElement as J, targetWrapper as J.RightPadded<J> | undefined);
|
|
94
|
+
if (!success) {
|
|
95
|
+
return this.abort(right);
|
|
96
|
+
}
|
|
97
|
+
return right;
|
|
84
98
|
}
|
|
85
|
-
|
|
99
|
+
|
|
100
|
+
// Not a capture wrapper - use parent implementation
|
|
101
|
+
return super.visitRightPadded(right, p);
|
|
86
102
|
}
|
|
87
103
|
|
|
88
104
|
override async visitMethodInvocation(methodInvocation: J.MethodInvocation, other: J): Promise<J | undefined> {
|
|
89
105
|
// Check if any arguments are variadic captures
|
|
90
106
|
const hasVariadicCapture = methodInvocation.arguments.elements.some(arg =>
|
|
91
|
-
PlaceholderUtils.isVariadicCapture(arg
|
|
107
|
+
PlaceholderUtils.isVariadicCapture(arg)
|
|
92
108
|
);
|
|
93
109
|
|
|
94
110
|
// If no variadic captures, use parent implementation (which includes semantic/type-aware matching)
|
|
@@ -172,10 +188,9 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
|
|
|
172
188
|
}
|
|
173
189
|
|
|
174
190
|
override async visitJsCompilationUnit(compilationUnit: JS.CompilationUnit, other: J): Promise<J | undefined> {
|
|
175
|
-
// Check if any statements are variadic captures
|
|
191
|
+
// Check if any statements are variadic captures
|
|
176
192
|
const hasVariadicCapture = compilationUnit.statements.some(stmt => {
|
|
177
|
-
|
|
178
|
-
return PlaceholderUtils.isVariadicCapture(unwrapped);
|
|
193
|
+
return PlaceholderUtils.isVariadicCapture(stmt);
|
|
179
194
|
});
|
|
180
195
|
|
|
181
196
|
// If no variadic captures, use parent implementation
|
|
@@ -394,14 +409,8 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
|
|
|
394
409
|
const savedMatch = this.match;
|
|
395
410
|
const savedState = this.matcher.saveState();
|
|
396
411
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
this.cursor = new Cursor(targetWrapper, this.cursor);
|
|
400
|
-
try {
|
|
401
|
-
await this.visit(patternElement, targetElement);
|
|
402
|
-
} finally {
|
|
403
|
-
this.cursor = savedCursor;
|
|
404
|
-
}
|
|
412
|
+
await this.visit(patternElement, targetElement);
|
|
413
|
+
|
|
405
414
|
if (!this.match) {
|
|
406
415
|
// Restore state on match failure
|
|
407
416
|
this.match = savedMatch;
|
|
@@ -164,10 +164,10 @@ export class Pattern {
|
|
|
164
164
|
* @returns This pattern for method chaining
|
|
165
165
|
*
|
|
166
166
|
* @example
|
|
167
|
-
* pattern`
|
|
167
|
+
* pattern`forwardRef((${props}, ${ref}) => ${body})`
|
|
168
168
|
* .configure({
|
|
169
|
-
*
|
|
170
|
-
* dependencies: {
|
|
169
|
+
* context: ['import { forwardRef } from "react"'],
|
|
170
|
+
* dependencies: {'@types/react': '^18.0.0'}
|
|
171
171
|
* })
|
|
172
172
|
*/
|
|
173
173
|
configure(options: PatternOptions): Pattern {
|
|
@@ -387,7 +387,7 @@ class Matcher {
|
|
|
387
387
|
// Default to true for backward compatibility with existing patterns
|
|
388
388
|
const lenientTypeMatching = this.pattern.options.lenientTypeMatching ?? true;
|
|
389
389
|
const comparator = new PatternMatchingComparator({
|
|
390
|
-
handleCapture: (p, t) => this.handleCapture(p, t),
|
|
390
|
+
handleCapture: (p, t, w) => this.handleCapture(p, t, w),
|
|
391
391
|
handleVariadicCapture: (p, ts, ws) => this.handleVariadicCapture(p, ts, ws),
|
|
392
392
|
saveState: () => this.saveState(),
|
|
393
393
|
restoreState: (state) => this.restoreState(state)
|
|
@@ -709,9 +709,33 @@ class TemplateProcessor {
|
|
|
709
709
|
// Mark as visited to avoid cycles
|
|
710
710
|
visited.add(node);
|
|
711
711
|
|
|
712
|
+
// Check if this is a RightPadded containing a capture identifier
|
|
713
|
+
// Attach marker to the wrapper to preserve markers (like semicolons) during capture
|
|
714
|
+
if (node.kind === J.Kind.RightPadded &&
|
|
715
|
+
node.element?.kind === J.Kind.Identifier &&
|
|
716
|
+
node.element.simpleName?.startsWith(PlaceholderUtils.CAPTURE_PREFIX)) {
|
|
717
|
+
|
|
718
|
+
const captureInfo = PlaceholderUtils.parseCapture(node.element.simpleName);
|
|
719
|
+
if (captureInfo) {
|
|
720
|
+
// Initialize markers on the RightPadded
|
|
721
|
+
if (!node.markers) {
|
|
722
|
+
node.markers = { kind: 'org.openrewrite.marker.Markers', id: randomId(), markers: [] };
|
|
723
|
+
}
|
|
724
|
+
if (!node.markers.markers) {
|
|
725
|
+
node.markers.markers = [];
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Find the original capture object to get variadic options
|
|
729
|
+
const captureObj = this.captures.find(c => c.getName() === captureInfo.name);
|
|
730
|
+
const variadicOptions = captureObj?.getVariadicOptions();
|
|
731
|
+
|
|
732
|
+
// Add CaptureMarker to the RightPadded
|
|
733
|
+
node.markers.markers.push(new CaptureMarker(captureInfo.name, variadicOptions));
|
|
734
|
+
}
|
|
735
|
+
}
|
|
712
736
|
// Check if this is an ExpressionStatement containing a capture identifier
|
|
713
737
|
// For statement-level captures, we attach the marker to the ExpressionStatement itself
|
|
714
|
-
if (node.kind === JS.Kind.ExpressionStatement &&
|
|
738
|
+
else if (node.kind === JS.Kind.ExpressionStatement &&
|
|
715
739
|
node.expression?.kind === J.Kind.Identifier &&
|
|
716
740
|
node.expression.simpleName?.startsWith(PlaceholderUtils.CAPTURE_PREFIX)) {
|
|
717
741
|
|
|
@@ -733,7 +757,7 @@ class TemplateProcessor {
|
|
|
733
757
|
node.markers.markers.push(new CaptureMarker(captureInfo.name, variadicOptions));
|
|
734
758
|
}
|
|
735
759
|
}
|
|
736
|
-
// For non-statement captures (expressions), attach marker to the identifier
|
|
760
|
+
// For non-statement, non-wrapped captures (expressions), attach marker to the identifier
|
|
737
761
|
else if (node.kind === J.Kind.Identifier && node.simpleName?.startsWith(PlaceholderUtils.CAPTURE_PREFIX)) {
|
|
738
762
|
const captureInfo = PlaceholderUtils.parseCapture(node.simpleName);
|
|
739
763
|
if (captureInfo) {
|
|
@@ -167,7 +167,28 @@ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
|
|
|
167
167
|
// Not a placeholder (or expansion failed) - process normally
|
|
168
168
|
const replacedElement = await this.visit(element, p);
|
|
169
169
|
if (replacedElement) {
|
|
170
|
-
|
|
170
|
+
// Check if the replacement came from a capture with a wrapper (to preserve markers)
|
|
171
|
+
const placeholderNode = unwrapElement(element);
|
|
172
|
+
const placeholderText = this.getPlaceholderText(placeholderNode);
|
|
173
|
+
let wrapperToUse = wrapped;
|
|
174
|
+
|
|
175
|
+
if (placeholderText && this.isPlaceholder(placeholderNode)) {
|
|
176
|
+
const param = this.substitutions.get(placeholderText);
|
|
177
|
+
if (param) {
|
|
178
|
+
const isCapture = param.value instanceof CaptureImpl ||
|
|
179
|
+
(param.value && typeof param.value === 'object' && param.value[CAPTURE_NAME_SYMBOL]);
|
|
180
|
+
if (isCapture) {
|
|
181
|
+
const name = param.value[CAPTURE_NAME_SYMBOL] || param.value.name;
|
|
182
|
+
const wrapper = this.wrappersMap.get(name);
|
|
183
|
+
// Use captured wrapper if available and not an array (non-variadic)
|
|
184
|
+
if (wrapper && !Array.isArray(wrapper)) {
|
|
185
|
+
wrapperToUse = wrapper as J.RightPadded<J>;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
newElements.push(produce(wrapperToUse, draft => {
|
|
171
192
|
draft.element = replacedElement;
|
|
172
193
|
}));
|
|
173
194
|
}
|
|
@@ -25,7 +25,7 @@ import {Template} from './template';
|
|
|
25
25
|
class RewriteRuleImpl implements RewriteRule {
|
|
26
26
|
constructor(
|
|
27
27
|
private readonly before: Pattern[],
|
|
28
|
-
private readonly after: Template | ((match: MatchResult) =>
|
|
28
|
+
private readonly after: Template | ((match: MatchResult) => Template)
|
|
29
29
|
) {
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -36,8 +36,9 @@ class RewriteRuleImpl implements RewriteRule {
|
|
|
36
36
|
let result: J | undefined;
|
|
37
37
|
|
|
38
38
|
if (typeof this.after === 'function') {
|
|
39
|
-
// Call the function
|
|
40
|
-
|
|
39
|
+
// Call the function to get a template, then apply it
|
|
40
|
+
const template = this.after(match);
|
|
41
|
+
result = await template.apply(cursor, node, match);
|
|
41
42
|
} else {
|
|
42
43
|
// Use template.apply() as before
|
|
43
44
|
result = await this.after.apply(cursor, node, match);
|
|
@@ -59,7 +60,7 @@ class RewriteRuleImpl implements RewriteRule {
|
|
|
59
60
|
constructor() {
|
|
60
61
|
// Pass empty patterns and a function that will never be called
|
|
61
62
|
// since we override tryOn
|
|
62
|
-
super([],
|
|
63
|
+
super([], () => undefined as unknown as Template);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
async tryOn(cursor: Cursor, node: J): Promise<J | undefined> {
|
|
@@ -72,6 +73,25 @@ class RewriteRuleImpl implements RewriteRule {
|
|
|
72
73
|
}
|
|
73
74
|
})();
|
|
74
75
|
}
|
|
76
|
+
|
|
77
|
+
orElse(alternative: RewriteRule): RewriteRule {
|
|
78
|
+
const first = this;
|
|
79
|
+
return new (class extends RewriteRuleImpl {
|
|
80
|
+
constructor() {
|
|
81
|
+
// Pass empty patterns and a function that will never be called
|
|
82
|
+
// since we override tryOn
|
|
83
|
+
super([], () => undefined as unknown as Template);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async tryOn(cursor: Cursor, node: J): Promise<J | undefined> {
|
|
87
|
+
const firstResult = await first.tryOn(cursor, node);
|
|
88
|
+
if (firstResult !== undefined) {
|
|
89
|
+
return firstResult;
|
|
90
|
+
}
|
|
91
|
+
return await alternative.tryOn(cursor, node);
|
|
92
|
+
}
|
|
93
|
+
})();
|
|
94
|
+
}
|
|
75
95
|
}
|
|
76
96
|
|
|
77
97
|
/**
|
|
@@ -165,7 +185,7 @@ export const fromRecipe = (recipe: Recipe, ctx: ExecutionContext): RewriteRule =
|
|
|
165
185
|
constructor() {
|
|
166
186
|
// Pass empty patterns and a function that will never be called
|
|
167
187
|
// since we override tryOn
|
|
168
|
-
super([],
|
|
188
|
+
super([], () => undefined as unknown as Template);
|
|
169
189
|
}
|
|
170
190
|
|
|
171
191
|
async tryOn(cursor: Cursor, tree: J): Promise<J | undefined> {
|
|
@@ -210,7 +210,7 @@ export class Template {
|
|
|
210
210
|
* @example
|
|
211
211
|
* template`isDate(${capture('date')})`
|
|
212
212
|
* .configure({
|
|
213
|
-
*
|
|
213
|
+
* context: ['import { isDate } from "util"'],
|
|
214
214
|
* dependencies: { 'util': '^1.0.0' }
|
|
215
215
|
* })
|
|
216
216
|
*/
|
|
@@ -331,6 +331,36 @@ export interface RewriteRule {
|
|
|
331
331
|
* ```
|
|
332
332
|
*/
|
|
333
333
|
andThen(next: RewriteRule): RewriteRule;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Creates a composite rule that tries this rule first, and if it doesn't match, tries an alternative rule.
|
|
337
|
+
*
|
|
338
|
+
* The resulting rule:
|
|
339
|
+
* 1. First applies this rule to the input node
|
|
340
|
+
* 2. If this rule matches and transforms the node, returns the result
|
|
341
|
+
* 3. If this rule returns undefined (no match), tries the alternative rule on the original node
|
|
342
|
+
*
|
|
343
|
+
* @param alternative The rule to try if this rule doesn't match
|
|
344
|
+
* @returns A new RewriteRule that tries both rules with fallback behavior
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```typescript
|
|
348
|
+
* // Try specific pattern first, fall back to general pattern
|
|
349
|
+
* const specific = rewrite(() => ({
|
|
350
|
+
* before: pattern`foo(${capture('x')}, 0)`,
|
|
351
|
+
* after: template`bar(${capture('x')})`
|
|
352
|
+
* }));
|
|
353
|
+
*
|
|
354
|
+
* const general = rewrite(() => ({
|
|
355
|
+
* before: pattern`foo(${capture('x')}, ${capture('y')})`,
|
|
356
|
+
* after: template`baz(${capture('x')}, ${capture('y')})`
|
|
357
|
+
* }));
|
|
358
|
+
*
|
|
359
|
+
* const combined = specific.orElse(general);
|
|
360
|
+
* // Will try specific pattern first, if no match, try general pattern
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
orElse(alternative: RewriteRule): RewriteRule;
|
|
334
364
|
}
|
|
335
365
|
|
|
336
366
|
/**
|
|
@@ -338,5 +368,5 @@ export interface RewriteRule {
|
|
|
338
368
|
*/
|
|
339
369
|
export interface RewriteConfig {
|
|
340
370
|
before: Pattern | Pattern[],
|
|
341
|
-
after: Template | ((match: MatchResult) =>
|
|
371
|
+
after: Template | ((match: MatchResult) => Template)
|
|
342
372
|
}
|
|
@@ -17,7 +17,7 @@ import {J} from '../../java';
|
|
|
17
17
|
import {JS} from '../index';
|
|
18
18
|
import {JavaScriptParser} from '../parser';
|
|
19
19
|
import {DependencyWorkspace} from '../dependency-workspace';
|
|
20
|
-
import {Marker} from '../../markers';
|
|
20
|
+
import {Marker, Markers} from '../../markers';
|
|
21
21
|
import {randomId} from '../../uuid';
|
|
22
22
|
import {VariadicOptions, Capture, Any} from './types';
|
|
23
23
|
|
|
@@ -177,7 +177,7 @@ export class PlaceholderUtils {
|
|
|
177
177
|
* @param node The node to check
|
|
178
178
|
* @returns The CaptureMarker or undefined
|
|
179
179
|
*/
|
|
180
|
-
static getCaptureMarker(node:
|
|
180
|
+
static getCaptureMarker(node: { markers: Markers }): CaptureMarker | undefined {
|
|
181
181
|
for (const marker of node.markers.markers) {
|
|
182
182
|
if (marker instanceof CaptureMarker) {
|
|
183
183
|
return marker;
|
|
@@ -235,7 +235,7 @@ export class PlaceholderUtils {
|
|
|
235
235
|
* @param node The node to check
|
|
236
236
|
* @returns true if the node has a variadic CaptureMarker, false otherwise
|
|
237
237
|
*/
|
|
238
|
-
static isVariadicCapture(node:
|
|
238
|
+
static isVariadicCapture(node: { markers: Markers }): boolean {
|
|
239
239
|
for (const marker of node.markers.markers) {
|
|
240
240
|
if (marker instanceof CaptureMarker && marker.variadicOptions) {
|
|
241
241
|
return true;
|
|
@@ -250,7 +250,7 @@ export class PlaceholderUtils {
|
|
|
250
250
|
* @param node The node to extract variadic options from
|
|
251
251
|
* @returns The VariadicOptions, or undefined if not a variadic capture
|
|
252
252
|
*/
|
|
253
|
-
static getVariadicOptions(node:
|
|
253
|
+
static getVariadicOptions(node: { markers: Markers }): VariadicOptions | undefined {
|
|
254
254
|
for (const marker of node.markers.markers) {
|
|
255
255
|
if (marker instanceof CaptureMarker) {
|
|
256
256
|
return marker.variadicOptions;
|
|
@@ -258,27 +258,4 @@ export class PlaceholderUtils {
|
|
|
258
258
|
}
|
|
259
259
|
return undefined;
|
|
260
260
|
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Checks if a statement is an ExpressionStatement wrapping a capture identifier.
|
|
264
|
-
* When a capture placeholder appears in statement position, the parser wraps it as
|
|
265
|
-
* an ExpressionStatement. This method unwraps it to get the identifier.
|
|
266
|
-
*
|
|
267
|
-
* @param stmt The statement to check
|
|
268
|
-
* @returns The unwrapped capture identifier, or the original statement if not wrapped
|
|
269
|
-
*/
|
|
270
|
-
static unwrapStatementCapture(stmt: J): J {
|
|
271
|
-
// Check if it's an ExpressionStatement containing a capture identifier
|
|
272
|
-
if (stmt.kind === JS.Kind.ExpressionStatement) {
|
|
273
|
-
const exprStmt = stmt as JS.ExpressionStatement;
|
|
274
|
-
if (exprStmt.expression?.kind === J.Kind.Identifier) {
|
|
275
|
-
const identifier = exprStmt.expression as J.Identifier;
|
|
276
|
-
// Check if this is a capture placeholder
|
|
277
|
-
if (identifier.simpleName?.startsWith(this.CAPTURE_PREFIX)) {
|
|
278
|
-
return identifier;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return stmt;
|
|
283
|
-
}
|
|
284
261
|
}
|