@@ -37,6 +37,7 @@ export abstract class LuaTranspiler {
3737 public isModule : boolean ;
3838 public sourceFile : ts . SourceFile ;
3939 public loopStack : number [ ] ;
40+ public classStack : string [ ] ;
4041
4142 constructor ( checker : ts . TypeChecker , options : ts . CompilerOptions , sourceFile : ts . SourceFile ) {
4243 this . indent = "" ;
@@ -49,6 +50,7 @@ export abstract class LuaTranspiler {
4950 this . sourceFile = sourceFile ;
5051 this . isModule = tsHelper . isFileModule ( sourceFile ) ;
5152 this . loopStack = [ ] ;
53+ this . classStack = [ ] ;
5254 }
5355
5456 public pushIndent ( ) : void {
@@ -131,28 +133,19 @@ export abstract class LuaTranspiler {
131133 // Shadow exports if it already exists
132134 result += "local exports = exports or {}\n" ;
133135 }
134- result += this . transpileBlock ( this . sourceFile ) ;
136+
137+ // Transpile content statements
138+ this . sourceFile . statements . forEach ( s => result += this . transpileNode ( s ) ) ;
139+
135140 if ( this . isModule ) {
136141 result += "return exports\n" ;
137142 }
138143 return result ;
139144 }
140145
141146 // Transpile a block
142- public transpileBlock ( node : ts . Node ) : string {
143- let result = "" ;
144-
145- if ( ts . isBlock ( node ) ) {
146- node . statements . forEach ( statement => {
147- result += this . transpileNode ( statement ) ;
148- } ) ;
149- } else {
150- node . forEachChild ( child => {
151- result += this . transpileNode ( child ) ;
152- } ) ;
153- }
154-
155- return result ;
147+ public transpileBlock ( block : ts . Block ) : string {
148+ return block . statements . map ( statement => this . transpileNode ( statement ) ) . join ( "" ) ;
156149 }
157150
158151 // Transpile a node of unknown kind.
@@ -857,10 +850,9 @@ export abstract class LuaTranspiler {
857850
858851 // Handle super calls properly
859852 if ( node . expression . kind === ts . SyntaxKind . SuperKeyword ) {
860- callPath = this . transpileExpression ( node . expression ) ;
861- params =
862- this . transpileArguments ( node . arguments , ts . createNode ( ts . SyntaxKind . ThisKeyword ) as ts . Expression ) ;
863- return `self.__base.constructor(${ params } )` ;
853+ params = this . transpileArguments ( node . arguments , ts . createNode ( ts . SyntaxKind . ThisKeyword ) as ts . Expression ) ;
854+ const className = this . classStack [ this . classStack . length - 1 ] ;
855+ return `${ className } .__base.constructor(${ params } )` ;
864856 }
865857
866858 callPath = this . transpileExpression ( node . expression ) ;
@@ -1288,60 +1280,17 @@ export abstract class LuaTranspiler {
12881280
12891281 // Transpile a class declaration
12901282 public transpileClass ( node : ts . ClassDeclaration ) : string {
1291- // Find extends class, ignore implements
1292- let extendsType : ts . ExpressionWithTypeArguments | undefined ;
1293- let noClassOr = false ;
1294- if ( node . heritageClauses ) { node . heritageClauses . forEach ( clause => {
1295- if ( clause . token === ts . SyntaxKind . ExtendsKeyword ) {
1296- const superType = this . checker . getTypeAtLocation ( clause . types [ 0 ] ) ;
1297- // Ignore purely abstract types (decorated with /** @PureAbstract */)
1298- if ( ! tsHelper . isPureAbstractClass ( superType , this . checker ) ) {
1299- extendsType = clause . types [ 0 ] ;
1300- }
1301- noClassOr = tsHelper . hasCustomDecorator ( superType , this . checker , "!NoClassOr" ) ;
1302- }
1303- } ) ;
1304- }
1305-
13061283 if ( ! node . name ) {
1307- throw new TranspileError ( "Unexpected Error: Node has no Name " , node ) ;
1284+ throw new TranspileError ( "Class declaration has no name. " , node ) ;
13081285 }
13091286
13101287 let className = node . name . escapedText as string ;
1311- let result = "" ;
13121288
1313- // Skip header if this is an extension class
1289+ // Find out if this class is extension of exising class
13141290 const isExtension = tsHelper . isExtensionClass ( this . checker . getTypeAtLocation ( node ) , this . checker ) ;
1315- if ( ! isExtension ) {
1316- // Write class declaration
1317- const classOr = noClassOr ? "" : `${ className } or ` ;
1318- if ( ! extendsType ) {
1319- result += this . indent + this . accessPrefix ( node ) + `${ className } = ${ classOr } {}\n` ;
1320- result += this . makeExport ( className , node ) ;
1321- } else {
1322- const baseName = ( extendsType . expression as ts . Identifier ) . escapedText ;
1323- result += this . indent + this . accessPrefix ( node ) + `${ className } = ${ classOr } ${ baseName } .new()\n` ;
1324- result += this . makeExport ( className , node ) ;
1325- }
1326- result += this . indent + `${ className } .__index = ${ className } \n` ;
1327- if ( extendsType ) {
1328- const baseName = ( extendsType . expression as ts . Identifier ) . escapedText ;
1329- result += this . indent + `${ className } .__base = ${ baseName } \n` ;
1330- }
1331- result += this . indent + `function ${ className } .new(construct, ...)\n` ;
1332- result += this . indent + ` local instance = setmetatable({}, ${ className } )\n` ;
1333- result += this . indent + ` if construct and ${ className } .constructor then `
1334- + `${ className } .constructor(instance, ...) end\n` ;
1335- result += this . indent + ` return instance\n` ;
1336- result += this . indent + `end\n` ;
1337- } else {
1338- // export empty table
1339- result += this . makeExport ( className , node , true ) ;
1340- // Overwrite the original className with the class we are overriding for extensions
1341- if ( extendsType ) {
1342- className = ( extendsType . expression as ts . Identifier ) . escapedText as string ;
1343- }
1344- }
1291+
1292+ // Get type that is extended
1293+ const extendsType = tsHelper . getExtendedType ( node , this . checker ) ;
13451294
13461295 // Get all properties with value
13471296 const properties = node . members . filter ( ts . isPropertyDeclaration )
@@ -1352,6 +1301,20 @@ export abstract class LuaTranspiler {
13521301 const staticFields = properties . filter ( isStatic ) ;
13531302 const instanceFields = properties . filter ( prop => ! isStatic ( prop ) ) ;
13541303
1304+ let result = "" ;
1305+
1306+ if ( ! isExtension ) {
1307+ result += this . transpileClassCreationMethods ( node , instanceFields , extendsType ) ;
1308+ } else {
1309+ // export empty table
1310+ result += this . makeExport ( className , node , true ) ;
1311+ }
1312+
1313+ // Overwrite the original className with the class we are overriding for extensions
1314+ if ( isExtension && extendsType ) {
1315+ className = extendsType . symbol . escapedName as string ;
1316+ }
1317+
13551318 // Add static declarations
13561319 for ( const field of staticFields ) {
13571320 const fieldName = ( field . name as ts . Identifier ) . escapedText ;
@@ -1363,14 +1326,11 @@ export abstract class LuaTranspiler {
13631326 const constructor = node . members . filter ( ts . isConstructorDeclaration ) [ 0 ] ;
13641327 if ( constructor ) {
13651328 // Add constructor plus initialisation of instance fields
1366- result += this . transpileConstructor ( constructor , className , instanceFields ) ;
1329+ result += this . transpileConstructor ( constructor , className ) ;
13671330 } else if ( ! isExtension ) {
13681331 // Generate a constructor if none was defined
1369- result += this . transpileConstructor (
1370- ts . createConstructor ( [ ] , [ ] , [ ] , ts . createBlock ( [ ] , true ) ) ,
1371- className ,
1372- instanceFields
1373- ) ;
1332+ result += this . transpileConstructor ( ts . createConstructor ( [ ] , [ ] , [ ] , ts . createBlock ( [ ] , true ) ) ,
1333+ className ) ;
13741334 }
13751335
13761336 // Transpile get accessors
@@ -1391,6 +1351,50 @@ export abstract class LuaTranspiler {
13911351 return result ;
13921352 }
13931353
1354+ public transpileClassCreationMethods ( node : ts . ClassDeclaration , instanceFields : ts . PropertyDeclaration [ ] ,
1355+ extendsType : ts . Type ) : string {
1356+ const className = node . name . escapedText as string ;
1357+
1358+ const noClassOr = extendsType && tsHelper . hasCustomDecorator ( extendsType , this . checker , "!NoClassOr" ) ;
1359+
1360+ let result = "" ;
1361+
1362+ // Write class declaration
1363+ const classOr = noClassOr ? "" : `${ className } or ` ;
1364+ if ( ! extendsType ) {
1365+ result += this . indent + this . accessPrefix ( node ) + `${ className } = ${ classOr } {}\n` ;
1366+ result += this . makeExport ( className , node ) ;
1367+ } else {
1368+ const baseName = extendsType . symbol . escapedName ;
1369+ result += this . indent + this . accessPrefix ( node ) + `${ className } = ${ classOr } ${ baseName } .new()\n` ;
1370+ result += this . makeExport ( className , node ) ;
1371+ }
1372+ result += this . indent + `${ className } .__index = ${ className } \n` ;
1373+ if ( extendsType ) {
1374+ const baseName = extendsType . symbol . escapedName ;
1375+ result += this . indent + `${ className } .__base = ${ baseName } \n` ;
1376+ }
1377+ result += this . indent + `function ${ className } .new(construct, ...)\n` ;
1378+ result += this . indent + ` local instance = setmetatable({}, ${ className } )\n` ;
1379+ result += this . indent + ` if construct and ${ className } .constructor then `
1380+ + `${ className } .constructor(instance, ...) end\n` ;
1381+
1382+ for ( const f of instanceFields ) {
1383+ // Get identifier
1384+ const fieldIdentifier = f . name as ts . Identifier ;
1385+ const fieldName = fieldIdentifier . escapedText ;
1386+
1387+ const value = this . transpileExpression ( f . initializer ) ;
1388+
1389+ result += this . indent + ` instance.${ fieldName } = ${ value } \n` ;
1390+ }
1391+
1392+ result += this . indent + ` return instance\n` ;
1393+ result += this . indent + `end\n` ;
1394+
1395+ return result ;
1396+ }
1397+
13941398 public transpileGetAccessorDeclaration ( getAccessor : ts . GetAccessorDeclaration , className : string ) : string {
13951399 const name = ( getAccessor . name as ts . Identifier ) . escapedText ;
13961400
@@ -1425,8 +1429,7 @@ export abstract class LuaTranspiler {
14251429 }
14261430
14271431 public transpileConstructor ( node : ts . ConstructorDeclaration ,
1428- className : string ,
1429- instanceFields : ts . PropertyDeclaration [ ] ) : string {
1432+ className : string ) : string {
14301433 const extraInstanceFields = [ ] ;
14311434
14321435 const parameters = [ "self" ] ;
@@ -1446,19 +1449,11 @@ export abstract class LuaTranspiler {
14461449 result += this . indent + ` self.${ f } = ${ f } \n` ;
14471450 }
14481451
1449- for ( const f of instanceFields ) {
1450- // Get identifier
1451- const fieldIdentifier = f . name as ts . Identifier ;
1452- const fieldName = fieldIdentifier . escapedText ;
1453-
1454- const value = this . transpileExpression ( f . initializer ) ;
1455-
1456- result += this . indent + ` self.${ fieldName } = ${ value } \n` ;
1457- }
1458-
14591452 // Transpile constructor body
14601453 this . pushIndent ( ) ;
1454+ this . classStack . push ( className ) ;
14611455 result += this . transpileBlock ( node . body ) ;
1456+ this . classStack . pop ( ) ;
14621457 this . popIndent ( ) ;
14631458
14641459 return result + this . indent + "end\n" ;
@@ -1512,7 +1507,7 @@ export abstract class LuaTranspiler {
15121507 let result = `function(${ paramNames . join ( "," ) } )\n` ;
15131508 this . pushIndent ( ) ;
15141509 result += this . transpileParameterDefaultValues ( defaultValueParams ) ;
1515- result += this . transpileBlock ( node . body ) ;
1510+ result += this . transpileBlock ( node . body as ts . Block ) ;
15161511 this . popIndent ( ) ;
15171512 return result + this . indent + "end\n" ;
15181513 } else {
0 commit comments