Skip to content

Commit

Permalink
Implemented Enum declaration support
Browse files Browse the repository at this point in the history
Implemented `visitEnumDeclaration` parser.
Enum members initialization in `visitEnumMember` is not currently implemented.
  • Loading branch information
arodionov authored and OlegDokuka committed Oct 22, 2024
1 parent 6032946 commit 375fb60
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 3 deletions.
75 changes: 72 additions & 3 deletions openrewrite/src/javascript/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export class JavaScriptParserVisitor {
);
}

private mapModifiers(node: ts.VariableDeclarationList | ts.VariableStatement | ts.ClassDeclaration | ts.PropertyDeclaration | ts.FunctionDeclaration | ts.ParameterDeclaration | ts.MethodDeclaration) {
private mapModifiers(node: ts.VariableDeclarationList | ts.VariableStatement | ts.ClassDeclaration | ts.PropertyDeclaration | ts.FunctionDeclaration | ts.ParameterDeclaration | ts.MethodDeclaration | ts.EnumDeclaration) {
if (ts.isVariableStatement(node)) {
return [new J.Modifier(
randomId(),
Expand All @@ -258,6 +258,8 @@ export class JavaScriptParserVisitor {
)];
} else if (ts.isClassDeclaration(node)) {
return node.modifiers ? node.modifiers?.filter(ts.isModifier).map(this.mapModifier) : [];
} else if (ts.isEnumDeclaration(node)) {
return node.modifiers ? node.modifiers?.filter(ts.isModifier).map(this.mapModifier) : [];
} else if (ts.isPropertyDeclaration(node)) {
return node.modifiers ? node.modifiers?.filter(ts.isModifier).map(this.mapModifier) : [];
} else if (ts.isFunctionDeclaration(node) || ts.isParameter(node) || ts.isMethodDeclaration(node)) {
Expand Down Expand Up @@ -1551,7 +1553,63 @@ export class JavaScriptParserVisitor {
}

visitEnumDeclaration(node: ts.EnumDeclaration) {
return this.visitUnknown(node);
return new J.ClassDeclaration(
randomId(),
this.prefix(node),
Markers.EMPTY,
[], // enum has no decorators
this.mapModifiers(node),
new J.ClassDeclaration.Kind(
randomId(),
node.modifiers ? this.suffix(node.modifiers[node.modifiers.length - 1]) : this.prefix(node),
Markers.EMPTY,
[],
J.ClassDeclaration.Kind.Type.Enum
),
node.name ? this.convert(node.name) : this.mapIdentifier(node, ""),
null, // enum has no type parameters
null, // enum has no constructor
null, // enum can't extend smth.
null, // enum can't implement smth.
null,
new J.Block(
randomId(),
this.prefix(node.getChildren().find(v => v.kind === ts.SyntaxKind.OpenBraceToken)!),
Markers.EMPTY,
new JRightPadded(false, Space.EMPTY, Markers.EMPTY),
this.convertEnumBlock(node),
this.prefix(node.getLastToken()!)
),
this.mapType(node)
);
}

// EnumMembers (got from EnumDeclaration#members) have no information about Commas between them,
// so we should parse the EnumDeclaration body as a SyntaxList
// The enum body has the following structure: [EnumMember, Coma, EnumMember, ...],
// and we should manually figure out Commas positions
convertEnumBlock(enumDeclaration: ts.EnumDeclaration) {
if (enumDeclaration.members.length == 0) {
return [];
}

// if the enum is not empty and have the following representation [ ..., '{', EnumBody, '}']
// we access the enum body in the following way
const node = enumDeclaration.getChildren()[enumDeclaration.getChildCount() - 2];
const children = node.getChildren();
const childCount = children.length;
const enumMembers: JRightPadded<J.EnumValue>[] = [];
for (let i = 0; i < childCount; i++) {
if (children[i].kind === ts.SyntaxKind.EnumMember) {
const rp = new JRightPadded(
this.convert<J.EnumValue>(children[i]),
i + 1 < childCount ? this.prefix(children[i+1]) : Space.EMPTY,
i + 1 < childCount ? Markers.build([new TrailingComma(randomId(), Space.EMPTY)]) : Markers.EMPTY
);
enumMembers.push(rp);
}
}
return enumMembers;
}

visitModuleDeclaration(node: ts.ModuleDeclaration) {
Expand Down Expand Up @@ -1743,7 +1801,18 @@ export class JavaScriptParserVisitor {
}

visitEnumMember(node: ts.EnumMember) {
return this.visitUnknown(node);
if (node.initializer) {
return this.visitUnknown(node);
}

return new J.EnumValue(
randomId(),
this.prefix(node),
Markers.EMPTY,
[],
node.name ? this.convert(node.name) : this.mapIdentifier(node, ""),
null // FIXME should implement initializer
)
}

visitBundle(node: ts.Bundle) {
Expand Down
125 changes: 125 additions & 0 deletions openrewrite/test/javascript/parser/enum.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import {connect, disconnect, rewriteRun, typeScript} from '../testHarness';

describe('empty mapping', () => {
beforeAll(() => connect());
afterAll(() => disconnect());

test('enum declaration', () => {
rewriteRun(
//language=typescript
typeScript(`
enum Test {
};
`)
);
});

test('enum empty declaration with modifiers', () => {
rewriteRun(
//language=typescript
typeScript(`
declare const enum Test {
};
`)
);
});

test('enum member', () => {
rewriteRun(
//language=typescript
typeScript(`
enum Test {
A
};
`)
);
});

test('enum member with coma', () => {
rewriteRun(
//language=typescript
typeScript(`
enum Test {
A ,
};
`)
);
});

test('enum members', () => {
rewriteRun(
//language=typescript
typeScript(`
enum Test {
A,
B,
C
};
`)
);
});

test('enum with const modifier', () => {
rewriteRun(
//language=typescript
typeScript(`
const enum Test {
A,
B,
C,
};
`)
);
});

test('enum with declare modifier', () => {
rewriteRun(
//language=typescript
typeScript(`
declare enum Test {
A,
B,
C,
};
`)
);
});

test('enum with declare const modifier', () => {
rewriteRun(
//language=typescript
typeScript(`
declare const enum Test {
A,
B,
C,
};
`)
);
});

test('enum members with comments', () => {
rewriteRun(
//language=typescript
typeScript(`
enum Test /*xx*/ {
A /*aa*/,
/*bb*/ B /*cc*/,
C, /*dd*/
};
`)
);
});

test.skip('enum members with initializer', () => {
rewriteRun(
//language=typescript
typeScript(`
enum Test {
A = 'A',
B = 10
};
`)
);
});
});

0 comments on commit 375fb60

Please sign in to comment.