From e4c0a9a9918ee88ab16e80a1af12e7c8f23c4f6a Mon Sep 17 00:00:00 2001 From: Andrii Rodionov Date: Mon, 4 Nov 2024 10:40:36 +0100 Subject: [PATCH] Implemented arrow function support (#132) Implemented visitors visitArrowFunction and visitCallSignature --- openrewrite/src/javascript/parser.ts | 65 ++++++++++++--- .../test/javascript/parser/arrow.test.ts | 81 +++++++++++++++++++ .../test/javascript/parser/interface.test.ts | 17 +++- .../internal/JavaScriptPrinter.java | 6 +- 4 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 openrewrite/test/javascript/parser/arrow.test.ts diff --git a/openrewrite/src/javascript/parser.ts b/openrewrite/src/javascript/parser.ts index eb570016..00753af4 100644 --- a/openrewrite/src/javascript/parser.ts +++ b/openrewrite/src/javascript/parser.ts @@ -1,6 +1,6 @@ import * as ts from 'typescript'; import * as J from '../java'; -import {JavaType, JContainer, JLeftPadded, JRightPadded, Semicolon, Space, TrailingComma} from '../java'; +import {JavaType, JContainer, JLeftPadded, JRightPadded, Lambda, Semicolon, Space, TrailingComma} from '../java'; import * as JS from '.'; import { ExecutionContext, @@ -778,7 +778,8 @@ export class JavaScriptParserVisitor { ); } - private mapTypeInfo(node: ts.MethodDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration | ts.ParameterDeclaration | ts.PropertySignature | ts.MethodSignature) { + private mapTypeInfo(node: ts.MethodDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration | ts.ParameterDeclaration + | ts.PropertySignature | ts.MethodSignature | ts.ArrowFunction | ts.CallSignatureDeclaration) { return node.type ? new JS.TypeInfo(randomId(), this.prefix(node.getChildAt(node.getChildren().indexOf(node.type) - 1)), Markers.EMPTY, this.visit(node.type)) : null; } @@ -799,7 +800,26 @@ export class JavaScriptParserVisitor { } visitCallSignature(node: ts.CallSignatureDeclaration) { - return this.visitUnknown(node); + return new JS.ArrowFunction( + randomId(), + this.prefix(node), + Markers.EMPTY, + [], + [], + new Lambda.Parameters( + randomId(), + this.prefix(node), + Markers.EMPTY, + true, + node.parameters.length > 0 ? + node.parameters.map(p => this.rightPadded(this.convert(p), this.suffix(p))) : + [this.rightPadded(this.newJEmpty(), this.prefix(node.getChildren().find(n => n.kind === ts.SyntaxKind.CloseParenToken)!))] // to handle the case: (/*no*/) => ... + ), + this.mapTypeInfo(node), + Space.EMPTY, + this.newJEmpty(), + null + ); } visitConstructSignature(node: ts.ConstructSignatureDeclaration) { @@ -1071,7 +1091,26 @@ export class JavaScriptParserVisitor { } visitArrowFunction(node: ts.ArrowFunction) { - return this.visitUnknown(node); + return new JS.ArrowFunction( + randomId(), + this.prefix(node), + Markers.EMPTY, + [], + [], + new Lambda.Parameters( + randomId(), + this.prefix(node), + Markers.EMPTY, + true, + node.parameters.length > 0 ? + node.parameters.map(p => this.rightPadded(this.convert(p), this.suffix(p))) : + [this.rightPadded(this.newJEmpty(), this.prefix(node.getChildren().find(n => n.kind === ts.SyntaxKind.CloseParenToken)!))] // to handle the case: (/*no*/) => ... + ), + this.mapTypeInfo(node), + this.prefix(node.equalsGreaterThanToken), + node.body ? this.convert(node.body) : this.newJEmpty(), + null + ); } visitDeleteExpression(node: ts.DeleteExpression) { @@ -1404,11 +1443,7 @@ export class JavaScriptParserVisitor { } visitEmptyStatement(node: ts.EmptyStatement) { - return new J.Empty( - randomId(), - this.prefix(node), - Markers.EMPTY - ); + return this.newJEmpty(this.prefix(node)); } visitVariableStatement(node: ts.VariableStatement) { @@ -1479,11 +1514,11 @@ export class JavaScriptParserVisitor { [node.initializer ? (ts.isVariableDeclarationList(node.initializer) ? this.rightPadded(this.visit(node.initializer), Space.EMPTY) : this.rightPadded(this.convert(node.initializer), this.suffix(node.initializer.getLastToken()!))) : - this.rightPadded(new J.Empty(randomId(), Space.EMPTY, Markers.EMPTY), Space.EMPTY)], + this.rightPadded(this.newJEmpty(), Space.EMPTY)], node.condition ? this.rightPadded(this.convert(node.condition), this.suffix(node.condition.getLastToken()!)) : - this.rightPadded(new J.Empty(randomId(), Space.EMPTY, Markers.EMPTY), Space.EMPTY), + this.rightPadded(this.newJEmpty(), Space.EMPTY), [node.incrementor ? this.rightPadded(this.convert(node.incrementor), this.suffix(node.incrementor.getLastToken()!)) : - this.rightPadded(new J.Empty(randomId(), Space.EMPTY, Markers.EMPTY), Space.EMPTY)], + this.rightPadded(this.newJEmpty(), Space.EMPTY)], ), this.rightPadded( this.convert(node.statement), @@ -2226,7 +2261,7 @@ export class JavaScriptParserVisitor { const args: JRightPadded[] = []; if (childCount === 0) { args.push(this.rightPadded( - new J.Empty(randomId(), Space.EMPTY, Markers.EMPTY) as T, + this.newJEmpty() as T, lastAfter, Markers.EMPTY )); @@ -2269,6 +2304,10 @@ export class JavaScriptParserVisitor { if (token?.kind === ts.SyntaxKind.SemicolonToken) return new Semicolon(randomId()); return null; } + + private newJEmpty(prefix: Space = Space.EMPTY) { + return new J.Empty(randomId(), prefix, Markers.EMPTY); + } } function prefixFromNode(node: ts.Node, sourceFile: ts.SourceFile): Space { diff --git a/openrewrite/test/javascript/parser/arrow.test.ts b/openrewrite/test/javascript/parser/arrow.test.ts new file mode 100644 index 00000000..91deeeca --- /dev/null +++ b/openrewrite/test/javascript/parser/arrow.test.ts @@ -0,0 +1,81 @@ +import {connect, disconnect, rewriteRun, typeScript} from '../testHarness'; + +describe('arrow mapping', () => { + beforeAll(() => connect()); + afterAll(() => disconnect()); + + test('function with simple body', () => { + rewriteRun( + //language=typescript + typeScript(` + const multiply = (a: number, b: number): number => a * b; + `) + ); + }); + + test('function with simple body and comments', () => { + rewriteRun( + //language=typescript + typeScript(` + const multiply = /*0*/ (/*1*/a/*2*/: /*3*/number/*4*/,/*5*/ b: /*6*/ number/*7*/) /*a*/ : /*b*/ number /*c*/ => a * b; + `) + ); + }); + + test('function with body', () => { + rewriteRun( + //language=typescript + typeScript(` + let sum = (x: number, y: number): number => { + return x + y; + } + `) + ); + }); + + test('function without params', () => { + rewriteRun( + //language=typescript + typeScript(` + const greet = (/*no*/): string => 'Hello'; + `) + ); + }); + + test('function with implicit return', () => { + rewriteRun( + //language=typescript + typeScript(` + let sum = (x: number, y: number) => x + y; + `) + ); + }); + + test('function with implicit return and comments', () => { + rewriteRun( + //language=typescript + typeScript(` + let sum = (x: number, y: number) /*a*/ => /*b*/ x + y; + `) + ); + }); + + test('function with default parameter', () => { + rewriteRun( + //language=typescript + typeScript(` + const increment = (value: number, step: number = 1): number => value + step; + `) + ); + }); + + test('function with default parameter and comments', () => { + rewriteRun( + //language=typescript + typeScript(` + const increment = (value: number, step: /*a*/ number /*b*/ = /*c*/1 /*d*/): number => value + step; + `) + ); + }); + +}); diff --git a/openrewrite/test/javascript/parser/interface.test.ts b/openrewrite/test/javascript/parser/interface.test.ts index 93fef141..1a8ac1de 100644 --- a/openrewrite/test/javascript/parser/interface.test.ts +++ b/openrewrite/test/javascript/parser/interface.test.ts @@ -269,18 +269,31 @@ describe('as mapping', () => { ); }); - test.skip('interface with call signature', () => { + test('interface with call signature', () => { rewriteRun( //language=typescript typeScript(` interface Add { greet: (name: string) => string; - (x: number, y: number): number; + (x: number, y: number): number, } `) ); }); + test('interface with call signature and comments', () => { + rewriteRun( + //language=typescript + typeScript(` + interface Add { + greet: (name: string) => string; + (x: number /*abc*/): number /*bcd*/, + (/*none*/) /*a*/:/*b*/ number /*c*/ + } + `) + ); + }); + test.skip('interface with indexable type', () => { rewriteRun( //language=typescript 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 7ed7272c..3a0aefbf 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 @@ -90,8 +90,10 @@ public J visitArrowFunction(JS.ArrowFunction arrowFunction, PrintOutputCapture

"); - visit(arrowFunction.getBody(), p); + if ((arrowFunction.getBody() != null) && !(arrowFunction.getBody() instanceof J.Empty)) { + p.append("=>"); + visit(arrowFunction.getBody(), p); + } afterSyntax(arrowFunction, p); return arrowFunction; }