@openrewrite/rewrite 8.66.1 → 8.66.3
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/java/tree.d.ts +10 -1
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js +21 -5
- package/dist/java/tree.js.map +1 -1
- package/dist/java/type-visitor.d.ts +1 -1
- package/dist/java/type-visitor.d.ts.map +1 -1
- package/dist/java/visitor.d.ts +2 -2
- package/dist/java/visitor.d.ts.map +1 -1
- package/dist/java/visitor.js +8 -2
- package/dist/java/visitor.js.map +1 -1
- package/dist/javascript/assertions.d.ts +6 -0
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +14 -6
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/comparator.d.ts +154 -7
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +623 -180
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/format.d.ts +5 -3
- package/dist/javascript/format.d.ts.map +1 -1
- package/dist/javascript/format.js +85 -43
- package/dist/javascript/format.js.map +1 -1
- package/dist/javascript/index.d.ts +1 -0
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +1 -0
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/parser.d.ts +2 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +39 -30
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/templating/capture.d.ts +81 -14
- package/dist/javascript/templating/capture.d.ts.map +1 -1
- package/dist/javascript/templating/capture.js +98 -8
- package/dist/javascript/templating/capture.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +125 -15
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +946 -118
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.d.ts +58 -25
- package/dist/javascript/templating/engine.d.ts.map +1 -1
- package/dist/javascript/templating/engine.js +527 -94
- package/dist/javascript/templating/engine.js.map +1 -1
- package/dist/javascript/templating/index.d.ts +3 -3
- package/dist/javascript/templating/index.d.ts.map +1 -1
- package/dist/javascript/templating/index.js +3 -1
- package/dist/javascript/templating/index.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts +121 -16
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +528 -257
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.d.ts +30 -5
- package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.js +183 -81
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/rewrite.d.ts +56 -11
- package/dist/javascript/templating/rewrite.d.ts.map +1 -1
- package/dist/javascript/templating/rewrite.js +143 -16
- package/dist/javascript/templating/rewrite.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +31 -5
- package/dist/javascript/templating/template.d.ts.map +1 -1
- package/dist/javascript/templating/template.js +89 -15
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +359 -12
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/javascript/templating/utils.d.ts +52 -35
- package/dist/javascript/templating/utils.d.ts.map +1 -1
- package/dist/javascript/templating/utils.js +107 -109
- package/dist/javascript/templating/utils.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +21 -11
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/json/rpc.js +2 -2
- package/dist/json/rpc.js.map +1 -1
- package/dist/recipe/order-imports.js.map +1 -1
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +10 -6
- package/dist/test/rewrite-test.js.map +1 -1
- package/dist/version.txt +1 -1
- package/dist/visitor.d.ts +4 -4
- package/dist/visitor.d.ts.map +1 -1
- package/dist/visitor.js +8 -3
- package/dist/visitor.js.map +1 -1
- package/package.json +4 -2
- package/src/java/tree.ts +10 -3
- package/src/java/type-visitor.ts +1 -1
- package/src/java/visitor.ts +11 -5
- package/src/javascript/assertions.ts +9 -3
- package/src/javascript/comparator.ts +676 -185
- package/src/javascript/format.ts +72 -34
- package/src/javascript/index.ts +1 -0
- package/src/javascript/parser.ts +51 -31
- package/src/javascript/templating/capture.ts +107 -15
- package/src/javascript/templating/comparator.ts +1087 -134
- package/src/javascript/templating/engine.ts +601 -103
- package/src/javascript/templating/index.ts +9 -2
- package/src/javascript/templating/pattern.ts +655 -281
- package/src/javascript/templating/placeholder-replacement.ts +183 -80
- package/src/javascript/templating/rewrite.ts +152 -18
- package/src/javascript/templating/template.ts +110 -22
- package/src/javascript/templating/types.ts +386 -12
- package/src/javascript/templating/utils.ts +116 -102
- package/src/javascript/type-mapping.ts +20 -11
- package/src/json/rpc.ts +2 -2
- package/src/recipe/order-imports.ts +1 -1
- package/src/test/rewrite-test.ts +12 -7
- package/src/visitor.ts +14 -6
package/src/javascript/format.ts
CHANGED
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {JS} from "./tree";
|
|
16
|
+
import {isJavaScript, JS} from "./tree";
|
|
17
17
|
import {JavaScriptVisitor} from "./visitor";
|
|
18
|
-
import {Comment, J, Statement} from "../java";
|
|
18
|
+
import {Comment, isJava, J, Statement} from "../java";
|
|
19
19
|
import {Draft, produce} from "immer";
|
|
20
20
|
import {Cursor, isScope, Tree} from "../tree";
|
|
21
21
|
import {
|
|
@@ -28,16 +28,15 @@ import {
|
|
|
28
28
|
} from "./style";
|
|
29
29
|
import {produceAsync} from "../visitor";
|
|
30
30
|
|
|
31
|
-
export async
|
|
31
|
+
export const maybeAutoFormat = async <J2 extends J, P>(before: J2, after: J2, p: P, stopAfter?: J, parent?: Cursor): Promise<J2> => {
|
|
32
32
|
if (before !== after) {
|
|
33
33
|
return autoFormat(after, p, stopAfter, parent);
|
|
34
34
|
}
|
|
35
35
|
return after;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export async
|
|
39
|
-
|
|
40
|
-
}
|
|
38
|
+
export const autoFormat = async <J2 extends J, P>(j: J2, p: P, stopAfter?: J, parent?: Cursor): Promise<J2> =>
|
|
39
|
+
(await new AutoformatVisitor(stopAfter).visit(j, p, parent) as J2);
|
|
41
40
|
|
|
42
41
|
export class AutoformatVisitor<P> extends JavaScriptVisitor<P> {
|
|
43
42
|
constructor(private stopAfter?: Tree) {
|
|
@@ -336,6 +335,13 @@ export class SpacesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
336
335
|
});
|
|
337
336
|
}
|
|
338
337
|
|
|
338
|
+
protected async visitPropertyAssignment(propertyAssignment: JS.PropertyAssignment, p: P): Promise<J | undefined> {
|
|
339
|
+
const pa = await super.visitPropertyAssignment(propertyAssignment, p) as JS.PropertyAssignment;
|
|
340
|
+
return produceAsync(pa, draft => {
|
|
341
|
+
draft.name.after.whitespace = this.style.other.beforePropertyNameValueSeparator ? " " : "";
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
339
345
|
protected async visitSwitch(switchNode: J.Switch, p: P): Promise<J | undefined> {
|
|
340
346
|
const ret = await super.visitSwitch(switchNode, p) as J.Switch;
|
|
341
347
|
return produceAsync(ret, async draft => {
|
|
@@ -406,11 +412,13 @@ export class SpacesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
406
412
|
const spacing = this.style.aroundOperators.unary;
|
|
407
413
|
|
|
408
414
|
switch (draft.operator.element) {
|
|
415
|
+
case J.Unary.Type.Not:
|
|
416
|
+
draft.expression.prefix.whitespace = this.style.aroundOperators.afterUnaryNotAndNotNull ? " " : "";
|
|
417
|
+
break;
|
|
409
418
|
case J.Unary.Type.PreIncrement:
|
|
410
419
|
case J.Unary.Type.PreDecrement:
|
|
411
420
|
case J.Unary.Type.Negative:
|
|
412
421
|
case J.Unary.Type.Positive:
|
|
413
|
-
case J.Unary.Type.Not:
|
|
414
422
|
case J.Unary.Type.Complement:
|
|
415
423
|
draft.expression.prefix.whitespace = spacing ? " " : "";
|
|
416
424
|
break;
|
|
@@ -462,18 +470,18 @@ export class SpacesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
462
470
|
}
|
|
463
471
|
|
|
464
472
|
private async spaceBeforeLeftPaddedElement<T extends J>(left: J.LeftPadded<T>, spaceBeforePadding: boolean, spaceBeforeElement: boolean): Promise<J.LeftPadded<T>> {
|
|
465
|
-
return produceAsync(left, async draft => {
|
|
473
|
+
return (await produceAsync(left, async draft => {
|
|
466
474
|
if (draft.before.comments.length == 0) {
|
|
467
475
|
draft.before.whitespace = spaceBeforePadding ? " " : "";
|
|
468
476
|
}
|
|
469
477
|
draft.element = await this.spaceBefore(left.element, spaceBeforeElement) as Draft<T>;
|
|
470
|
-
})
|
|
478
|
+
}))!;
|
|
471
479
|
}
|
|
472
480
|
|
|
473
481
|
private async spaceBeforeRightPaddedElement<T extends J>(right: J.RightPadded<T>, spaceBefore: boolean): Promise<J.RightPadded<T>> {
|
|
474
|
-
return produceAsync(right, async draft => {
|
|
482
|
+
return (await produceAsync(right, async draft => {
|
|
475
483
|
draft.element = await this.spaceBefore(right.element, spaceBefore) as Draft<T>;
|
|
476
|
-
})
|
|
484
|
+
}))!;
|
|
477
485
|
}
|
|
478
486
|
|
|
479
487
|
private async spaceBefore<T extends J>(j: T, spaceBefore: boolean): Promise<T> {
|
|
@@ -639,7 +647,9 @@ export class WrappingAndBracesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
639
647
|
const b = await super.visitBlock(block, p) as J.Block;
|
|
640
648
|
return produce(b, draft => {
|
|
641
649
|
if (!draft.end.whitespace.includes("\n") && (draft.statements.length == 0 || !draft.statements[draft.statements.length - 1].after.whitespace.includes("\n"))) {
|
|
642
|
-
|
|
650
|
+
if (this.cursor.parent?.value.kind !== J.Kind.NewClass) {
|
|
651
|
+
draft.end = this.withNewlineSpace(draft.end);
|
|
652
|
+
}
|
|
643
653
|
}
|
|
644
654
|
});
|
|
645
655
|
}
|
|
@@ -825,8 +835,8 @@ export class MinimumViableSpacingVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
825
835
|
const ret = await super.visitNewClass(newClass, p) as J.NewClass;
|
|
826
836
|
return produce(ret, draft => {
|
|
827
837
|
if (draft.class) {
|
|
828
|
-
if (draft.class.kind ==
|
|
829
|
-
this.ensureSpace((draft.class as Draft<
|
|
838
|
+
if (draft.class.kind == J.Kind.Identifier) {
|
|
839
|
+
this.ensureSpace((draft.class as Draft<J.Identifier>).prefix);
|
|
830
840
|
}
|
|
831
841
|
}
|
|
832
842
|
});
|
|
@@ -925,28 +935,30 @@ export class BlankLinesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
925
935
|
super();
|
|
926
936
|
}
|
|
927
937
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const cu = produce(tree as JS.CompilationUnit, draft => {
|
|
938
|
+
protected async preVisit(tree: J, p: P): Promise<J | undefined> {
|
|
939
|
+
let ret = await super.preVisit(tree, p) as J;
|
|
940
|
+
|
|
941
|
+
if (ret.kind === JS.Kind.CompilationUnit) {
|
|
942
|
+
ret = produce(ret as JS.CompilationUnit, draft => {
|
|
934
943
|
if (draft.prefix.comments.length == 0) {
|
|
935
944
|
draft.prefix.whitespace = "";
|
|
936
945
|
}
|
|
937
946
|
});
|
|
938
|
-
return super.visit(cu, p, cursor);
|
|
939
947
|
}
|
|
940
|
-
if (
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
});
|
|
944
|
-
} else if (tree.kind === J.Kind.MethodDeclaration && this.cursor.value.kind != JS.Kind.StatementExpression
|
|
945
|
-
&& (this.cursor.parent?.value.kind != JS.Kind.CompilationUnit || (this.cursor.parent?.value as JS.CompilationUnit).statements[0].element !== tree)) {
|
|
946
|
-
tree = produce(tree as J.MethodDeclaration, draft => {
|
|
948
|
+
if (ret.kind === J.Kind.MethodDeclaration
|
|
949
|
+
&& this.cursor.parent?.parent?.parent?.value.kind === J.Kind.ClassDeclaration) {
|
|
950
|
+
ret = produce(ret as JS.StatementExpression, draft => {
|
|
947
951
|
this.ensurePrefixHasNewLine(draft);
|
|
948
952
|
});
|
|
949
953
|
}
|
|
954
|
+
|
|
955
|
+
return ret;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
override async visit<R extends J>(tree: Tree, p: P, cursor?: Cursor): Promise<R | undefined> {
|
|
959
|
+
if (this.cursor?.getNearestMessage("stop") != null) {
|
|
960
|
+
return tree as R;
|
|
961
|
+
}
|
|
950
962
|
return super.visit(tree, p, cursor);
|
|
951
963
|
}
|
|
952
964
|
|
|
@@ -1018,7 +1030,7 @@ export class BlankLinesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
1018
1030
|
}
|
|
1019
1031
|
this.keepMaximumBlankLines(draft, this.style.keepMaximum.inCode);
|
|
1020
1032
|
}
|
|
1021
|
-
} else if (parent?.kind === J.Kind.Block ||
|
|
1033
|
+
} else if (parent?.kind === J.Kind.Block && grandparent?.kind !== J.Kind.NewClass ||
|
|
1022
1034
|
(parent?.kind === JS.Kind.CompilationUnit && (parent! as JS.CompilationUnit).statements[0].element.id != draft.id) ||
|
|
1023
1035
|
(parent?.kind === J.Kind.Case)) {
|
|
1024
1036
|
if (draft.kind != J.Kind.Case) {
|
|
@@ -1031,8 +1043,10 @@ export class BlankLinesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
1031
1043
|
protected async visitBlock(block: J.Block, p: P): Promise<J.Block> {
|
|
1032
1044
|
const b = await super.visitBlock(block, p) as J.Block;
|
|
1033
1045
|
return produce(b, draft => {
|
|
1034
|
-
if (
|
|
1035
|
-
draft.end.whitespace
|
|
1046
|
+
if (this.cursor.parent?.value.kind != J.Kind.NewClass) {
|
|
1047
|
+
if (!draft.end.whitespace.includes("\n")) {
|
|
1048
|
+
draft.end.whitespace = draft.end.whitespace.replace(/[ \t]+$/, '') + "\n";
|
|
1049
|
+
}
|
|
1036
1050
|
}
|
|
1037
1051
|
});
|
|
1038
1052
|
}
|
|
@@ -1108,7 +1122,7 @@ export class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
1108
1122
|
let indentShouldIncrease =
|
|
1109
1123
|
tree.kind === J.Kind.Block
|
|
1110
1124
|
|| this.cursor.parent?.parent?.parent?.value.kind == J.Kind.Case
|
|
1111
|
-
|| (tree.kind === JS.Kind.StatementExpression && (tree as JS.StatementExpression).statement.kind == J.Kind.MethodDeclaration);
|
|
1125
|
+
|| (tree.kind === JS.Kind.StatementExpression && (tree as JS.StatementExpression).statement.kind == J.Kind.MethodDeclaration && tree.prefix.whitespace.includes("\n"));
|
|
1112
1126
|
|
|
1113
1127
|
const previousIndent = this.currentIndent;
|
|
1114
1128
|
|
|
@@ -1168,10 +1182,34 @@ export class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
1168
1182
|
if (this.cursor?.getNearestMessage("stop") != null) {
|
|
1169
1183
|
return tree as R;
|
|
1170
1184
|
}
|
|
1171
|
-
|
|
1185
|
+
|
|
1186
|
+
if (parent) {
|
|
1187
|
+
this.cursor = new Cursor(tree, parent);
|
|
1188
|
+
for (let c: Cursor | undefined = this.cursor; c != null; c = c.parent) {
|
|
1189
|
+
let space: J.Space;
|
|
1190
|
+
const v = c.value;
|
|
1191
|
+
if (v.kind == J.Kind.RightPadded) {
|
|
1192
|
+
space = v.after;
|
|
1193
|
+
} else if (v.kind == J.Kind.LeftPadded || v.kind == J.Kind.Container) {
|
|
1194
|
+
space = v.before;
|
|
1195
|
+
} else if (isJava(v) || isJavaScript(v)) {
|
|
1196
|
+
space = v.prefix;
|
|
1197
|
+
} else {
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
const lastWhitespace = space.comments.length > 0 ? space.comments[space.comments.length - 1].suffix : space.whitespace;
|
|
1202
|
+
const idx = lastWhitespace.lastIndexOf('\n');
|
|
1203
|
+
if (idx !== -1) {
|
|
1204
|
+
c.messages.set("indentToUse", lastWhitespace.substring(idx + 1));
|
|
1205
|
+
break;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return await super.visit(tree, p) as R;
|
|
1172
1210
|
}
|
|
1173
1211
|
|
|
1174
|
-
public async visitLeftPadded<T extends J | J.Space | number | string | boolean>(left: J.LeftPadded<T>, p: P): Promise<J.LeftPadded<T
|
|
1212
|
+
public async visitLeftPadded<T extends J | J.Space | number | string | boolean>(left: J.LeftPadded<T>, p: P): Promise<J.LeftPadded<T> | undefined> {
|
|
1175
1213
|
const ret = await super.visitLeftPadded(left, p);
|
|
1176
1214
|
if (ret == undefined) {
|
|
1177
1215
|
return ret;
|
package/src/javascript/index.ts
CHANGED
package/src/javascript/parser.ts
CHANGED
|
@@ -76,7 +76,7 @@ export class JavaScriptParser extends Parser {
|
|
|
76
76
|
private readonly compilerOptions: ts.CompilerOptions;
|
|
77
77
|
private readonly styles?: NamedStyles[];
|
|
78
78
|
private oldProgram?: ts.Program;
|
|
79
|
-
private sourceFileCache?: Map<string, ts.SourceFile>;
|
|
79
|
+
private readonly sourceFileCache?: Map<string, ts.SourceFile>;
|
|
80
80
|
|
|
81
81
|
constructor(
|
|
82
82
|
{
|
|
@@ -612,13 +612,10 @@ export class JavaScriptParserVisitor {
|
|
|
612
612
|
}
|
|
613
613
|
for (let heritageClause of node.heritageClauses) {
|
|
614
614
|
if (heritageClause.token == ts.SyntaxKind.ExtendsKeyword) {
|
|
615
|
-
return this.leftPadded<TypeTree>(
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
markers: emptyMarkers,
|
|
620
|
-
expression: this.visit(heritageClause.types[0])
|
|
621
|
-
} satisfies JS.TypeTreeExpression as JS.TypeTreeExpression);
|
|
615
|
+
return this.leftPadded<TypeTree>(
|
|
616
|
+
this.prefix(heritageClause.getFirstToken()!),
|
|
617
|
+
this.mapTypeTree(heritageClause.types[0])
|
|
618
|
+
);
|
|
622
619
|
}
|
|
623
620
|
}
|
|
624
621
|
return undefined;
|
|
@@ -666,6 +663,33 @@ export class JavaScriptParserVisitor {
|
|
|
666
663
|
return undefined;
|
|
667
664
|
}
|
|
668
665
|
|
|
666
|
+
private mapTypeTree(node: ts.LeftHandSideExpression | ts.ExpressionWithTypeArguments | ts.Expression): TypeTree {
|
|
667
|
+
const expression = this.visit(node);
|
|
668
|
+
|
|
669
|
+
// Check if the expression is already a TypeTree
|
|
670
|
+
// J.Identifier, J.FieldAccess, J.ArrayType, and J.ParameterizedType all implement TypeTree
|
|
671
|
+
if (expression.kind === J.Kind.Identifier ||
|
|
672
|
+
expression.kind === J.Kind.FieldAccess ||
|
|
673
|
+
expression.kind === J.Kind.ArrayType ||
|
|
674
|
+
expression.kind === J.Kind.ParameterizedType) {
|
|
675
|
+
return expression as TypeTree;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// For expressions that don't implement TypeTree, wrap them in JS.TypeTreeExpression
|
|
679
|
+
// Transfer the prefix from the expression to the wrapper
|
|
680
|
+
const prefix = expression.prefix;
|
|
681
|
+
return {
|
|
682
|
+
kind: JS.Kind.TypeTreeExpression,
|
|
683
|
+
id: randomId(),
|
|
684
|
+
prefix: prefix,
|
|
685
|
+
markers: emptyMarkers,
|
|
686
|
+
expression: {
|
|
687
|
+
...expression,
|
|
688
|
+
prefix: emptySpace
|
|
689
|
+
},
|
|
690
|
+
} satisfies JS.TypeTreeExpression as JS.TypeTreeExpression;
|
|
691
|
+
}
|
|
692
|
+
|
|
669
693
|
visitNumericLiteral(node: ts.NumericLiteral): J.Literal {
|
|
670
694
|
// Parse the numeric value from the text
|
|
671
695
|
const text = node.text;
|
|
@@ -906,28 +930,25 @@ export class JavaScriptParserVisitor {
|
|
|
906
930
|
let annotationType: NameTree | TypeTree;
|
|
907
931
|
let _arguments: J.Container<Expression> | undefined = undefined;
|
|
908
932
|
|
|
909
|
-
if (ts.isCallExpression(node.expression)) {
|
|
933
|
+
if (ts.isCallExpression(node.expression) && node.expression.typeArguments) {
|
|
910
934
|
annotationType = {
|
|
911
935
|
kind: JS.Kind.ExpressionWithTypeArguments,
|
|
912
936
|
id: randomId(),
|
|
913
937
|
prefix: emptySpace,
|
|
914
938
|
markers: emptyMarkers,
|
|
915
939
|
clazz: this.convert<J>(node.expression.expression) as Expression,
|
|
916
|
-
typeArguments:
|
|
940
|
+
typeArguments: this.mapTypeArguments(this.suffix(node.expression.expression), node.expression.typeArguments)
|
|
917
941
|
} satisfies JS.ExpressionWithTypeArguments as JS.ExpressionWithTypeArguments;
|
|
918
942
|
_arguments = this.mapCommaSeparatedList(node.expression.getChildren(this.sourceFile).slice(-3))
|
|
943
|
+
} else if (ts.isCallExpression(node.expression)) {
|
|
944
|
+
annotationType = this.convert<J>(node.expression.expression) as Expression;
|
|
945
|
+
_arguments = this.mapCommaSeparatedList(node.expression.getChildren(this.sourceFile).slice(-3))
|
|
919
946
|
} else if (ts.isIdentifier(node.expression)) {
|
|
920
947
|
annotationType = this.convert(node.expression);
|
|
921
948
|
} else if (ts.isPropertyAccessExpression(node.expression)) {
|
|
922
949
|
annotationType = this.convert(node.expression);
|
|
923
950
|
} else if (ts.isParenthesizedExpression(node.expression)) {
|
|
924
|
-
annotationType =
|
|
925
|
-
kind: JS.Kind.TypeTreeExpression,
|
|
926
|
-
id: randomId(),
|
|
927
|
-
prefix: this.prefix(node.expression),
|
|
928
|
-
markers: emptyMarkers,
|
|
929
|
-
expression: this.convert(node.expression) as Expression
|
|
930
|
-
} satisfies JS.TypeTreeExpression as JS.TypeTreeExpression;
|
|
951
|
+
annotationType = this.mapTypeTree(node.expression);
|
|
931
952
|
} else {
|
|
932
953
|
return this.visitUnknown(node);
|
|
933
954
|
}
|
|
@@ -1999,22 +2020,21 @@ export class JavaScriptParserVisitor {
|
|
|
1999
2020
|
id: randomId(),
|
|
2000
2021
|
prefix: this.prefix(node.expression),
|
|
2001
2022
|
markers: emptyMarkers,
|
|
2002
|
-
class: {
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
prefix
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2023
|
+
class: (() => {
|
|
2024
|
+
// For parameterized types, we need to handle the prefix differently
|
|
2025
|
+
const typeTree = this.mapTypeTree(node.expression);
|
|
2026
|
+
// If it's a TypeTreeExpression, clear the prefix since it was already set on the ParameterizedType
|
|
2027
|
+
if (typeTree.kind === JS.Kind.TypeTreeExpression) {
|
|
2028
|
+
return {
|
|
2029
|
+
...typeTree,
|
|
2030
|
+
prefix: emptySpace
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
return typeTree;
|
|
2034
|
+
})(),
|
|
2009
2035
|
typeParameters: this.mapTypeArguments(this.prefix(this.findChildNode(node, ts.SyntaxKind.LessThanToken)!), node.typeArguments),
|
|
2010
2036
|
type: undefined
|
|
2011
|
-
} satisfies J.ParameterizedType as J.ParameterizedType :
|
|
2012
|
-
kind: JS.Kind.TypeTreeExpression,
|
|
2013
|
-
id: randomId(),
|
|
2014
|
-
prefix: this.prefix(node.expression),
|
|
2015
|
-
markers: emptyMarkers,
|
|
2016
|
-
expression: this.visit(node.expression),
|
|
2017
|
-
} satisfies JS.TypeTreeExpression as JS.TypeTreeExpression,
|
|
2037
|
+
} satisfies J.ParameterizedType as J.ParameterizedType : this.mapTypeTree(node.expression),
|
|
2018
2038
|
arguments: node.arguments ?
|
|
2019
2039
|
this.mapCommaSeparatedList(this.getParameterListNodes(node)) : {
|
|
2020
2040
|
...emptyContainer<Expression>(),
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import {Cursor} from '../..';
|
|
17
|
+
import {J, Type} from '../../java';
|
|
18
|
+
import {Any, Capture, CaptureOptions, ConstraintFunction, TemplateParam, VariadicOptions} from './types';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Combines multiple constraints with AND logic.
|
|
@@ -29,8 +30,8 @@ import {Capture, Any, TemplateParam, CaptureOptions, VariadicOptions} from './ty
|
|
|
29
30
|
* )
|
|
30
31
|
* });
|
|
31
32
|
*/
|
|
32
|
-
export function and<T>(...constraints: ((node: T) => boolean)[]): (node: T) => boolean {
|
|
33
|
-
return (node: T) => constraints.every(c => c(node));
|
|
33
|
+
export function and<T>(...constraints: ((node: T, cursor?: Cursor) => boolean)[]): (node: T, cursor?: Cursor) => boolean {
|
|
34
|
+
return (node: T, cursor?: Cursor) => constraints.every(c => c(node, cursor));
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/**
|
|
@@ -45,8 +46,8 @@ export function and<T>(...constraints: ((node: T) => boolean)[]): (node: T) => b
|
|
|
45
46
|
* )
|
|
46
47
|
* });
|
|
47
48
|
*/
|
|
48
|
-
export function or<T>(...constraints: ((node: T) => boolean)[]): (node: T) => boolean {
|
|
49
|
-
return (node: T) => constraints.some(c => c(node));
|
|
49
|
+
export function or<T>(...constraints: ((node: T, cursor?: Cursor) => boolean)[]): (node: T, cursor?: Cursor) => boolean {
|
|
50
|
+
return (node: T, cursor?: Cursor) => constraints.some(c => c(node, cursor));
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
/**
|
|
@@ -58,8 +59,8 @@ export function or<T>(...constraints: ((node: T) => boolean)[]): (node: T) => bo
|
|
|
58
59
|
* constraint: not((node) => typeof node.value === 'string')
|
|
59
60
|
* });
|
|
60
61
|
*/
|
|
61
|
-
export function not<T>(constraint: (node: T) => boolean): (node: T) => boolean {
|
|
62
|
-
return (node: T) => !constraint(node);
|
|
62
|
+
export function not<T>(constraint: (node: T, cursor?: Cursor) => boolean): (node: T, cursor?: Cursor) => boolean {
|
|
63
|
+
return (node: T, cursor?: Cursor) => !constraint(node, cursor);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
// Symbol to access the internal capture name without triggering Proxy
|
|
@@ -70,13 +71,18 @@ export const CAPTURE_VARIADIC_SYMBOL = Symbol('captureVariadic');
|
|
|
70
71
|
export const CAPTURE_CONSTRAINT_SYMBOL = Symbol('captureConstraint');
|
|
71
72
|
// Symbol to access capturing flag without triggering Proxy
|
|
72
73
|
export const CAPTURE_CAPTURING_SYMBOL = Symbol('captureCapturing');
|
|
74
|
+
// Symbol to access type information without triggering Proxy
|
|
75
|
+
export const CAPTURE_TYPE_SYMBOL = Symbol('captureType');
|
|
76
|
+
// Symbol to identify RawCode instances
|
|
77
|
+
export const RAW_CODE_SYMBOL = Symbol('rawCode');
|
|
73
78
|
|
|
74
79
|
export class CaptureImpl<T = any> implements Capture<T> {
|
|
75
80
|
public readonly name: string;
|
|
76
81
|
[CAPTURE_NAME_SYMBOL]: string;
|
|
77
82
|
[CAPTURE_VARIADIC_SYMBOL]: VariadicOptions | undefined;
|
|
78
|
-
[CAPTURE_CONSTRAINT_SYMBOL]:
|
|
83
|
+
[CAPTURE_CONSTRAINT_SYMBOL]: ConstraintFunction<T> | undefined;
|
|
79
84
|
[CAPTURE_CAPTURING_SYMBOL]: boolean;
|
|
85
|
+
[CAPTURE_TYPE_SYMBOL]: string | Type | undefined;
|
|
80
86
|
|
|
81
87
|
constructor(name: string, options?: CaptureOptions<T>, capturing: boolean = true) {
|
|
82
88
|
this.name = name;
|
|
@@ -99,6 +105,11 @@ export class CaptureImpl<T = any> implements Capture<T> {
|
|
|
99
105
|
if (options?.constraint) {
|
|
100
106
|
this[CAPTURE_CONSTRAINT_SYMBOL] = options.constraint;
|
|
101
107
|
}
|
|
108
|
+
|
|
109
|
+
// Store type if provided
|
|
110
|
+
if (options?.type) {
|
|
111
|
+
this[CAPTURE_TYPE_SYMBOL] = options.type;
|
|
112
|
+
}
|
|
102
113
|
}
|
|
103
114
|
|
|
104
115
|
getName(): string {
|
|
@@ -113,13 +124,17 @@ export class CaptureImpl<T = any> implements Capture<T> {
|
|
|
113
124
|
return this[CAPTURE_VARIADIC_SYMBOL];
|
|
114
125
|
}
|
|
115
126
|
|
|
116
|
-
getConstraint():
|
|
127
|
+
getConstraint(): ConstraintFunction<T> | undefined {
|
|
117
128
|
return this[CAPTURE_CONSTRAINT_SYMBOL];
|
|
118
129
|
}
|
|
119
130
|
|
|
120
131
|
isCapturing(): boolean {
|
|
121
132
|
return this[CAPTURE_CAPTURING_SYMBOL];
|
|
122
133
|
}
|
|
134
|
+
|
|
135
|
+
getType(): string | Type | undefined {
|
|
136
|
+
return this[CAPTURE_TYPE_SYMBOL];
|
|
137
|
+
}
|
|
123
138
|
}
|
|
124
139
|
|
|
125
140
|
export class TemplateParamImpl<T = any> implements TemplateParam<T> {
|
|
@@ -291,9 +306,17 @@ function createCaptureProxy<T>(impl: CaptureImpl<T>): any {
|
|
|
291
306
|
if (prop === CAPTURE_CAPTURING_SYMBOL) {
|
|
292
307
|
return target[CAPTURE_CAPTURING_SYMBOL];
|
|
293
308
|
}
|
|
309
|
+
if (prop === CAPTURE_TYPE_SYMBOL) {
|
|
310
|
+
return target[CAPTURE_TYPE_SYMBOL];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Support using Capture as object key via computed properties {[x]: value}
|
|
314
|
+
if (prop === Symbol.toPrimitive || prop === 'toString' || prop === 'valueOf') {
|
|
315
|
+
return () => target[CAPTURE_NAME_SYMBOL];
|
|
316
|
+
}
|
|
294
317
|
|
|
295
318
|
// Allow methods to be called directly on the target
|
|
296
|
-
if (prop === 'getName' || prop === 'isVariadic' || prop === 'getVariadicOptions' || prop === 'getConstraint' || prop === 'isCapturing') {
|
|
319
|
+
if (prop === 'getName' || prop === 'isVariadic' || prop === 'getVariadicOptions' || prop === 'getConstraint' || prop === 'isCapturing' || prop === 'getType') {
|
|
297
320
|
return target[prop].bind(target);
|
|
298
321
|
}
|
|
299
322
|
|
|
@@ -330,12 +353,12 @@ function createCaptureProxy<T>(impl: CaptureImpl<T>): any {
|
|
|
330
353
|
|
|
331
354
|
// Overload 1: Options object with constraint (no variadic)
|
|
332
355
|
export function capture<T = any>(
|
|
333
|
-
options:
|
|
356
|
+
options: CaptureOptions<T> & { variadic?: never }
|
|
334
357
|
): Capture<T> & T;
|
|
335
358
|
|
|
336
359
|
// Overload 2: Options object with variadic
|
|
337
360
|
export function capture<T = any>(
|
|
338
|
-
options: { name?: string; variadic: true | VariadicOptions; constraint?:
|
|
361
|
+
options: { name?: string; variadic: true | VariadicOptions; constraint?: ConstraintFunction<T[]>; min?: number; max?: number }
|
|
339
362
|
): Capture<T[]> & T[];
|
|
340
363
|
|
|
341
364
|
// Overload 3: Just a string name (simple named capture)
|
|
@@ -417,12 +440,12 @@ capture.nextUnnamedId = 1;
|
|
|
417
440
|
*/
|
|
418
441
|
// Overload 1: Regular any with constraint (most specific - no variadic property)
|
|
419
442
|
export function any<T = any>(
|
|
420
|
-
options: { constraint:
|
|
443
|
+
options: { constraint: ConstraintFunction<T> } & { variadic?: never }
|
|
421
444
|
): Any<T> & T;
|
|
422
445
|
|
|
423
446
|
// Overload 2: Variadic any with constraint
|
|
424
447
|
export function any<T = any>(
|
|
425
|
-
options: { variadic: true | VariadicOptions; constraint?:
|
|
448
|
+
options: { variadic: true | VariadicOptions; constraint?: ConstraintFunction<T[]>; min?: number; max?: number }
|
|
426
449
|
): Any<T[]> & T[];
|
|
427
450
|
|
|
428
451
|
// Overload 3: Catch-all for simple any without special options
|
|
@@ -489,6 +512,75 @@ export function param<T = any>(name?: string): TemplateParam<T> {
|
|
|
489
512
|
return new TemplateParamImpl<T>(paramName);
|
|
490
513
|
}
|
|
491
514
|
|
|
515
|
+
/**
|
|
516
|
+
* Represents raw code that should be inserted verbatim into templates at construction time.
|
|
517
|
+
* This is useful for dynamic code generation where the code structure is determined at runtime.
|
|
518
|
+
*/
|
|
519
|
+
export class RawCode {
|
|
520
|
+
[RAW_CODE_SYMBOL] = true;
|
|
521
|
+
|
|
522
|
+
constructor(public readonly code: string) {}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Creates a raw code specification for inserting literal code strings into templates.
|
|
527
|
+
*
|
|
528
|
+
* Use `raw()` when you need to insert code that is generated dynamically (e.g., from recipe options,
|
|
529
|
+
* computed field names, or programmatic string manipulation) directly into a template at construction time.
|
|
530
|
+
*
|
|
531
|
+
* The string is spliced into the template before parsing, so it becomes part of the template's AST.
|
|
532
|
+
* This is different from `param()` or `capture()` which are placeholders replaced during application.
|
|
533
|
+
*
|
|
534
|
+
* @param code The code string to insert verbatim into the template
|
|
535
|
+
* @returns A RawCode object that will be spliced into the template
|
|
536
|
+
*
|
|
537
|
+
* @remarks
|
|
538
|
+
* **When to use `raw()` vs `param()` vs `capture()`:**
|
|
539
|
+
*
|
|
540
|
+
* - Use `raw()` when you have a **code string** to insert at **template construction time**
|
|
541
|
+
* - Use `param()` when you have an **AST node** to substitute at **template application time**
|
|
542
|
+
* - Use `capture()` when working with **pattern matching** and need to reference matched values
|
|
543
|
+
*
|
|
544
|
+
* **Safety Considerations:**
|
|
545
|
+
* - No validation is performed on the code string
|
|
546
|
+
* - The code must be syntactically valid at the position where it's inserted
|
|
547
|
+
* - Recipe authors are trusted to provide valid code
|
|
548
|
+
*
|
|
549
|
+
* @example
|
|
550
|
+
* // Recipe option determines the log level
|
|
551
|
+
* class MyRecipe extends Recipe {
|
|
552
|
+
* @Option
|
|
553
|
+
* logLevel: string = "info";
|
|
554
|
+
*
|
|
555
|
+
* getVisitor() {
|
|
556
|
+
* // Template constructed with dynamic method name
|
|
557
|
+
* const replacement = template`logger.${raw(this.logLevel)}(${_('msg')})`;
|
|
558
|
+
* // Produces: logger.info(...) or logger.warn(...) etc.
|
|
559
|
+
* }
|
|
560
|
+
* }
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* // Build object literal from collected field names
|
|
564
|
+
* const fields = ["userId", "timestamp", "status"];
|
|
565
|
+
* template`{ ${raw(fields.join(', '))} }`
|
|
566
|
+
* // Produces: { userId, timestamp, status }
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* // Dynamic import path
|
|
570
|
+
* const modulePath = "./utils";
|
|
571
|
+
* template`import { helper } from ${raw(`'${modulePath}'`)}`
|
|
572
|
+
* // Produces: import { helper } from './utils'
|
|
573
|
+
*
|
|
574
|
+
* @example
|
|
575
|
+
* // Configurable operator
|
|
576
|
+
* const operator = ">=";
|
|
577
|
+
* template`${_('value')} ${raw(operator)} threshold`
|
|
578
|
+
* // Produces: value >= threshold
|
|
579
|
+
*/
|
|
580
|
+
export function raw(code: string): RawCode {
|
|
581
|
+
return new RawCode(code);
|
|
582
|
+
}
|
|
583
|
+
|
|
492
584
|
/**
|
|
493
585
|
* Concise alias for `capture`. Works well for inline captures in patterns and templates.
|
|
494
586
|
*
|