/** * @namespace JSDOC * @class Packer * Create a new packer * * Use with pack.js * * * Usage: * * var x = new JSON.Packer(target, debugTarget); 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 * * * * */ namespace JSDOC { public class Packer : Object { /** * @cfg {String} target to write files to - must be full path. */ string target; /** * @cfg {String} debugTarget target to write files debug version to (uncompacted)- must be full path. */ string debugTarget; /** * @cfg {String} tmpDir (optional) where to put the temporary files. * if you set this, then files will not be cleaned up */ public string tmpDir = "/tmp"; // FIXME??? in ctor? /** * @cfg {Boolean} cleanup (optional) clean up temp files after done - * Defaults to false if you set tmpDir, otherwise true. */ public bool cleanup = true; /** * @cfg {Boolean} keepWhite (optional) do not remove white space in output. * usefull for debugging compressed files. */ public bool keepWhite = true; // list of files to compile... Gee.ArrayList files; public string out = ""; // if no target is specified - then this will contain the result public Packer(string target, string debugTarget) { this.target = target; this.debugTarget = debugTarget; } public void loadSourceIndexes(Gee.ArrayList indexes) { foreach(var f in indexes) { this.loadSourceIndex(f); } } public void loadFiles(Gee.ArrayList fs) { foreach(var f in fs) { this.files.add(f); //?? easier way? } } FileOutputStream targetStream = null; FileOutputStream targetDebugStream = null; public void pack() { if (!this.files) { throw new Packer.ArgumentError("No Files loaded before pack() called"); } if (this.target.length > 0 ) { this.targetStream = File.new_for_path(this.target).replace(null, false,FileCreateFlags.NONE); } if (this.targetDebug.length > 0 ) { this.targetDebugStream = File.new_for_path(this.targetDebug).replace(null, false,FileCreateFlags.NONE); } this.packAll(); } /** * load a dependancy list -f option * @param {String} srcfile sourcefile to parse * */ public void loadSourceIndex(string srcfile) { string str; FileUtils.get_contents(srcfile,out str); var lines = str.split("\n"); for(var i =0; i < lines.length;i++) { var f = lines[i].strip(); if (f.length < 1 || Regex.match_simple ("^\/", f) || !Regex.match_simple ("[a-zA-Z]+", f) ){ continue; // blank comment or not starting with a-z } if (Regex.match_simple ("\.js$", f)) { this.files.add( f); // js file.. continue; } // this maps Roo.bootstrap.XXX to Roo/bootstrap/xxx.js // should we prefix? =- or should this be done elsewhere? var add = f.replace(".", "/") + ".js"; if (_this.files.contains(add)) { continue; } _this.files.add( add ); } } private void packAll() // do the packing (run from constructor) { //this.transOrigFile= bpath + '/../lang.en.js'; // needs better naming... //File.write(this.transfile, ""); if (this.target.length) { 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.packFile(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... * */ packFile : 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