Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 49 additions & 44 deletions openrewrite/src/javascript/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,6 @@ export class JavaScriptParserVisitor {
}

visitClassDeclaration(node: ts.ClassDeclaration) {
if (node.typeParameters) {
return this.visitUnknown(node);
}

return new J.ClassDeclaration(
randomId(),
this.prefix(node),
Expand All @@ -411,7 +407,7 @@ export class JavaScriptParserVisitor {
J.ClassDeclaration.Kind.Type.Class
),
node.name ? this.convert(node.name) : this.mapIdentifier(node, ""),
this.mapTypeParameters(node),
this.mapTypeParametersAsJContainer(node),
null, // FIXME primary constructor
this.mapExtends(node),
this.mapImplements(node),
Expand Down Expand Up @@ -618,7 +614,7 @@ export class JavaScriptParserVisitor {
this.prefix(parent),
Markers.EMPTY,
fieldAccess,
this.mapTypeArguments(parent.typeArguments),
this.mapTypeArguments(this.suffix(parent.typeName), parent.typeArguments),
this.mapType(parent)
)
} else {
Expand Down Expand Up @@ -843,9 +839,8 @@ export class JavaScriptParserVisitor {
Markers.EMPTY,
[], // no decorators allowed
[], // no modifiers allowed
node.typeParameters
? new J.TypeParameters(randomId(), this.suffix(node.name), Markers.EMPTY, [], node.typeParameters.map(tp => this.rightPadded(this.visit(tp), this.suffix(tp))))
: null,

this.mapTypeParametersAsObject(node),
this.mapTypeInfo(node),
this.getOptionalUnary(node),
this.mapCommaSeparatedList(this.getParameterListNodes(node)),
Expand All @@ -862,9 +857,7 @@ export class JavaScriptParserVisitor {
Markers.EMPTY,
[], // no decorators allowed
[], // no modifiers allowed
node.typeParameters
? new J.TypeParameters(randomId(), this.suffix(node.name), Markers.EMPTY, [], node.typeParameters.map(tp => this.rightPadded(this.visit(tp), this.suffix(tp))))
: null,
this.mapTypeParametersAsObject(node),
this.mapTypeInfo(node),
new J.MethodDeclaration.IdentifierWithAnnotations(
node.name ? this.visit(node.name) : this.mapIdentifier(node, ""),
Expand Down Expand Up @@ -905,9 +898,7 @@ export class JavaScriptParserVisitor {
Markers.EMPTY,
this.mapDecorators(node),
this.mapModifiers(node),
node.typeParameters
? new J.TypeParameters(randomId(), this.suffix(node.name), Markers.EMPTY, [], node.typeParameters.map(tp => this.rightPadded(this.visit(tp), this.suffix(tp))))
: null,
this.mapTypeParametersAsObject(node),
this.mapTypeInfo(node),
new J.MethodDeclaration.IdentifierWithAnnotations(
node.name ? this.visit(node.name) : this.mapIdentifier(node, ""),
Expand All @@ -922,7 +913,7 @@ export class JavaScriptParserVisitor {
}

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

Expand Down Expand Up @@ -1004,25 +995,30 @@ export class JavaScriptParserVisitor {
}

visitCallSignature(node: ts.CallSignatureDeclaration) {
return new JS.ArrowFunction(
return new J.MethodDeclaration(
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.mapTypeParametersAsObject(node),
this.mapTypeInfo(node),
Space.EMPTY,
this.newJEmpty(),
null
new J.MethodDeclaration.IdentifierWithAnnotations(
new J.Identifier(
randomId(),
Space.EMPTY/* this.prefix(node.getChildren().find(n => n.kind == ts.SyntaxKind.OpenBraceToken)!) */,
Markers.EMPTY,
[], // FIXME decorators
"",
null,
null
), []
),
this.mapCommaSeparatedList(this.getParameterListNodes(node)),
null,
null,
null,
this.mapMethodType(node)
);
}

Expand Down Expand Up @@ -1064,6 +1060,7 @@ export class JavaScriptParserVisitor {
}

visitConstructorType(node: ts.ConstructorTypeNode) {
return this.visitUnknown(node);
}

visitTypeQuery(node: ts.TypeQueryNode) {
Expand Down Expand Up @@ -1857,10 +1854,6 @@ export class JavaScriptParserVisitor {
}

visitFunctionDeclaration(node: ts.FunctionDeclaration) {
if (node.typeParameters) {
return this.visitUnknown(node);
}

return new J.MethodDeclaration(
randomId(),
this.prefix(node),
Expand All @@ -1874,8 +1867,8 @@ export class JavaScriptParserVisitor {
J.Modifier.Type.LanguageExtension,
[]
), ...this.mapModifiers(node)],
null, // FIXME type parameters
node.type ? this.visit(node.type) : null,
this.mapTypeParametersAsObject(node), // FIXME type parameters
this.mapTypeInfo(node),
new J.MethodDeclaration.IdentifierWithAnnotations(
node.name ? this.visit(node.name) : this.mapIdentifier(node, ""),
[]
Expand All @@ -1899,10 +1892,6 @@ export class JavaScriptParserVisitor {
}

visitInterfaceDeclaration(node: ts.InterfaceDeclaration) {
if (node.typeParameters) {
return this.visitUnknown(node);
}

return new J.ClassDeclaration(
randomId(),
this.prefix(node),
Expand All @@ -1917,7 +1906,7 @@ export class JavaScriptParserVisitor {
J.ClassDeclaration.Kind.Type.Interface
),
node.name ? this.convert(node.name) : this.mapIdentifier(node, ""),
this.mapTypeParameters(node),
this.mapTypeParametersAsJContainer(node),
null, // interface has no constructor
null, // implements should be used
this.mapInterfaceExtends(node), // interface extends modeled as implements
Expand Down Expand Up @@ -2505,7 +2494,7 @@ export class JavaScriptParserVisitor {
return this.mapToContainer(nodes, this.trailingComma(nodes));
}

private mapTypeArguments(nodes: readonly ts.Node[]): JContainer<J.Expression> {
private mapTypeArguments(prefix: Space, nodes: readonly ts.Node[]): JContainer<J.Expression> {
if (nodes.length === 0) {
return JContainer.empty();
}
Expand All @@ -2517,7 +2506,7 @@ export class JavaScriptParserVisitor {
Markers.EMPTY
))
return new JContainer(
this.prefix(nodes[0]),
prefix,
args,
Markers.EMPTY
);
Expand Down Expand Up @@ -2573,8 +2562,24 @@ export class JavaScriptParserVisitor {
return node.modifiers?.filter(ts.isDecorator)?.map(this.convert<J.Annotation>) ?? [];
}

private mapTypeParameters(node: ts.ClassDeclaration | ts.InterfaceDeclaration): JContainer<J.TypeParameter> | null {
return null; // FIXME
private mapTypeParametersAsJContainer(node: ts.ClassDeclaration | ts.InterfaceDeclaration): JContainer<J.TypeParameter> | null {
return node.typeParameters
? JContainer.build(
this.suffix(this.findChildNode(node, ts.SyntaxKind.Identifier)!),
this.mapTypeParametersList(node.typeParameters),
Markers.EMPTY
)
: null;
}

private mapTypeParametersAsObject(node: ts.MethodDeclaration | ts.MethodSignature | ts.FunctionDeclaration | ts.CallSignatureDeclaration) : J.TypeParameters | null {
return node.typeParameters
? new J.TypeParameters(randomId(), node.questionToken ? this.suffix(node.questionToken) : node.name ? this.suffix(node.name) : Space.EMPTY, Markers.EMPTY, [], node.typeParameters.map(tp => this.rightPadded(this.visit(tp), this.suffix(tp))))
: null;
}

private mapTypeParametersList(typeParamsNodeArray: ts.NodeArray<ts.TypeParameterDeclaration>) : JRightPadded<J.TypeParameter>[] {
return typeParamsNodeArray.map(tp => this.rightPadded<J.TypeParameter>(this.visit(tp), this.suffix(tp)));
}

private findChildNode(node: ts.Node, kind: ts.SyntaxKind): ts.Node | undefined {
Expand Down
38 changes: 35 additions & 3 deletions openrewrite/test/javascript/parser/class.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ describe('class mapping', () => {
);
});
test('type parameter', () => {
rewriteRunWithOptions(
{expectUnknowns: true},
rewriteRun(
//language=typescript
typeScript('class A<T> {}')
typeScript('class A < T , G> {}')
);
});
test('body', () => {
Expand Down Expand Up @@ -211,6 +210,39 @@ describe('class mapping', () => {
);
});

test('class with type parameters', () => {
rewriteRun(
//language=typescript
typeScript(`
class A<T> {
}
`)
);
});

test.skip('anonymous class declaration', () => {
rewriteRun(
//language=typescript
typeScript(`
class OuterClass {
public static InnerClass = class extends Number { };
}
const a: typeof OuterClass.InnerClass.prototype = 1;
`)
);
});

test.skip('nested class qualified name', () => {
rewriteRun(
//language=typescript
typeScript(`
class OuterClass extends (class extends Number { }) {
}
const a: typeof OuterClass.InnerClass.prototype = 1;
`)
);
});

test('class with optional properties, ctor and modifiers', () => {
rewriteRun(
//language=typescript
Expand Down
23 changes: 23 additions & 0 deletions openrewrite/test/javascript/parser/function.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,33 @@ describe('function mapping', () => {
typeScript('function f(a = 2 , b) {}')
);
});

test('parameter with trailing comma', () => {
rewriteRun(
//language=typescript
typeScript('function f(a , ) {}')
);
});

test('parameter with trailing comma', () => {
rewriteRun(
//language=typescript
typeScript(`
function /*1*/ identity /*2*/ < Type , G , C > (arg: Type) /*3*/ : G {
return arg;
}
`)
);
});

test.skip('parameter with anonymous type', () => {
rewriteRun(
//language=typescript
typeScript(`
function create<Type>(c: { new (): Type }): Type {
return new c();
}
`)
);
});
});
23 changes: 23 additions & 0 deletions openrewrite/test/javascript/parser/interface.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,27 @@ describe('as mapping', () => {
);
});

test('interface with generics', () => {
rewriteRun(
//language=typescript
typeScript(`
interface GenericIdentityFn< T > {
/*1231*/ < Type /*1*/ > ( arg : Type ) : T ;
/*1231*/ /*1231*/ add < Type /*1*/ , R > (arg: Type): (x: T, y: Type) => R; //Function signature
}
`)
);
});

test('interface with generics', () => {
rewriteRun(
//language=typescript
typeScript(`
interface X {
find ? <R, T> (v1: R, v2: T): string;
}
`)
);
});

});
14 changes: 14 additions & 0 deletions openrewrite/test/javascript/parser/method.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,18 @@ describe('method mapping', () => {
`)
);
});

test('method with generics', () => {
rewriteRun(
//language=typescript
typeScript(`
class Handler< T1 , T2> {
test < T3 > ( input: string , t3: T3 ) /*1*/ : /*asda*/ string {
// hello world comment
return input;
}
}
`)
);
});
});
13 changes: 12 additions & 1 deletion openrewrite/test/javascript/parser/qualifiedName.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('empty mapping', () => {
test('globalThis qualified name with generic', () => {
rewriteRun(
//language=typescript
typeScript('const value: globalThis.Promise< string > = null')
typeScript('const value: globalThis.Promise < string > = null')
);
});

Expand All @@ -37,6 +37,17 @@ describe('empty mapping', () => {
);
});

test.skip('nested class qualified name', () => {
rewriteRun(
//language=typescript
typeScript(`
class OuterClass extends (class extends Number { }) {
}
const a: typeof OuterClass.InnerClass.prototype = 1;
`)
);
});

test('namespace qualified name', () => {
rewriteRun(
//language=typescript
Expand Down