@openrewrite/rewrite 8.67.0-20251105-121415 → 8.67.0-20251105-160319
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.js +7 -7
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +3 -3
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +18 -42
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +135 -128
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/javascript/comparator.ts +7 -7
- package/src/javascript/templating/comparator.ts +22 -32
- package/src/javascript/templating/pattern.ts +116 -118
|
@@ -13,14 +13,16 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {produce} from 'immer';
|
|
17
16
|
import {J, Type} from '../../java';
|
|
18
17
|
import {JS} from '../index';
|
|
19
|
-
import {
|
|
18
|
+
import {JavaScriptVisitor} from '../visitor';
|
|
19
|
+
import {produceAsync} from '../../visitor';
|
|
20
|
+
import {updateIfChanged} from '../../util';
|
|
20
21
|
import {Any, Capture, PatternOptions} from './types';
|
|
21
22
|
import {CAPTURE_CAPTURING_SYMBOL, CAPTURE_NAME_SYMBOL, CAPTURE_TYPE_SYMBOL, CaptureImpl} from './capture';
|
|
22
23
|
import {PatternMatchingComparator} from './comparator';
|
|
23
24
|
import {CaptureMarker, CaptureStorageValue, PlaceholderUtils, templateCache, WRAPPERS_MAP_SYMBOL} from './utils';
|
|
25
|
+
import {isTree, Tree} from "../../tree";
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* Builder for creating patterns programmatically.
|
|
@@ -171,7 +173,7 @@ export class Pattern {
|
|
|
171
173
|
* })
|
|
172
174
|
*/
|
|
173
175
|
configure(options: PatternOptions): Pattern {
|
|
174
|
-
this._options = {
|
|
176
|
+
this._options = {...this._options, ...options};
|
|
175
177
|
return this;
|
|
176
178
|
}
|
|
177
179
|
|
|
@@ -375,7 +377,7 @@ class Matcher {
|
|
|
375
377
|
private async matchNode(pattern: J, target: J): Promise<boolean> {
|
|
376
378
|
// Check if pattern is a capture placeholder
|
|
377
379
|
if (PlaceholderUtils.isCapture(pattern)) {
|
|
378
|
-
return this.handleCapture(pattern
|
|
380
|
+
return this.handleCapture(PlaceholderUtils.getCaptureMarker(pattern)!, target);
|
|
379
381
|
}
|
|
380
382
|
|
|
381
383
|
// Check if nodes have the same kind
|
|
@@ -387,8 +389,8 @@ class Matcher {
|
|
|
387
389
|
// Default to true for backward compatibility with existing patterns
|
|
388
390
|
const lenientTypeMatching = this.pattern.options.lenientTypeMatching ?? true;
|
|
389
391
|
const comparator = new PatternMatchingComparator({
|
|
390
|
-
handleCapture: (
|
|
391
|
-
handleVariadicCapture: (
|
|
392
|
+
handleCapture: (capture, t, w) => this.handleCapture(capture, t, w),
|
|
393
|
+
handleVariadicCapture: (capture, ts, ws) => this.handleVariadicCapture(capture, ts, ws),
|
|
392
394
|
saveState: () => this.saveState(),
|
|
393
395
|
restoreState: (state) => this.restoreState(state)
|
|
394
396
|
}, lenientTypeMatching);
|
|
@@ -417,13 +419,13 @@ class Matcher {
|
|
|
417
419
|
/**
|
|
418
420
|
* Handles a capture placeholder.
|
|
419
421
|
*
|
|
420
|
-
* @param
|
|
422
|
+
* @param capture The pattern node capture
|
|
421
423
|
* @param target The target node
|
|
422
424
|
* @param wrapper Optional wrapper containing the target (for preserving markers)
|
|
423
425
|
* @returns true if the capture is successful, false otherwise
|
|
424
426
|
*/
|
|
425
|
-
private handleCapture(
|
|
426
|
-
const captureName =
|
|
427
|
+
private handleCapture(capture: CaptureMarker, target: J, wrapper?: J.RightPadded<J>): boolean {
|
|
428
|
+
const captureName = capture.captureName;
|
|
427
429
|
|
|
428
430
|
if (!captureName) {
|
|
429
431
|
return false;
|
|
@@ -451,13 +453,13 @@ class Matcher {
|
|
|
451
453
|
/**
|
|
452
454
|
* Handles a variadic capture placeholder.
|
|
453
455
|
*
|
|
454
|
-
* @param
|
|
456
|
+
* @param capture The pattern node capture (the variadic capture)
|
|
455
457
|
* @param targets The target nodes that were matched
|
|
456
458
|
* @param wrappers Optional wrappers to preserve markers
|
|
457
459
|
* @returns true if the capture is successful, false otherwise
|
|
458
460
|
*/
|
|
459
|
-
private handleVariadicCapture(
|
|
460
|
-
const captureName =
|
|
461
|
+
private handleVariadicCapture(capture: CaptureMarker, targets: J[], wrappers?: J.RightPadded<J>[]): boolean {
|
|
462
|
+
const captureName = capture.captureName;
|
|
461
463
|
|
|
462
464
|
if (!captureName) {
|
|
463
465
|
return false;
|
|
@@ -487,6 +489,101 @@ class Matcher {
|
|
|
487
489
|
}
|
|
488
490
|
}
|
|
489
491
|
|
|
492
|
+
/**
|
|
493
|
+
* Visitor that attaches CaptureMarkers to capture identifiers in the AST.
|
|
494
|
+
* Markers are attached to Identifiers, then moved up to wrappers (RightPadded, ExpressionStatement).
|
|
495
|
+
* Uses JavaScriptVisitor to properly handle AST traversal and avoid cycles in Type objects.
|
|
496
|
+
*/
|
|
497
|
+
class MarkerAttachmentVisitor extends JavaScriptVisitor<undefined> {
|
|
498
|
+
constructor(private readonly captures: (Capture | Any<any>)[]) {
|
|
499
|
+
super();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Attaches CaptureMarker to capture identifiers.
|
|
504
|
+
*/
|
|
505
|
+
protected override async visitIdentifier(ident: J.Identifier, p: undefined): Promise<J | undefined> {
|
|
506
|
+
// First call parent to handle standard visitation
|
|
507
|
+
const visited = await super.visitIdentifier(ident, p);
|
|
508
|
+
if (!visited || visited.kind !== J.Kind.Identifier) {
|
|
509
|
+
return visited;
|
|
510
|
+
}
|
|
511
|
+
ident = visited as J.Identifier;
|
|
512
|
+
|
|
513
|
+
// Check if this is a capture placeholder
|
|
514
|
+
if (ident.simpleName?.startsWith(PlaceholderUtils.CAPTURE_PREFIX)) {
|
|
515
|
+
const captureInfo = PlaceholderUtils.parseCapture(ident.simpleName);
|
|
516
|
+
if (captureInfo) {
|
|
517
|
+
// Find the original capture object to get variadic options
|
|
518
|
+
const captureObj = this.captures.find(c => c.getName() === captureInfo.name);
|
|
519
|
+
const variadicOptions = captureObj?.getVariadicOptions();
|
|
520
|
+
|
|
521
|
+
// Add CaptureMarker to the Identifier
|
|
522
|
+
const marker = new CaptureMarker(captureInfo.name, variadicOptions);
|
|
523
|
+
return updateIfChanged(ident, {
|
|
524
|
+
markers: {
|
|
525
|
+
...ident.markers,
|
|
526
|
+
markers: [...ident.markers.markers, marker]
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return ident;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Propagates markers from element to RightPadded wrapper.
|
|
537
|
+
*/
|
|
538
|
+
public override async visitRightPadded<T extends J | boolean>(right: J.RightPadded<T>, p: undefined): Promise<J.RightPadded<T>> {
|
|
539
|
+
if (!isTree(right.element)) {
|
|
540
|
+
return right;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const visitedElement = await this.visit(right.element as J, p);
|
|
544
|
+
if (visitedElement && visitedElement !== right.element as Tree) {
|
|
545
|
+
return produceAsync<J.RightPadded<T>>(right, async (draft: any) => {
|
|
546
|
+
// Visit element first
|
|
547
|
+
if (right.element && (right.element as any).kind) {
|
|
548
|
+
// Check if element has a CaptureMarker
|
|
549
|
+
const elementMarker = PlaceholderUtils.getCaptureMarker(visitedElement);
|
|
550
|
+
if (elementMarker) {
|
|
551
|
+
draft.markers.markers.push(elementMarker);
|
|
552
|
+
} else {
|
|
553
|
+
draft.element = visitedElement;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return right;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Propagates markers from expression to ExpressionStatement.
|
|
564
|
+
*/
|
|
565
|
+
protected override async visitExpressionStatement(expressionStatement: JS.ExpressionStatement, p: undefined): Promise<J | undefined> {
|
|
566
|
+
// Visit the expression
|
|
567
|
+
const visitedExpression = await this.visit(expressionStatement.expression, p);
|
|
568
|
+
|
|
569
|
+
// Check if expression has a CaptureMarker
|
|
570
|
+
const expressionMarker = PlaceholderUtils.getCaptureMarker(visitedExpression as any);
|
|
571
|
+
if (expressionMarker) {
|
|
572
|
+
return updateIfChanged(expressionStatement, {
|
|
573
|
+
markers: {
|
|
574
|
+
...expressionStatement.markers,
|
|
575
|
+
markers: [...expressionStatement.markers.markers, expressionMarker]
|
|
576
|
+
},
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// No marker to move, just update with visited expression
|
|
581
|
+
return updateIfChanged(expressionStatement, {
|
|
582
|
+
expression: visitedExpression
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
490
587
|
/**
|
|
491
588
|
* Processor for template strings.
|
|
492
589
|
* Converts a template string with captures into an AST pattern.
|
|
@@ -535,7 +632,7 @@ class TemplateProcessor {
|
|
|
535
632
|
|
|
536
633
|
// Extract the relevant part of the AST
|
|
537
634
|
// The pattern code is always the last statement (after context + preamble)
|
|
538
|
-
return this.extractPatternFromAst(cu);
|
|
635
|
+
return await this.extractPatternFromAst(cu);
|
|
539
636
|
}
|
|
540
637
|
|
|
541
638
|
/**
|
|
@@ -647,7 +744,7 @@ class TemplateProcessor {
|
|
|
647
744
|
* @param cu The compilation unit
|
|
648
745
|
* @returns The extracted pattern
|
|
649
746
|
*/
|
|
650
|
-
private extractPatternFromAst(cu: JS.CompilationUnit): J {
|
|
747
|
+
private async extractPatternFromAst(cu: JS.CompilationUnit): Promise<J> {
|
|
651
748
|
// Check if we have any statements
|
|
652
749
|
if (!cu.statements || cu.statements.length === 0) {
|
|
653
750
|
throw new Error(`No statements found in compilation unit`);
|
|
@@ -676,119 +773,20 @@ class TemplateProcessor {
|
|
|
676
773
|
}
|
|
677
774
|
|
|
678
775
|
// Attach CaptureMarkers to capture identifiers
|
|
679
|
-
return this.attachCaptureMarkers(extracted);
|
|
776
|
+
return await this.attachCaptureMarkers(extracted);
|
|
680
777
|
}
|
|
681
778
|
|
|
682
779
|
/**
|
|
683
780
|
* Attaches CaptureMarkers to capture identifiers in the AST.
|
|
684
781
|
* This allows efficient capture detection without string parsing.
|
|
782
|
+
* Uses JavaScriptVisitor to properly handle AST traversal and avoid cycles in Type objects.
|
|
685
783
|
*
|
|
686
784
|
* @param ast The AST to process
|
|
687
785
|
* @returns The AST with CaptureMarkers attached
|
|
688
786
|
*/
|
|
689
|
-
private attachCaptureMarkers(ast: J): J {
|
|
690
|
-
const
|
|
691
|
-
return
|
|
692
|
-
this.visitAndAttachMarkers(draft, visited);
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
* Recursively visits AST nodes and attaches CaptureMarkers to capture identifiers.
|
|
698
|
-
* For statement-level captures (identifiers in ExpressionStatement), the marker
|
|
699
|
-
* is attached to the ExpressionStatement itself rather than the nested identifier.
|
|
700
|
-
*
|
|
701
|
-
* @param node The node to visit
|
|
702
|
-
* @param visited Set of already visited nodes to avoid cycles
|
|
703
|
-
*/
|
|
704
|
-
private visitAndAttachMarkers(node: any, visited: Set<J | object>): void {
|
|
705
|
-
if (!node || typeof node !== 'object' || visited.has(node)) {
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// Mark as visited to avoid cycles
|
|
710
|
-
visited.add(node);
|
|
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
|
-
}
|
|
736
|
-
// Check if this is an ExpressionStatement containing a capture identifier
|
|
737
|
-
// For statement-level captures, we attach the marker to the ExpressionStatement itself
|
|
738
|
-
else if (node.kind === JS.Kind.ExpressionStatement &&
|
|
739
|
-
node.expression?.kind === J.Kind.Identifier &&
|
|
740
|
-
node.expression.simpleName?.startsWith(PlaceholderUtils.CAPTURE_PREFIX)) {
|
|
741
|
-
|
|
742
|
-
const captureInfo = PlaceholderUtils.parseCapture(node.expression.simpleName);
|
|
743
|
-
if (captureInfo) {
|
|
744
|
-
// Initialize markers on the ExpressionStatement
|
|
745
|
-
if (!node.markers) {
|
|
746
|
-
node.markers = { kind: 'org.openrewrite.marker.Markers', id: randomId(), markers: [] };
|
|
747
|
-
}
|
|
748
|
-
if (!node.markers.markers) {
|
|
749
|
-
node.markers.markers = [];
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// Find the original capture object to get variadic options
|
|
753
|
-
const captureObj = this.captures.find(c => c.getName() === captureInfo.name);
|
|
754
|
-
const variadicOptions = captureObj?.getVariadicOptions();
|
|
755
|
-
|
|
756
|
-
// Add CaptureMarker to the ExpressionStatement
|
|
757
|
-
node.markers.markers.push(new CaptureMarker(captureInfo.name, variadicOptions));
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
// For non-statement, non-wrapped captures (expressions), attach marker to the identifier
|
|
761
|
-
else if (node.kind === J.Kind.Identifier && node.simpleName?.startsWith(PlaceholderUtils.CAPTURE_PREFIX)) {
|
|
762
|
-
const captureInfo = PlaceholderUtils.parseCapture(node.simpleName);
|
|
763
|
-
if (captureInfo) {
|
|
764
|
-
// Initialize markers if needed
|
|
765
|
-
if (!node.markers) {
|
|
766
|
-
node.markers = { kind: 'org.openrewrite.marker.Markers', id: randomId(), markers: [] };
|
|
767
|
-
}
|
|
768
|
-
if (!node.markers.markers) {
|
|
769
|
-
node.markers.markers = [];
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// Find the original capture object to get variadic options
|
|
773
|
-
const captureObj = this.captures.find(c => c.getName() === captureInfo.name);
|
|
774
|
-
const variadicOptions = captureObj?.getVariadicOptions();
|
|
775
|
-
|
|
776
|
-
// Add CaptureMarker with variadic options if available
|
|
777
|
-
node.markers.markers.push(new CaptureMarker(captureInfo.name, variadicOptions));
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// Recursively visit all properties
|
|
782
|
-
for (const key in node) {
|
|
783
|
-
if (node.hasOwnProperty(key)) {
|
|
784
|
-
const value = node[key];
|
|
785
|
-
if (Array.isArray(value)) {
|
|
786
|
-
value.forEach(item => this.visitAndAttachMarkers(item, visited));
|
|
787
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
788
|
-
this.visitAndAttachMarkers(value, visited);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
787
|
+
private async attachCaptureMarkers(ast: J): Promise<J> {
|
|
788
|
+
const visitor = new MarkerAttachmentVisitor(this.captures);
|
|
789
|
+
return (await visitor.visit(ast, undefined))!;
|
|
792
790
|
}
|
|
793
791
|
}
|
|
794
792
|
|