From 58c9086ce9133a3876b1a72dccb68765444a6eb6 Mon Sep 17 00:00:00 2001 From: traceyyoshima Date: Tue, 21 Nov 2023 14:44:52 -0700 Subject: [PATCH] Added JS.Tuple. Updated mapModifiers to create J.Modifier with LanguageExtension. Updated various JS modifiers from J.Annotation to J.Modifier. Updated leading and trailing annotation to work with changes from J.Annotation to J.Modifier. --- .../javascript/JavaScriptIsoVisitor.java | 5 + .../javascript/JavaScriptVisitor.java | 14 + .../internal/JavaScriptPrinter.java | 10 + .../internal/TypeScriptParserVisitor.java | 379 +++++++++--------- .../org/openrewrite/javascript/tree/JS.java | 75 ++++ .../javascript/tree/JsContainer.java | 3 +- .../javascript/tree/JsRightPadded.java | 1 + .../openrewrite/javascript/tree/JsSpace.java | 3 + .../javascript/tree/TupleTest.java | 45 ++- 9 files changed, 338 insertions(+), 197 deletions(-) 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 , ] + """ + ) + ); + } }