From 86e36b6a89c7c4a7e1299d4fc28bcaaf424e9a66 Mon Sep 17 00:00:00 2001 From: Andrii Rodionov Date: Fri, 8 Nov 2024 13:37:08 +0100 Subject: [PATCH] Implemented support for optional methods (#139) 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 --- openrewrite/src/javascript/parser.ts | 185 +++++++++++++++-- openrewrite/src/javascript/remote/receiver.ts | 37 +++- openrewrite/src/javascript/remote/sender.ts | 19 +- .../src/javascript/tree/support_types.ts | 6 +- openrewrite/src/javascript/tree/tree.ts | 187 ++++++++++++++++++ openrewrite/src/javascript/visitor.ts | 23 ++- .../test/javascript/parser/class.test.ts | 120 +++++++++++ .../test/javascript/parser/interface.test.ts | 45 ++++- .../javascript/remote/JavaScriptReceiver.java | 36 ++++ .../javascript/remote/JavaScriptSender.java | 18 ++ .../javascript/JavaScriptVisitor.java | 36 +++- .../internal/JavaScriptPrinter.java | 46 ++++- .../org/openrewrite/javascript/tree/JS.java | 185 +++++++++++++++++ .../javascript/tree/JsContainer.java | 4 +- .../javascript/tree/JsRightPadded.java | 3 +- .../openrewrite/javascript/tree/JsSpace.java | 6 +- 16 files changed, 924 insertions(+), 32 deletions(-) diff --git a/openrewrite/src/javascript/parser.ts b/openrewrite/src/javascript/parser.ts index 64673566..714b917d 100644 --- a/openrewrite/src/javascript/parser.ts +++ b/openrewrite/src/javascript/parser.ts @@ -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(), @@ -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}`); } @@ -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) @@ -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), @@ -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), @@ -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), @@ -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(node.body) : null, + null, + this.mapMethodType(node) + ); + } + return new J.MethodDeclaration( randomId(), this.prefix(node), @@ -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) { @@ -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(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(node.body) : null, + null, + this.mapMethodType(node) + ); } visitCallSignature(node: ts.CallSignatureDeclaration) { @@ -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 { diff --git a/openrewrite/src/javascript/remote/receiver.ts b/openrewrite/src/javascript/remote/receiver.ts index 2c7b0b7d..afa776c0 100644 --- a/openrewrite/src/javascript/remote/receiver.ts +++ b/openrewrite/src/javascript/remote/receiver.ts @@ -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"; @@ -323,6 +323,23 @@ class Visitor extends JavaScriptVisitor { 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)!); @@ -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(null, ctx.receiveTree)!, + ctx.receiveNodes(null, ctx.receiveTree)!, + ctx.receiveNode(null, ctx.receiveTree), + ctx.receiveNode(null, ctx.receiveTree), + ctx.receiveNode(null, ctx.receiveTree)!, + ctx.receiveNode>(null, receiveContainer)!, + ctx.receiveNode>(null, receiveContainer), + ctx.receiveNode(null, ctx.receiveTree), + ctx.receiveNode>(null, receiveLeftPaddedTree), + ctx.receiveValue(null, ValueType.Object) + ); + } + if (type === "org.openrewrite.javascript.tree.JS$NamespaceDeclaration") { return new NamespaceDeclaration( ctx.receiveValue(null, ValueType.UUID)!, diff --git a/openrewrite/src/javascript/remote/sender.ts b/openrewrite/src/javascript/remote/sender.ts index ef938444..a0b1620f 100644 --- a/openrewrite/src/javascript/remote/sender.ts +++ b/openrewrite/src/javascript/remote/sender.ts @@ -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"; @@ -318,6 +318,23 @@ class Visitor extends JavaScriptVisitor { 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); diff --git a/openrewrite/src/javascript/tree/support_types.ts b/openrewrite/src/javascript/tree/support_types.ts index 9d856270..961121b9 100644 --- a/openrewrite/src/javascript/tree/support_types.ts +++ b/openrewrite/src/javascript/tree/support_types.ts @@ -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 { @@ -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 { @@ -257,5 +259,7 @@ export namespace JsContainer { JS_IMPORT_IMPORTS, OBJECT_BINDING_DECLARATIONS_BINDINGS, TUPLE_ELEMENTS, + JSMETHOD_DECLARATION_PARAMETERS, + JSMETHOD_DECLARATION_THROWZ } } diff --git a/openrewrite/src/javascript/tree/tree.ts b/openrewrite/src/javascript/tree/tree.ts index 5c5e29eb..30898b83 100644 --- a/openrewrite/src/javascript/tree/tree.ts +++ b/openrewrite/src/javascript/tree/tree.ts @@ -2649,6 +2649,193 @@ export namespace JSVariableDeclarations { } +@LstType("org.openrewrite.javascript.tree.JS$JSMethodDeclaration") +export class JSMethodDeclaration extends JSMixin(Object) implements Statement, TypedTree { + public constructor(id: UUID, prefix: Space, markers: Markers, leadingAnnotations: Java.Annotation[], modifiers: Java.Modifier[], typeParameters: Java.TypeParameters | null, returnTypeExpression: TypeTree | null, name: Expression, parameters: JContainer, throwz: JContainer | null, body: Java.Block | null, defaultValue: JLeftPadded | null, methodType: JavaType.Method | null) { + super(); + this._id = id; + this._prefix = prefix; + this._markers = markers; + this._leadingAnnotations = leadingAnnotations; + this._modifiers = modifiers; + this._typeParameters = typeParameters; + this._returnTypeExpression = returnTypeExpression; + this._name = name; + this._parameters = parameters; + this._throwz = throwz; + this._body = body; + this._defaultValue = defaultValue; + this._methodType = methodType; + } + + private readonly _id: UUID; + + public get id(): UUID { + return this._id; + } + + public withId(id: UUID): JSMethodDeclaration { + return id === this._id ? this : new JSMethodDeclaration(id, this._prefix, this._markers, this._leadingAnnotations, this._modifiers, this._typeParameters, this._returnTypeExpression, this._name, this._parameters, this._throwz, this._body, this._defaultValue, this._methodType); + } + + private readonly _prefix: Space; + + public get prefix(): Space { + return this._prefix; + } + + public withPrefix(prefix: Space): JSMethodDeclaration { + return prefix === this._prefix ? this : new JSMethodDeclaration(this._id, prefix, this._markers, this._leadingAnnotations, this._modifiers, this._typeParameters, this._returnTypeExpression, this._name, this._parameters, this._throwz, this._body, this._defaultValue, this._methodType); + } + + private readonly _markers: Markers; + + public get markers(): Markers { + return this._markers; + } + + public withMarkers(markers: Markers): JSMethodDeclaration { + return markers === this._markers ? this : new JSMethodDeclaration(this._id, this._prefix, markers, this._leadingAnnotations, this._modifiers, this._typeParameters, this._returnTypeExpression, this._name, this._parameters, this._throwz, this._body, this._defaultValue, this._methodType); + } + + private readonly _leadingAnnotations: Java.Annotation[]; + + public get leadingAnnotations(): Java.Annotation[] { + return this._leadingAnnotations; + } + + public withLeadingAnnotations(leadingAnnotations: Java.Annotation[]): JSMethodDeclaration { + return leadingAnnotations === this._leadingAnnotations ? this : new JSMethodDeclaration(this._id, this._prefix, this._markers, leadingAnnotations, this._modifiers, this._typeParameters, this._returnTypeExpression, this._name, this._parameters, this._throwz, this._body, this._defaultValue, this._methodType); + } + + private readonly _modifiers: Java.Modifier[]; + + public get modifiers(): Java.Modifier[] { + return this._modifiers; + } + + public withModifiers(modifiers: Java.Modifier[]): JSMethodDeclaration { + return modifiers === this._modifiers ? this : new JSMethodDeclaration(this._id, this._prefix, this._markers, this._leadingAnnotations, modifiers, this._typeParameters, this._returnTypeExpression, this._name, this._parameters, this._throwz, this._body, this._defaultValue, this._methodType); + } + + private readonly _typeParameters: Java.TypeParameters | null; + + public get typeParameters(): Java.TypeParameters | null { + return this._typeParameters; + } + + public withTypeParameters(typeParameters: Java.TypeParameters | null): JSMethodDeclaration { + return typeParameters === this._typeParameters ? this : new JSMethodDeclaration(this._id, this._prefix, this._markers, this._leadingAnnotations, this._modifiers, typeParameters, this._returnTypeExpression, this._name, this._parameters, this._throwz, this._body, this._defaultValue, this._methodType); + } + + private readonly _returnTypeExpression: TypeTree | null; + + public get returnTypeExpression(): TypeTree | null { + return this._returnTypeExpression; + } + + public withReturnTypeExpression(returnTypeExpression: TypeTree | null): JSMethodDeclaration { + return returnTypeExpression === this._returnTypeExpression ? this : new JSMethodDeclaration(this._id, this._prefix, this._markers, this._leadingAnnotations, this._modifiers, this._typeParameters, returnTypeExpression, this._name, this._parameters, this._throwz, this._body, this._defaultValue, this._methodType); + } + + private readonly _name: Expression; + + public get name(): Expression { + return this._name; + } + + public withName(name: Expression): JSMethodDeclaration { + return name === this._name ? this : new JSMethodDeclaration(this._id, this._prefix, this._markers, this._leadingAnnotations, this._modifiers, this._typeParameters, this._returnTypeExpression, name, this._parameters, this._throwz, this._body, this._defaultValue, this._methodType); + } + + private readonly _parameters: JContainer; + + public get parameters(): Statement[] { + return this._parameters.elements; + } + + public withParameters(parameters: Statement[]): JSMethodDeclaration { + return this.padding.withParameters(JContainer.withElements(this._parameters, parameters)); + } + + private readonly _throwz: JContainer | null; + + public get throwz(): NameTree[] | null { + return this._throwz === null ? null : this._throwz.elements; + } + + public withThrowz(throwz: NameTree[] | null): JSMethodDeclaration { + return this.padding.withThrowz(JContainer.withElementsNullable(this._throwz, throwz)); + } + + private readonly _body: Java.Block | null; + + public get body(): Java.Block | null { + return this._body; + } + + public withBody(body: Java.Block | null): JSMethodDeclaration { + return body === this._body ? this : new JSMethodDeclaration(this._id, this._prefix, this._markers, this._leadingAnnotations, this._modifiers, this._typeParameters, this._returnTypeExpression, this._name, this._parameters, this._throwz, body, this._defaultValue, this._methodType); + } + + private readonly _defaultValue: JLeftPadded | null; + + public get defaultValue(): Expression | null { + return this._defaultValue === null ? null : this._defaultValue.element; + } + + public withDefaultValue(defaultValue: Expression | null): JSMethodDeclaration { + return this.padding.withDefaultValue(JLeftPadded.withElement(this._defaultValue, defaultValue)); + } + + private readonly _methodType: JavaType.Method | null; + + public get methodType(): JavaType.Method | null { + return this._methodType; + } + + public withMethodType(methodType: JavaType.Method | null): JSMethodDeclaration { + return methodType === this._methodType ? this : new JSMethodDeclaration(this._id, this._prefix, this._markers, this._leadingAnnotations, this._modifiers, this._typeParameters, this._returnTypeExpression, this._name, this._parameters, this._throwz, this._body, this._defaultValue, methodType); + } + + public acceptJavaScript

(v: JavaScriptVisitor

, p: P): J | null { + return v.visitJSMethodDeclaration(this, p); + } + + public get type(): JavaType | null { + return extensions.getJavaType(this); + } + + public withType(type: JavaType): JSMethodDeclaration { + return extensions.withJavaType(this, type); + } + + get padding() { + const t = this; + return new class { + public get parameters(): JContainer { + return t._parameters; + } + public withParameters(parameters: JContainer): JSMethodDeclaration { + return t._parameters === parameters ? t : new JSMethodDeclaration(t._id, t._prefix, t._markers, t._leadingAnnotations, t._modifiers, t._typeParameters, t._returnTypeExpression, t._name, parameters, t._throwz, t._body, t._defaultValue, t._methodType); + } + public get throwz(): JContainer | null { + return t._throwz; + } + public withThrowz(throwz: JContainer | null): JSMethodDeclaration { + return t._throwz === throwz ? t : new JSMethodDeclaration(t._id, t._prefix, t._markers, t._leadingAnnotations, t._modifiers, t._typeParameters, t._returnTypeExpression, t._name, t._parameters, throwz, t._body, t._defaultValue, t._methodType); + } + public get defaultValue(): JLeftPadded | null { + return t._defaultValue; + } + public withDefaultValue(defaultValue: JLeftPadded | null): JSMethodDeclaration { + return t._defaultValue === defaultValue ? t : new JSMethodDeclaration(t._id, t._prefix, t._markers, t._leadingAnnotations, t._modifiers, t._typeParameters, t._returnTypeExpression, t._name, t._parameters, t._throwz, t._body, defaultValue, t._methodType); + } + } + } + +} + @LstType("org.openrewrite.javascript.tree.JS$NamespaceDeclaration") export class NamespaceDeclaration extends JSMixin(Object) implements Statement { public constructor(id: UUID, prefix: Space, markers: Markers, modifiers: Java.Modifier[], namespace: Space, name: JRightPadded, body: Java.Block) { diff --git a/openrewrite/src/javascript/visitor.ts b/openrewrite/src/javascript/visitor.ts index e3d12b8d..2493620d 100644 --- a/openrewrite/src/javascript/visitor.ts +++ b/openrewrite/src/javascript/visitor.ts @@ -1,7 +1,7 @@ import * as extensions from "./extensions"; import {ListUtils, SourceFile, Tree, TreeVisitor} from "../core"; import {JS, isJavaScript, JsLeftPadded, JsRightPadded, JsContainer, JsSpace} from "./tree"; -import {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 {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/tree"; import {JavaVisitor} from "../java"; import * as Java from "../java/tree"; @@ -421,6 +421,27 @@ export class JavaScriptVisitor

extends JavaVisitor

{ return jSNamedVariable; } + public visitJSMethodDeclaration(jSMethodDeclaration: JSMethodDeclaration, p: P): J | null { + jSMethodDeclaration = jSMethodDeclaration.withPrefix(this.visitJsSpace(jSMethodDeclaration.prefix, JsSpace.Location.JSMETHOD_DECLARATION_PREFIX, p)!); + let tempStatement = this.visitStatement(jSMethodDeclaration, p) as Statement; + if (!(tempStatement instanceof JSMethodDeclaration)) + { + return tempStatement; + } + jSMethodDeclaration = tempStatement as JSMethodDeclaration; + jSMethodDeclaration = jSMethodDeclaration.withMarkers(this.visitMarkers(jSMethodDeclaration.markers, p)); + jSMethodDeclaration = jSMethodDeclaration.withLeadingAnnotations(ListUtils.map(jSMethodDeclaration.leadingAnnotations, el => this.visitAndCast(el, p))); + jSMethodDeclaration = jSMethodDeclaration.withModifiers(ListUtils.map(jSMethodDeclaration.modifiers, el => this.visitAndCast(el, p))); + jSMethodDeclaration = jSMethodDeclaration.withTypeParameters(this.visitAndCast(jSMethodDeclaration.typeParameters, p)); + jSMethodDeclaration = jSMethodDeclaration.withReturnTypeExpression(this.visitAndCast(jSMethodDeclaration.returnTypeExpression, p)); + jSMethodDeclaration = jSMethodDeclaration.withName(this.visitAndCast(jSMethodDeclaration.name, p)!); + jSMethodDeclaration = jSMethodDeclaration.padding.withParameters(this.visitJsContainer(jSMethodDeclaration.padding.parameters, JsContainer.Location.JSMETHOD_DECLARATION_PARAMETERS, p)!); + jSMethodDeclaration = jSMethodDeclaration.padding.withThrowz(this.visitJsContainer(jSMethodDeclaration.padding.throwz, JsContainer.Location.JSMETHOD_DECLARATION_THROWZ, p)); + jSMethodDeclaration = jSMethodDeclaration.withBody(this.visitAndCast(jSMethodDeclaration.body, p)); + jSMethodDeclaration = jSMethodDeclaration.padding.withDefaultValue(this.visitJsLeftPadded(jSMethodDeclaration.padding.defaultValue, JsLeftPadded.Location.JSMETHOD_DECLARATION_DEFAULT_VALUE, p)); + return jSMethodDeclaration; + } + public visitNamespaceDeclaration(namespaceDeclaration: NamespaceDeclaration, p: P): J | null { namespaceDeclaration = namespaceDeclaration.withPrefix(this.visitJsSpace(namespaceDeclaration.prefix, JsSpace.Location.NAMESPACE_DECLARATION_PREFIX, p)!); let tempStatement = this.visitStatement(namespaceDeclaration, p) as Statement; diff --git a/openrewrite/test/javascript/parser/class.test.ts b/openrewrite/test/javascript/parser/class.test.ts index 774502d7..8c7323a0 100644 --- a/openrewrite/test/javascript/parser/class.test.ts +++ b/openrewrite/test/javascript/parser/class.test.ts @@ -210,4 +210,124 @@ describe('class mapping', () => { }`) ); }); + + test('class with optional properties, ctor and modifiers', () => { + rewriteRun( + //language=typescript + typeScript(` + class Employee { + public id: number; + protected name: string; + private department?: string; // Optional property + + constructor(id: number, name: string, department?: string) { + this.id = id; + this.name = name; + this.department = department; + } + } + `) + ); + }); + + test('class with optional properties and methods', () => { + rewriteRun( + //language=typescript + typeScript(` + class Person { + name: string; + age?: number; // Optional property + greetFirst ?: () => void; // Optional method + greetSecond ?(): string; // Optional method + } + `) + ); + }); + + test('class with get/set accessors', () => { + rewriteRun( + //language=typescript + typeScript(` + class Person { + private _name = ''; + + // Getter + public get name(): string { + return this._name; + } + + Setter + set name(value: string) { + this._name = value; + } + } + `) + ); + }); + + test('class with get/set accessors with comments', () => { + rewriteRun( + //language=typescript + typeScript(` + class Person { + private _name = ''; + + // Getter + public /*a*/ get /*b*/ name/*c*/(/*d*/): string { + return this._name; + } + + // Setter + public /*a*/ set /*b*/ name/*c*/(/*d*/value/*e*/: /*f*/ string /*g*/) { + this._name = value; + } + } + `) + ); + }); + + test('class with static blocks', () => { + rewriteRun( + //language=typescript + typeScript(` + class Example { + static valueA: number; + static valueB: number; + + static { + this.valueA = 10 + console.log("Static block 1 executed. valueA:", this.valueA); + } + + static { + this.valueB = this.valueA * 2 + console.log("Static block 2 executed. valueB:", this.valueB); + } + } + `) + ); + }); + + test('class with static blocks and comments', () => { + rewriteRun( + //language=typescript + typeScript(` + class Example { + static /*1*/ valueA /*2*/: /*3*/ number; + static valueB: number; + + /*a*/ static /*b*/{ /*c*/ + this.valueA = 10 /*d*/; /*e*/ + console.log("Static block 1 executed. valueA:", this.valueA) /*f*/ + } + + /*g*/static{ + this.valueB = this.valueA * 2 /*h*/ + console.log("Static block 2 executed. valueB:", this.valueB); + } + } + `) + ); + }); + }); diff --git a/openrewrite/test/javascript/parser/interface.test.ts b/openrewrite/test/javascript/parser/interface.test.ts index e227a452..7f242d7b 100644 --- a/openrewrite/test/javascript/parser/interface.test.ts +++ b/openrewrite/test/javascript/parser/interface.test.ts @@ -173,19 +173,41 @@ describe('as mapping', () => { ); }); - test.skip('interface with get/set methods', () => { + test('interface with get/set methods', () => { rewriteRun( //language=typescript typeScript(` interface Person { name: string; - get age(): number; // Getter for age - set age(a: number); // Setter for age + get age() : number ; // Getter for age + set age(a: number) ; // Setter for age } `) ); }); + test.skip('interface with constructor signature', () => { + rewriteRun( + //language=typescript + typeScript(` + interface Constructible { + new (name: string, age: number): Person; // Interface that defines a constructor signature + } + `) + ); + }); + + test.skip('interface with optional constructor signature', () => { + rewriteRun( + //language=typescript + typeScript(` + interface Constructible { + new? (name: string, age: number): Person; // Interface that defines a constructor signature + } + `) + ); + }); + test('interface with properties and methods with modifiers ', () => { rewriteRun( //language=typescript @@ -198,12 +220,25 @@ describe('as mapping', () => { ); }); - test.skip('interface with properties and methods with optional ', () => { + test('interface with optional property signature', () => { + rewriteRun( + //language=typescript + typeScript(` + interface Person { + surname?: string; + readonly name ?: string + } + `) + ); + }); + + test('interface with optional properties and methods ', () => { rewriteRun( //language=typescript typeScript(` interface Person { - greet?(name: string): void + greet ?(name: string): void + add ?(): (x: number, y?: number) => number; readonly name?: string } `) diff --git a/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptReceiver.java b/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptReceiver.java index a435777f..100cf56c 100644 --- a/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptReceiver.java +++ b/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptReceiver.java @@ -398,6 +398,24 @@ public JS.JSVariableDeclarations.JSNamedVariable visitJSVariableDeclarationsJSNa return jSNamedVariable; } + @Override + public JS.JSMethodDeclaration visitJSMethodDeclaration(JS.JSMethodDeclaration jSMethodDeclaration, ReceiverContext ctx) { + jSMethodDeclaration = jSMethodDeclaration.withId(ctx.receiveNonNullValue(jSMethodDeclaration.getId(), UUID.class)); + jSMethodDeclaration = jSMethodDeclaration.withPrefix(ctx.receiveNonNullNode(jSMethodDeclaration.getPrefix(), JavaScriptReceiver::receiveSpace)); + jSMethodDeclaration = jSMethodDeclaration.withMarkers(ctx.receiveNonNullNode(jSMethodDeclaration.getMarkers(), ctx::receiveMarkers)); + jSMethodDeclaration = jSMethodDeclaration.withLeadingAnnotations(ctx.receiveNonNullNodes(jSMethodDeclaration.getLeadingAnnotations(), ctx::receiveTree)); + jSMethodDeclaration = jSMethodDeclaration.withModifiers(ctx.receiveNonNullNodes(jSMethodDeclaration.getModifiers(), ctx::receiveTree)); + jSMethodDeclaration = jSMethodDeclaration.withTypeParameters(ctx.receiveNode(jSMethodDeclaration.getTypeParameters(), ctx::receiveTree)); + jSMethodDeclaration = jSMethodDeclaration.withReturnTypeExpression(ctx.receiveNode(jSMethodDeclaration.getReturnTypeExpression(), ctx::receiveTree)); + jSMethodDeclaration = jSMethodDeclaration.withName(ctx.receiveNonNullNode(jSMethodDeclaration.getName(), ctx::receiveTree)); + jSMethodDeclaration = jSMethodDeclaration.getPadding().withParameters(ctx.receiveNonNullNode(jSMethodDeclaration.getPadding().getParameters(), JavaScriptReceiver::receiveContainer)); + jSMethodDeclaration = jSMethodDeclaration.getPadding().withThrowz(ctx.receiveNode(jSMethodDeclaration.getPadding().getThrowz(), JavaScriptReceiver::receiveContainer)); + jSMethodDeclaration = jSMethodDeclaration.withBody(ctx.receiveNode(jSMethodDeclaration.getBody(), ctx::receiveTree)); + jSMethodDeclaration = jSMethodDeclaration.getPadding().withDefaultValue(ctx.receiveNode(jSMethodDeclaration.getPadding().getDefaultValue(), JavaScriptReceiver::receiveLeftPaddedTree)); + jSMethodDeclaration = jSMethodDeclaration.withMethodType(ctx.receiveValue(jSMethodDeclaration.getMethodType(), JavaType.Method.class)); + return jSMethodDeclaration; + } + @Override public JS.NamespaceDeclaration visitNamespaceDeclaration(JS.NamespaceDeclaration namespaceDeclaration, ReceiverContext ctx) { namespaceDeclaration = namespaceDeclaration.withId(ctx.receiveNonNullValue(namespaceDeclaration.getId(), UUID.class)); @@ -1435,6 +1453,24 @@ public T create(Class type, ReceiverContext ctx) { ); } + if (type == JS.JSMethodDeclaration.class) { + return (T) new JS.JSMethodDeclaration( + ctx.receiveNonNullValue(null, UUID.class), + ctx.receiveNonNullNode(null, JavaScriptReceiver::receiveSpace), + ctx.receiveNonNullNode(null, ctx::receiveMarkers), + ctx.receiveNonNullNodes(null, ctx::receiveTree), + ctx.receiveNonNullNodes(null, ctx::receiveTree), + ctx.receiveNode(null, ctx::receiveTree), + ctx.receiveNode(null, ctx::receiveTree), + ctx.receiveNonNullNode(null, ctx::receiveTree), + ctx.receiveNonNullNode(null, JavaScriptReceiver::receiveContainer), + ctx.receiveNode(null, JavaScriptReceiver::receiveContainer), + ctx.receiveNode(null, ctx::receiveTree), + ctx.receiveNode(null, JavaScriptReceiver::receiveLeftPaddedTree), + ctx.receiveValue(null, JavaType.Method.class) + ); + } + if (type == JS.NamespaceDeclaration.class) { return (T) new JS.NamespaceDeclaration( ctx.receiveNonNullValue(null, UUID.class), diff --git a/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptSender.java b/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptSender.java index 21a0c695..f9e4fb95 100644 --- a/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptSender.java +++ b/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptSender.java @@ -381,6 +381,24 @@ public JS.JSVariableDeclarations.JSNamedVariable visitJSVariableDeclarationsJSNa return jSNamedVariable; } + @Override + public JS.JSMethodDeclaration visitJSMethodDeclaration(JS.JSMethodDeclaration jSMethodDeclaration, SenderContext ctx) { + ctx.sendValue(jSMethodDeclaration, JS.JSMethodDeclaration::getId); + ctx.sendNode(jSMethodDeclaration, JS.JSMethodDeclaration::getPrefix, JavaScriptSender::sendSpace); + ctx.sendNode(jSMethodDeclaration, JS.JSMethodDeclaration::getMarkers, ctx::sendMarkers); + ctx.sendNodes(jSMethodDeclaration, JS.JSMethodDeclaration::getLeadingAnnotations, ctx::sendTree, Tree::getId); + ctx.sendNodes(jSMethodDeclaration, JS.JSMethodDeclaration::getModifiers, ctx::sendTree, Tree::getId); + ctx.sendNode(jSMethodDeclaration, JS.JSMethodDeclaration::getTypeParameters, ctx::sendTree); + ctx.sendNode(jSMethodDeclaration, JS.JSMethodDeclaration::getReturnTypeExpression, ctx::sendTree); + ctx.sendNode(jSMethodDeclaration, JS.JSMethodDeclaration::getName, ctx::sendTree); + ctx.sendNode(jSMethodDeclaration, e -> e.getPadding().getParameters(), JavaScriptSender::sendContainer); + ctx.sendNode(jSMethodDeclaration, e -> e.getPadding().getThrowz(), JavaScriptSender::sendContainer); + ctx.sendNode(jSMethodDeclaration, JS.JSMethodDeclaration::getBody, ctx::sendTree); + ctx.sendNode(jSMethodDeclaration, e -> e.getPadding().getDefaultValue(), JavaScriptSender::sendLeftPadded); + ctx.sendTypedValue(jSMethodDeclaration, JS.JSMethodDeclaration::getMethodType); + return jSMethodDeclaration; + } + @Override public JS.NamespaceDeclaration visitNamespaceDeclaration(JS.NamespaceDeclaration namespaceDeclaration, SenderContext ctx) { ctx.sendValue(namespaceDeclaration, JS.NamespaceDeclaration::getId); diff --git a/rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java b/rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java index 842e926d..fcc8a89a 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java @@ -625,9 +625,41 @@ public J visitJSVariableDeclarationsJSNamedVariable(JS.JSVariableDeclarations.JS return v; } + public J visitJSMethodDeclaration(JS.JSMethodDeclaration method, P p) { + JS.JSMethodDeclaration m = method; + m = m.withPrefix(visitSpace(m.getPrefix(), Space.Location.METHOD_DECLARATION_PREFIX, p)); + m = m.withMarkers(visitMarkers(m.getMarkers(), p)); + Statement temp = (Statement) visitStatement(m, p); + if (!(temp instanceof JS.JSMethodDeclaration)) { + return temp; + } else { + m = (JS.JSMethodDeclaration) temp; + } + m = m.withLeadingAnnotations(ListUtils.map(m.getLeadingAnnotations(), a -> visitAndCast(a, p))); + m = m.withModifiers(ListUtils.map(m.getModifiers(), e -> visitAndCast(e, p))); + m = m.withTypeParameters(visitAndCast(m.getTypeParameters(), p)); + m = m.withReturnTypeExpression(visitAndCast(m.getReturnTypeExpression(), p)); + m = m.withReturnTypeExpression( + m.getReturnTypeExpression() == null ? + null : + visitTypeName(m.getReturnTypeExpression(), p)); + m = m.withName(this.visitAndCast(m.getName(), p)); + m = m.getPadding().withParameters(visitContainer(m.getPadding().getParameters(), JContainer.Location.METHOD_DECLARATION_PARAMETERS, p)); + if (m.getPadding().getThrowz() != null) { + m = m.getPadding().withThrowz(visitContainer(m.getPadding().getThrowz(), JContainer.Location.THROWS, p)); + } + m = m.getPadding().withThrowz(visitTypeNames(m.getPadding().getThrowz(), p)); + m = m.withBody(visitAndCast(m.getBody(), p)); + if (m.getPadding().getDefaultValue() != null) { + m = m.getPadding().withDefaultValue(visitLeftPadded(m.getPadding().getDefaultValue(), JLeftPadded.Location.METHOD_DECLARATION_DEFAULT_VALUE, p)); + } + m = m.withMethodType((JavaType.Method) visitType(m.getMethodType(), p)); + return m; + } + public J visitNamespaceDeclaration(JS.NamespaceDeclaration namespaceDeclaration, P p) { JS.NamespaceDeclaration ns = namespaceDeclaration; - ns = ns.withPrefix(visitSpace(ns.getPrefix(), JsSpace.Location.NAMESPACE_DECLARATION_PREFIX, p)); + ns = ns.withPrefix(visitSpace(ns.getPrefix(), JsSpace.Location.JSNAMESPACE_DECLARATION_PREFIX, p)); ns = ns.withMarkers(visitMarkers(ns.getMarkers(), p)); ns = ns.withModifiers(ListUtils.map(ns.getModifiers(), mod -> mod.withPrefix(visitSpace(mod.getPrefix(), Space.Location.MODIFIER_PREFIX, p)))); @@ -638,7 +670,7 @@ public J visitNamespaceDeclaration(JS.NamespaceDeclaration namespaceDeclaration, } else { ns = (JS.NamespaceDeclaration) temp; } - ns = ns.withNamespace(visitSpace(ns.getNamespace(), JsSpace.Location.NAMESPACE_KEYWORD_DECLARATION_PREFIX, p)); + ns = ns.withNamespace(visitSpace(ns.getNamespace(), JsSpace.Location.JSNAMESPACE_KEYWORD_DECLARATION_PREFIX, p)); ns = ns.getPadding().withName(visitRightPadded(ns.getPadding().getName(), JsRightPadded.Location.NAMESPACE_DECLARATION_NAME, p)); ns = ns.withBody(visitAndCast(ns.getBody(), p)); return ns; diff --git a/rewrite-javascript/src/main/java/org/openrewrite/javascript/internal/JavaScriptPrinter.java b/rewrite-javascript/src/main/java/org/openrewrite/javascript/internal/JavaScriptPrinter.java index 018a07e0..c6f95301 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/internal/JavaScriptPrinter.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/internal/JavaScriptPrinter.java @@ -252,9 +252,9 @@ public J visitPropertyAssignment(JS.PropertyAssignment propertyAssignment, Print @Override public J visitNamespaceDeclaration(JS.NamespaceDeclaration namespaceDeclaration, PrintOutputCapture

p) { - beforeSyntax(namespaceDeclaration, JsSpace.Location.NAMESPACE_DECLARATION_PREFIX, p); + beforeSyntax(namespaceDeclaration, JsSpace.Location.JSNAMESPACE_DECLARATION_PREFIX, p); namespaceDeclaration.getModifiers().forEach(it -> delegate.visitModifier(it, p)); - visitSpace(namespaceDeclaration.getNamespace(), JsSpace.Location.NAMESPACE_KEYWORD_DECLARATION_PREFIX, p); + visitSpace(namespaceDeclaration.getNamespace(), JsSpace.Location.JSNAMESPACE_KEYWORD_DECLARATION_PREFIX, p); p.append("namespace"); visit(namespaceDeclaration.getName(), p); visit(namespaceDeclaration.getBody(), p); @@ -460,7 +460,7 @@ public J visitTypeInfo(JS.TypeInfo typeInfo, PrintOutputCapture

p) { public J visitJSVariableDeclarations(JS.JSVariableDeclarations multiVariable, PrintOutputCapture

p) { beforeSyntax(multiVariable, JsSpace.Location.JSVARIABLE_DECLARATIONS_PREFIX, p); visit(multiVariable.getLeadingAnnotations(), p); - multiVariable.getModifiers().forEach(it -> visitModifier(it, p)); + multiVariable.getModifiers().forEach(it -> delegate.visitModifier(it, p)); List> variables = multiVariable.getPadding().getVariables(); for (int i = 0; i < variables.size(); i++) { @@ -502,6 +502,46 @@ public J visitJSVariableDeclarationsJSNamedVariable(JS.JSVariableDeclarations.JS return variable; } + @Override + public J visitJSMethodDeclaration(JS.JSMethodDeclaration method, PrintOutputCapture

p) { + beforeSyntax(method, JsSpace.Location.JSMETHOD_DECLARATION_PREFIX, p); + visitSpace(Space.EMPTY, Space.Location.ANNOTATIONS, p); + visit(method.getLeadingAnnotations(), p); + method.getModifiers().forEach(it -> visitModifier(it, p)); + + FunctionKeyword functionKeyword = method.getMarkers().findFirst(FunctionKeyword.class).orElse(null); + if (functionKeyword != null) { + visitSpace(functionKeyword.getPrefix(), Space.Location.LANGUAGE_EXTENSION, p); + p.append("function"); + } + + Asterisk asterisk = method.getMarkers().findFirst(Asterisk.class).orElse(null); + if (asterisk != null) { + visitSpace(asterisk.getPrefix(), Space.Location.LANGUAGE_EXTENSION, p); + p.append("*"); + } + visit(method.getName(), p); + + J.TypeParameters typeParameters = method.getTypeParameters(); + if (typeParameters != null) { + visit(typeParameters.getAnnotations(), p); + visitSpace(typeParameters.getPrefix(), Space.Location.TYPE_PARAMETERS, p); + visitMarkers(typeParameters.getMarkers(), p); + p.append("<"); + visitRightPadded(typeParameters.getPadding().getTypeParameters(), JRightPadded.Location.TYPE_PARAMETER, ",", p); + p.append(">"); + } + + visitContainer("(", method.getPadding().getParameters(), JsContainer.Location.JSMETHOD_DECLARATION_PARAMETERS, ",", ")", p); + if (method.getReturnTypeExpression() != null) { + visit(method.getReturnTypeExpression(), p); + } + + visit(method.getBody(), p); + afterSyntax(method, p); + return method; + } + private class JavaScriptJavaPrinter extends JavaPrinter

{ @Override diff --git a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JS.java b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JS.java index 9377d198..baccf784 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JS.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JS.java @@ -2375,6 +2375,191 @@ public JSVariableDeclarations withVariables(List> } } + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) + @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true) + @RequiredArgsConstructor + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @Data + final class JSMethodDeclaration implements JS, Statement, TypedTree { + @Nullable + @NonFinal + transient WeakReference padding; + + @With + @EqualsAndHashCode.Include + @Getter + UUID id; + + @With + @Getter + Space prefix; + + @With + @Getter + Markers markers; + + @With + @Getter + List leadingAnnotations; + + @With + @Getter + List modifiers; + + @With + J.@Nullable TypeParameters typeParameters; + + /** + * Null for constructor declarations. + */ + @With + @Getter + @Nullable + TypeTree returnTypeExpression; + + @With + @Getter + Expression name; // simplified model by excluding IdentifierWithAnnotations + + JContainer parameters; + + public List getParameters() { + return parameters.getElements(); + } + + public JSMethodDeclaration withParameters(List parameters) { + return getPadding().withParameters(JContainer.withElements(this.parameters, parameters)); + } + + @Nullable + JContainer throwz; + + public @Nullable List getThrowz() { + return throwz == null ? null : throwz.getElements(); + } + + public JSMethodDeclaration withThrowz(@Nullable List throwz) { + return getPadding().withThrowz(JContainer.withElementsNullable(this.throwz, throwz)); + } + + /** + * Null for abstract method declarations and interface method declarations. + */ + @With + @Getter + @Nullable + Block body; + + /** + * For default values on definitions of annotation parameters. + */ + @Nullable + JLeftPadded defaultValue; + + public @Nullable Expression getDefaultValue() { + return defaultValue == null ? null : defaultValue.getElement(); + } + + public JSMethodDeclaration withDefaultValue(@Nullable Expression defaultValue) { + return getPadding().withDefaultValue(JLeftPadded.withElement(this.defaultValue, defaultValue)); + } + + @Getter + JavaType.@Nullable Method methodType; + + public JSMethodDeclaration withMethodType(JavaType.@Nullable Method type) { + if (type == this.methodType) { + return this; + } + return new JSMethodDeclaration(id, prefix, markers, leadingAnnotations, modifiers, typeParameters, returnTypeExpression, name, parameters, throwz, body, defaultValue, type); + } + + @Override + public JavaType getType() { + return methodType == null ? null : methodType.getReturnType(); + } + + @SuppressWarnings("unchecked") + @Override + public JSMethodDeclaration withType(@Nullable JavaType type) { + throw new UnsupportedOperationException("To change the return type of this method declaration, use withMethodType(..)"); + } + + @Override + public

J acceptJavaScript(JavaScriptVisitor

v, P p) { + return v.visitJSMethodDeclaration(this, p); + } + + public boolean isAbstract() { + return body == null; + } + + public boolean isConstructor() { + return getReturnTypeExpression() == null; + } + + public boolean hasModifier(Modifier.Type modifier) { + return Modifier.hasModifier(getModifiers(), modifier); + } + + @Override + @Transient + public CoordinateBuilder.Statement getCoordinates() { + return new CoordinateBuilder.Statement(this); + } + + @Override + public String toString() { + return "MethodDeclaration{" + + (getMethodType() == null ? "unknown" : getMethodType()) + + "}"; + } + + public JSMethodDeclaration.Padding getPadding() { + JSMethodDeclaration.Padding p; + if (this.padding == null) { + p = new JSMethodDeclaration.Padding(this); + this.padding = new WeakReference<>(p); + } else { + p = this.padding.get(); + if (p == null || p.t != this) { + p = new JSMethodDeclaration.Padding(this); + this.padding = new WeakReference<>(p); + } + } + return p; + } + + @RequiredArgsConstructor + public static class Padding { + private final JSMethodDeclaration t; + + public JContainer getParameters() { + return t.parameters; + } + + public JSMethodDeclaration withParameters(JContainer parameters) { + return t.parameters == parameters ? t : new JSMethodDeclaration(t.id, t.prefix, t.markers, t.leadingAnnotations, t.modifiers, t.typeParameters, t.returnTypeExpression, t.name, parameters, t.throwz, t.body, t.defaultValue, t.methodType); + } + + public @Nullable JContainer getThrowz() { + return t.throwz; + } + + public JSMethodDeclaration withThrowz(@Nullable JContainer throwz) { + return t.throwz == throwz ? t : new JSMethodDeclaration(t.id, t.prefix, t.markers, t.leadingAnnotations, t.modifiers, t.typeParameters, t.returnTypeExpression, t.name, t.parameters, throwz, t.body, t.defaultValue, t.methodType); + } + + public @Nullable JLeftPadded getDefaultValue() { + return t.defaultValue; + } + + public JSMethodDeclaration withDefaultValue(@Nullable JLeftPadded defaultValue) { + return t.defaultValue == defaultValue ? t : new JSMethodDeclaration(t.id, t.prefix, t.markers, t.leadingAnnotations, t.modifiers, t.typeParameters, t.returnTypeExpression, t.name, t.parameters, t.throwz, t.body, defaultValue, t.methodType); + } + } + } + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true) @RequiredArgsConstructor diff --git a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsContainer.java b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsContainer.java index dd9e8166..dd688f00 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsContainer.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsContainer.java @@ -25,7 +25,9 @@ public enum Location { 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), - TUPLE_ELEMENT(JsSpace.Location.TUPLE_ELEMENT, JsRightPadded.Location.TUPLE_ELEMENT_SUFFIX); + TUPLE_ELEMENT(JsSpace.Location.TUPLE_ELEMENT, JsRightPadded.Location.TUPLE_ELEMENT_SUFFIX), + JSMETHOD_DECLARATION_PARAMETERS(JsSpace.Location.JSMETHOD_DECLARATION_PARAMETERS, JsRightPadded.Location.JSMETHOD_DECLARATION_PARAMETER), + ; private final JsSpace.Location beforeLocation; private final JsRightPadded.Location elementLocation; diff --git a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsRightPadded.java b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsRightPadded.java index a5e48ae8..5e76f9d0 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsRightPadded.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsRightPadded.java @@ -34,7 +34,8 @@ public enum Location { TUPLE_ELEMENT_SUFFIX(JsSpace.Location.TUPLE_ELEMENT_SUFFIX), UNION_TYPE(JsSpace.Location.UNION_TYPE_SUFFIX), JSNAMED_VARIABLE(JsSpace.Location.JSNAMED_VARIABLE_SUFFIX), - NAMESPACE_DECLARATION_NAME(JsSpace.Location.NAMESPACE_DECLARATION_PREFIX); + NAMESPACE_DECLARATION_NAME(JsSpace.Location.JSNAMESPACE_DECLARATION_PREFIX), + JSMETHOD_DECLARATION_PARAMETER(JsSpace.Location.JSMETHOD_DECLARATION_PARAMETERS); private final JsSpace.Location afterLocation; diff --git a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsSpace.java b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsSpace.java index 9d510c2b..3c0bdfb0 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsSpace.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsSpace.java @@ -77,7 +77,9 @@ public enum Location { JSVARIABLE_PREFIX, JSVARIABLE_INITIALIZER, JSNAMED_VARIABLE_SUFFIX, - NAMESPACE_DECLARATION_PREFIX, - NAMESPACE_KEYWORD_DECLARATION_PREFIX + JSNAMESPACE_DECLARATION_PREFIX, + JSMETHOD_DECLARATION_PARAMETERS, + JSNAMESPACE_KEYWORD_DECLARATION_PREFIX, + JSMETHOD_DECLARATION_PREFIX } }