diff --git a/src/main/java/org/openrewrite/javascript/JavaScriptIsoVisitor.java b/src/main/java/org/openrewrite/javascript/JavaScriptIsoVisitor.java
index 0a14268d..e7966c9b 100644
--- a/src/main/java/org/openrewrite/javascript/JavaScriptIsoVisitor.java
+++ b/src/main/java/org/openrewrite/javascript/JavaScriptIsoVisitor.java
@@ -93,6 +93,11 @@ public JS.TemplateExpression.Value visitTemplateExpressionValue(JS.TemplateExpre
return (JS.TemplateExpression.Value) super.visitTemplateExpressionValue(value, p);
}
+ @Override
+ public JS.Tuple visitTuple(JS.Tuple tuple, P p) {
+ return (JS.Tuple) super.visitTuple(tuple, p);
+ }
+
@Override
public JS.TypeDeclaration visitTypeDeclaration(JS.TypeDeclaration typeDeclaration, P p) {
return (JS.TypeDeclaration) super.visitTypeDeclaration(typeDeclaration, p);
diff --git a/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java b/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java
index a4d08df9..5c5dcfd6 100644
--- a/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java
+++ b/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java
@@ -310,6 +310,20 @@ public J visitTemplateExpressionValue(JS.TemplateExpression.Value value, P p) {
return s;
}
+ public J visitTuple(JS.Tuple tuple, P p) {
+ JS.Tuple t = tuple;
+ t = t.withPrefix(visitSpace(t.getPrefix(), JsSpace.Location.TUPLE_PREFIX, p));
+ t = t.withMarkers(visitMarkers(t.getMarkers(), p));
+ Expression temp = (Expression) visitExpression(t, p);
+ if (!(temp instanceof JS.Tuple)) {
+ return temp;
+ } else {
+ t = (JS.Tuple) temp;
+ }
+ t = t.withType(visitType(t.getType(), p));
+ return t;
+ }
+
public J visitTypeDeclaration(JS.TypeDeclaration typeDeclaration, P p) {
JS.TypeDeclaration t = typeDeclaration;
t = t.withPrefix(visitSpace(t.getPrefix(), JsSpace.Location.TYPE_DECLARATION_PREFIX, p));
diff --git a/src/main/java/org/openrewrite/javascript/internal/JavaScriptPrinter.java b/src/main/java/org/openrewrite/javascript/internal/JavaScriptPrinter.java
index be582f55..f8c6b380 100644
--- a/src/main/java/org/openrewrite/javascript/internal/JavaScriptPrinter.java
+++ b/src/main/java/org/openrewrite/javascript/internal/JavaScriptPrinter.java
@@ -300,6 +300,14 @@ public J visitTemplateExpressionValue(JS.TemplateExpression.Value value, PrintOu
return value;
}
+ @Override
+ public J visitTuple(JS.Tuple tuple, PrintOutputCapture
p) {
+ beforeSyntax(tuple, JsSpace.Location.TUPLE_PREFIX, p);
+ visitContainer("[", tuple.getPadding().getTypes(), JsContainer.Location.TUPLE_ELEMENT, ",", "]", p);
+ afterSyntax(tuple, p);
+ return tuple;
+ }
+
@Override
public J visitTypeDeclaration(JS.TypeDeclaration typeDeclaration, PrintOutputCapture
p) {
beforeSyntax(typeDeclaration, JsSpace.Location.TYPE_DECLARATION_PREFIX, p);
@@ -589,6 +597,8 @@ public void visitModifier(J.Modifier mod, PrintOutputCapture
p) {
case Volatile:
keyword = "volatile";
break;
+ default:
+ keyword = mod.getKeyword();
}
beforeSyntax(mod, Space.Location.MODIFIER_PREFIX, p);
p.append(keyword);
diff --git a/src/main/java/org/openrewrite/javascript/internal/TypeScriptParserVisitor.java b/src/main/java/org/openrewrite/javascript/internal/TypeScriptParserVisitor.java
index b053f3b1..2c455c06 100644
--- a/src/main/java/org/openrewrite/javascript/internal/TypeScriptParserVisitor.java
+++ b/src/main/java/org/openrewrite/javascript/internal/TypeScriptParserVisitor.java
@@ -190,8 +190,12 @@ private J visitArrowFunction(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List annotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), annotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
+ if (!trailing.isEmpty()) {
+ throw new UnsupportedOperationException("Add support for trailing annotations on: " + node.getText());
+ }
int saveCursor = getCursor();
Space before = whitespace();
@@ -223,7 +227,7 @@ private J visitArrowFunction(TSCNode node) {
randomId(),
prefix,
markers,
- annotations,
+ leading.isEmpty() ? emptyList() : leading,
modifiers,
params,
returnTypeExpression,
@@ -605,17 +609,16 @@ private J.ClassDeclaration visitClassDeclaration(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List leadingAnnotation = new ArrayList<>();
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
List modifiers;
List modifierNodes = node.getOptionalNodeListProperty("modifiers");
if (modifierNodes != null) {
- modifiers = mapModifiers(node.getNodeListProperty("modifiers"), leadingAnnotation);
+ modifiers = mapModifiers(node.getNodeListProperty("modifiers"), leading, trailing);
} else {
modifiers = emptyList();
}
- List kindAnnotations = emptyList();
-
Space kindPrefix;
TSCSyntaxKind syntaxKind = node.syntaxKind();
J.ClassDeclaration.Kind.Type type;
@@ -633,7 +636,7 @@ private J.ClassDeclaration visitClassDeclaration(TSCNode node) {
type = J.ClassDeclaration.Kind.Type.Class;
}
- J.ClassDeclaration.Kind kind = new J.ClassDeclaration.Kind(randomId(), kindPrefix, Markers.EMPTY, kindAnnotations, type);
+ J.ClassDeclaration.Kind kind = new J.ClassDeclaration.Kind(randomId(), kindPrefix, Markers.EMPTY, trailing, type);
J.Identifier name;
TSCNode nameNode = node.getOptionalNodeProperty("name");
@@ -720,7 +723,7 @@ private J.ClassDeclaration visitClassDeclaration(TSCNode node) {
randomId(),
prefix,
markers,
- leadingAnnotation.isEmpty() ? emptyList() : leadingAnnotation,
+ leading.isEmpty() ? emptyList() : leading,
modifiers,
kind,
name,
@@ -792,10 +795,11 @@ private J.MethodDeclaration visitConstructor(TSCNode node) {
Space prefix = whitespace();
List modifiers;
- List leadingAnnotations = new ArrayList<>();
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
TSCNodeList modifierNodes = node.getOptionalNodeListProperty("modifiers");
if (modifierNodes != null) {
- modifiers = mapModifiers(modifierNodes, leadingAnnotations);
+ modifiers = mapModifiers(modifierNodes, leading, trailing);
} else {
modifiers = emptyList();
}
@@ -817,7 +821,7 @@ private J.MethodDeclaration visitConstructor(TSCNode node) {
randomId(),
prefix,
Markers.EMPTY,
- leadingAnnotations.isEmpty() ? emptyList() : leadingAnnotations,
+ leading.isEmpty() ? emptyList() : leading,
modifiers,
typeParameters,
null,
@@ -1153,8 +1157,9 @@ private J.MethodDeclaration visitFunctionDeclaration(TSCNode node) {
Space prefix = whitespace();
- List annotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), annotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
Space before = sourceBefore(TSCSyntaxKind.FunctionKeyword);
Markers markers = Markers.build(singletonList(new FunctionKeyword(randomId(), before)));
@@ -1169,7 +1174,9 @@ private J.MethodDeclaration visitFunctionDeclaration(TSCNode node) {
// Function expressions do not require a name `function (..)`
name = convertToIdentifier(EMPTY, "");
}
-
+ if (!trailing.isEmpty()) {
+ name = name.withAnnotations(trailing);
+ }
name = name.withType(method);
J.TypeParameters typeParameters = mapTypeParameters(node.getOptionalNodeListProperty("typeParameters"));
@@ -1195,7 +1202,7 @@ private J.MethodDeclaration visitFunctionDeclaration(TSCNode node) {
randomId(),
prefix,
markers,
- annotations,
+ leading.isEmpty() ? emptyList() : leading,
modifiers,
typeParameters,
returnTypeExpression,
@@ -1212,12 +1219,15 @@ private Statement visitFunctionParameter(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List leadingAnnotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leadingAnnotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
Space variablePrefix = whitespace();
J.Identifier name = visitIdentifier(node.getNodeProperty("name"));
-
+ if (!trailing.isEmpty()) {
+ name = name.withAnnotations(trailing);
+ }
TypeTree typeTree = null;
TSCNode type = node.getOptionalNodeProperty("type");
if (type != null) {
@@ -1249,8 +1259,8 @@ private Statement visitFunctionParameter(TSCNode node) {
randomId(),
prefix,
markers,
- leadingAnnotations.isEmpty() ? emptyList() : leadingAnnotations,
- modifiers,
+ leading.isEmpty() ? emptyList() : leading,
+ modifiers.isEmpty() ? emptyList() : modifiers,
typeTree,
varargs,
dimensionsBeforeName,
@@ -1508,8 +1518,9 @@ private J.MethodDeclaration visitMethodDeclaration(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List annotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), annotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
if (node.hasProperty("asteriskToken")) {
markers = markers.addIfAbsent(new Asterisk(randomId(), sourceBefore(TSCSyntaxKind.AsteriskToken)));
@@ -1522,7 +1533,9 @@ private J.MethodDeclaration visitMethodDeclaration(TSCNode node) {
} else {
name = visitIdentifier(nameNode, methodType);
}
-
+ if (!trailing.isEmpty()) {
+ name = name.withAnnotations(trailing);
+ }
J.TypeParameters typeParameters = mapTypeParameters(node.getOptionalNodeListProperty("typeParameters"));
JContainer parameters = mapContainer(
@@ -1550,7 +1563,7 @@ private J.MethodDeclaration visitMethodDeclaration(TSCNode node) {
randomId(),
prefix,
markers,
- annotations.isEmpty() ? emptyList() : annotations,
+ leading.isEmpty() ? emptyList() : leading,
modifiers,
typeParameters,
returnTypeExpression,
@@ -1738,35 +1751,18 @@ private JS.ObjectBindingDeclarations mapObjectBindingDeclaration(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List annotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), annotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
Space beforeVariableModifier = whitespace();
TSCSyntaxKind keyword = scan();
if (keyword == TSCSyntaxKind.ConstKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "const"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "const", trailing));
} else if (keyword == TSCSyntaxKind.LetKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "let"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "let", trailing));
} else if (keyword == TSCSyntaxKind.VarKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "var"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "var", trailing));
} else {
// Unclear if the modifier should be `@Nullable` in the `JSVariableDeclaration`.
implementMe(node);
@@ -1850,7 +1846,7 @@ private JS.ObjectBindingDeclarations mapObjectBindingDeclaration(TSCNode node) {
randomId(),
prefix,
markers,
- annotations,
+ leading.isEmpty() ? emptyList() : leading,
modifiers,
typeTree,
JContainer.build(beforeBraces, bindings, Markers.EMPTY),
@@ -1893,8 +1889,9 @@ private J.VariableDeclarations visitPropertyDeclaration(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List leadingAnnotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leadingAnnotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
TSCNode varArgNode = node.getOptionalNodeProperty("dotDotDotToken");
Space varArg = varArgNode != null ? sourceBefore(TSCSyntaxKind.DotDotDotToken) : null;
@@ -1905,7 +1902,9 @@ private J.VariableDeclarations visitPropertyDeclaration(TSCNode node) {
} else {
implementMe(node);
}
-
+ if (!trailing.isEmpty()) {
+ name = name.withAnnotations(trailing);
+ }
TypeTree typeTree = null;
TSCNode typeNode = node.getOptionalNodeProperty("type");
if (typeNode != null) {
@@ -1952,8 +1951,8 @@ private J.VariableDeclarations visitPropertyDeclaration(TSCNode node) {
randomId(),
prefix,
markers,
- leadingAnnotations.isEmpty() ? emptyList() : leadingAnnotations,
- modifiers,
+ leading.isEmpty() ? emptyList() : leading,
+ modifiers.isEmpty() ? emptyList() : modifiers,
typeTree,
varArg,
dimensions,
@@ -1965,8 +1964,9 @@ private J visitPropertyAssignment(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List leadingAnnotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leadingAnnotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
Space variablePrefix = whitespace();
J j = visitNode(node.getOptionalNodeProperty("name"));
@@ -1981,7 +1981,9 @@ private J visitPropertyAssignment(TSCNode node) {
} else {
implementMe(node);
}
-
+ if (!trailing.isEmpty()) {
+ name = name.withAnnotations(trailing);
+ }
TypeTree typeTree = null;
// The initializer on a property declaration is the type reference.
@@ -2023,8 +2025,8 @@ private J visitPropertyAssignment(TSCNode node) {
randomId(),
prefix,
markers,
- leadingAnnotations.isEmpty() ? emptyList() : leadingAnnotations,
- modifiers,
+ leading.isEmpty() ? emptyList() : leading,
+ modifiers.isEmpty() ? emptyList() : modifiers,
typeTree,
null,
dimensions,
@@ -2257,18 +2259,35 @@ private J.Try visitTryStatement(TSCNode node) {
}
private J visitTupleType(TSCNode node) {
- return unknown(node);
+ Space prefix = whitespace();
+ TSCNodeList nodes = node.getOptionalNodeListProperty("elements");
+ JContainer types = mapContainer(
+ TSCSyntaxKind.OpenBracketToken,
+ nodes == null ? emptyList() : nodes,
+ TSCSyntaxKind.CommaToken,
+ TSCSyntaxKind.CloseBracketToken,
+ this::visitNode,
+ true
+ );
+ return new JS.Tuple(
+ randomId(),
+ prefix,
+ Markers.EMPTY,
+ types,
+ typeMapping.type(node)
+ );
}
private J visitTypeAliasDeclaration(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List annotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), annotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
Space before = sourceBefore(TSCSyntaxKind.TypeKeyword);
- annotations = mapKeywordToAnnotation(before, "type", annotations);
+ modifiers.add(mapModifier(before, "type", trailing));
TSCNode nameNode = node.getNodeProperty("name");
J.Identifier name = (J.Identifier) visitNode(nameNode);
@@ -2284,7 +2303,7 @@ private J visitTypeAliasDeclaration(TSCNode node) {
randomId(),
prefix,
markers,
- annotations,
+ leading,
modifiers,
name,
typeParameters,
@@ -2344,8 +2363,12 @@ private JS.TypeOperator visitTypeOperator(TSCNode node) {
private J.TypeParameter visitTypeParameter(TSCNode node) {
Space prefix = whitespace();
- List annotations = new ArrayList<>();
- mapModifiers(node.getOptionalNodeListProperty("modifiers"), annotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
+ if (!trailing.isEmpty()) {
+ throw new UnsupportedOperationException("Add support for trailing annotations on: " + node.getText());
+ }
implementMe(node, "expression");
TSCNode defaultType = node.getOptionalNodeProperty("default");
Expression name = (Expression) visitNode(node.getNodeProperty("name"));
@@ -2382,8 +2405,8 @@ private J.TypeParameter visitTypeParameter(TSCNode node) {
randomId(),
prefix,
Markers.EMPTY,
- annotations.isEmpty() ? emptyList() : annotations,
- emptyList(),
+ leading.isEmpty() ? emptyList() : leading,
+ modifiers,
name,
bounds
);
@@ -2500,35 +2523,21 @@ private J.VariableDeclarations visitVariableDeclaration(TSCNode node) {
Space prefix = whitespace();
Markers markers = Markers.EMPTY;
- List annotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), annotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
int saveCursor = getCursor();
Space beforeVariableModifier = whitespace();
TSCSyntaxKind keyword = scan();
if (keyword == TSCSyntaxKind.ConstKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "const"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "const", trailing));
+ trailing = null;
} else if (keyword == TSCSyntaxKind.LetKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "let"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "let", trailing));
+ trailing = null;
} else if (keyword == TSCSyntaxKind.VarKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "var"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "var", trailing));
+ trailing = null;
} else {
cursor(saveCursor);
}
@@ -2540,6 +2549,9 @@ private J.VariableDeclarations visitVariableDeclaration(TSCNode node) {
J.Identifier name = null;
if (j instanceof J.Identifier) {
name = (J.Identifier) j;
+ if (!trailing.isEmpty()) {
+ name = name.withAnnotations(trailing);
+ }
} else {
implementMe(node);
}
@@ -2578,8 +2590,8 @@ private J.VariableDeclarations visitVariableDeclaration(TSCNode node) {
randomId(),
prefix,
markers,
- annotations,
- modifiers,
+ leading.isEmpty() ? emptyList() : leading,
+ modifiers.isEmpty() ? emptyList() : modifiers,
typeTree,
null,
emptyList(),
@@ -2593,31 +2605,13 @@ private J.VariableDeclarations visitVariableDeclarationList(TSCNode node) {
Space beforeVariableModifier = whitespace();
TSCSyntaxKind keyword = scan();
- List annotations = new ArrayList<>();
+ List modifiers = new ArrayList<>();
if (keyword == TSCSyntaxKind.ConstKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "const"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "const", null));
} else if (keyword == TSCSyntaxKind.LetKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "let"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "let", null));
} else if (keyword == TSCSyntaxKind.VarKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "var"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "var", null));
} else {
// Unclear if the modifier should be `@Nullable` in the `JSVariableDeclaration`.
throw new UnsupportedOperationException("Unsupported variable modifier.");
@@ -2686,8 +2680,8 @@ private J.VariableDeclarations visitVariableDeclarationList(TSCNode node) {
randomId(),
prefix,
markers,
- annotations,
emptyList(),
+ modifiers.isEmpty() ? emptyList() : modifiers,
typeTree,
null,
emptyList(),
@@ -2701,35 +2695,18 @@ private J.VariableDeclarations visitVariableStatement(TSCNode node) {
implementMe(node, "exclamationToken");
- List annotations = new ArrayList<>();
- List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), annotations);
+ List leading = new ArrayList<>();
+ List trailing = new ArrayList<>();
+ List modifiers = mapModifiers(node.getOptionalNodeListProperty("modifiers"), leading, trailing);
Space beforeVariableModifier = whitespace();
TSCSyntaxKind keyword = scan();
if (keyword == TSCSyntaxKind.ConstKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "const"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "const", trailing));
} else if (keyword == TSCSyntaxKind.LetKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "let"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "let", trailing));
} else if (keyword == TSCSyntaxKind.VarKeyword) {
- annotations.add(new J.Annotation(
- randomId(),
- beforeVariableModifier,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, "var"),
- null)
- );
+ modifiers.add(mapModifier(beforeVariableModifier, "var", trailing));
} else {
// Unclear if the modifier should be `@Nullable` in the `JSVariableDeclaration`.
throw new UnsupportedOperationException("Unsupported variable modifier.");
@@ -2805,8 +2782,8 @@ private J.VariableDeclarations visitVariableStatement(TSCNode node) {
randomId(),
prefix,
markers,
- annotations,
- modifiers,
+ leading.isEmpty() ? emptyList() : leading,
+ modifiers.isEmpty() ? emptyList() : modifiers,
typeTree,
null,
emptyList(),
@@ -3368,23 +3345,29 @@ private J.ArrayType mapArrayType(TSCNode node) {
);
}
- private List mapModifiers(@Nullable List nodes, List leadingAnnotations) {
+ private List mapModifiers(@Nullable List nodes, List leadingAnnotations, List trailingAnnotations) {
if (nodes == null) {
- return emptyList();
+ return new ArrayList<>();
}
List modifiers = new ArrayList<>(nodes.size());
- List annotations = null;
+ List leading = new ArrayList<>();
+ List trailing = null;
for (TSCNode node : nodes) {
Space prefix = whitespace();
switch (node.syntaxKind()) {
// JS/TS equivalent of an annotation.
case Decorator: {
J.Annotation annotation = (J.Annotation) visitNode(node);
- if (annotations == null) {
- annotations = new ArrayList<>(1);
+ if (leading != null) {
+ leading.add(annotation);
+ } else {
+ if (trailing == null) {
+ trailing = new ArrayList<>(1);
+ } else {
+ trailing.add(annotation);
+ }
}
- annotations.add(annotation);
break;
}
// JS/TS keywords.
@@ -3392,78 +3375,80 @@ private List mapModifiers(@Nullable List nodes, List mapKeywordToAnnotation(Space prefix, String keyword, @Nullable List annotations) {
- J.Annotation annotation = new J.Annotation(
- randomId(),
- prefix,
- Markers.build(singletonList(new Keyword(randomId()))),
- convertToIdentifier(EMPTY, keyword),
- null
- );
- if (annotations == null) {
- annotations = new ArrayList<>(1);
+ if (trailing != null) {
+ trailingAnnotations.addAll(trailing);
}
- annotations.add(annotation);
- return annotations;
- }
-
- private List mapKeywordToAnnotation(Space prefix, TSCNode node, @Nullable List annotations) {
- J.Annotation annotation = new J.Annotation(
- randomId(),
- prefix,
- Markers.build(singletonList(new Keyword(randomId()))),
- (NameTree) visitNode(node),
- null
- );
- if (annotations == null) {
- annotations = new ArrayList<>(1);
- }
- annotations.add(annotation);
- return annotations;
+ return modifiers.isEmpty() ? new ArrayList<>() : modifiers;
}
@Nullable
@@ -3503,6 +3488,16 @@ private Space sourceBefore(TSCSyntaxKind syntaxKind) {
return prefix;
}
+ private J.Modifier mapModifier(Space prefix, String name, @Nullable List annotations) {
+ return new J.Modifier(
+ randomId(),
+ prefix,
+ Markers.EMPTY,
+ name,
+ J.Modifier.Type.LanguageExtension,
+ annotations == null ? emptyList() : annotations
+ );
+ }
/**
* Consume whitespace and leading comments until the current node.
* The type-script spec is not actively maintained, so we need to rely on the parser elements to collect
diff --git a/src/main/java/org/openrewrite/javascript/tree/JS.java b/src/main/java/org/openrewrite/javascript/tree/JS.java
index ff4ca489..ce4bdfa7 100644
--- a/src/main/java/org/openrewrite/javascript/tree/JS.java
+++ b/src/main/java/org/openrewrite/javascript/tree/JS.java
@@ -1440,6 +1440,81 @@ public TemplateExpression withTag(@Nullable JRightPadded tag) {
}
}
+ @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
+ @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
+ @RequiredArgsConstructor
+ @AllArgsConstructor(access = AccessLevel.PRIVATE)
+ @Data
+ final class Tuple implements JS, Expression, TypeTree {
+
+ @Nullable
+ @NonFinal
+ transient WeakReference padding;
+
+ @EqualsAndHashCode.Include
+ @With
+ UUID id;
+
+ @With
+ Space prefix;
+
+ @With
+ Markers markers;
+
+ JContainer types;
+
+ @With
+ @Nullable
+ JavaType type;
+
+ public List getTypes() {
+ return types.getElements();
+ }
+
+ public Tuple withBindings(List types) {
+ return getPadding().withTypes(JContainer.withElements(this.types, types));
+ }
+
+ @Override
+ public J acceptJavaScript(JavaScriptVisitor
v, P p) {
+ return v.visitTuple(this, p);
+ }
+
+ @Transient
+ @Override
+ public CoordinateBuilder.Expression getCoordinates() {
+ return new CoordinateBuilder.Expression(this);
+ }
+
+ public Tuple.Padding getPadding() {
+ Tuple.Padding p;
+ if (this.padding == null) {
+ p = new Tuple.Padding(this);
+ this.padding = new WeakReference<>(p);
+ } else {
+ p = this.padding.get();
+ if (p == null || p.t != this) {
+ p = new Tuple.Padding(this);
+ this.padding = new WeakReference<>(p);
+ }
+ }
+ return p;
+ }
+
+ @RequiredArgsConstructor
+ public static class Padding {
+ private final Tuple t;
+
+ public JContainer getTypes() {
+ return t.types;
+ }
+
+ public Tuple withTypes(JContainer types) {
+ return t.types == types ? t : new Tuple(t.id, t.prefix, t.markers, types, t.type);
+ }
+ }
+ }
+
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@RequiredArgsConstructor
diff --git a/src/main/java/org/openrewrite/javascript/tree/JsContainer.java b/src/main/java/org/openrewrite/javascript/tree/JsContainer.java
index 2bc460d0..f656ba1f 100644
--- a/src/main/java/org/openrewrite/javascript/tree/JsContainer.java
+++ b/src/main/java/org/openrewrite/javascript/tree/JsContainer.java
@@ -21,7 +21,8 @@ public enum Location {
BINDING_ELEMENT(JsSpace.Location.BINDING_ELEMENTS, JsRightPadded.Location.BINDING_ELEMENT_SUFFIX),
EXPORT_ELEMENT(JsSpace.Location.EXPORT_ELEMENTS, JsRightPadded.Location.EXPORT_ELEMENT_SUFFIX),
FUNCTION_TYPE_PARAMETER(JsSpace.Location.FUNCTION_TYPE_PARAMETERS, JsRightPadded.Location.FUNCTION_TYPE_PARAMETER_SUFFIX),
- IMPORT_ELEMENT(JsSpace.Location.IMPORT_ELEMENTS, JsRightPadded.Location.IMPORT_ELEMENT_SUFFIX);
+ IMPORT_ELEMENT(JsSpace.Location.IMPORT_ELEMENTS, JsRightPadded.Location.IMPORT_ELEMENT_SUFFIX),
+ TUPLE_ELEMENT(JsSpace.Location.TUPLE_ELEMENT, JsRightPadded.Location.TUPLE_ELEMENT_SUFFIX);
private final JsSpace.Location beforeLocation;
private final JsRightPadded.Location elementLocation;
diff --git a/src/main/java/org/openrewrite/javascript/tree/JsRightPadded.java b/src/main/java/org/openrewrite/javascript/tree/JsRightPadded.java
index 6b1d98cf..1f91d284 100644
--- a/src/main/java/org/openrewrite/javascript/tree/JsRightPadded.java
+++ b/src/main/java/org/openrewrite/javascript/tree/JsRightPadded.java
@@ -26,6 +26,7 @@ public enum Location {
IMPORT_ELEMENT_SUFFIX(JsSpace.Location.IMPORT_ELEMENT_SUFFIX),
IMPORT_NAME_SUFFIX(JsSpace.Location.IMPORT_NAME_SUFFIX),
TAG(JsSpace.Location.TAG_SUFFIX),
+ TUPLE_ELEMENT_SUFFIX(JsSpace.Location.TUPLE_ELEMENT_SUFFIX),
UNION_TYPE(JsSpace.Location.UNION_TYPE_SUFFIX);
private final JsSpace.Location afterLocation;
diff --git a/src/main/java/org/openrewrite/javascript/tree/JsSpace.java b/src/main/java/org/openrewrite/javascript/tree/JsSpace.java
index 83fc84be..598c4cbb 100644
--- a/src/main/java/org/openrewrite/javascript/tree/JsSpace.java
+++ b/src/main/java/org/openrewrite/javascript/tree/JsSpace.java
@@ -53,6 +53,9 @@ public enum Location {
TEMPLATE_EXPRESSION_PREFIX,
TEMPLATE_EXPRESSION_VALUE_PREFIX,
TEMPLATE_EXPRESSION_VALUE_SUFFIX,
+ TUPLE_PREFIX,
+ TUPLE_ELEMENT,
+ TUPLE_ELEMENT_SUFFIX,
TYPE_DECLARATION_PREFIX,
TYPE_DECLARATION_INITIALIZER_PREFIX,
TYPE_OPERATOR_PREFIX,
diff --git a/src/test/java/org/openrewrite/javascript/tree/TupleTest.java b/src/test/java/org/openrewrite/javascript/tree/TupleTest.java
index 6adceada..7d0ed84e 100644
--- a/src/test/java/org/openrewrite/javascript/tree/TupleTest.java
+++ b/src/test/java/org/openrewrite/javascript/tree/TupleTest.java
@@ -24,7 +24,6 @@
@SuppressWarnings({"JSUnresolvedVariable", "JSUnusedLocalSymbols"})
class TupleTest implements RewriteTest {
- @ExpectedToFail
@Test
void emptyTuple() {
rewriteRun(
@@ -36,19 +35,17 @@ void emptyTuple() {
);
}
- @ExpectedToFail
@Test
void tuple() {
rewriteRun(
javaScript(
"""
- let tuple : [ number , boolean , ] = [ 1 , true ]
+ let tuple : [ number , boolean ] = [ 1 , true ]
"""
)
);
}
- @ExpectedToFail
@Test
void typed() {
rewriteRun(
@@ -59,4 +56,44 @@ void typed() {
)
);
}
+
+ @ExpectedToFail("Requires spread operator")
+ @Test
+ void spreadOperators() {
+ rewriteRun(
+ javaScript(
+ """
+ function concat(arr1, arr2) {
+ return [...arr1, ...arr2]
+ }
+ """
+ )
+ );
+ }
+
+ @ExpectedToFail("Requires ArrayBindingPattern.")
+ @Test
+ void arrayBindingPattern() {
+ rewriteRun(
+ javaScript(
+ """
+ function tail(arg) {
+ const [_, ...result] = arg;
+ return result;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void trailingCommas() {
+ rewriteRun(
+ javaScript(
+ """
+ let input : [ number , boolean , ] = [ 1 , true , ]
+ """
+ )
+ );
+ }
}