/** * @namespace JSDOC * @class Packer * Create a new packer * * Use with pack.js * * * Usage: * * var x = new JSON.Packer(); x.files = an array of files x.srcfiles = array of files (that list other files...) << not supported? x.target = "output.pathname.js" x.debugTarget = "output.pathname.debug.js" x.debugTranslateTarget : "/tmp/output.translate.js" << this used to be the single vs double quotes.. we may not use it in future.. x.translateJSON: "/tmp/translate.json", x.packAll(); // writes files etc.. * * * Notes for improving compacting: * if you add a jsdoc comment * * /** * eval:var:avarname * eval:var:bvarname * .... * * directly before an eval statement, it will compress all the code around the eval, * and not rename the variables 'avarname' * * Dont try running this on a merged uncompressed large file - it's used to be horrifically slow. not sure about now.. * Best to use lot's of small classes, and use it to merge, as it will cache the compaction * * * * Notes for translation * - translation relies on you using double quotes for strings if they need translating * - single quoted strings are ignored. * * Generation of indexFiles * - translateIndex = the indexfile * * * * */ const Packer = function(cfg) { XObject.extend(this, cfg); var _this = this; if (this.srcfiles && this.srcfiles.length) { this.srcfiles.forEach(function(f) { _this.loadSourceFile(f); }); } if (!this.files) { throw "No Files"; } var link = false; if (cfg.autoBuild) { function dateString(d){ function pad(n){return n<10 ? '0'+n : n} return d.getFullYear() + pad(d.getMonth()+1)+ pad(d.getDate())+'_'+ pad(d.getHours())+ pad(d.getMinutes())+ pad(d.getSeconds()); } var version = 0; this.files.forEach(function(f) { version = Math.max(File.mtime(f), version); }); var version = dateString(new Date(version)); var dirname = GLib.path_get_dirname(this.files[0]); var outname = this.module ? this.module : GLib.path_get_basename(dirname); this.target = dirname + '/compiled/' + outname + '-' + version + '.js'; if (File.exists(this.target)) { print("Target file already exists: " + this.target); Seed.quit(); } this.prefix = dirname +'/'; this.translateJSON = dirname + '/compiled/_translation_.js'; } print(this.translateJSON); this.timer = new Date() * 1; this.packAll(); } Packer.prototype = { /** * @cfg {String} srcfiles file containing a list of files/or classes to use. */ srcfile : false, /** * @cfg {Array} files list of files to compress (must be full path) */ files : false, /** * @cfg {String} target to write files to - must be full path. */ target : '', /** * @cfg {Boolean} autoBuild - turn on autobuild feature (puts files in compiled directory, * and enables translation toolkit. */ autoBuild : false, /** * @cfg {String} module used with autoBuild to force a file name */ module: false, /** * @cfg {String} debugTarget target to write files debug version to (uncompacted)- must be full path. */ debugTarget : '', // merged file without compression. /** * @cfg {String} debugTranslateTarget target to write files debug version * to (uncompacted) but with translation- must be full path. */ debugTranslateTarget : '', /** * @cfg {String} tmpDir (optional) where to put the temporary files. * if you set this, then files will not be cleaned up */ tmpDir : '/tmp', translateJSON : '', // json based list of strings in all files. /** * @cfg {Boolean} cleanup (optional) clean up temp files after done - * Defaults to false if you set tmpDir, otherwise true. */ cleanup : true, /** * @cfg {Boolean} keepWhite (optional) do not remove white space in output. * usefull for debugging compressed files. */ keepWhite: true, /** * @cfg {String} prefix (optional) prefix of directory to be stripped of when * Calculating md5 of filename */ prefix : '', out : '', // if no target is specified - then this will contain the result /** * load a dependancy list -f option * @param {String} srcfile sourcefile to parse * */ loadSourceFile : function(srcfile) { var lines = File.read(srcfile).split("\n"); var _this = this; lines.forEach(function(f) { if (/^\s*\//.test(f) || !/[a-z]+/i.test(f)) { // skip comments.. return; } if (/\.js$/.test(f)) { _this.files.push( f); // js file.. return; } //println("ADD"+ f.replace(/\./g, '/')); var add = f.replace(/\./g, '/').replace(/\s+/g,'')+'.js'; if (_this.files.indexOf(f) > -1) { return; } _this.files.push( add ); }) }, packAll : function() // do the packing (run from constructor) { //this.transOrigFile= bpath + '/../lang.en.js'; // needs better naming... //File.write(this.transfile, ""); if (this.target) { File.write(this.target, ""); } if (this.debugTarget) { File.write(this.debugTarget, ""); } if (this.debugTranslateTarget) { File.write(this.debugTarget, ""); } for(var i=0; i < this.files.length; i++) { var file = this.files[i]; print("reading " +file ); if (!File.isFile(file)) { print("SKIP (is not a file) " + file); continue; } // debug Target if (this.debugTarget) { File.append(this.debugTarget, File.read(file)); } // it's a good idea to check with 0 compression to see if the code can parse!! // debug file.. //File.append(dout, str +"\n"); var minfile = this.tmpDir + '/' +file.replace(/\//g, '.'); // let's see if we have a min file already? // this might happen if tmpDir is set .. if (true && File.exists(minfile)) { var mt = File.mtime(minfile); var ot = File.mtime(file); print("compare : " + mt + "=>" + ot); if (mt >= ot) { continue; } } print("COMPRESSING "); //var codeComp = pack(str, 10, 0, 0); if (File.exists(minfile)) { File.remove(minfile); } var str = File.read(file); var str = this.pack(str, file, minfile); } // if we are translating, write the translations strings at the top // of the file.. if (this.translateJSON) { print("MERGING LANGUAGE"); var out = "if (typeof(_T) == 'undefined') { _T={};}\n" if (this.target) { File.write(this.target, out); } else { this.out += out; } File.write(this.translateJSON, ""); for(var i=0; i < this.files.length; i++) { var file = this.files[i]; var transfile= this.tmpDir + '/' +file.replace(/\//g, '.') +'.lang.trans'; var transmd5 = this.tmpDir + '/' +file.replace(/\//g, '.') +'.lang'; if (File.exists(transmd5)) { var str = File.read(transmd5); if (str.length) { if (this.target) { File.append(this.target, str + "\n"); } else { this.out += str + "\n"; } } if (this.cleanup) { File.remove(transmd5); } } if (File.exists(transfile)) { var str = File.read(transfile); if (str.length) { File.append(this.translateJSON, str); } if (this.cleanup) { File.remove(transfile); } } } } print("MERGING SOURCE"); for(var i=0; i < this.files.length; i++) { var file = this.files[i]; var minfile = this.tmpDir + '/' + file.replace(/\//g, '.'); if (!File.exists(minfile)) { continue; } var str = File.read(minfile); print("using MIN FILE "+ minfile); if (str.length) { if (this.target) { File.append(this.target, '//' + file + "\n"); File.append(this.target, str + "\n"); } else { this.out += '//' + file + "\n"; this.out += str + "\n"; } } if (this.cleanup) { File.remove(minfile); } } print("Output file: " + this.target); if (this.debugTarget) print("Output debug file: " + this.debugTarget); }, /** * Core packing routine for a file * * @param str - str source text.. * @param fn - filename (for reference?) * @param minfile - min file location... * */ pack : function (str,fn,minfile) { var tr = new TokenReader( { keepDocs :true, keepWhite : true, keepComments : true, sepIdents : true, collapseWhite : false, filename : fn }); this.timerPrint("START" + fn); // we can load translation map here... var toks = tr.tokenize(new TextStream(str)); // dont merge xxx + . + yyyy etc. // at this point we can write a language file... if (this.translateJSON) { this.writeTranslateFile(fn, minfile, toks); } this.activeFile = fn; // and replace if we are generating a different language.. this.timerPrint("Tokenized"); //var ts = new TokenStream(toks); //print(JSON.stringify(toks, null,4 )); Seed.quit(); var ts = new Collapse(toks); // print(JSON.stringify(ts.tokens, null,4 )); Seed.quit(); //return;// var sp = new ScopeParser(ts); this.timerPrint("Converted to Parser"); sp.packer = this; sp.buildSymbolTree(); this.timerPrint("Built Sym tree"); sp.mungeSymboltree(); this.timerPrint("Munged Sym tree"); print(sp.warnings.join("\n")); this.timerPrint("Compressed"); var out = CompressWhite(new TokenStream(toks), this, this.keepWhite); // do not kill whitespace.. this.timerPrint("Compressed"); if (out.length) { File.write(minfile, out); this.timerPrint("Write (" + out.length + "bytes) " + minfile); } return out; }, timerPrint: function (str) { var ntime = new Date() * 1; var tdif = ntime -this.timer; this.timer = ntime; print('['+tdif+']'+str); }, /** * * Translation concept... * -> replace text strings with _T.... * -> this file will need inserting at the start of the application.... * -> we need to generate 2 files, * -> a reference used to do the translation, and the _T file.. * * * We store the trsum on the token... * */ writeTranslateFile : function(fn, minfile, toks) { var map = {}; // 'string=> md5sum' var _this = this; var t, last, next; var tokfind = function (j,dir) { while (1) { if ((dir < 0) && (j < 0)) { return false; } if ((dir > 0) && (j >= toks.length)) { return false; } j += dir; if (toks[j].type != 'WHIT') { return toks[j]; } } return false; } for (var i=0;i