Skip to content

Commit

Permalink
Implemented support for optional methods (#139)
Browse files Browse the repository at this point in the history
Added custom JS.JSMethodDeclaration to wrap the questionToken by replacing Identifier with Expression for the method name.

* Implemented support for optional methods

* added get/set accessors

* added static block support in classes

---------

Co-authored-by: Andrii Rodionov <[email protected]>
  • Loading branch information
arodionov and Andrii Rodionov authored Nov 8, 2024
1 parent 82bdbeb commit 86e36b6
Show file tree
Hide file tree
Showing 16 changed files with 924 additions and 32 deletions.
185 changes: 171 additions & 14 deletions openrewrite/src/javascript/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,9 @@ export class JavaScriptParserVisitor {
);
}

private mapModifiers(node: ts.VariableDeclarationList | ts.VariableStatement | ts.ClassDeclaration | ts.PropertyDeclaration | ts.FunctionDeclaration | ts.ParameterDeclaration | ts.MethodDeclaration | ts.EnumDeclaration | ts.InterfaceDeclaration | ts.PropertySignature | ts.ConstructorDeclaration | ts.ModuleDeclaration) {
private mapModifiers(node: ts.VariableDeclarationList | ts.VariableStatement | ts.ClassDeclaration | ts.PropertyDeclaration
| ts.FunctionDeclaration | ts.ParameterDeclaration | ts.MethodDeclaration | ts.EnumDeclaration | ts.InterfaceDeclaration
| ts.PropertySignature | ts.ConstructorDeclaration | ts.ModuleDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration) {
if (ts.isVariableStatement(node)) {
return [new J.Modifier(
randomId(),
Expand Down Expand Up @@ -286,6 +288,24 @@ export class JavaScriptParserVisitor {
J.Modifier.Type.LanguageExtension,
[]
)] : [];
} else if (ts.isGetAccessorDeclaration(node)) {
return (node.modifiers ? node.modifiers?.filter(ts.isModifier).map(this.mapModifier) : []).concat(new J.Modifier(
randomId(),
this.prefix(node.getChildren().find(c => c.getText() === 'get')!),
Markers.EMPTY,
'get',
J.Modifier.Type.LanguageExtension,
[]
));
} else if (ts.isSetAccessorDeclaration(node)) {
return (node.modifiers ? node.modifiers?.filter(ts.isModifier).map(this.mapModifier) : []).concat(new J.Modifier(
randomId(),
this.prefix(node.getChildren().find(c => c.getText() === 'set')!),
Markers.EMPTY,
'set',
J.Modifier.Type.LanguageExtension,
[]
));
}
throw new Error(`Cannot get modifiers from ${node}`);
}
Expand Down Expand Up @@ -650,14 +670,7 @@ export class JavaScriptParserVisitor {
randomId(),
this.prefix(node.name),
Markers.EMPTY,
new JS.Unary(
randomId(),
Space.EMPTY,
Markers.EMPTY,
this.leftPadded(this.suffix(node.name), JS.Unary.Type.Optional),
this.visit(node.name),
this.mapType(node)
),
this.getOptionalUnary(node),
[],
node.initializer ? this.leftPadded(this.prefix(node.getChildAt(node.getChildCount(this.sourceFile) - 2)), this.visit(node.initializer)) : null,
this.mapVariableType(node)
Expand Down Expand Up @@ -725,6 +738,30 @@ export class JavaScriptParserVisitor {
}

visitPropertySignature(node: ts.PropertySignature) {
if (node.questionToken) {
return new JS.JSVariableDeclarations(
randomId(),
this.prefix(node),
Markers.EMPTY,
[], // no decorators allowed
this.mapModifiers(node),
this.mapTypeInfo(node),
null,
[this.rightPadded(
new JS.JSVariableDeclarations.JSNamedVariable(
randomId(),
this.prefix(node.name),
Markers.EMPTY,
this.getOptionalUnary(node),
[],
null,
this.mapVariableType(node)
),
Space.EMPTY
)]
);
}

return new J.VariableDeclarations(
randomId(),
this.prefix(node),
Expand All @@ -750,6 +787,30 @@ export class JavaScriptParserVisitor {
}

visitPropertyDeclaration(node: ts.PropertyDeclaration) {
if (node.questionToken) {
return new JS.JSVariableDeclarations(
randomId(),
this.prefix(node),
Markers.EMPTY,
[],
this.mapModifiers(node),
this.mapTypeInfo(node),
null,
[this.rightPadded(
new JS.JSVariableDeclarations.JSNamedVariable(
randomId(),
this.prefix(node.name),
Markers.EMPTY,
this.getOptionalUnary(node),
[],
node.initializer ? this.leftPadded(this.prefix(node.getChildAt(node.getChildren().indexOf(node.initializer) - 1)), this.visit(node.initializer)) : null,
this.mapVariableType(node)
),
Space.EMPTY
)]
);
}

return new J.VariableDeclarations(
randomId(),
this.prefix(node),
Expand All @@ -769,12 +830,32 @@ export class JavaScriptParserVisitor {
node.initializer ? this.leftPadded(this.prefix(node.getChildAt(node.getChildren().indexOf(node.initializer) - 1)), this.visit(node.initializer)) : null,
this.mapVariableType(node)
),
Space.EMPTY // FIXME check for semicolon
Space.EMPTY
)]
);
}

visitMethodSignature(node: ts.MethodSignature) {
if (node.questionToken) {
return new JS.JSMethodDeclaration(
randomId(),
this.prefix(node),
Markers.EMPTY,
[], // no decorators allowed
[], // no modifiers allowed
node.typeParameters
? new J.TypeParameters(randomId(), this.suffix(node.name), Markers.EMPTY, [], node.typeParameters.map(tp => this.rightPadded(this.visit(tp), this.suffix(tp))))
: null,
this.mapTypeInfo(node),
this.getOptionalUnary(node),
this.mapCommaSeparatedList(this.getParameterListNodes(node)),
null,
null,
null,
this.mapMethodType(node)
);
}

return new J.MethodDeclaration(
randomId(),
this.prefix(node),
Expand All @@ -798,6 +879,26 @@ export class JavaScriptParserVisitor {
}

visitMethodDeclaration(node: ts.MethodDeclaration) {
if (node.questionToken) {
return new JS.JSMethodDeclaration(
randomId(),
this.prefix(node),
Markers.EMPTY,
this.mapDecorators(node),
this.mapModifiers(node),
node.typeParameters
? new J.TypeParameters(randomId(), this.suffix(node.name), Markers.EMPTY, [], node.typeParameters.map(tp => this.rightPadded(this.visit(tp), this.suffix(tp))))
: null,
this.mapTypeInfo(node),
this.getOptionalUnary(node),
this.mapCommaSeparatedList(this.getParameterListNodes(node)),
null,
node.body ? this.convert<J.Block>(node.body) : null,
null,
this.mapMethodType(node)
);
}

return new J.MethodDeclaration(
randomId(),
this.prefix(node),
Expand All @@ -821,12 +922,23 @@ export class JavaScriptParserVisitor {
}

private mapTypeInfo(node: ts.MethodDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration | ts.ParameterDeclaration
| ts.PropertySignature | ts.MethodSignature | ts.ArrowFunction | ts.CallSignatureDeclaration) {
| ts.PropertySignature | ts.MethodSignature | ts.ArrowFunction | ts.CallSignatureDeclaration | ts.GetAccessorDeclaration) {
return node.type ? new JS.TypeInfo(randomId(), this.prefix(node.getChildAt(node.getChildren().indexOf(node.type) - 1)), Markers.EMPTY, this.visit(node.type)) : null;
}

visitClassStaticBlockDeclaration(node: ts.ClassStaticBlockDeclaration) {
return this.visitUnknown(node);
return new J.Block(
randomId(),
this.prefix(node),
Markers.EMPTY,
new JRightPadded(true,this.prefix(node.body.getChildren().find(v => v.kind === ts.SyntaxKind.OpenBraceToken)!), Markers.EMPTY),
node.body.statements.map(ce => new JRightPadded(
this.convert(ce),
ce.getLastToken()?.kind === ts.SyntaxKind.SemicolonToken ? this.prefix(ce.getLastToken()!) : Space.EMPTY,
ce.getLastToken()?.kind === ts.SyntaxKind.SemicolonToken ? Markers.build([new Semicolon(randomId())]) : Markers.EMPTY
)),
this.prefix(node.getLastToken()!)
);
}

visitConstructor(node: ts.ConstructorDeclaration) {
Expand All @@ -850,11 +962,45 @@ export class JavaScriptParserVisitor {
}

visitGetAccessor(node: ts.GetAccessorDeclaration) {
return this.visitUnknown(node);
return new J.MethodDeclaration(
randomId(),
this.prefix(node),
Markers.EMPTY,
[],
this.mapModifiers(node),
null,
this.mapTypeInfo(node),
new J.MethodDeclaration.IdentifierWithAnnotations(
this.visit(node.name),
[]
),
this.mapCommaSeparatedList(this.getParameterListNodes(node)),
null,
node.body ? this.convert<J.Block>(node.body) : null,
null,
this.mapMethodType(node)
);
}

visitSetAccessor(node: ts.SetAccessorDeclaration) {
return this.visitUnknown(node);
return new J.MethodDeclaration(
randomId(),
this.prefix(node),
Markers.EMPTY,
[],
this.mapModifiers(node),
null,
null,
new J.MethodDeclaration.IdentifierWithAnnotations(
this.visit(node.name),
[]
),
this.mapCommaSeparatedList(this.getParameterListNodes(node)),
null,
node.body ? this.convert<J.Block>(node.body) : null,
null,
this.mapMethodType(node)
);
}

visitCallSignature(node: ts.CallSignatureDeclaration) {
Expand Down Expand Up @@ -2449,6 +2595,17 @@ export class JavaScriptParserVisitor {
private newJEmpty(prefix: Space = Space.EMPTY) {
return new J.Empty(randomId(), prefix, Markers.EMPTY);
}

private getOptionalUnary(node: ts.MethodSignature | ts.MethodDeclaration | ts.ParameterDeclaration | ts.PropertySignature | ts.PropertyDeclaration) {
return new JS.Unary(
randomId(),
Space.EMPTY,
Markers.EMPTY,
this.leftPadded(this.suffix(node.name), JS.Unary.Type.Optional),
this.visit(node.name),
this.mapType(node)
);
}
}

function prefixFromNode(node: ts.Node, sourceFile: ts.SourceFile): Space {
Expand Down
37 changes: 36 additions & 1 deletion openrewrite/src/javascript/remote/receiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as extensions from "./remote_extensions";
import {Checksum, Cursor, FileAttributes, ListUtils, Tree} from '../../core';
import {DetailsReceiver, Receiver, ReceiverContext, ReceiverFactory, ValueType} from '@openrewrite/rewrite-remote';
import {JavaScriptVisitor} from '..';
import {JS, JsLeftPadded, JsRightPadded, JsContainer, JsSpace, CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Void, Yield, TypeInfo, JSVariableDeclarations, NamespaceDeclaration} from '../tree';
import {JS, JsLeftPadded, JsRightPadded, JsContainer, JsSpace, CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, NamespaceDeclaration} from '../tree';
import {Expression, J, JContainer, JLeftPadded, JRightPadded, NameTree, Space, Statement, TypeTree, TypedTree} from "../../java";
import * as Java from "../../java/tree";

Expand Down Expand Up @@ -323,6 +323,23 @@ class Visitor extends JavaScriptVisitor<ReceiverContext> {
return jSNamedVariable;
}

public visitJSMethodDeclaration(jSMethodDeclaration: JSMethodDeclaration, ctx: ReceiverContext): J {
jSMethodDeclaration = jSMethodDeclaration.withId(ctx.receiveValue(jSMethodDeclaration.id, ValueType.UUID)!);
jSMethodDeclaration = jSMethodDeclaration.withPrefix(ctx.receiveNode(jSMethodDeclaration.prefix, receiveSpace)!);
jSMethodDeclaration = jSMethodDeclaration.withMarkers(ctx.receiveNode(jSMethodDeclaration.markers, ctx.receiveMarkers)!);
jSMethodDeclaration = jSMethodDeclaration.withLeadingAnnotations(ctx.receiveNodes(jSMethodDeclaration.leadingAnnotations, ctx.receiveTree)!);
jSMethodDeclaration = jSMethodDeclaration.withModifiers(ctx.receiveNodes(jSMethodDeclaration.modifiers, ctx.receiveTree)!);
jSMethodDeclaration = jSMethodDeclaration.withTypeParameters(ctx.receiveNode(jSMethodDeclaration.typeParameters, ctx.receiveTree));
jSMethodDeclaration = jSMethodDeclaration.withReturnTypeExpression(ctx.receiveNode(jSMethodDeclaration.returnTypeExpression, ctx.receiveTree));
jSMethodDeclaration = jSMethodDeclaration.withName(ctx.receiveNode(jSMethodDeclaration.name, ctx.receiveTree)!);
jSMethodDeclaration = jSMethodDeclaration.padding.withParameters(ctx.receiveNode(jSMethodDeclaration.padding.parameters, receiveContainer)!);
jSMethodDeclaration = jSMethodDeclaration.padding.withThrowz(ctx.receiveNode(jSMethodDeclaration.padding.throwz, receiveContainer));
jSMethodDeclaration = jSMethodDeclaration.withBody(ctx.receiveNode(jSMethodDeclaration.body, ctx.receiveTree));
jSMethodDeclaration = jSMethodDeclaration.padding.withDefaultValue(ctx.receiveNode(jSMethodDeclaration.padding.defaultValue, receiveLeftPaddedTree));
jSMethodDeclaration = jSMethodDeclaration.withMethodType(ctx.receiveValue(jSMethodDeclaration.methodType, ValueType.Object));
return jSMethodDeclaration;
}

public visitNamespaceDeclaration(namespaceDeclaration: NamespaceDeclaration, ctx: ReceiverContext): J {
namespaceDeclaration = namespaceDeclaration.withId(ctx.receiveValue(namespaceDeclaration.id, ValueType.UUID)!);
namespaceDeclaration = namespaceDeclaration.withPrefix(ctx.receiveNode(namespaceDeclaration.prefix, receiveSpace)!);
Expand Down Expand Up @@ -1329,6 +1346,24 @@ class Factory implements ReceiverFactory {
);
}

if (type === "org.openrewrite.javascript.tree.JS$JSMethodDeclaration") {
return new JSMethodDeclaration(
ctx.receiveValue(null, ValueType.UUID)!,
ctx.receiveNode(null, receiveSpace)!,
ctx.receiveNode(null, ctx.receiveMarkers)!,
ctx.receiveNodes<Java.Annotation>(null, ctx.receiveTree)!,
ctx.receiveNodes<Java.Modifier>(null, ctx.receiveTree)!,
ctx.receiveNode<Java.TypeParameters>(null, ctx.receiveTree),
ctx.receiveNode<TypeTree>(null, ctx.receiveTree),
ctx.receiveNode<Expression>(null, ctx.receiveTree)!,
ctx.receiveNode<JContainer<Statement>>(null, receiveContainer)!,
ctx.receiveNode<JContainer<NameTree>>(null, receiveContainer),
ctx.receiveNode<Java.Block>(null, ctx.receiveTree),
ctx.receiveNode<JLeftPadded<Expression>>(null, receiveLeftPaddedTree),
ctx.receiveValue(null, ValueType.Object)
);
}

if (type === "org.openrewrite.javascript.tree.JS$NamespaceDeclaration") {
return new NamespaceDeclaration(
ctx.receiveValue(null, ValueType.UUID)!,
Expand Down
19 changes: 18 additions & 1 deletion openrewrite/src/javascript/remote/sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as extensions from "./remote_extensions";
import {Cursor, ListUtils, Tree} from '../../core';
import {Sender, SenderContext, ValueType} from '@openrewrite/rewrite-remote';
import {JavaScriptVisitor} from '..';
import {JS, JsLeftPadded, JsRightPadded, JsContainer, JsSpace, CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Void, Yield, TypeInfo, JSVariableDeclarations, NamespaceDeclaration} from '../tree';
import {JS, JsLeftPadded, JsRightPadded, JsContainer, JsSpace, CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, NamespaceDeclaration} from '../tree';
import {Expression, J, JContainer, JLeftPadded, JRightPadded, Space, Statement} from "../../java";
import * as Java from "../../java/tree";

Expand Down Expand Up @@ -318,6 +318,23 @@ class Visitor extends JavaScriptVisitor<SenderContext> {
return jSNamedVariable;
}

public visitJSMethodDeclaration(jSMethodDeclaration: JSMethodDeclaration, ctx: SenderContext): J {
ctx.sendValue(jSMethodDeclaration, v => v.id, ValueType.UUID);
ctx.sendNode(jSMethodDeclaration, v => v.prefix, Visitor.sendSpace);
ctx.sendNode(jSMethodDeclaration, v => v.markers, ctx.sendMarkers);
ctx.sendNodes(jSMethodDeclaration, v => v.leadingAnnotations, ctx.sendTree, t => t.id);
ctx.sendNodes(jSMethodDeclaration, v => v.modifiers, ctx.sendTree, t => t.id);
ctx.sendNode(jSMethodDeclaration, v => v.typeParameters, ctx.sendTree);
ctx.sendNode(jSMethodDeclaration, v => v.returnTypeExpression, ctx.sendTree);
ctx.sendNode(jSMethodDeclaration, v => v.name, ctx.sendTree);
ctx.sendNode(jSMethodDeclaration, v => v.padding.parameters, Visitor.sendContainer(ValueType.Tree));
ctx.sendNode(jSMethodDeclaration, v => v.padding.throwz, Visitor.sendContainer(ValueType.Tree));
ctx.sendNode(jSMethodDeclaration, v => v.body, ctx.sendTree);
ctx.sendNode(jSMethodDeclaration, v => v.padding.defaultValue, Visitor.sendLeftPadded(ValueType.Tree));
ctx.sendTypedValue(jSMethodDeclaration, v => v.methodType, ValueType.Object);
return jSMethodDeclaration;
}

public visitNamespaceDeclaration(namespaceDeclaration: NamespaceDeclaration, ctx: SenderContext): J {
ctx.sendValue(namespaceDeclaration, v => v.id, ValueType.UUID);
ctx.sendNode(namespaceDeclaration, v => v.prefix, Visitor.sendSpace);
Expand Down
6 changes: 5 additions & 1 deletion openrewrite/src/javascript/tree/support_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ export namespace JsSpace {
JSVARIABLE_DECLARATIONS_VARARGS,
JSVARIABLE_DECLARATIONS_JSNAMED_VARIABLE_PREFIX,
NAMESPACE_DECLARATION_PREFIX,
NAMESPACE_DECLARATION_NAMESPACE
NAMESPACE_DECLARATION_NAMESPACE,
JSMETHOD_DECLARATION_PREFIX
}
}
export namespace JsLeftPadded {
Expand All @@ -234,6 +235,7 @@ export namespace JsLeftPadded {
UNARY_OPERATOR,
JSVARIABLE_DECLARATIONS_JSNAMED_VARIABLE_INITIALIZER,
JSVARIABLE_DECLARATIONS_JSNAMED_VARIABLE_DIMENSIONS_AFTER_NAME,
JSMETHOD_DECLARATION_DEFAULT_VALUE
}
}
export namespace JsRightPadded {
Expand All @@ -257,5 +259,7 @@ export namespace JsContainer {
JS_IMPORT_IMPORTS,
OBJECT_BINDING_DECLARATIONS_BINDINGS,
TUPLE_ELEMENTS,
JSMETHOD_DECLARATION_PARAMETERS,
JSMETHOD_DECLARATION_THROWZ
}
}
Loading

0 comments on commit 86e36b6

Please sign in to comment.