Skip to content

Commit f5b8c01

Browse files
authored
Correctly export decorated classes (#1153)
1 parent 31dda3b commit f5b8c01

File tree

4 files changed

+77
-10
lines changed

4 files changed

+77
-10
lines changed

src/transformation/utils/export.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ export function hasDefaultExportModifier(node: ts.Node): boolean {
1010
return (node.modifiers ?? []).some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword);
1111
}
1212

13-
export const createDefaultExportIdentifier = (original: ts.Node): lua.Identifier =>
14-
lua.createIdentifier("default", original);
13+
export function hasExportModifier(node: ts.Node): boolean {
14+
return (node.modifiers ?? []).some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword);
15+
}
1516

16-
export const createDefaultExportStringLiteral = (original: ts.Node): lua.StringLiteral =>
17+
export const createDefaultExportStringLiteral = (original?: ts.Node): lua.StringLiteral =>
1718
lua.createStringLiteral("default", original);
1819

1920
export function getExportedSymbolDeclaration(symbol: ts.Symbol): ts.Declaration | undefined {
@@ -142,3 +143,7 @@ export function createExportedIdentifier(
142143

143144
return lua.createTableIndexExpression(exportTable, lua.createStringLiteral(identifier.text));
144145
}
146+
147+
export function createDefaultExportExpression(node: ts.Node): lua.AssignmentLeftHandSideExpression {
148+
return lua.createTableIndexExpression(createExportsIdentifier(), createDefaultExportStringLiteral(node), node);
149+
}

src/transformation/visitors/class/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { FunctionVisitor, TransformationContext } from "../../context";
55
import { AnnotationKind, getTypeAnnotations } from "../../utils/annotations";
66
import { annotationRemoved } from "../../utils/diagnostics";
77
import {
8-
createDefaultExportIdentifier,
8+
createDefaultExportExpression,
99
createExportedIdentifier,
1010
hasDefaultExportModifier,
11+
hasExportModifier,
1112
isSymbolExported,
1213
} from "../../utils/export";
1314
import { createSelfIdentifier, unwrapVisitorResult } from "../../utils/lua-ast";
@@ -30,7 +31,7 @@ import { getExtendedNode, getExtendedType, isStaticNode } from "./utils";
3031
export const transformClassDeclaration: FunctionVisitor<ts.ClassLikeDeclaration> = (declaration, context) => {
3132
// If declaration is a default export, transform to export variable assignment instead
3233
if (hasDefaultExportModifier(declaration)) {
33-
const left = createExportedIdentifier(context, createDefaultExportIdentifier(declaration));
34+
const left = createDefaultExportExpression(declaration);
3435
const right = transformClassAsExpression(declaration, context);
3536
return [lua.createAssignmentStatement(left, right, declaration)];
3637
}
@@ -220,6 +221,15 @@ function transformClassLikeDeclaration(
220221
);
221222
const decoratingStatement = lua.createAssignmentStatement(localClassName, decoratingExpression);
222223
result.push(decoratingStatement);
224+
225+
if (hasExportModifier(classDeclaration)) {
226+
const exportExpression = hasDefaultExportModifier(classDeclaration)
227+
? createDefaultExportExpression(classDeclaration)
228+
: createExportedIdentifier(context, className);
229+
230+
const classAssignment = lua.createAssignmentStatement(exportExpression, localClassName);
231+
result.push(classAssignment);
232+
}
223233
}
224234

225235
superInfo.pop();

src/transformation/visitors/modules/export.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as lua from "../../../LuaAST";
33
import { assert } from "../../../utils";
44
import { FunctionVisitor, TransformationContext } from "../../context";
55
import {
6-
createDefaultExportIdentifier,
6+
createDefaultExportExpression,
77
createDefaultExportStringLiteral,
88
createExportedIdentifier,
99
} from "../../utils/export";
@@ -114,10 +114,9 @@ function transformExportSpecifier(context: TransformationContext, node: ts.Expor
114114
const exportedExpression = createShorthandIdentifier(context, exportedSymbol, exportedIdentifier);
115115

116116
const isDefault = isDefaultExportSpecifier(node);
117-
const identifierToExport = isDefault
118-
? createDefaultExportIdentifier(node)
119-
: transformIdentifier(context, node.name);
120-
const exportAssignmentLeftHandSide = createExportedIdentifier(context, identifierToExport);
117+
const exportAssignmentLeftHandSide = isDefault
118+
? createDefaultExportExpression(node)
119+
: createExportedIdentifier(context, transformIdentifier(context, node.name));
121120

122121
return lua.createAssignmentStatement(exportAssignmentLeftHandSide, exportedExpression, node);
123122
}

test/unit/classes/decorators.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,56 @@ describe("Decorators /w descriptors", () => {
184184
}
185185
);
186186
});
187+
188+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1149
189+
test("exported class with decorator", () => {
190+
util.testModule`
191+
import { MyClass } from "./other";
192+
const inst = new MyClass();
193+
export const result = inst.foo();
194+
`
195+
.addExtraFile(
196+
"other.ts",
197+
`function myDecorator(target: {new(): any}) {
198+
return class extends target {
199+
foo() {
200+
return "overridden";
201+
}
202+
}
203+
}
204+
205+
@myDecorator
206+
export class MyClass {
207+
foo() {
208+
return "foo";
209+
}
210+
}`
211+
)
212+
.expectToEqual({ result: "overridden" });
213+
});
214+
215+
test("default exported class with decorator", () => {
216+
util.testModule`
217+
import MyClass from "./other";
218+
const inst = new MyClass();
219+
export const result = inst.foo();
220+
`
221+
.addExtraFile(
222+
"other.ts",
223+
`function myDecorator(target: {new(): any}) {
224+
return class extends target {
225+
foo() {
226+
return "overridden";
227+
}
228+
}
229+
}
230+
231+
@myDecorator
232+
export default class {
233+
foo() {
234+
return "foo";
235+
}
236+
}`
237+
)
238+
.expectToEqual({ result: "overridden" });
239+
});

0 commit comments

Comments
 (0)