JSDOC/Packer.js
[gnome.introspection-doc-generator] / JSDOC / Packer.js
1 // <script type="text/javascript">
2 XObject         = imports.XObject.XObject;
3 File            = imports.File.File;
4
5 TokenReader     = imports['JSDOC/TokenReader.js'].TokenReader;
6 ScopeParser     = imports['JSDOC/ScopeParser.js'].ScopeParser;
7 TokenStream     = imports['JSDOC/TokenStream.js'].TokenStream;
8 CompressWhite   = imports['JSDOC/CompressWhite.js'].CompressWhite;
9
10 GLib = imports.gi.GLib;
11 /**
12  * @namespace JSDOC
13  * @class  Packer
14  * Create a new packer
15  * 
16  * Usage:
17  * <code>
18  *
19 Packer = imports['JSDOC/Packer.js'].Packer;
20 var x = new  Packer(
21     [ "/location/of/file1.js", "/location/of/file2.js", ... ],
22     "/location/of"
23 );
24 x.packFiles(
25     "/location/of/temp_batch_dir", 
26     "/location/of/output-compacted-file.js",
27     "/location/of/output-debug-merged-file.js"
28 );
29     
30  *</code> 
31  *
32  * Notes for improving compacting:
33  *  if you add a jsdoc comment 
34  * <code>
35  * /**
36  *   eval:var:avarname
37  *   eval:var:bvarname
38  *   ....
39  * </code>
40  * directly before an eval statement, it will compress all the code around the eval, 
41  * and not rename the variables 'avarname'
42  * 
43  * Dont try running this on a merged uncompressed large file - it's horrifically slow.
44  * Best to use lot's of small classes, and use it to merge, as it will cache the compaction
45  * 
46  * 
47  * 
48  * @param {Array} files List of Files - MUST BE WITH ABSOLUTE PATH eg. [ '/usr/src/xyz/abc.js', .... ]
49  * @param {String} source_path top level directory of source (used to work out the relative names for the minimized temp files)
50  */
51 Packer = function(files, spath)
52 {
53     this.files = files;
54     this.spath  = spath; // source path
55     this.aliasList = { }; // list of maps Roo.asdfasdfasf => Roo.A1
56     this.timer =  new Date() * 1;
57     this.translate = true;
58 }
59 Packer.prototype = {
60     
61     bpath : '',
62     
63     // set to false to stop translation happening..
64     
65     /**
66      * Pack the files.
67      * 
68      * @param {String} batch_path location of batched temporary min files.
69      * @param {String} compressed_file eg. roo-all.js
70      * @param {String} debug_file eg. roo-debug.js
71      * 
72      */
73     
74     packFiles : function(bpath, allfile, debugfile) {
75         var str;
76         var spath = this.spath;
77         var files = this.files;
78         this.bpath = bpath;
79         // old var names - fixme..
80         var dout = debugfile;
81         //File.write(dout, "");
82         
83         var outpath = allfile;
84       
85         var transfile = bpath + '/_translation_.js';
86         //this.transOrigFile= bpath + '/../lang.en.js'; // needs better naming...
87         //File.write(this.transfile, "");
88         File.write(dout, "");
89         File.write(allfile, "");
90         for(var i=0; i < files.length; i++)  {
91             
92             print("reading " +files[i] );
93             if (!File.exists(files[i])) {
94                 print("SKIP (does not exist) " + files[i]);
95                 continue;
96             }
97            
98             
99             File.append(dout, File.read(files[i]));
100             // it's a good idea to check with 0 compression to see if the code can parse!!
101             
102             // debug file..
103             //File.append(dout, str +"\n"); 
104             
105        
106             
107             var minfile = bpath + '/' +files[i].substr(spath.length+1).replace(/\//g, '.');
108             var transfile = bpath + '/' +files[i].substr(spath.length+1).replace(/\//g, '.') +'.lang';        
109             // let's see if we have a min file already?
110             if (true && File.exists(minfile)) {
111                 var mt = File.mtime(minfile);
112                 var ot = File.mtime(files[i]);
113                 print("compare : " + mt + "=>" + ot);
114                 if (mt >= ot) {
115                     continue;
116                     /*
117                     // then the min'files time is > than original..
118                     var str = File.read(minfile);
119                     print("using MIN FILE  "+ minfile);
120                     if (str.length) {
121                         File.append(outpath, str + "\n");
122                     }
123                     
124                     continue;
125                     */
126                 }
127                 
128             }
129             
130             print("COMPRESSING ");
131             //var codeComp = pack(str, 10, 0, 0);
132             var str = File.read(files[i]);
133             var str = this.pack(str, files[i], minfile);
134             if (str.length) {
135                 File.write(minfile, str);   
136             }
137             
138              
139             
140             //var str = File.read(minfile);
141             //print("using MIN FILE  "+ minfile);
142             //File.append(outpath, str + "\n");
143             //this.timerPrint("Wrote Files");
144             /*
145             if (codeComp.length) {
146                 //print(codeComp);
147                 
148                 File.append(outpath, codeComp+"\n");
149                 File.write(minfile, codeComp);
150             }
151             */
152             //print(codeComp);
153            // if (i > 10) return;
154         }  
155         if (this.translate) {
156             
157                
158             print("MERGING LANGUAGE");
159             File.write(outpath, "if (typeof(_T) == 'undefined') { _T={};}\n");
160             
161             var transfileAll =  bpath + '/_translation_.js';
162             File.write(transfileAll, "");
163             for(var i=0; i < files.length; i++)  {
164                 var transfile= bpath + '/' +files[i].substr(spath.length+1).replace(/\//g, '.') +'.lang.trans';
165                 var transmd5 = bpath + '/' +files[i].substr(spath.length+1).replace(/\//g, '.') +'.lang';
166                 if (File.exists(transmd5)) {
167                     var str = File.read(transmd5);
168                     if (str.length) {
169                         File.append(outpath, str + "\n");
170                     }
171                 }
172                 if (File.exists(transfile)) {
173                     var str = File.read(transfile);
174                     if (str.length) {
175                         File.append(transfileAll, str);
176                     }
177                 }
178                 
179                
180             }
181         }
182         print("MERGING SOURCE");
183         
184         for(var i=0; i < files.length; i++)  {
185          
186             var minfile = bpath + '/' +files[i].substr(spath.length+1).replace(/\//g, '.');
187             if (!File.exists(minfile)) {
188                 continue;
189             }
190             var str = File.read(minfile);
191             print("using MIN FILE  "+ minfile);
192             if (str.length) {
193                 File.append(outpath, str + "\n");
194             }
195         }
196         
197         
198         //File.append(dout, "\n");// end the function 
199         
200     
201     
202     },
203     /**
204      * Core packing routine  for a file
205      * 
206      * @param str - str source text..
207      * @param fn - filename (for reference?)
208      * @param minfile - min file location...
209      * 
210      */
211     
212     pack : function (str,fn,minfile)
213     {
214     
215         var tr = new  TokenReader();
216         this.timerPrint("START" + fn);
217         
218         // we can load translation map here...
219         
220         var toks = tr.tokenize(str,false); // dont merge xxx + . + yyyy etc.
221         
222         // at this point we can write a language file...
223         if (this.translate) {
224             this.writeTranslateFile(fn, minfile, tr.translateMap);
225         }
226         
227         this.activeFile = fn;
228         
229         // and replace if we are generating a different language..
230         
231         
232         
233         
234         this.timerPrint("Tokenized");
235         //return;//
236         var sp = new ScopeParser(new TokenStream(toks, str.length));
237         this.timerPrint("Converted to Parser");
238         sp.packer = this;
239         sp.buildSymbolTree();
240         this.timerPrint("Built Sym tree");
241         sp.mungeSymboltree();
242         this.timerPrint("Munged Sym tree");
243         print(sp.warnings.join("\n"));
244         var out = JSDOC.CompressWhite(sp.ts, this);
245         this.timerPrint("Compressed");
246         return out;
247         
248     },
249     
250     timerPrint: function (str) {
251         var ntime = new Date() * 1;
252         var tdif =  ntime -this.timer;
253         this.timer = ntime;
254         print('['+tdif+']'+str);
255     },
256     
257     /**
258      * 
259      * Translation concept...
260      * -> replace text strings with _T....
261      * -> this file will need inserting at the start of the application....
262      * -> we need to generate 2 files, 
263      * -> a reference used to do the translation, and the _T file..
264      * 
265      */
266     
267     writeTranslateFile : function(fn, minfile, map) 
268     {
269         var transfile = minfile + '.lang.trans';
270         var transmd5 = minfile + '.lang';
271         var i = 0;
272         var v = '';
273         if (File.exists(transfile)) {
274             File.remove(transfile);
275         }
276         if (File.exists(transmd5)) {
277             File.remove(transmd5);
278         }
279         for(v in map) { i++; break };
280         if (!i ) {
281             return; // no strings in file...
282         }
283         var ff = fn.split('/');
284         var ffn = ff[ff.length-1];
285          
286          
287         File.write(transfile, "\n" + ffn.toSource() + " : {");
288         var l = '';
289         var _tout = {}
290          
291         File.write(transmd5, '');
292         for(v in map) {
293             File.append(transfile, l + "\n\t \"" + v + '" : "' + v + '"');
294             l = ',';
295             // strings are raw... - as the where encoded to start with!!!
296             File.append(transmd5, '_T["' + this.md5(ffn + '-' + v) + '"]="'+v+"\";\n");
297         }
298         File.append(transfile, "\n},"); // always one trailing..
299         
300          
301     },
302     md5 : function (string)
303     {
304         
305         return GLib.compute_checksum_for_string(GLib.ChecksumType.MD5, string, string.length);
306         
307     },
308     stringHandler : function(tok)
309     {
310         //print("STRING HANDLER");
311        // callback when outputing compressed file, 
312         if (!this.translate) {
313          //   print("TURNED OFF");
314             return tok.outData;
315         }
316         if (tok.qc != '"') {
317             return tok.outData;
318         }
319         var sval = tok.data.substring(1,tok.data.length-1);
320         // blank with tabs or spaces..
321         //if (!sval.replace(new RegExp("(\\\\n|\\\\t| )+",'g'), '').length) {
322        //     return tok.outData;
323        // }
324         
325         
326         
327         
328         var ff = this.activeFile.split('/');
329         var ffn = ff[ff.length-1];
330         return '_T["' + this.md5(ffn + '-' + sval) + '"]';
331         
332         
333     }
334     
335     
336 });