From c8c2d47736b499f46a1e50c3d6f54dc857a50915 Mon Sep 17 00:00:00 2001 From: Andrii Rodionov Date: Fri, 22 Nov 2024 17:16:51 +0100 Subject: [PATCH] Implemented ConstructorType and TypeQuery (#156) * Implemented ConstructorType and TypeQuery - added custom class JS.TypeQuery - added for TypeParameter bounds and default type support - fix rest parameters --------- Co-authored-by: Andrii Rodionov --- openrewrite/src/javascript/parser.ts | 81 ++++++++++-- openrewrite/src/javascript/remote/receiver.ts | 23 +++- openrewrite/src/javascript/remote/sender.ts | 12 +- .../src/javascript/tree/support_types.ts | 3 - openrewrite/src/javascript/tree/tree.ts | 100 +++++++++++++-- openrewrite/src/javascript/visitor.ts | 16 ++- .../e2e/google-maps-services-js_files.test.ts | 2 +- .../test/javascript/parser/class.test.ts | 5 +- ...est.ts => elementAccessExpression.test.ts} | 0 .../test/javascript/parser/for.test.ts | 2 +- .../test/javascript/parser/function.test.ts | 120 ++++++++++++++++++ .../test/javascript/parser/interface.test.ts | 2 +- .../test/javascript/parser/method.test.ts | 2 +- .../javascript/parser/qualifiedName.test.ts | 5 +- .../test/javascript/parser/typeAlias.test.ts | 47 ++++++- .../test/javascript/parser/typeQuery.test.ts | 89 +++++++++++++ .../javascript/remote/JavaScriptReceiver.java | 22 ++++ .../javascript/remote/JavaScriptSender.java | 11 ++ .../remote/JavaScriptValidator.java | 6 + .../javascript/JavaScriptVisitor.java | 16 +++ .../internal/JavaScriptPrinter.java | 39 ++++++ .../org/openrewrite/javascript/tree/JS.java | 49 ++++++- .../javascript/tree/JsRightPadded.java | 4 +- .../openrewrite/javascript/tree/JsSpace.java | 2 + 24 files changed, 623 insertions(+), 35 deletions(-) rename openrewrite/test/javascript/parser/{element-access-expression.test.ts => elementAccessExpression.test.ts} (100%) create mode 100644 openrewrite/test/javascript/parser/typeQuery.test.ts diff --git a/openrewrite/src/javascript/parser.ts b/openrewrite/src/javascript/parser.ts index 5763a3b3..d883e465 100644 --- a/openrewrite/src/javascript/parser.ts +++ b/openrewrite/src/javascript/parser.ts @@ -514,6 +514,10 @@ export class JavaScriptParserVisitor { return this.mapIdentifier(node, 'any'); } + visitObjectKeyword(node: ts.Node) { + return this.mapIdentifier(node, 'object'); + } + visitUnknownKeyword(node: ts.Node) { return this.mapIdentifier(node, 'unknown'); } @@ -628,10 +632,6 @@ export class JavaScriptParserVisitor { } visitTypeParameter(node: ts.TypeParameterDeclaration) { - if (node.constraint || (node.modifiers && node.modifiers.length) || node.default) { - return this.visitUnknown(node); - } - return new J.TypeParameter( randomId(), this.prefix(node), @@ -639,7 +639,13 @@ export class JavaScriptParserVisitor { [], [], this.visit(node.name), - null + (node.constraint || node.default) ? + new JContainer( + this.prefix(this.findChildNode(node, ts.SyntaxKind.ExtendsKeyword) ?? this.findChildNode(node, ts.SyntaxKind.EqualsToken)!), + [node.constraint ? this.rightPadded(this.visit(node.constraint), this.suffix(node.constraint)) : this.rightPadded(this.newJEmpty(), Space.EMPTY), + node.default ? this.rightPadded(this.visit(node.default), this.suffix(node.default)) : this.rightPadded(this.newJEmpty(), Space.EMPTY)], + Markers.EMPTY) + : null ); } @@ -668,6 +674,37 @@ export class JavaScriptParserVisitor { ); } + if (node.dotDotDotToken) { + return new JS.JSVariableDeclarations( + randomId(), + this.prefix(node), + Markers.EMPTY, + [], + this.mapModifiers(node), + this.mapTypeInfo(node), + null, + [this.rightPadded( + new JS.JSVariableDeclarations.JSNamedVariable( + randomId(), + Space.EMPTY, + Markers.EMPTY, + new JS.Unary( + randomId(), + Space.EMPTY, + Markers.EMPTY, + this.leftPadded(Space.EMPTY, JS.Unary.Type.Spread), + this.visit(node.name), + this.mapType(node) + ), + [], + node.initializer ? this.leftPadded(this.prefix(node.getChildAt(node.getChildren().indexOf(node.initializer) - 1)), this.visit(node.initializer)) : null, + this.mapVariableType(node) + ), + this.suffix(node.name) + )] + ); + } + const nameExpression = this.visit(node.name) if (nameExpression instanceof J.Identifier) { @@ -1164,21 +1201,39 @@ export class JavaScriptParserVisitor { randomId(), this.prefix(node), Markers.EMPTY, + this.rightPadded(false, Space.EMPTY), new JContainer( this.prefix(node), node.parameters.map(p => this.rightPadded(this.visit(p), this.suffix(p))), Markers.EMPTY), - this.prefix(node.getChildren().find(v => v.kind === ts.SyntaxKind.EqualsGreaterThanToken)!), + this.prefix(this.findChildNode(node, ts.SyntaxKind.EqualsGreaterThanToken)!), this.convert(node.type), null); } visitConstructorType(node: ts.ConstructorTypeNode) { - return this.visitUnknown(node); + return new JS.FunctionType( + randomId(), + this.prefix(node), + Markers.EMPTY, + this.rightPadded(true, this.suffix(this.findChildNode(node, ts.SyntaxKind.NewKeyword)!)), + new JContainer( + this.prefix(node), + node.parameters.map(p => this.rightPadded(this.visit(p), this.suffix(p))), + Markers.EMPTY), + this.prefix(this.findChildNode(node, ts.SyntaxKind.EqualsGreaterThanToken)!), + this.convert(node.type), + null); } visitTypeQuery(node: ts.TypeQueryNode) { - return this.visitUnknown(node); + return new JS.TypeQuery( + randomId(), + this.prefix(node), + Markers.EMPTY, + this.convert(node.exprName), + this.mapType(node) + ) } visitTypeLiteral(node: ts.TypeLiteralNode) { @@ -1203,7 +1258,15 @@ export class JavaScriptParserVisitor { } visitArrayType(node: ts.ArrayTypeNode) { - return this.visitUnknown(node); + return new J.ArrayType( + randomId(), + this.prefix(node), + Markers.EMPTY, + this.convert(node.elementType), + null, + this.leftPadded(this.prefix(this.findChildNode(node, ts.SyntaxKind.OpenBracketToken)!), this.prefix(this.findChildNode(node, ts.SyntaxKind.CloseBracketToken)!) ), + this.mapType(node)! + ) } visitTupleType(node: ts.TupleTypeNode) { diff --git a/openrewrite/src/javascript/remote/receiver.ts b/openrewrite/src/javascript/remote/receiver.ts index 5acca652..22059f3f 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, JsImportSpecifier, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSMethodInvocation, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration} from '../tree'; +import {JS, JsLeftPadded, JsRightPadded, JsContainer, JsSpace, CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsImportSpecifier, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSMethodInvocation, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration} from '../tree'; import {Expression, J, JContainer, JLeftPadded, JRightPadded, NameTree, Space, Statement, TypeTree, TypedTree} from "../../java"; import * as Java from "../../java/tree"; @@ -116,6 +116,7 @@ class Visitor extends JavaScriptVisitor { functionType = functionType.withId(ctx.receiveValue(functionType.id, ValueType.UUID)!); functionType = functionType.withPrefix(ctx.receiveNode(functionType.prefix, receiveSpace)!); functionType = functionType.withMarkers(ctx.receiveNode(functionType.markers, ctx.receiveMarkers)!); + functionType = functionType.padding.withConstructorType(ctx.receiveNode(functionType.padding.constructorType, rightPaddedValueReceiver(ValueType.Primitive))!); functionType = functionType.padding.withParameters(ctx.receiveNode(functionType.padding.parameters, receiveContainer)!); functionType = functionType.withArrow(ctx.receiveNode(functionType.arrow, receiveSpace)!); functionType = functionType.withReturnType(ctx.receiveNode(functionType.returnType, ctx.receiveTree)!); @@ -258,6 +259,15 @@ class Visitor extends JavaScriptVisitor { return typeOf; } + public visitTypeQuery(typeQuery: TypeQuery, ctx: ReceiverContext): J { + typeQuery = typeQuery.withId(ctx.receiveValue(typeQuery.id, ValueType.UUID)!); + typeQuery = typeQuery.withPrefix(ctx.receiveNode(typeQuery.prefix, receiveSpace)!); + typeQuery = typeQuery.withMarkers(ctx.receiveNode(typeQuery.markers, ctx.receiveMarkers)!); + typeQuery = typeQuery.withTypeExpression(ctx.receiveNode(typeQuery.typeExpression, ctx.receiveTree)!); + typeQuery = typeQuery.withType(ctx.receiveValue(typeQuery.type, ValueType.Object)); + return typeQuery; + } + public visitTypeOperator(typeOperator: TypeOperator, ctx: ReceiverContext): J { typeOperator = typeOperator.withId(ctx.receiveValue(typeOperator.id, ValueType.UUID)!); typeOperator = typeOperator.withPrefix(ctx.receiveNode(typeOperator.prefix, receiveSpace)!); @@ -1214,6 +1224,7 @@ class Factory implements ReceiverFactory { ctx.receiveValue(null, ValueType.UUID)!, ctx.receiveNode(null, receiveSpace)!, ctx.receiveNode(null, ctx.receiveMarkers)!, + ctx.receiveNode>(null, rightPaddedValueReceiver(ValueType.Primitive))!, ctx.receiveNode>(null, receiveContainer)!, ctx.receiveNode(null, receiveSpace)!, ctx.receiveNode(null, ctx.receiveTree)!, @@ -1369,6 +1380,16 @@ class Factory implements ReceiverFactory { ); } + if (type === "org.openrewrite.javascript.tree.JS$TypeQuery") { + return new TypeQuery( + ctx.receiveValue(null, ValueType.UUID)!, + ctx.receiveNode(null, receiveSpace)!, + ctx.receiveNode(null, ctx.receiveMarkers)!, + ctx.receiveNode(null, ctx.receiveTree)!, + ctx.receiveValue(null, ValueType.Object) + ); + } + if (type === "org.openrewrite.javascript.tree.JS$TypeOperator") { return new TypeOperator( ctx.receiveValue(null, ValueType.UUID)!, diff --git a/openrewrite/src/javascript/remote/sender.ts b/openrewrite/src/javascript/remote/sender.ts index d45b15c1..f72ff2fd 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, JsImportSpecifier, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSMethodInvocation, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration} from '../tree'; +import {JS, JsLeftPadded, JsRightPadded, JsContainer, JsSpace, CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsImportSpecifier, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSMethodInvocation, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration} from '../tree'; import {Expression, J, JContainer, JLeftPadded, JRightPadded, Space, Statement} from "../../java"; import * as Java from "../../java/tree"; @@ -111,6 +111,7 @@ class Visitor extends JavaScriptVisitor { ctx.sendValue(functionType, v => v.id, ValueType.UUID); ctx.sendNode(functionType, v => v.prefix, Visitor.sendSpace); ctx.sendNode(functionType, v => v.markers, ctx.sendMarkers); + ctx.sendNode(functionType, v => v.padding.constructorType, Visitor.sendRightPadded(ValueType.Primitive)); ctx.sendNode(functionType, v => v.padding.parameters, Visitor.sendContainer(ValueType.Tree)); ctx.sendNode(functionType, v => v.arrow, Visitor.sendSpace); ctx.sendNode(functionType, v => v.returnType, ctx.sendTree); @@ -253,6 +254,15 @@ class Visitor extends JavaScriptVisitor { return typeOf; } + public visitTypeQuery(typeQuery: TypeQuery, ctx: SenderContext): J { + ctx.sendValue(typeQuery, v => v.id, ValueType.UUID); + ctx.sendNode(typeQuery, v => v.prefix, Visitor.sendSpace); + ctx.sendNode(typeQuery, v => v.markers, ctx.sendMarkers); + ctx.sendNode(typeQuery, v => v.typeExpression, ctx.sendTree); + ctx.sendTypedValue(typeQuery, v => v.type, ValueType.Object); + return typeQuery; + } + public visitTypeOperator(typeOperator: TypeOperator, ctx: SenderContext): J { ctx.sendValue(typeOperator, v => v.id, ValueType.UUID); ctx.sendNode(typeOperator, v => v.prefix, Visitor.sendSpace); diff --git a/openrewrite/src/javascript/tree/support_types.ts b/openrewrite/src/javascript/tree/support_types.ts index c614c12e..fc72ccab 100644 --- a/openrewrite/src/javascript/tree/support_types.ts +++ b/openrewrite/src/javascript/tree/support_types.ts @@ -277,9 +277,6 @@ export namespace JsRightPadded { JSMETHOD_INVOCATION_SELECT, TYPE_LITERAL_MEMBERS, INDEX_SIGNATURE_DECLARATION_PARAMETERS, - JSFOR_OF_LOOP_AWAIT, - JSFOR_OF_LOOP_INITIALIZER, - JSFOR_OF_LOOP_ITERABLE, JSFOR_IN_OF_LOOP_CONTROL_VARIABLE, JSFOR_IN_OF_LOOP_CONTROL_ITERABLE, JSFOR_OF_LOOP_BODY, diff --git a/openrewrite/src/javascript/tree/tree.ts b/openrewrite/src/javascript/tree/tree.ts index 03bc993d..dcf142d5 100644 --- a/openrewrite/src/javascript/tree/tree.ts +++ b/openrewrite/src/javascript/tree/tree.ts @@ -714,11 +714,12 @@ export class Export extends JSMixin(Object) implements Statement { @LstType("org.openrewrite.javascript.tree.JS$FunctionType") export class FunctionType extends JSMixin(Object) implements Expression, TypeTree { - public constructor(id: UUID, prefix: Space, markers: Markers, parameters: JContainer, arrow: Space, returnType: Expression, _type: JavaType | null) { + public constructor(id: UUID, prefix: Space, markers: Markers, constructorType: JRightPadded, parameters: JContainer, arrow: Space, returnType: Expression, _type: JavaType | null) { super(); this._id = id; this._prefix = prefix; this._markers = markers; + this._constructorType = constructorType; this._parameters = parameters; this._arrow = arrow; this._returnType = returnType; @@ -732,7 +733,7 @@ export class FunctionType extends JSMixin(Object) implements Expression, TypeTre } public withId(id: UUID): FunctionType { - return id === this._id ? this : new FunctionType(id, this._prefix, this._markers, this._parameters, this._arrow, this._returnType, this._type); + return id === this._id ? this : new FunctionType(id, this._prefix, this._markers, this._constructorType, this._parameters, this._arrow, this._returnType, this._type); } private readonly _prefix: Space; @@ -742,7 +743,7 @@ export class FunctionType extends JSMixin(Object) implements Expression, TypeTre } public withPrefix(prefix: Space): FunctionType { - return prefix === this._prefix ? this : new FunctionType(this._id, prefix, this._markers, this._parameters, this._arrow, this._returnType, this._type); + return prefix === this._prefix ? this : new FunctionType(this._id, prefix, this._markers, this._constructorType, this._parameters, this._arrow, this._returnType, this._type); } private readonly _markers: Markers; @@ -752,7 +753,17 @@ export class FunctionType extends JSMixin(Object) implements Expression, TypeTre } public withMarkers(markers: Markers): FunctionType { - return markers === this._markers ? this : new FunctionType(this._id, this._prefix, markers, this._parameters, this._arrow, this._returnType, this._type); + return markers === this._markers ? this : new FunctionType(this._id, this._prefix, markers, this._constructorType, this._parameters, this._arrow, this._returnType, this._type); + } + + private readonly _constructorType: JRightPadded; + + public get constructorType(): boolean { + return this._constructorType.element; + } + + public withConstructorType(constructorType: boolean): FunctionType { + return this.padding.withConstructorType(this._constructorType.withElement(constructorType)); } private readonly _parameters: JContainer; @@ -772,7 +783,7 @@ export class FunctionType extends JSMixin(Object) implements Expression, TypeTre } public withArrow(arrow: Space): FunctionType { - return arrow === this._arrow ? this : new FunctionType(this._id, this._prefix, this._markers, this._parameters, arrow, this._returnType, this._type); + return arrow === this._arrow ? this : new FunctionType(this._id, this._prefix, this._markers, this._constructorType, this._parameters, arrow, this._returnType, this._type); } private readonly _returnType: Expression; @@ -782,7 +793,7 @@ export class FunctionType extends JSMixin(Object) implements Expression, TypeTre } public withReturnType(returnType: Expression): FunctionType { - return returnType === this._returnType ? this : new FunctionType(this._id, this._prefix, this._markers, this._parameters, this._arrow, returnType, this._type); + return returnType === this._returnType ? this : new FunctionType(this._id, this._prefix, this._markers, this._constructorType, this._parameters, this._arrow, returnType, this._type); } private readonly _type: JavaType | null; @@ -792,7 +803,7 @@ export class FunctionType extends JSMixin(Object) implements Expression, TypeTre } public withType(_type: JavaType | null): FunctionType { - return _type === this._type ? this : new FunctionType(this._id, this._prefix, this._markers, this._parameters, this._arrow, this._returnType, _type); + return _type === this._type ? this : new FunctionType(this._id, this._prefix, this._markers, this._constructorType, this._parameters, this._arrow, this._returnType, _type); } public acceptJavaScript

(v: JavaScriptVisitor

, p: P): J | null { @@ -802,11 +813,17 @@ export class FunctionType extends JSMixin(Object) implements Expression, TypeTre get padding() { const t = this; return new class { + public get constructorType(): JRightPadded { + return t._constructorType; + } + public withConstructorType(constructorType: JRightPadded): FunctionType { + return t._constructorType === constructorType ? t : new FunctionType(t._id, t._prefix, t._markers, constructorType, t._parameters, t._arrow, t._returnType, t._type); + } public get parameters(): JContainer { return t._parameters; } public withParameters(parameters: JContainer): FunctionType { - return t._parameters === parameters ? t : new FunctionType(t._id, t._prefix, t._markers, parameters, t._arrow, t._returnType, t._type); + return t._parameters === parameters ? t : new FunctionType(t._id, t._prefix, t._markers, t._constructorType, parameters, t._arrow, t._returnType, t._type); } } } @@ -2056,6 +2073,73 @@ export class TypeOf extends JSMixin(Object) implements Expression { } +@LstType("org.openrewrite.javascript.tree.JS$TypeQuery") +export class TypeQuery extends JSMixin(Object) implements Expression, TypeTree { + public constructor(id: UUID, prefix: Space, markers: Markers, typeExpression: TypeTree, _type: JavaType | null) { + super(); + this._id = id; + this._prefix = prefix; + this._markers = markers; + this._typeExpression = typeExpression; + this._type = _type; + } + + private readonly _id: UUID; + + public get id(): UUID { + return this._id; + } + + public withId(id: UUID): TypeQuery { + return id === this._id ? this : new TypeQuery(id, this._prefix, this._markers, this._typeExpression, this._type); + } + + private readonly _prefix: Space; + + public get prefix(): Space { + return this._prefix; + } + + public withPrefix(prefix: Space): TypeQuery { + return prefix === this._prefix ? this : new TypeQuery(this._id, prefix, this._markers, this._typeExpression, this._type); + } + + private readonly _markers: Markers; + + public get markers(): Markers { + return this._markers; + } + + public withMarkers(markers: Markers): TypeQuery { + return markers === this._markers ? this : new TypeQuery(this._id, this._prefix, markers, this._typeExpression, this._type); + } + + private readonly _typeExpression: TypeTree; + + public get typeExpression(): TypeTree { + return this._typeExpression; + } + + public withTypeExpression(typeExpression: TypeTree): TypeQuery { + return typeExpression === this._typeExpression ? this : new TypeQuery(this._id, this._prefix, this._markers, typeExpression, this._type); + } + + private readonly _type: JavaType | null; + + public get type(): JavaType | null { + return this._type; + } + + public withType(_type: JavaType | null): TypeQuery { + return _type === this._type ? this : new TypeQuery(this._id, this._prefix, this._markers, this._typeExpression, _type); + } + + public acceptJavaScript

(v: JavaScriptVisitor

, p: P): J | null { + return v.visitTypeQuery(this, p); + } + +} + @LstType("org.openrewrite.javascript.tree.JS$TypeOperator") export class TypeOperator extends JSMixin(Object) implements Expression, TypedTree, NameTree { public constructor(id: UUID, prefix: Space, markers: Markers, operator: TypeOperator.Type, expression: JLeftPadded) { diff --git a/openrewrite/src/javascript/visitor.ts b/openrewrite/src/javascript/visitor.ts index 9ba8d5e2..cea5020d 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, JsImportSpecifier, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSMethodInvocation, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration} from "./tree"; +import {CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsImportSpecifier, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSMethodInvocation, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration} from "./tree"; import {Expression, J, JContainer, JLeftPadded, JRightPadded, Space, Statement} from "../java/tree"; import {JavaVisitor} from "../java"; import * as Java from "../java/tree"; @@ -136,6 +136,7 @@ export class JavaScriptVisitor

extends JavaVisitor

{ } functionType = tempExpression as FunctionType; functionType = functionType.withMarkers(this.visitMarkers(functionType.markers, p)); + functionType = functionType.padding.withConstructorType(this.visitJsRightPadded(functionType.padding.constructorType, JsRightPadded.Location.FUNCTION_TYPE_CONSTRUCTOR_TYPE, p)!); functionType = functionType.padding.withParameters(this.visitJsContainer(functionType.padding.parameters, JsContainer.Location.FUNCTION_TYPE_PARAMETERS, p)!); functionType = functionType.withArrow(this.visitJsSpace(functionType.arrow, JsSpace.Location.FUNCTION_TYPE_ARROW, p)!); functionType = functionType.withReturnType(this.visitAndCast(functionType.returnType, p)!); @@ -321,6 +322,19 @@ export class JavaScriptVisitor

extends JavaVisitor

{ return typeOf; } + public visitTypeQuery(typeQuery: TypeQuery, p: P): J | null { + typeQuery = typeQuery.withPrefix(this.visitJsSpace(typeQuery.prefix, JsSpace.Location.TYPE_QUERY_PREFIX, p)!); + let tempExpression = this.visitExpression(typeQuery, p) as Expression; + if (!(tempExpression instanceof TypeQuery)) + { + return tempExpression; + } + typeQuery = tempExpression as TypeQuery; + typeQuery = typeQuery.withMarkers(this.visitMarkers(typeQuery.markers, p)); + typeQuery = typeQuery.withTypeExpression(this.visitAndCast(typeQuery.typeExpression, p)!); + return typeQuery; + } + public visitTypeOperator(typeOperator: TypeOperator, p: P): J | null { typeOperator = typeOperator.withPrefix(this.visitJsSpace(typeOperator.prefix, JsSpace.Location.TYPE_OPERATOR_PREFIX, p)!); let tempExpression = this.visitExpression(typeOperator, p) as Expression; diff --git a/openrewrite/test/javascript/e2e/google-maps-services-js_files.test.ts b/openrewrite/test/javascript/e2e/google-maps-services-js_files.test.ts index 40103261..8208514d 100644 --- a/openrewrite/test/javascript/e2e/google-maps-services-js_files.test.ts +++ b/openrewrite/test/javascript/e2e/google-maps-services-js_files.test.ts @@ -120,7 +120,7 @@ describe('google-maps-services-js files tests', () => { test('reversegeocode.ts', () => { rewriteRunWithOptions( - {expectUnknowns : true}, + {expectUnknowns : false}, //language=typescript typeScript(` /** diff --git a/openrewrite/test/javascript/parser/class.test.ts b/openrewrite/test/javascript/parser/class.test.ts index ad704dc1..18214d8d 100644 --- a/openrewrite/test/javascript/parser/class.test.ts +++ b/openrewrite/test/javascript/parser/class.test.ts @@ -265,7 +265,7 @@ describe('class mapping', () => { ); }); - test.skip('class expressions inline', () => { + test('class expressions inline', () => { rewriteRun( //language=typescript typeScript(` @@ -282,14 +282,13 @@ describe('class mapping', () => { ); }); - test.skip('inner class declaration with extends', () => { + test('inner class declaration with extends', () => { rewriteRun( //language=typescript typeScript(` class OuterClass { public static InnerClass = class extends Number { }; } - const a: typeof OuterClass.InnerClass.prototype = 1; `) ); }); diff --git a/openrewrite/test/javascript/parser/element-access-expression.test.ts b/openrewrite/test/javascript/parser/elementAccessExpression.test.ts similarity index 100% rename from openrewrite/test/javascript/parser/element-access-expression.test.ts rename to openrewrite/test/javascript/parser/elementAccessExpression.test.ts diff --git a/openrewrite/test/javascript/parser/for.test.ts b/openrewrite/test/javascript/parser/for.test.ts index c8058e7e..4ff75742 100644 --- a/openrewrite/test/javascript/parser/for.test.ts +++ b/openrewrite/test/javascript/parser/for.test.ts @@ -160,7 +160,7 @@ describe('for mapping', () => { ); }); - test.skip('for-in with dynamic object', () => { + test('for-in with dynamic object', () => { rewriteRun( //language=typescript typeScript(` diff --git a/openrewrite/test/javascript/parser/function.test.ts b/openrewrite/test/javascript/parser/function.test.ts index 16c84ca3..89207288 100644 --- a/openrewrite/test/javascript/parser/function.test.ts +++ b/openrewrite/test/javascript/parser/function.test.ts @@ -199,4 +199,124 @@ describe('function mapping', () => { `) ); }); + + test('function with simple type bound', () => { + rewriteRun( + //language=typescript + typeScript(` + function greet(person: T): void {} + `) + ); + }); + + test('function with simple type bound and comments', () => { + rewriteRun( + //language=typescript + typeScript(` + function /*a*/greet/*b*//*g*/(person: T): void {} + `) + ); + }); + + test('function with union type bound', () => { + rewriteRun( + //language=typescript + typeScript(` + function getLength(input: T) : void {} + `) + ); + }); + + test('function with multiple type bound', () => { + rewriteRun( + //language=typescript + typeScript(` + function merge(obj1: T, obj2: U): T & U { + return { ...obj1, ...obj2 }; + } + `) + ); + }); + + test('function with default type', () => { + rewriteRun( + //language=typescript + typeScript(` + function createArray(value: T, count: number): T[] { + return Array(count).fill(value); + } + `) + ); + }); + + test('function with default type and comments', () => { + rewriteRun( + //language=typescript + typeScript(` + function createArray(value: T, count: number): T[] { + return Array(count).fill(value); + } + `) + ); + }); + + test.skip('function with multiple default types', () => { + rewriteRun( + //language=typescript + typeScript(` + function createMap(key: K, value: V): [K, V] { + return [key, value]; + } + `) + ); + }); + + test('function with extends and default type', () => { + rewriteRun( + //language=typescript + typeScript(` + function createArray(value: T, length: number): T[] { + return Array(length).fill(value); + } + `) + ); + }); + + test('function with extends and default type with comments', () => { + rewriteRun( + //language=typescript + typeScript(` + function createArray/*3*/(value: T, length: number): /*a*/T/*b*/[/*c*/]/*d*/ { + return Array(length).fill(value); + } + `) + ); + }); + + test('function with constrained type literal ', () => { + rewriteRun( + //language=typescript + typeScript(` + function logLength(input: T): void {} + `) + ); + }); + + test('function with rest type parameters ', () => { + rewriteRun( + //language=typescript + typeScript(` + function f(...args: any[]): void {} + `) + ); + }); + + test('function with rest type parameters and comments', () => { + rewriteRun( + //language=typescript + typeScript(` + function /*a*/f/*b*/(/*c*/.../*d*/args/*e*/: /*f*/any[]): void {} + `) + ); + }); }); diff --git a/openrewrite/test/javascript/parser/interface.test.ts b/openrewrite/test/javascript/parser/interface.test.ts index 3880bf96..e434ae62 100644 --- a/openrewrite/test/javascript/parser/interface.test.ts +++ b/openrewrite/test/javascript/parser/interface.test.ts @@ -340,7 +340,7 @@ describe('interface mapping', () => { ); }); - test.skip('interface with indexable type', () => { + test('interface with indexable type', () => { rewriteRun( //language=typescript typeScript(` diff --git a/openrewrite/test/javascript/parser/method.test.ts b/openrewrite/test/javascript/parser/method.test.ts index 96a2d7f1..fb5b954a 100644 --- a/openrewrite/test/javascript/parser/method.test.ts +++ b/openrewrite/test/javascript/parser/method.test.ts @@ -122,7 +122,7 @@ describe('method mapping', () => { ); }); - test.skip('type parameters with bounds', () => { + test('type parameters with bounds', () => { rewriteRun( //language=typescript typeScript(` diff --git a/openrewrite/test/javascript/parser/qualifiedName.test.ts b/openrewrite/test/javascript/parser/qualifiedName.test.ts index 90d2ab91..e38870b0 100644 --- a/openrewrite/test/javascript/parser/qualifiedName.test.ts +++ b/openrewrite/test/javascript/parser/qualifiedName.test.ts @@ -37,11 +37,12 @@ describe('empty mapping', () => { ); }); - test.skip('nested class qualified name', () => { + test('nested class qualified name', () => { rewriteRun( //language=typescript typeScript(` - class OuterClass extends (class extends Number { }) { + class OuterClass { + public static InnerClass = class extends Number { }; } const a: typeof OuterClass.InnerClass.prototype = 1; `) diff --git a/openrewrite/test/javascript/parser/typeAlias.test.ts b/openrewrite/test/javascript/parser/typeAlias.test.ts index 0db5ef91..8f968d8e 100644 --- a/openrewrite/test/javascript/parser/typeAlias.test.ts +++ b/openrewrite/test/javascript/parser/typeAlias.test.ts @@ -31,7 +31,7 @@ describe('type alias mapping', () => { ); }); - test('generic type alias', () => { + test('generic function type alias', () => { rewriteRun( //language=typescript typeScript(` @@ -66,4 +66,49 @@ describe('type alias mapping', () => { `) ); }); + + test('construct function type alias', () => { + rewriteRun( + //language=typescript + typeScript(` + type MyConstructor = new (arg: string) => string; + `) + ); + }); + + test('construct function type alias with comments', () => { + rewriteRun( + //language=typescript + typeScript(` + type MyConstructor = /*a*/new/*b*/ (/*c*/arg: string) => string; + `) + ); + }); + + test('recursive array type', () => { + rewriteRun( + //language=typescript + typeScript(` + type NestedArray = T | NestedArray; + `) + ); + }); + + test.skip('conditional type', () => { + rewriteRun( + //language=typescript + typeScript(` + type Flatten = T extends (infer R)[] ? Flatten : T; + `) + ); + }); + + test('construct function type alias with generic', () => { + rewriteRun( + //language=typescript + typeScript(` + type GenericConstructor = new (/*a*/.../*b*/args: any[]) => T; + `) + ); + }); }); diff --git a/openrewrite/test/javascript/parser/typeQuery.test.ts b/openrewrite/test/javascript/parser/typeQuery.test.ts new file mode 100644 index 00000000..ab546f22 --- /dev/null +++ b/openrewrite/test/javascript/parser/typeQuery.test.ts @@ -0,0 +1,89 @@ +import {connect, disconnect, rewriteRun, typeScript} from '../testHarness'; + +describe('type-query operator mapping', () => { + beforeAll(() => connect()); + afterAll(() => disconnect()); + + test('typeof', () => { + rewriteRun( + //language=typescript + typeScript(` + type UserType = typeof Number; + `) + ); + }); + + test('typeof with comments', () => { + rewriteRun( + //language=typescript + typeScript(` + type UserType = /*a*/ typeof /*b*/ Number /*c*/; + `) + ); + }); + + test('typeof as a type', () => { + rewriteRun( + //language=typescript + typeScript(` + const createUser: typeof Number = Number; + `) + ); + }); + + test('typeof as a type with comments', () => { + rewriteRun( + //language=typescript + typeScript(` + const createUser: /*a*/ typeof /*b*/ Number /*c*/ = /*d*/ Number /*e*/; + `) + ); + }); + + test('typeof as a type as function', () => { + rewriteRun( + //language=typescript + typeScript(` + function greet(name: string) { + } + + function hello() { + } + + const sayHello: typeof greet = (name) => name | hello; + `) + ); + }); + + test('typeof as a type as array', () => { + rewriteRun( + //language=typescript + typeScript(` + const numbers = []; + + const moreNumbers: typeof numbers = [4, 5, 6]; + `) + ); + }); + + test('typeof as a type as a union', () => { + rewriteRun( + //language=typescript + typeScript(` + const obj1 = { type: "type1", value: 42 }; + const obj2 = { type: "type2", description: "TypeScript is awesome" }; + + const un: typeof obj1 | typeof obj2; + `) + ); + }); + + test.skip('index access type', () => { + rewriteRun( + //language=typescript + typeScript(` + type DatabaseConfig = typeof config["database"]; + `) + ); + }); +}); 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 8de13406..611b1b18 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 @@ -171,6 +171,7 @@ public JS.FunctionType visitFunctionType(JS.FunctionType functionType, ReceiverC functionType = functionType.withId(ctx.receiveNonNullValue(functionType.getId(), UUID.class)); functionType = functionType.withPrefix(ctx.receiveNonNullNode(functionType.getPrefix(), JavaScriptReceiver::receiveSpace)); functionType = functionType.withMarkers(ctx.receiveNonNullNode(functionType.getMarkers(), ctx::receiveMarkers)); + functionType = functionType.getPadding().withConstructorType(ctx.receiveNonNullNode(functionType.getPadding().getConstructorType(), rightPaddedValueReceiver(java.lang.Boolean.class))); functionType = functionType.getPadding().withParameters(ctx.receiveNonNullNode(functionType.getPadding().getParameters(), JavaScriptReceiver::receiveContainer)); functionType = functionType.withArrow(ctx.receiveNonNullNode(functionType.getArrow(), JavaScriptReceiver::receiveSpace)); functionType = functionType.withReturnType(ctx.receiveNonNullNode(functionType.getReturnType(), ctx::receiveTree)); @@ -326,6 +327,16 @@ public JS.TypeOf visitTypeOf(JS.TypeOf typeOf, ReceiverContext ctx) { return typeOf; } + @Override + public JS.TypeQuery visitTypeQuery(JS.TypeQuery typeQuery, ReceiverContext ctx) { + typeQuery = typeQuery.withId(ctx.receiveNonNullValue(typeQuery.getId(), UUID.class)); + typeQuery = typeQuery.withPrefix(ctx.receiveNonNullNode(typeQuery.getPrefix(), JavaScriptReceiver::receiveSpace)); + typeQuery = typeQuery.withMarkers(ctx.receiveNonNullNode(typeQuery.getMarkers(), ctx::receiveMarkers)); + typeQuery = typeQuery.withTypeExpression(ctx.receiveNonNullNode(typeQuery.getTypeExpression(), ctx::receiveTree)); + typeQuery = typeQuery.withType(ctx.receiveValue(typeQuery.getType(), JavaType.class)); + return typeQuery; + } + @Override public JS.TypeOperator visitTypeOperator(JS.TypeOperator typeOperator, ReceiverContext ctx) { typeOperator = typeOperator.withId(ctx.receiveNonNullValue(typeOperator.getId(), UUID.class)); @@ -1330,6 +1341,7 @@ public T create(Class type, ReceiverContext ctx) { ctx.receiveNonNullValue(null, UUID.class), ctx.receiveNonNullNode(null, JavaScriptReceiver::receiveSpace), ctx.receiveNonNullNode(null, ctx::receiveMarkers), + ctx.receiveNonNullNode(null, rightPaddedValueReceiver(java.lang.Boolean.class)), ctx.receiveNonNullNode(null, JavaScriptReceiver::receiveContainer), ctx.receiveNonNullNode(null, JavaScriptReceiver::receiveSpace), ctx.receiveNonNullNode(null, ctx::receiveTree), @@ -1485,6 +1497,16 @@ public T create(Class type, ReceiverContext ctx) { ); } + if (type == JS.TypeQuery.class) { + return (T) new JS.TypeQuery( + ctx.receiveNonNullValue(null, UUID.class), + ctx.receiveNonNullNode(null, JavaScriptReceiver::receiveSpace), + ctx.receiveNonNullNode(null, ctx::receiveMarkers), + ctx.receiveNonNullNode(null, ctx::receiveTree), + ctx.receiveValue(null, JavaType.class) + ); + } + if (type == JS.TypeOperator.class) { return (T) new JS.TypeOperator( 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 a502a5b7..f5ef8f4c 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 @@ -154,6 +154,7 @@ public JS.FunctionType visitFunctionType(JS.FunctionType functionType, SenderCon ctx.sendValue(functionType, JS.FunctionType::getId); ctx.sendNode(functionType, JS.FunctionType::getPrefix, JavaScriptSender::sendSpace); ctx.sendNode(functionType, JS.FunctionType::getMarkers, ctx::sendMarkers); + ctx.sendNode(functionType, e -> e.getPadding().getConstructorType(), JavaScriptSender::sendRightPadded); ctx.sendNode(functionType, e -> e.getPadding().getParameters(), JavaScriptSender::sendContainer); ctx.sendNode(functionType, JS.FunctionType::getArrow, JavaScriptSender::sendSpace); ctx.sendNode(functionType, JS.FunctionType::getReturnType, ctx::sendTree); @@ -309,6 +310,16 @@ public JS.TypeOf visitTypeOf(JS.TypeOf typeOf, SenderContext ctx) { return typeOf; } + @Override + public JS.TypeQuery visitTypeQuery(JS.TypeQuery typeQuery, SenderContext ctx) { + ctx.sendValue(typeQuery, JS.TypeQuery::getId); + ctx.sendNode(typeQuery, JS.TypeQuery::getPrefix, JavaScriptSender::sendSpace); + ctx.sendNode(typeQuery, JS.TypeQuery::getMarkers, ctx::sendMarkers); + ctx.sendNode(typeQuery, JS.TypeQuery::getTypeExpression, ctx::sendTree); + ctx.sendTypedValue(typeQuery, JS.TypeQuery::getType); + return typeQuery; + } + @Override public JS.TypeOperator visitTypeOperator(JS.TypeOperator typeOperator, SenderContext ctx) { ctx.sendValue(typeOperator, JS.TypeOperator::getId); diff --git a/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptValidator.java b/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptValidator.java index 75475dde..5ffe373a 100644 --- a/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptValidator.java +++ b/rewrite-javascript-remote/src/main/java/org/openrewrite/javascript/remote/JavaScriptValidator.java @@ -207,6 +207,12 @@ public JS.TypeOf visitTypeOf(JS.TypeOf typeOf, P p) { return typeOf; } + @Override + public JS.TypeQuery visitTypeQuery(JS.TypeQuery typeQuery, P p) { + visitAndValidate(typeQuery.getTypeExpression(), TypeTree.class, p); + return typeQuery; + } + @Override public JS.TypeOperator visitTypeOperator(JS.TypeOperator typeOperator, P p) { visitAndValidate(typeOperator.getExpression(), Expression.class, p); 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 dc283482..c01effa3 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java @@ -226,6 +226,7 @@ public J visitFunctionType(JS.FunctionType functionType, P p) { } else { f = (JS.FunctionType) temp; } + f = f.getPadding().withConstructorType(visitRightPadded(f.getPadding().getConstructorType(), JsRightPadded.Location.FUNCTION_TYPE_CONSTRUCTOR, p)); f = f.getPadding().withParameters(visitContainer(f.getPadding().getParameters(), JContainer.Location.LANGUAGE_EXTENSION, p)); f = f.withParameters(ListUtils.map(f.getParameters(), e -> visitAndCast(e, p))); f = f.withArrow(visitSpace(f.getArrow(), JsSpace.Location.FUNCTION_TYPE_ARROW_PREFIX, p)); @@ -587,6 +588,21 @@ public J visitTypeOf(JS.TypeOf typeOf, P p) { return t; } + public J visitTypeQuery(JS.TypeQuery typeQuery, P p) { + JS.TypeQuery t = typeQuery; + t = t.withPrefix(visitSpace(t.getPrefix(), JsSpace.Location.TYPE_QUERY_PREFIX, p)); + t = t.withMarkers(visitMarkers(t.getMarkers(), p)); + Expression temp = (Expression) visitExpression(t, p); + if (!(temp instanceof JS.TypeQuery)) { + return temp; + } else { + t = (JS.TypeQuery) temp; + } + t = t.withTypeExpression(visitAndCast(t.getTypeExpression(), p)); + t = t.withType(visitType(t.getType(), p)); + return t; + } + public J visitVoid(JS.Void aVoid, P p) { JS.Void v = aVoid; v = v.withPrefix(visitSpace(v.getPrefix(), JsSpace.Location.VOID_PREFIX, p)); 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 2a944120..57dcf035 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 @@ -183,6 +183,10 @@ public J visitExport(JS.Export export, PrintOutputCapture

p) { @Override public J visitFunctionType(JS.FunctionType functionType, PrintOutputCapture

p) { beforeSyntax(functionType, JsSpace.Location.FUNCTION_TYPE_PREFIX, p); + if (functionType.isConstructorType()) { + p.append("new"); + visitRightPadded(functionType.getPadding().getConstructorType(), JsRightPadded.Location.FUNCTION_TYPE_CONSTRUCTOR, p); + } visitContainer("(", functionType.getPadding().getParameters(), JsContainer.Location.FUNCTION_TYPE_PARAMETER, ",", ")", p); visitSpace(functionType.getArrow(), JsSpace.Location.FUNCTION_TYPE_ARROW_PREFIX, p); p.append("=>"); @@ -415,6 +419,15 @@ public J visitTypeOf(JS.TypeOf typeOf, PrintOutputCapture

p) { return typeOf; } + @Override + public J visitTypeQuery(JS.TypeQuery typeQuery, PrintOutputCapture

p) { + beforeSyntax(typeQuery, JsSpace.Location.TYPE_QUERY_PREFIX, p); + p.append("typeof"); + visit(typeQuery.getTypeExpression(), p); + afterSyntax(typeQuery, p); + return typeQuery; + } + @Override public J visitTypeOperator(JS.TypeOperator typeOperator, PrintOutputCapture

p) { beforeSyntax(typeOperator, JsSpace.Location.TYPE_OPERATOR_PREFIX, p); @@ -1035,6 +1048,32 @@ public J visitTypeCast(J.TypeCast typeCast, PrintOutputCapture

p) { return typeCast; } + @Override + public J visitTypeParameter(J.TypeParameter typeParameter, PrintOutputCapture

p) { + beforeSyntax(typeParameter, Space.Location.TYPE_PARAMETERS_PREFIX, p); + visit(typeParameter.getAnnotations(), p); + visit(typeParameter.getName(), p); + + JContainer bounds = typeParameter.getPadding().getBounds(); + if (bounds != null) { + visitSpace(bounds.getBefore(), JContainer.Location.TYPE_BOUNDS.getBeforeLocation(), p); + JRightPadded constraintType = bounds.getPadding().getElements().get(0); + if (!(constraintType.getElement() instanceof J.Empty)) { + p.append("extends"); + this.visitRightPadded(constraintType, JContainer.Location.TYPE_BOUNDS.getElementLocation(), p); + } + + JRightPadded defaultType = bounds.getPadding().getElements().get(1); + if (!(defaultType.getElement() instanceof J.Empty)) { + p.append("="); + this.visitRightPadded(defaultType, JContainer.Location.TYPE_BOUNDS.getElementLocation(), p); + } + } + + afterSyntax(typeParameter, p); + return typeParameter; + } + @Override public J visitVariableDeclarations(J.VariableDeclarations multiVariable, PrintOutputCapture

p) { beforeSyntax(multiVariable, Space.Location.VARIABLE_DECLARATIONS_PREFIX, p); 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 d299628c..0a6f5738 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 @@ -715,6 +715,16 @@ class FunctionType implements JS, Expression, TypeTree { @With Markers markers; + JRightPadded constructorType; + + public boolean isConstructorType() { + return constructorType.getElement(); + } + + public FunctionType withConstructorType(boolean constructor) { + return getPadding().withConstructorType(this.constructorType.withElement(constructor)); + } + JContainer parameters; public List getParameters() { @@ -767,12 +777,20 @@ public FunctionType.Padding getPadding() { public static class Padding { private final FunctionType t; + public JRightPadded getConstructorType() { + return t.constructorType; + } + + public FunctionType withConstructorType(JRightPadded constructor) { + return t.constructorType == constructor ? t : new FunctionType(t.id, t.prefix, t.markers, constructor, t.parameters, t.arrow, t.returnType, t.type); + } + public JContainer getParameters() { return t.parameters; } public FunctionType withParameters(JContainer parameters) { - return t.parameters == parameters ? t : new FunctionType(t.id, t.prefix, t.markers, parameters, t.arrow, t.returnType, t.type); + return t.parameters == parameters ? t : new FunctionType(t.id, t.prefix, t.markers, t.constructorType, parameters, t.arrow, t.returnType, t.type); } } } @@ -1879,6 +1897,35 @@ public CoordinateBuilder.Expression getCoordinates() { } } + @Getter + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) + @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true) + @RequiredArgsConstructor + @With + final class TypeQuery implements JS, Expression, TypeTree { + + @EqualsAndHashCode.Include + UUID id; + + Space prefix; + Markers markers; + + TypeTree typeExpression; + + @Nullable + JavaType type; + + @Override + public

J acceptJavaScript(JavaScriptVisitor

v, P p) { + return v.visitTypeQuery(this, p); + } + + @Override + public CoordinateBuilder.Expression getCoordinates() { + return new CoordinateBuilder.Expression(this); + } + } + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true) @RequiredArgsConstructor 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 5a6bcf18..9392e839 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 @@ -43,7 +43,9 @@ public enum Location { INDEXED_SIGNATURE_DECLARATION_PARAMETERS(JsSpace.Location.INDEXED_SIGNATURE_DECLARATION_PARAMETERS_SUFFIX), FOR_CONTROL_VAR(JsSpace.Location.FOR_INIT_SUFFIX), FOR_CONTROL_ITER(JsSpace.Location.FOR_ITER_SUFFIX), - FOR_BODY(JsSpace.Location.FOR_BODY_SUFFIX); + FOR_BODY(JsSpace.Location.FOR_BODY_SUFFIX), + FUNCTION_TYPE_CONSTRUCTOR(JsSpace.Location.FUNCTION_TYPE_CONSTRUCTOR_SUFFIX) + ; 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 5d49ac5b..3427f623 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 @@ -105,6 +105,8 @@ public enum Location { FOR_INIT_SUFFIX, FOR_ITER_SUFFIX, FOR_IN_LOOP_PREFIX, + TYPE_QUERY_PREFIX, + FUNCTION_TYPE_CONSTRUCTOR_SUFFIX, } }