From 7941c987d1a5538b0e448336c8afe5058c5d4cf0 Mon Sep 17 00:00:00 2001 From: Andrii Rodionov Date: Fri, 29 Nov 2024 16:08:44 +0100 Subject: [PATCH] - introduced JS.ImportType --- openrewrite/src/javascript/parser.ts | 22 +++- openrewrite/src/javascript/remote/receiver.ts | 27 +++- openrewrite/src/javascript/remote/sender.ts | 14 +- .../src/javascript/tree/support_types.ts | 4 + openrewrite/src/javascript/tree/tree.ts | 124 ++++++++++++++++++ openrewrite/src/javascript/visitor.ts | 18 ++- .../test/javascript/parser/import.test.ts | 9 ++ .../test/javascript/parser/importType.test.ts | 111 ++++++++++++++++ .../javascript/remote/JavaScriptReceiver.java | 26 ++++ .../javascript/remote/JavaScriptSender.java | 13 ++ .../remote/JavaScriptValidator.java | 8 ++ .../javascript/JavaScriptVisitor.java | 20 ++- .../internal/JavaScriptPrinter.java | 15 +++ .../org/openrewrite/javascript/tree/JS.java | 119 +++++++++++++++++ .../javascript/tree/JsContainer.java | 3 +- .../javascript/tree/JsLeftPadded.java | 3 +- .../javascript/tree/JsRightPadded.java | 4 +- .../openrewrite/javascript/tree/JsSpace.java | 5 + 18 files changed, 537 insertions(+), 8 deletions(-) create mode 100644 openrewrite/test/javascript/parser/importType.test.ts diff --git a/openrewrite/src/javascript/parser.ts b/openrewrite/src/javascript/parser.ts index 2818c815..6f1facad 100644 --- a/openrewrite/src/javascript/parser.ts +++ b/openrewrite/src/javascript/parser.ts @@ -1553,7 +1553,27 @@ export class JavaScriptParserVisitor { } visitImportType(node: ts.ImportTypeNode) { - return this.visitUnknown(node); + return new JS.ImportType( + randomId(), + this.prefix(node), + Markers.EMPTY, + node.isTypeOf ? this.rightPadded(true, this.suffix(this.findChildNode(node, ts.SyntaxKind.TypeOfKeyword)!)) : this.rightPadded(false, Space.EMPTY), + new J.ParenthesizedTypeTree( + randomId(), + this.suffix(this.findChildNode(node, ts.SyntaxKind.ImportKeyword)!), + Markers.EMPTY, + [], + new J.Parentheses( + randomId(), + Space.EMPTY, + Markers.EMPTY, + this.rightPadded(this.visit(node.argument), this.suffix(node.argument)) + ) + ), + node.qualifier ? this.leftPadded(this.prefix(this.findChildNode(node, ts.SyntaxKind.DotToken)!), this.visit(node.qualifier)): null, + node.typeArguments ? this.mapTypeArguments(this.suffix(node.qualifier!), node.typeArguments) : null, + this.mapType(node) + ); } visitObjectBindingPattern(node: ts.ObjectBindingPattern) { diff --git a/openrewrite/src/javascript/remote/receiver.ts b/openrewrite/src/javascript/remote/receiver.ts index 4ad7d904..0b771462 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, ConditionalType, DefaultType, Delete, Export, ExpressionStatement, ExpressionWithTypeArguments, FunctionType, InferType, JsImport, JsImportSpecifier, JsBinary, LiteralType, ObjectBindingDeclarations, PropertyAssignment, SatisfiesExpression, ScopedVariableDeclarations, StatementExpression, TaggedTemplateExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, TypePredicate, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration, ArrayBindingPattern, BindingElement} from '../tree'; +import {JS, JsLeftPadded, JsRightPadded, JsContainer, JsSpace, CompilationUnit, Alias, ArrowFunction, Await, ConditionalType, DefaultType, Delete, Export, ExpressionStatement, ExpressionWithTypeArguments, FunctionType, InferType, ImportType, JsImport, JsImportSpecifier, JsBinary, LiteralType, ObjectBindingDeclarations, PropertyAssignment, SatisfiesExpression, ScopedVariableDeclarations, StatementExpression, TaggedTemplateExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, TypePredicate, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration, ArrayBindingPattern, BindingElement} from '../tree'; import {Expression, J, JContainer, JLeftPadded, JRightPadded, NameTree, Space, Statement, TypeTree, TypedTree} from "../../java"; import * as Java from "../../java/tree"; @@ -153,6 +153,18 @@ class Visitor extends JavaScriptVisitor { return inferType; } + public visitImportType(importType: ImportType, ctx: ReceiverContext): J { + importType = importType.withId(ctx.receiveValue(importType.id, ValueType.UUID)!); + importType = importType.withPrefix(ctx.receiveNode(importType.prefix, receiveSpace)!); + importType = importType.withMarkers(ctx.receiveNode(importType.markers, ctx.receiveMarkers)!); + importType = importType.padding.withHasTypeof(ctx.receiveNode(importType.padding.hasTypeof, rightPaddedValueReceiver(ValueType.Primitive))!); + importType = importType.withImportArgument(ctx.receiveNode(importType.importArgument, ctx.receiveTree)!); + importType = importType.padding.withQualifier(ctx.receiveNode(importType.padding.qualifier, receiveLeftPaddedTree)); + importType = importType.padding.withTypeArguments(ctx.receiveNode(importType.padding.typeArguments, receiveContainer)); + importType = importType.withType(ctx.receiveValue(importType.type, ValueType.Object)); + return importType; + } + public visitJsImport(jsImport: JsImport, ctx: ReceiverContext): J { jsImport = jsImport.withId(ctx.receiveValue(jsImport.id, ValueType.UUID)!); jsImport = jsImport.withPrefix(ctx.receiveNode(jsImport.prefix, receiveSpace)!); @@ -1327,6 +1339,19 @@ class Factory implements ReceiverFactory { ); } + if (type === "org.openrewrite.javascript.tree.JS$ImportType") { + return new ImportType( + ctx.receiveValue(null, ValueType.UUID)!, + ctx.receiveNode(null, receiveSpace)!, + ctx.receiveNode(null, ctx.receiveMarkers)!, + ctx.receiveNode>(null, rightPaddedValueReceiver(ValueType.Primitive))!, + ctx.receiveNode(null, ctx.receiveTree)!, + ctx.receiveNode>(null, receiveLeftPaddedTree), + ctx.receiveNode>(null, receiveContainer), + ctx.receiveValue(null, ValueType.Object) + ); + } + if (type === "org.openrewrite.javascript.tree.JS$JsImport") { return new JsImport( ctx.receiveValue(null, ValueType.UUID)!, diff --git a/openrewrite/src/javascript/remote/sender.ts b/openrewrite/src/javascript/remote/sender.ts index c7ffbb12..cc365e0e 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, ConditionalType, DefaultType, Delete, Export, ExpressionStatement, ExpressionWithTypeArguments, FunctionType, InferType, JsImport, JsImportSpecifier, JsBinary, LiteralType, ObjectBindingDeclarations, PropertyAssignment, SatisfiesExpression, ScopedVariableDeclarations, StatementExpression, TaggedTemplateExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, TypePredicate, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration, ArrayBindingPattern, BindingElement} from '../tree'; +import {JS, JsLeftPadded, JsRightPadded, JsContainer, JsSpace, CompilationUnit, Alias, ArrowFunction, Await, ConditionalType, DefaultType, Delete, Export, ExpressionStatement, ExpressionWithTypeArguments, FunctionType, InferType, ImportType, JsImport, JsImportSpecifier, JsBinary, LiteralType, ObjectBindingDeclarations, PropertyAssignment, SatisfiesExpression, ScopedVariableDeclarations, StatementExpression, TaggedTemplateExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, TypePredicate, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration, ArrayBindingPattern, BindingElement} from '../tree'; import {Expression, J, JContainer, JLeftPadded, JRightPadded, Space, Statement} from "../../java"; import * as Java from "../../java/tree"; @@ -148,6 +148,18 @@ class Visitor extends JavaScriptVisitor { return inferType; } + public visitImportType(importType: ImportType, ctx: SenderContext): J { + ctx.sendValue(importType, v => v.id, ValueType.UUID); + ctx.sendNode(importType, v => v.prefix, Visitor.sendSpace); + ctx.sendNode(importType, v => v.markers, ctx.sendMarkers); + ctx.sendNode(importType, v => v.padding.hasTypeof, Visitor.sendRightPadded(ValueType.Primitive)); + ctx.sendNode(importType, v => v.importArgument, ctx.sendTree); + ctx.sendNode(importType, v => v.padding.qualifier, Visitor.sendLeftPadded(ValueType.Tree)); + ctx.sendNode(importType, v => v.padding.typeArguments, Visitor.sendContainer(ValueType.Tree)); + ctx.sendTypedValue(importType, v => v.type, ValueType.Object); + return importType; + } + public visitJsImport(jsImport: JsImport, ctx: SenderContext): J { ctx.sendValue(jsImport, v => v.id, ValueType.UUID); ctx.sendNode(jsImport, v => v.prefix, Visitor.sendSpace); diff --git a/openrewrite/src/javascript/tree/support_types.ts b/openrewrite/src/javascript/tree/support_types.ts index fc202cb7..cb606910 100644 --- a/openrewrite/src/javascript/tree/support_types.ts +++ b/openrewrite/src/javascript/tree/support_types.ts @@ -245,6 +245,7 @@ export namespace JsSpace { TYPE_PREDICATE_PREFIX, LITERAL_TYPE_PREFIX, SATISFIES_EXPRESSION_PREFIX, + IMPORT_TYPE_PREFIX, } } export namespace JsLeftPadded { @@ -274,6 +275,7 @@ export namespace JsLeftPadded { TYPE_PREDICATE_ASSERTS, TYPE_PREDICATE_EXPRESSION, SATISFIES_EXPRESSION_SATISFIES_TYPE, + IMPORT_TYPE_QUALIFIER, } } export namespace JsRightPadded { @@ -297,6 +299,7 @@ export namespace JsRightPadded { FUNCTION_TYPE_CONSTRUCTOR_TYPE, TEMPLATE_EXPRESSION_TEMPLATE_SPANS, TAGGED_TEMPLATE_EXPRESSION_TAG, + IMPORT_TYPE_HAS_TYPEOF, } } export namespace JsContainer { @@ -315,5 +318,6 @@ export namespace JsContainer { EXPRESSION_WITH_TYPE_ARGUMENTS_TYPE_ARGUMENTS, TAGGED_TEMPLATE_EXPRESSION_TYPE_ARGUMENTS, CONDITIONAL_TYPE_CONDITION, + IMPORT_TYPE_TYPE_ARGUMENTS, } } diff --git a/openrewrite/src/javascript/tree/tree.ts b/openrewrite/src/javascript/tree/tree.ts index 1c5b6919..71ef8c51 100644 --- a/openrewrite/src/javascript/tree/tree.ts +++ b/openrewrite/src/javascript/tree/tree.ts @@ -1089,6 +1089,130 @@ export class InferType extends JSMixin(Object) implements TypeTree, Expression { } +@LstType("org.openrewrite.javascript.tree.JS$ImportType") +export class ImportType extends JSMixin(Object) implements Expression, TypeTree { + public constructor(id: UUID, prefix: Space, markers: Markers, hasTypeof: JRightPadded, importArgument: Java.ParenthesizedTypeTree, qualifier: JLeftPadded | null, typeArguments: JContainer | null, _type: JavaType | null) { + super(); + this._id = id; + this._prefix = prefix; + this._markers = markers; + this._hasTypeof = hasTypeof; + this._importArgument = importArgument; + this._qualifier = qualifier; + this._typeArguments = typeArguments; + this._type = _type; + } + + private readonly _id: UUID; + + public get id(): UUID { + return this._id; + } + + public withId(id: UUID): ImportType { + return id === this._id ? this : new ImportType(id, this._prefix, this._markers, this._hasTypeof, this._importArgument, this._qualifier, this._typeArguments, this._type); + } + + private readonly _prefix: Space; + + public get prefix(): Space { + return this._prefix; + } + + public withPrefix(prefix: Space): ImportType { + return prefix === this._prefix ? this : new ImportType(this._id, prefix, this._markers, this._hasTypeof, this._importArgument, this._qualifier, this._typeArguments, this._type); + } + + private readonly _markers: Markers; + + public get markers(): Markers { + return this._markers; + } + + public withMarkers(markers: Markers): ImportType { + return markers === this._markers ? this : new ImportType(this._id, this._prefix, markers, this._hasTypeof, this._importArgument, this._qualifier, this._typeArguments, this._type); + } + + private readonly _hasTypeof: JRightPadded; + + public get hasTypeof(): boolean { + return this._hasTypeof.element; + } + + public withHasTypeof(hasTypeof: boolean): ImportType { + return this.padding.withHasTypeof(this._hasTypeof.withElement(hasTypeof)); + } + + private readonly _importArgument: Java.ParenthesizedTypeTree; + + public get importArgument(): Java.ParenthesizedTypeTree { + return this._importArgument; + } + + public withImportArgument(importArgument: Java.ParenthesizedTypeTree): ImportType { + return importArgument === this._importArgument ? this : new ImportType(this._id, this._prefix, this._markers, this._hasTypeof, importArgument, this._qualifier, this._typeArguments, this._type); + } + + private readonly _qualifier: JLeftPadded | null; + + public get qualifier(): Expression | null { + return this._qualifier === null ? null : this._qualifier.element; + } + + public withQualifier(qualifier: Expression | null): ImportType { + return this.padding.withQualifier(JLeftPadded.withElement(this._qualifier, qualifier)); + } + + private readonly _typeArguments: JContainer | null; + + public get typeArguments(): Expression[] | null { + return this._typeArguments === null ? null : this._typeArguments.elements; + } + + public withTypeArguments(typeArguments: Expression[] | null): ImportType { + return this.padding.withTypeArguments(JContainer.withElementsNullable(this._typeArguments, typeArguments)); + } + + private readonly _type: JavaType | null; + + public get type(): JavaType | null { + return this._type; + } + + public withType(_type: JavaType | null): ImportType { + return _type === this._type ? this : new ImportType(this._id, this._prefix, this._markers, this._hasTypeof, this._importArgument, this._qualifier, this._typeArguments, _type); + } + + public acceptJavaScript

(v: JavaScriptVisitor

, p: P): J | null { + return v.visitImportType(this, p); + } + + get padding() { + const t = this; + return new class { + public get hasTypeof(): JRightPadded { + return t._hasTypeof; + } + public withHasTypeof(hasTypeof: JRightPadded): ImportType { + return t._hasTypeof === hasTypeof ? t : new ImportType(t._id, t._prefix, t._markers, hasTypeof, t._importArgument, t._qualifier, t._typeArguments, t._type); + } + public get qualifier(): JLeftPadded | null { + return t._qualifier; + } + public withQualifier(qualifier: JLeftPadded | null): ImportType { + return t._qualifier === qualifier ? t : new ImportType(t._id, t._prefix, t._markers, t._hasTypeof, t._importArgument, qualifier, t._typeArguments, t._type); + } + public get typeArguments(): JContainer | null { + return t._typeArguments; + } + public withTypeArguments(typeArguments: JContainer | null): ImportType { + return t._typeArguments === typeArguments ? t : new ImportType(t._id, t._prefix, t._markers, t._hasTypeof, t._importArgument, t._qualifier, typeArguments, t._type); + } + } + } + +} + @LstType("org.openrewrite.javascript.tree.JS$JsImport") export class JsImport extends JSMixin(Object) implements Statement { public constructor(id: UUID, prefix: Space, markers: Markers, name: JRightPadded | null, importType: JLeftPadded, imports: JContainer | null, _from: Space | null, target: Java.Literal | null, initializer: JLeftPadded | null) { diff --git a/openrewrite/src/javascript/visitor.ts b/openrewrite/src/javascript/visitor.ts index f0f3435c..1749579d 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, ConditionalType, DefaultType, Delete, Export, ExpressionStatement, ExpressionWithTypeArguments, FunctionType, InferType, JsImport, JsImportSpecifier, JsBinary, LiteralType, ObjectBindingDeclarations, PropertyAssignment, SatisfiesExpression, ScopedVariableDeclarations, StatementExpression, TaggedTemplateExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, TypePredicate, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration, ArrayBindingPattern, BindingElement} from "./tree"; +import {CompilationUnit, Alias, ArrowFunction, Await, ConditionalType, DefaultType, Delete, Export, ExpressionStatement, ExpressionWithTypeArguments, FunctionType, InferType, ImportType, JsImport, JsImportSpecifier, JsBinary, LiteralType, ObjectBindingDeclarations, PropertyAssignment, SatisfiesExpression, ScopedVariableDeclarations, StatementExpression, TaggedTemplateExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeQuery, TypeOperator, TypePredicate, Unary, Union, Intersection, Void, Yield, TypeInfo, JSVariableDeclarations, JSMethodDeclaration, JSForOfLoop, JSForInLoop, JSForInOfLoopControl, NamespaceDeclaration, FunctionDeclaration, TypeLiteral, IndexSignatureDeclaration, ArrayBindingPattern, BindingElement} from "./tree"; import {Expression, J, JContainer, JLeftPadded, JRightPadded, Space, Statement} from "../java/tree"; import {JavaVisitor} from "../java"; import * as Java from "../java/tree"; @@ -184,6 +184,22 @@ export class JavaScriptVisitor

extends JavaVisitor

{ return inferType; } + public visitImportType(importType: ImportType, p: P): J | null { + importType = importType.withPrefix(this.visitJsSpace(importType.prefix, JsSpace.Location.IMPORT_TYPE_PREFIX, p)!); + let tempExpression = this.visitExpression(importType, p) as Expression; + if (!(tempExpression instanceof ImportType)) + { + return tempExpression; + } + importType = tempExpression as ImportType; + importType = importType.withMarkers(this.visitMarkers(importType.markers, p)); + importType = importType.padding.withHasTypeof(this.visitJsRightPadded(importType.padding.hasTypeof, JsRightPadded.Location.IMPORT_TYPE_HAS_TYPEOF, p)!); + importType = importType.withImportArgument(this.visitAndCast(importType.importArgument, p)!); + importType = importType.padding.withQualifier(this.visitJsLeftPadded(importType.padding.qualifier, JsLeftPadded.Location.IMPORT_TYPE_QUALIFIER, p)); + importType = importType.padding.withTypeArguments(this.visitJsContainer(importType.padding.typeArguments, JsContainer.Location.IMPORT_TYPE_TYPE_ARGUMENTS, p)); + return importType; + } + public visitJsImport(jsImport: JsImport, p: P): J | null { jsImport = jsImport.withPrefix(this.visitJsSpace(jsImport.prefix, JsSpace.Location.JS_IMPORT_PREFIX, p)!); let tempStatement = this.visitStatement(jsImport, p) as Statement; diff --git a/openrewrite/test/javascript/parser/import.test.ts b/openrewrite/test/javascript/parser/import.test.ts index ca4e4feb..fbcd0c25 100644 --- a/openrewrite/test/javascript/parser/import.test.ts +++ b/openrewrite/test/javascript/parser/import.test.ts @@ -93,4 +93,13 @@ describe('import mapping', () => { typeScript(`import type { Component } from "react";`) ); }); + + test.skip('experimental: import with import attributes', () => { + rewriteRun( + //language=typescript + typeScript(` + import foo from 'module-name' with { type: "json" }; + `) + ); + }); }); diff --git a/openrewrite/test/javascript/parser/importType.test.ts b/openrewrite/test/javascript/parser/importType.test.ts new file mode 100644 index 00000000..38e4e414 --- /dev/null +++ b/openrewrite/test/javascript/parser/importType.test.ts @@ -0,0 +1,111 @@ +import {connect, disconnect, rewriteRun, typeScript} from '../testHarness'; + +describe('import type mapping', () => { + beforeAll(() => connect()); + afterAll(() => disconnect()); + + test('simple import', () => { + rewriteRun( + //language=typescript + typeScript(`type ModuleType = import('fs');`) + ); + }); + + test('simple import with isTypeOf', () => { + rewriteRun( + //language=typescript + typeScript(`type MyType = typeof import("module-name");`) + ); + }); + + test('simple import with isTypeOf and comments', () => { + rewriteRun( + //language=typescript + typeScript(`type MyType = /*a*/typeof /*b*/ import/*c*/(/*d*/"module-name"/*e*/)/*f*/;`) + ); + }); + + test('import with qualifier', () => { + rewriteRun( + //language=typescript + typeScript(`type ReadStream = import("fs").ReadStream;`) + ); + }); + + test('import with qualifier and comments', () => { + rewriteRun( + //language=typescript + typeScript(`type ReadStream = import("fs")/*a*/./*b*/ReadStream/*c*/;`) + ); + }); + + test('import with sub qualifiers', () => { + rewriteRun( + //language=typescript + typeScript(` + export default class Utils { + static Tools = class { + static UtilityName = "Helper"; + }; + } + + // main.ts + type UtilityNameType = import("./module")/*a*/./*b*/default/*c*/./*d*/Tools/*e*/./*f*/UtilityName; + `) + ); + }); + + + test('function with import type', () => { + rewriteRun( + //language=typescript + typeScript(` + function useModule(module: import("fs")): void { + console.log(module); + } + `) + ); + }); + + test('import type with type argument', () => { + rewriteRun( + //language=typescript + typeScript(` + type AnotherType = import("module-name").GenericType; + `) + ); + }); + + test('import type with type argument adv1', () => { + rewriteRun( + //language=typescript + typeScript(` + export namespace Shapes { + export type Box = { value: T; size: number }; + export type Circle = { radius: number }; + } + + // main.ts + type CircleBox = import("./shapes").Shapes.Box; + `) + ); + }); + + test('import type with type argument adv2', () => { + rewriteRun( + //language=typescript + typeScript(` + export namespace Models { + export type Response = { + status: number; + data: T; + }; + } + + // main.ts + type UserResponse = import("./library").Models.Response<{ id: string; 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 40e9ab00..38f5792b 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 @@ -211,6 +211,19 @@ public JS.InferType visitInferType(JS.InferType inferType, ReceiverContext ctx) return inferType; } + @Override + public JS.ImportType visitImportType(JS.ImportType importType, ReceiverContext ctx) { + importType = importType.withId(ctx.receiveNonNullValue(importType.getId(), UUID.class)); + importType = importType.withPrefix(ctx.receiveNonNullNode(importType.getPrefix(), JavaScriptReceiver::receiveSpace)); + importType = importType.withMarkers(ctx.receiveNonNullNode(importType.getMarkers(), ctx::receiveMarkers)); + importType = importType.getPadding().withHasTypeof(ctx.receiveNonNullNode(importType.getPadding().getHasTypeof(), rightPaddedValueReceiver(java.lang.Boolean.class))); + importType = importType.withImportArgument(ctx.receiveNonNullNode(importType.getImportArgument(), ctx::receiveTree)); + importType = importType.getPadding().withQualifier(ctx.receiveNode(importType.getPadding().getQualifier(), JavaScriptReceiver::receiveLeftPaddedTree)); + importType = importType.getPadding().withTypeArguments(ctx.receiveNode(importType.getPadding().getTypeArguments(), JavaScriptReceiver::receiveContainer)); + importType = importType.withType(ctx.receiveValue(importType.getType(), JavaType.class)); + return importType; + } + @Override public JS.JsImport visitJsImport(JS.JsImport jsImport, ReceiverContext ctx) { jsImport = jsImport.withId(ctx.receiveNonNullValue(jsImport.getId(), UUID.class)); @@ -1451,6 +1464,19 @@ public T create(Class type, ReceiverContext ctx) { ); } + if (type == JS.ImportType.class) { + return (T) new JS.ImportType( + 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, ctx::receiveTree), + ctx.receiveNode(null, JavaScriptReceiver::receiveLeftPaddedTree), + ctx.receiveNode(null, JavaScriptReceiver::receiveContainer), + ctx.receiveValue(null, JavaType.class) + ); + } + if (type == JS.JsImport.class) { return (T) new JS.JsImport( 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 b736d7ff..02ad28d5 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 @@ -194,6 +194,19 @@ public JS.InferType visitInferType(JS.InferType inferType, SenderContext ctx) { return inferType; } + @Override + public JS.ImportType visitImportType(JS.ImportType importType, SenderContext ctx) { + ctx.sendValue(importType, JS.ImportType::getId); + ctx.sendNode(importType, JS.ImportType::getPrefix, JavaScriptSender::sendSpace); + ctx.sendNode(importType, JS.ImportType::getMarkers, ctx::sendMarkers); + ctx.sendNode(importType, e -> e.getPadding().getHasTypeof(), JavaScriptSender::sendRightPadded); + ctx.sendNode(importType, JS.ImportType::getImportArgument, ctx::sendTree); + ctx.sendNode(importType, e -> e.getPadding().getQualifier(), JavaScriptSender::sendLeftPadded); + ctx.sendNode(importType, e -> e.getPadding().getTypeArguments(), JavaScriptSender::sendContainer); + ctx.sendTypedValue(importType, JS.ImportType::getType); + return importType; + } + @Override public JS.JsImport visitJsImport(JS.JsImport jsImport, SenderContext ctx) { ctx.sendValue(jsImport, JS.JsImport::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 d7fb6950..0b0c650e 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 @@ -133,6 +133,14 @@ public JS.InferType visitInferType(JS.InferType inferType, P p) { return inferType; } + @Override + public JS.ImportType visitImportType(JS.ImportType importType, P p) { + visitAndValidate(importType.getImportArgument(), J.ParenthesizedTypeTree.class, p); + visitAndValidate(importType.getQualifier(), Expression.class, p); + visitAndValidate(importType.getTypeArguments(), Expression.class, p); + return importType; + } + @Override public JS.JsImport visitJsImport(JS.JsImport jsImport, P p) { visitAndValidate(jsImport.getName(), J.Identifier.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 a807b0f7..589579dc 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java @@ -237,7 +237,6 @@ public J visitExpressionWithTypeArguments(JS.ExpressionWithTypeArguments express if (ta.getPadding().getTypeArguments() != null) { ta = ta.getPadding().withTypeArguments(visitContainer(ta.getPadding().getTypeArguments(), JsContainer.Location.EXPR_WITH_TYPE_ARG_PARAMETERS, p)); } - ta = ta.getPadding().withTypeArguments(visitTypeNames(ta.getPadding().getTypeArguments(), p)); ta = ta.withType(visitType(ta.getType(), p)); return ta; } @@ -864,6 +863,25 @@ public J visitTypeLiteral(JS.TypeLiteral typeLiteral, P p) { return t; } + public J visitImportType(JS.ImportType importType, P p) { + JS.ImportType t = importType; + t = t.withPrefix(visitSpace(t.getPrefix(), JsSpace.Location.IMPORT_TYPE_PREFIX, p)); + t = t.withMarkers(visitMarkers(t.getMarkers(), p)); + Expression temp = (Expression) visitExpression(t, p); + if (!(temp instanceof JS.ImportType)) { + return temp; + } else { + t = (JS.ImportType) temp; + } + t = t.getPadding().withHasTypeof(visitRightPadded(t.getPadding().getHasTypeof(), JsRightPadded.Location.IMPORT_TYPE_TYPEOF, p)); + t = t.withImportArgument(visitAndCast(t.getImportArgument(), p)); + t = t.getPadding().withQualifier(visitLeftPadded(t.getPadding().getQualifier(), JsLeftPadded.Location.IMPORT_TYPE_QUALIFIER, p)); + if (t.getPadding().getTypeArguments() != null) { + t = t.getPadding().withTypeArguments(visitContainer(t.getPadding().getTypeArguments(), JsContainer.Location.IMPORT_TYPE_TYPE_ARGUMENTS, p)); + } + return t; + } + public J visitIndexSignatureDeclaration(JS.IndexSignatureDeclaration indexSignatureDeclaration, P p) { JS.IndexSignatureDeclaration isd = indexSignatureDeclaration; isd = isd.withPrefix(visitSpace(isd.getPrefix(), JsSpace.Location.INDEXED_SIGNATURE_DECLARATION_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 f6343610..8ccccc85 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 @@ -211,6 +211,21 @@ public J visitInferType(JS.InferType inferType, PrintOutputCapture

p) { return inferType; } + @Override + public J visitImportType(JS.ImportType importType, PrintOutputCapture

p) { + beforeSyntax(importType, JsSpace.Location.IMPORT_TYPE_PREFIX, p); + if (importType.isHasTypeof()) { + p.append("typeof"); + visitRightPadded(importType.getPadding().getHasTypeof(), JsRightPadded.Location.IMPORT_TYPE_TYPEOF, p); + } + p.append("import"); + visit(importType.getImportArgument(), p); + visitLeftPadded(".", importType.getPadding().getQualifier(), JsLeftPadded.Location.IMPORT_TYPE_QUALIFIER, p); + visitContainer("<", importType.getPadding().getTypeArguments(), JsContainer.Location.IMPORT_TYPE_TYPE_ARGUMENTS, ",", ">", p); + afterSyntax(importType, p); + return importType; + } + @Override public J visitJsImport(JS.JsImport jsImport, PrintOutputCapture

p) { beforeSyntax(jsImport, JsSpace.Location.EXPORT_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 488b61b3..087b25f1 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 @@ -1037,6 +1037,125 @@ public InferType withTypeParameter(JLeftPadded typeParameter) { } } + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) + @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true) + @RequiredArgsConstructor + @AllArgsConstructor(access = AccessLevel.PRIVATE) + class ImportType implements JS, Expression, TypeTree { + + @Nullable + @NonFinal + transient WeakReference padding; + + @Getter + @With + @EqualsAndHashCode.Include + UUID id; + + @Getter + @With + Space prefix; + + @Getter + @With + Markers markers; + + JRightPadded hasTypeof; + + public boolean isHasTypeof() { + return hasTypeof.getElement(); + } + + public ImportType withHasTypeof(boolean hasTypeof) { + return getPadding().withHasTypeof(this.hasTypeof.withElement(hasTypeof)); + } + + @Getter + @With + J.ParenthesizedTypeTree importArgument; + + @Nullable + JLeftPadded qualifier; + + public @Nullable Expression getQualifier() { + return qualifier == null ? null : qualifier.getElement(); + } + + public ImportType withQualifier(@Nullable Expression qualifier) { + return getPadding().withQualifier(JLeftPadded.withElement(this.qualifier, qualifier)); + } + + @Nullable + JContainer typeArguments; + + public @Nullable List getTypeArguments() { + return typeArguments == null ? null : typeArguments.getElements(); + } + + public ImportType withTypeArguments(@Nullable List typeArguments) { + return getPadding().withTypeArguments(JContainer.withElementsNullable(this.typeArguments, typeArguments)); + } + + @Getter + @With + @Nullable + JavaType type; + + @Override + public

J acceptJavaScript(JavaScriptVisitor

v, P p) { + return v.visitImportType(this, p); + } + + @Override + public CoordinateBuilder.Expression getCoordinates() { + return new CoordinateBuilder.Expression(this); + } + + public ImportType.Padding getPadding() { + ImportType.Padding p; + if (this.padding == null) { + p = new ImportType.Padding(this); + this.padding = new WeakReference<>(p); + } else { + p = this.padding.get(); + if (p == null || p.t != this) { + p = new ImportType.Padding(this); + this.padding = new WeakReference<>(p); + } + } + return p; + } + + @RequiredArgsConstructor + public static class Padding { + private final ImportType t; + + public JRightPadded getHasTypeof() { + return t.hasTypeof; + } + + public ImportType withHasTypeof(JRightPadded hasTypeof) { + return t.hasTypeof == hasTypeof ? t : new ImportType(t.id, t.prefix, t.markers, hasTypeof, t.importArgument, t.qualifier, t.typeArguments, t.type); + } + + public @Nullable JLeftPadded getQualifier() { + return t.qualifier; + } + + public ImportType withQualifier(@Nullable JLeftPadded qualifier) { + return t.qualifier == qualifier ? t : new ImportType(t.id, t.prefix, t.markers, t.hasTypeof, t.importArgument, qualifier, t.typeArguments, t.type); + } + + public @Nullable JContainer getTypeArguments() { + return t.typeArguments; + } + + public ImportType withTypeArguments(@Nullable JContainer typeArguments) { + return t.typeArguments == typeArguments ? t : new ImportType(t.id, t.prefix, t.markers, t.hasTypeof, t.importArgument, t.qualifier, typeArguments, t.type); + } + } + } + @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 d21d3da2..2210ef45 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 @@ -32,7 +32,8 @@ public enum Location { ARRAY_BINDING_PATTERN_ELEMENTS(JsSpace.Location.ARRAY_BINDING_PATTERN_ELEMENTS_PREFIX, JsRightPadded.Location.ARRAY_BINDING_PATTERN_ELEMENTS), EXPR_WITH_TYPE_ARG_PARAMETERS(JsSpace.Location.EXPR_WITH_TYPE_ARG_PARAMETERS, JsRightPadded.Location.EXPR_WITH_TYPE_ARG_PARAMETERS_SUFFIX), TEMPLATE_EXPRESSION_TYPE_ARG_PARAMETERS(JsSpace.Location.TEMPLATE_EXPRESSION_TYPE_ARG_PARAMETERS, JsRightPadded.Location.TEMPLATE_EXPRESSION_TYPE_ARG_PARAMETERS_SUFFIX), - CONDITIONAL_TYPE_CONDITION(JsSpace.Location.CONDITIONAL_TYPE_CONDITION, JsRightPadded.Location.CONDITIONAL_TYPE_CONDITION); + CONDITIONAL_TYPE_CONDITION(JsSpace.Location.CONDITIONAL_TYPE_CONDITION, JsRightPadded.Location.CONDITIONAL_TYPE_CONDITION), + IMPORT_TYPE_TYPE_ARGUMENTS(JsSpace.Location.IMPORT_TYPE_TYPE_ARGUMENTS, JsRightPadded.Location.IMPORT_TYPE_TYPE_ARGUMENTS); private final JsSpace.Location beforeLocation; private final JsRightPadded.Location elementLocation; diff --git a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsLeftPadded.java b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsLeftPadded.java index eed4e539..de46a0f7 100644 --- a/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsLeftPadded.java +++ b/rewrite-javascript/src/main/java/org/openrewrite/javascript/tree/JsLeftPadded.java @@ -37,7 +37,8 @@ public enum Location { INFER_TYPE_PARAMETER(JsSpace.Location.INFER_TYPE_PARAMETER_PREFIX), TYPE_PREDICATE_ASSERTS(JsSpace.Location.TYPE_PREDICATE_ASSERTS_PREFIX), TYPE_PREDICATE_EXPRESSION(JsSpace.Location.TYPE_PREDICATE_EXPRESSION_PREFIX), - SATISFIES_EXPRESSION_TYPE(JsSpace.Location.SATISFIES_EXPRESSION_TYPE_PREFIX); + SATISFIES_EXPRESSION_TYPE(JsSpace.Location.SATISFIES_EXPRESSION_TYPE_PREFIX), + IMPORT_TYPE_QUALIFIER(JsSpace.Location.IMPORT_TYPE_QUALIFIER_PREFIX); private final JsSpace.Location beforeLocation; 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 d8c1a117..7d9baf43 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 @@ -48,7 +48,9 @@ public enum Location { TEMPLATE_EXPRESSION_TAG(JsSpace.Location.TEMPLATE_EXPRESSION_TAG_SUFFIX), TEMPLATE_EXPRESSION_TYPE_ARG_PARAMETERS_SUFFIX(JsSpace.Location.TEMPLATE_EXPRESSION_TYPE_ARG_PARAMETERS_SUFFIX), TEMPLATE_EXPRESSION_TEMPLATE_SPAN(JsSpace.Location.TEMPLATE_EXPRESSION_TEMPLATE_SPAN_SUFFIX), - CONDITIONAL_TYPE_CONDITION(JsSpace.Location.CONDITIONAL_TYPE_CONDITION_SUFFIX); + CONDITIONAL_TYPE_CONDITION(JsSpace.Location.CONDITIONAL_TYPE_CONDITION_SUFFIX), + IMPORT_TYPE_TYPEOF(JsSpace.Location.IMPORT_TYPE_TYPEOF_SUFFIX), + IMPORT_TYPE_TYPE_ARGUMENTS(JsSpace.Location.IMPORT_TYPE_TYPE_ARGUMENTS_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 f3182b50..78e19bc2 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 @@ -127,6 +127,11 @@ public enum Location { LITERAL_TYPE_PREFIX, SATISFIES_EXPRESSION_PREFIX, SATISFIES_EXPRESSION_TYPE_PREFIX, + IMPORT_TYPE_PREFIX, + IMPORT_TYPE_TYPEOF_SUFFIX, + IMPORT_TYPE_QUALIFIER_PREFIX, + IMPORT_TYPE_TYPE_ARGUMENTS, + IMPORT_TYPE_TYPE_ARGUMENTS_SUFFIX, } }