├── README.md └── ts2haxe.js /README.md: -------------------------------------------------------------------------------- 1 | TypeScript to Haxe Convertor 2 | =========================== 3 | This script help converting TypeScript code to haxe. 4 | it's not a compiler it only automate common conversion tasks allowing you to easily target multiple platforms from your TS code (via haxe multi-platform support) 5 | 6 | Usage 7 | --------------------- 8 | 9 | ```js 10 | node ts2haxe MyClass.ts MyClass.hx 11 | ``` 12 | 13 | What's implemented 14 | ------------------ 15 | 16 | * Types conversion (bool => Bool, any => Dynamic ...etc) 17 | * convert TypeScript module encapsulation to Haxe package header 18 | * class variables 19 | * constructors 20 | * functions 21 | * unfold for loops to while loops since haxe does not support JS for loop syntax 22 | 23 | 24 | TODO LIST 25 | --------- 26 | 27 | * convert setTimer/setInterval syntax to Timer/Timer.delay. 28 | * add tests and samples 29 | 30 | feel free to post new [features requests](https://github.com/Ezelia/ts2haxe/issues) . 31 | 32 | 33 | Help to improve ts2haxe 34 | ----------------------- 35 | 36 | to help improving this script please report any issue with tast cases to the [Issue Tracker](https://github.com/Ezelia/ts2haxe/issues) . 37 | you are also welcome to fork and make pull requests to enhance/fix ts2haxe 38 | 39 | 40 | License 41 | ------- 42 | ``` 43 | // MIT License 44 | // Copyright(c) 2013 Ezelia.com and other contributors 45 | // Author : Alaa-eddine KADDOURI 46 | // 47 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 48 | // this software and associated documentation files(the 'Software'), to deal in the 49 | // Software without restriction, including without limitation the rights to use, 50 | // copy, modify, merge, publish, distribute, sublicense, and / or sell copies of 51 | // the Software, and to permit persons to whom the Software is furnished to do so, 52 | // subject to the following conditions: 53 | // 54 | // The above copyright notice and this permission notice shall be included in 55 | // all copies or substantial portions of the Software. 56 | // 57 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 58 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 59 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 60 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 61 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 62 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 63 | // IN THE SOFTWARE. 64 | ``` -------------------------------------------------------------------------------- /ts2haxe.js: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // Copyright(c) 2013 Ezelia.com and other contributors 3 | // Author : Alaa-eddine KADDOURI (alaa.eddine@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files(the 'Software'), to deal in the 7 | // Software without restriction, including without limitation the rights to use, 8 | // copy, modify, merge, publish, distribute, sublicense, and / or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | // IN THE SOFTWARE. 22 | 23 | 24 | 25 | 26 | var fs = require('fs'); 27 | var path = require('path'); 28 | 29 | var infile, outfile; 30 | 31 | 32 | 33 | console.log( 34 | '\n ts2haxe v0.1 - a TypeScript to Haxe converter.\n'+ 35 | ' Author - Alaa-eddine KADDOURI\n'+ 36 | ' \n'); 37 | 38 | var args = process.argv.slice(2); 39 | var scriptName = 'node '+path.basename(__filename); 40 | if (args.length == 0) 41 | { 42 | console.log( 43 | ' usage : '+scriptName+ ' inputfile \n\n'+ 44 | ' inputfile : is the file you want to generate jsdoc for.\n'+ 45 | ' outputfile : (optional) the resulting file is sent to outputfile, otherwise will overwrite inputfile .\n'+ 46 | ' \n'+ 47 | ' *Example : '+scriptName+' myClass.ts'); 48 | process.exit(); 49 | } 50 | infile = args[0]; 51 | outfile = args[1] || infile+'.hx'; 52 | 53 | 54 | console.log(infile, ' => ', outfile); 55 | 56 | var data = fs.readFileSync(infile, 'utf-8'); 57 | 58 | 59 | //List of TS reserved words (actually, they are JS reserved words) 60 | var jsReservedWords = [ 61 | 'abstract', 'else', 'instanceof', 'super', 62 | 'boolean', 'enum', 'int', 'switch', 63 | 'break', 'export', 'interface', 'synchronized', 64 | 'byte', 'extends', 'let', 'this', 65 | 'case', 'false', 'long', 'throw', 66 | 'catch', 'final', 'native', 'throws', 67 | 'char', 'finally', 'new', 'transient', 68 | 'class', 'float', 'null', 'true', 69 | 'const', 'for', 'package', 'try', 70 | 'continue', 'function', 'private', 'typeof', 71 | 'debugger', 'goto', 'protected', 'var', 72 | 'default', 'if', 'public', 'void', 73 | 'delete', 'implements', 'return', 'volatile', 74 | 'do', 'import', 'short', 'while', 75 | 'double', 'in', 'static', 'with' 76 | ]; 77 | 78 | 79 | var ts2hxTypes = { 80 | 'void' :'Void', 81 | 'number' :'Float', 82 | 'int' :'Int', 83 | 'float' :'Float', 84 | 'bool' :'Bool', 85 | 'string' :'String', 86 | 'any' :'Dynamic' 87 | } 88 | /* 89 | var hxTypes = [ 90 | 'Void', 91 | 'Float', 92 | 'Int', 93 | 'Float', 94 | 'Bool', 95 | 'String', 96 | 'Dynamic' 97 | ]; 98 | */ 99 | function getCodeBlock(data, startIndex, open, close) 100 | { 101 | var result=''; 102 | var stack=[]; 103 | for (var i=startIndex; i= 0) return original; 170 | 171 | console.log(' Function : ', fnName); 172 | 173 | if (fnName == 'constructor') 174 | { 175 | fnName = 'new'; 176 | visibility = 'public '; 177 | } 178 | 179 | 180 | var args = ''; 181 | if (params) 182 | { 183 | //parse function's parameters 184 | args = params.replace(/(public\s|private\s|static\s)*\s*([a-zA-Z_$][^:]+)\s*:\s*([a-zA-Z0-9_\$\[\]]+)\s*([^,]*)([\,]{0,1})/g, function() { 185 | var name = arguments[2].replace(/\s/g, ''); 186 | if (name.indexOf('?') == name.length-1) name = '?'+name.substr(0, name.length-1); 187 | 188 | var type = arguments[3]; 189 | if (ts2hxTypes[type]) type = ts2hxTypes[type]; 190 | //if (ts2hxTypes.indexOf(type) >= 0) type = hxTypes[tsTypes.indexOf(type)]; 191 | 192 | var defValue = arguments[4]; 193 | var sep = arguments[5]; 194 | //console.log(arguments); 195 | //return indent+'* @param '+name + ' {'+type+'} \n'; 196 | return name+':'+type+sep+' '; 197 | }); 198 | } 199 | 200 | 201 | //if (tsTypes.indexOf(ret) >= 0) ret = hxTypes[tsTypes.indexOf(ret)]; 202 | if (ts2hxTypes[ret]) ret = ts2hxTypes[ret]; 203 | 204 | if (ret) ret = ' : '+ret; 205 | ret += ' {'; 206 | 207 | 208 | return '\n'+indent+visibility+'function '+fnName+'('+args+')'+ret; 209 | 210 | }); 211 | return result; 212 | } 213 | 214 | function convertVars(data) 215 | { 216 | //FIXME : this regex will not match class variables if they are not prefixed with public/private; 217 | var result = data.replace(/([\n\r\s]*)(public\s|private\s|static\s)*([,:]*)\s*([a-zA-Z_$][^=:;,.{}\(\)]+)\s*:\s*([a-zA-Z_$\{\}\s\.][^=:;\(\)]*)\s*(\=[^;,:]*)*;/g, function() { 218 | var original = arguments[0]; 219 | var indent = '\n'+arguments[1].replace(/(\r\n|\n|\r)/gm,""); //get original indentation 220 | var visibility = arguments[2]||'public '; //public / private 221 | var fakePositive = arguments[3]; //var name 222 | var varName = arguments[4]; //var name 223 | var varType = arguments[5]||''; 224 | var defVal = arguments[6]; 225 | 226 | var comment = original.match(/([\n\r\s]*)\/\/(.+)/g) 227 | if (comment || fakePositive) return original; 228 | 229 | //console.log('*'+indent+'* ==> *'+varSep+'*'); 230 | 231 | //console.log('* ',visibility, varName, varType,defVal); 232 | console.log(' Var : %s, type= %s, default value= %s',varName, varType, defVal); 233 | 234 | if (ts2hxTypes[varType]) varType = ts2hxTypes[varType]; 235 | 236 | return indent+visibility+'var '+varName+' : '+varType+(defVal?defVal:'') + ';'; 237 | 238 | }); 239 | return result; 240 | } 241 | 242 | function convertForLoops(data) 243 | { 244 | 245 | var indent=''; 246 | var rePattern =/([\n\r\s]*)(\/\/.*)*for\s*\(([^;]*);([^;]*);([^{]*){/g 247 | //var matches = data.match(/([\n\r\s]*)for\s*\(([^;]*);([^;]*);([^{]*){/g); 248 | 249 | 250 | while (matches = rePattern.exec(data)) 251 | { 252 | 253 | if (!matches || !matches[0]) continue; 254 | 255 | //is it a comment ? FIXME : find better way to detect comments 256 | var comment = matches[0].match(/([\n\r\s]*)\/\/(.+)/g) 257 | if (comment) continue; 258 | 259 | //console.log( matches[0]); 260 | 261 | 262 | 263 | var block = getCodeBlock(data, matches.index, '{', '}'); 264 | var forblock = getCodeBlock(block, 0, '(', ')'); //need trim ? 265 | 266 | 267 | var init=''; 268 | var cond=''; 269 | var inc=''; 270 | 271 | var status=0; 272 | var i=0; 273 | 274 | for (i=0; i= forblock.lastIndexOf(')')) 292 | { 293 | status++; 294 | continue; 295 | } 296 | 297 | inc+=forblock[i]; 298 | break; 299 | } 300 | } 301 | 302 | var indent = '\n'+matches[1].replace(/(\r\n|\n|\r)/gm,""); //get original indentation 303 | 304 | var whileBlock = ''; 305 | whileBlock += indent+'// haxe does not support for loops with C/JS syntaxt ... unfolding : '; 306 | whileBlock += indent+'// '+forblock.replace(/(\r\n|\n|\r)/gm,"").replace(/(\s+)/gm," "); 307 | whileBlock += indent+init+';'; 308 | whileBlock += indent+'while('+cond+') /*#FORLOOP#*//*#'+inc+'#*/'; 309 | 310 | var newBlock = block.replace(forblock, whileBlock); 311 | 312 | 313 | 314 | data = data.substr(0, matches.index) + newBlock + data.substr(matches.index+block.length); 315 | 316 | } 317 | //data = data.replace(block, newBlock); 318 | 319 | 320 | 321 | // return data; 322 | // var result = data.replace(/([\n\r\s]*)for\s*\(([^;]*);([^;]*);([^{]*){/g, function() { 323 | // var original = arguments[0].replace(/(\r\n|\n|\r|\s)/gm,""); 324 | // indent = arguments[1].replace(/(\r\n|\n|\r)/gm,""); //get original indentation 325 | // var init = arguments[2]; //public / private 326 | // var cond = arguments[3]; //var name 327 | // var inc = arguments[4]; 328 | // console.log('* ',arguments); 329 | // console.log(' *ForLoop %s|%s|%s',init, inc, cond); 330 | // 331 | // var r = '\n'; 332 | // r += indent+'// haxe does not support for loop C/JS syntaxt unfolding : \n'; 333 | // r += indent+'// '+original+'\n'; 334 | // r += indent+init+';\n'; 335 | // r += indent+'while('+cond+') { /*#FORLOOP#*//*#'+inc+'#*/'; 336 | // return r; 337 | // }); 338 | 339 | var result = data; 340 | while (result.indexOf('/*#FORLOOP#*/') > 0) 341 | { 342 | 343 | var stack=[]; 344 | var start = result.indexOf('/*#FORLOOP#*/') 345 | result = result.substr(0, start)+result.substr(start+13, result.length); 346 | var curpos = 0; 347 | start+= 3; //start after /*# 348 | var inc=''; 349 | var gotInc = false; 350 | for (var i=start; i