sync fixes
[app.jsdoc] / JSDOC / BuildDocs.js
1          
2 Gio = imports.gi.Gio;
3
4 XObject      = imports.XObject.XObject;
5 File        = imports.File.File;
6
7
8
9 Template     = imports.JsTemplate.Template.Template;
10 Link        = imports.JsTemplate.Link.Link; // ?? fixme!??
11
12       
13    
14 /**
15  * This is the main container for the JSDOC application.
16  *
17  * <PRE>
18 usage::
19     
20     BuildDocs = imports.JSDOC.BuildDocs.BuildDocs
21     
22     new BuildDocs(opts)
23     
24     ..
25     
26     or command line:
27     
28     new ArgsParser(argv, imports.BuildDocs.BuildDocs )
29  * </PRE>
30  *
31  *
32  * What it does..
33  *  - tokenizes, collapse, then runs each file through ScopeNamer.
34  *  - this is cached (so reparsing is not always needed)
35  *  This in turn builds 'symbolSet' - which
36  *  - after all symbols are read - symbolSet.relate(); is called.
37  *
38  
39  
40  
41  * @scope JSDOC
42  * @class  BuildDocs
43  *
44  * Main class used to build documentation
45  */
46 BuildDocs = XObject.define(
47     function(opts) {
48         XObject.extend(this, opts); // overlay cfg..
49         //print(JSON.stringify(this, null,4));
50         if (!this.LOG) {
51             this.LOG = imports.JSDOC.Log.Log;
52         }
53         
54         imports.SymbolSet.SymbolSet.LOG = this.LOG
55         /*this.parser = new imports.JSDOC.Parser.Parser({
56             LOG : this.LOG,
57             docPrivate : this.docPrivate
58
59         });
60         */
61         imports.ScopeNamer.ScopeNamer.builder = this;
62         
63         this.build();
64     
65     },
66     Object,
67     {
68         
69         
70         /**
71          * @cfg {Object} LOG  an implementation of the log interface..
72          */
73         LOG: false,
74         
75          /**
76          * @cfg  {String} baseDir root directory (for relative publishing?)
77          */
78         baseDir: false,
79         
80         
81          /**
82          * @cfg {String} cacheDirectory  where to store the cached parse.
83          */
84         cacheDirectory: false,
85         
86         /**
87          * @cfg {String} ext  comma-delimited list of extensions to handle..
88          */
89         ext: 'js' ,
90          
91         /**
92          * @cfg {String} lang Language to parse (eg. js / php ...)
93          */
94         lang: 'js' ,
95         
96         /**
97          * @cfg  {Array} src base source files/directories.
98          */
99         src : false,
100         
101         /**
102          * @cfg  {Array} excludeSrc base source files/directories.
103          */
104         excludeSrc : false,
105          /**
106          * @cfg  {String} target where to write the output to.
107          */
108         
109         target : false,
110         
111         
112         
113         /**
114          * @cfg  {String} templateDir where get the templates from.
115          */
116         templateDir : false,
117          /**
118          * @cfg {String} publishExt  output extenstion.
119          */
120         
121         publishExt : 'html',
122         
123         
124           /**
125          * @cfg {Boolean} docPrivate  document private variables.
126          */
127         
128         docPrivate : false,
129         
130           /**
131          * @cfg {String}  roojs Path to roojs installation.
132          */
133         roojs : 'http://roojs.com/roojs1/',
134         
135         
136           /**
137          * @cfg {Boolean} ignoreNamespace  should the memberOf be stripped for seed classes
138          */
139         ignoreNamespace : false,
140         
141         /**
142          * @cfg {Boolean} treatUnderscoredAsPrivate automatically flag underscore prefixed properties as private.
143          */
144         treatUnderscoredAsPrivate : true,
145        
146         /**
147          * Full list of source files.
148          * @private
149          */
150         srcFiles : false,
151         
152          
153         
154         /**
155          * the symbolset
156          * @private
157          */
158         
159         symbolSet: false,
160         /**
161          * FIXME
162          * @private
163          */
164         
165         VERSION : "2.0.0",
166         
167         
168         /**
169          * start the build process
170          */
171         build : function ()
172         {
173             
174             
175             this.LOG.inform("JsDoc Toolkit main() running at "+new Date()+".");
176            
177             
178             if (this.cacheDirectory.length && !File.isDirectory(this.cacheDirectory)) {   
179                 File.mkdir(this.cacheDirectory)
180             }
181             
182             this.srcFiles = this._getSrcFiles();
183              this._parseSrcFiles();
184             this.symbolSet  = imports.ScopeNamer.ScopeNamer.symbols;
185             //this.symbolSet = this.parser.symbols;
186             
187             // this currently uses the concept of publish.js...
188             
189             this.publish();
190              
191             
192             
193         },
194         /**
195          * create a list of files in this.srcFiles using list of directories / files in Options.src
196          * @private
197          */
198         
199         _getSrcFiles : function() 
200         {
201              var _this = this;
202             var ret= [];
203             
204             var ext = this.ext.split(",").map(function($) {return $.toLowerCase()});
205             
206             this.src.forEach(function(src_in) {
207                 // add to sourcefiles..
208                 var src = File.join(_this.baseDir ,  src_in);
209                 
210                 
211                 if (!File.isDirectory(src)) {
212                     ret.push(src);
213                     return;
214                 }
215                 
216                 File.list(src ).forEach(function($) {
217                     if (_this.excludeSrc.indexOf($) > -1) {
218                         return;
219                     }
220                     var thisExt = $.split(".").pop().toLowerCase();
221                     if (ext.indexOf(thisExt) < 0) {
222                         return;
223                     }
224                     ret.push( File.join(src,  $));
225                 });
226                     
227             });
228             //Seed.print(JSON.stringify(this.srcFiles, null,4));Seed.quit();
229             return ret;
230         },
231         /**
232          * Parse the source files.
233          * @private
234          */
235     
236         _parseSrcFiles : function() 
237         {
238             
239             var _this = this;
240             this.srcFiles.forEach(function(srcFile) {
241                 
242              
243                 var cacheFile = !_this.cacheDirectory.length ? false : 
244                     _this.cacheDirectory + srcFile.replace(/\//g, '_') + ".cache";
245                 
246                 //print(cacheFile);
247                 // disabled at present!@!!
248                 
249                 if (cacheFile  && File.exists(cacheFile)) {
250                     // check filetime?
251                     
252                     var c_mt = File.mtime(cacheFile);
253                     var o_mt = File.mtime(srcFile);
254                     //println(c_mt.toSource());
255                    // println(o_mt.toSource());
256                    
257                     // this check does not appear to work according to the doc's - need to check it out.
258                    
259                     if (c_mt > o_mt) { // cached time  > original time!
260                         // use the cached mtimes..
261                         print("Read " + cacheFile);
262                         
263                         var syms =  JSON.parse(File.read(cacheFile), function(k, v) {
264                             //print(k);
265                             if (typeof(v) != 'object') {
266                                 return v;
267                             }
268                             if (typeof(v['*object']) == 'undefined') {
269                                 return v;
270                             }
271                             
272                             var cls = imports[v['*object']][v['*object']];
273                             //print(v['*object']);
274                             delete v['*object'];
275                             var ret = new cls();
276                             XObject.extend(ret, v);
277                             return ret; //????
278                             
279                         });
280                         //print("Add sybmols " + cacheFile); 
281                         for (var sy in syms._index) {
282                           //  print("ADD:" + sy );
283                           imports.ScopeNamer.ScopeNamer.symbols.addSymbol(syms._index[sy]);
284                         }
285                         return;
286                     }
287                 }
288                 
289                 var src = ''
290                 try {
291                     _this.LOG.inform("reading : " + srcFile);
292                     src = File.read(srcFile);
293                 }
294                 catch(e) {
295                     _this.LOG.warn("Can't read source file '"+srcFile+"': "+e.message);
296                     return;
297                 }
298     
299                 var txs = new imports.TextStream.TextStream(src);
300                 
301                 var tr = new imports.TokenReader.TokenReader({
302                         lang : _this.lang,
303                         keepComments : true,
304                         keepWhite : true ,
305                         sepIdents: false,
306                         filename : _this.srcFileRelName(srcFile)
307                     });
308                 //print(JSON.stringify(tr.tokenize(txs), null, 4));
309                 
310                 
311                 var sn = new imports.ScopeNamer.ScopeNamer({
312                         tokens : tr.tokenize(txs),
313                         filename : _this.srcFileRelName(srcFile)
314                 });
315                 sn.buildSymbols();
316                 return;
317                 Seed.quit();
318                 
319                 
320                 // old way..
321                 
322                  
323                 if (cacheFile) {
324                     File.write(cacheFile,
325                       JSON.stringify(
326                         _this.parser.symbolsToObject(srcFile),
327                         null,2
328                       )
329                     );
330                 
331                 }
332                  
333             });
334             
335             print(JSON.stringify(
336                 XObject.keys(imports.ScopeNamer.ScopeNamer.symbols._index),
337             null, 4));
338             
339             imports.ScopeNamer.ScopeNamer.symbols.relate();
340             
341              print(JSON.stringify(
342                 XObject.keys(imports.ScopeNamer.ScopeNamer.symbols._index),
343             null, 4));
344             
345              
346              //print(JSON.stringify(imports.ScopeNamer.ScopeNamer.symbols._index, null,4));
347             //this.parser.finish();
348         },
349         
350          /**
351          * publish the parsed data into  html, js etc.
352          */
353             
354         publish  : function() {
355             this.LOG.inform("Publishing");
356              
357             // link!!!
358             
359             var _this = this;
360             this.LOG.inform("Making directories:" + this.target);
361             if (!File.isDirectory(this.target))
362                 File.mkdir(this.target);
363                 
364             if (!File.isDirectory(this.target+"/symbols"))
365                 File.mkdir(this.target+"/symbols");
366             if (!File.isDirectory(this.target+"/symbols/src"))
367                 File.mkdir(this.target+"/symbols/src");
368             
369             if (!File.isDirectory(this.target +"/json")) {
370                 File.mkdir(this.target +"/json");
371             }
372             
373             this.LOG.inform("Copying files from static: " +this.templateDir);
374             // copy everything in 'static' into 
375             File.list(this.templateDir + '/static').forEach(function (f) {
376                 _this.LOG.inform("Copy " + _this.templateDir + '/static/' + f + ' to  ' + _this.target + '/' + f);
377                 File.copyFile(_this.templateDir + '/static/' + f, _this.target + '/' + f,  Gio.FileCopyFlags.OVERWRITE);
378             });
379             
380             
381             this.LOG.inform("Setting up templates");
382             
383             
384             // used to check the details of things being linked to
385             Link.symbolSet = this.symbolSet;
386             Link.base = "../";
387             
388             Link.srcFileFlatName = XObject.createDelegate(this.srcFileFlatName, this);
389             Link.srcFileRelName = XObject.createDelegate(this.srcFileRelName, this);
390             
391             var classTemplate = new Template({
392                  templateFile : this.templateDir  + "/class.html",
393                  Link : Link
394             });
395             var classesTemplate = new Template({
396                 templateFile : this.templateDir +"/allclasses.html",
397                 Link : Link
398             });
399             var classesindexTemplate = new Template({
400                 templateFile : this.templateDir +"/index.html",
401                 Link : Link
402             });
403             var fileindexTemplate = new Template({   
404                 templateFile : this.templateDir +"/allfiles.html",
405                 Link: Link
406             });
407     
408             
409             classTemplate.symbolSet = this.symbolSet;
410             
411             
412             function hasNoParent($) {
413                 return ($.memberOf == "")
414             }
415             function isaFile($) {
416                 return ($.is("FILE"))
417             }
418             function isaClass($) { 
419                 //return ($.is("CONSTRUCTOR") || $.isNamespace); 
420                 return ($.is("CONSTRUCTOR") || $.is("OBJECT")); 
421             }
422             
423             
424             
425             
426             
427             
428             
429             
430             
431             
432             var symbols = this.symbolSet.toArray();
433             
434             var files = this.srcFiles;
435             
436             for (var i = 0, l = files.length; i < l; i++) {
437                 var file = files[i];
438                 var targetDir = _this.target + "/symbols/src/";
439                 this.makeSrcFile(file, targetDir);
440             }
441             var makeSortby = imports.JsTemplate.Template.Template.prototype.makeSortby;
442             //print(JSON.stringify(symbols,null, 4));
443             var classes = symbols.filter(isaClass).sort(makeSortby("alias"));
444              
445             
446             this.LOG.inform("iterate classes");
447             
448             var jsonAll = {}; 
449             
450             for (var i = 0, l = classes.length; i < l; i++) {
451                 var symbol = classes[i];
452                 var output = "";
453                 
454                 
455                 
456                 //symbol.ignoreNamespace = this.ignoreNamespace;
457                 var ns = '';
458                 //var ns = File.dirname(symbol.srcFile).replace(/\//g,'.');
459                 //if (symbol.alias.substring(0, ns.length) == ns) {
460                 //    ns = '';
461                // }
462                 //ns += ns.length ? '.' : ''; 
463                 
464                 
465                 this.LOG.inform("classTemplate Process : " +
466                                 this.target+"/symbols/" + ns + symbol.alias+'.' + this.publishExt);
467                 
468                 File.write(this.target+"/symbols/" + ns + symbol.alias+'.' + this.publishExt ,
469                         classTemplate.process(symbol));
470                 
471                 jsonAll[symbol.alias] = this.publishJSON(symbol);
472                 
473                 
474                 
475             }
476             
477             File.write(this.target+"/json/roodata.json",
478                     JSON.stringify({
479                         success : true,
480                         data : jsonAll
481                     }, null, 1)
482             );
483             
484             
485             // regenrate the index with different relative links
486             Link.base = "";
487              
488             this.LOG.inform("build index");
489             
490             File.write(this.target +  "/index."+ this.publishExt, 
491                 classesindexTemplate.process(classes)
492             );
493             
494             // blank everything???? classesindexTemplate = classesIndex = classes = null;
495             
496      
497             
498             var documentedFiles = symbols.filter(function ($) {
499                 return ($.is("FILE"))
500             });
501             
502             var allFiles = [];
503             
504             for (var i = 0; i < files.length; i++) {
505                 allFiles.push(new  imports.Symbol.Symbol(files[i], [], "FILE", new imports.DocComment.DocComment("/** */")));
506             }
507             
508             for (var i = 0; i < documentedFiles.length; i++) {
509                 var offset = files.indexOf(documentedFiles[i].alias);
510                 allFiles[offset] = documentedFiles[i];
511             }
512                 
513             allFiles = allFiles.sort(makeSortby("name"));
514             this.LOG.inform("write files index");
515             
516             File.write(this.target + "/files."+this.publishExt, 
517                 fileindexTemplate.process(allFiles)
518             );
519             
520             
521             
522             
523         },
524         /**
525          * JSON files are lookup files for the documentation
526          * - can be used by IDE's or AJAX based doc tools
527          * @param {Symbol} data The symbol to publish
528          * 
529          */
530         publishJSON : function(data)
531         {
532             // what we need to output to be usefull...
533             // a) props..
534             var makeSortby = imports.JsTemplate.Template.Template.prototype.makeSortby;
535    
536             var cfgProperties = [];
537             if (!data.comment.getTag('singleton').length) {
538                 cfgProperties = data.configToArray();
539                 cfgProperties = cfgProperties.sort(makeSortby("alias"));
540                 
541             }
542             var props = []; 
543             //println(cfgProperties.toSource());
544             var p ='';
545             for(var i =0; i < cfgProperties.length;i++) {
546                 p = cfgProperties[i];
547                 props.push( {
548                     name : p.name,
549                     type : p.type,
550                     desc : p.desc,
551                     memberOf : p.memberOf == data.alias ? '' : p.memberOf
552                 });
553             }
554             
555              
556             var ownEvents = data.methods.filter( function(e){
557                     return e.isEvent && !e.comment.getTag('hide').length;
558                 }).sort(makeSortby("name"));
559                  
560             
561             var events = [];
562             var m;
563             for(var i =0; i < ownEvents.length;i++) {
564                 m = ownEvents[i];
565                 events.push( {
566                     name : m.name.substring(1),
567                     sig : this.makeFuncSkel(m.params),
568                     type : 'function',
569                     desc : m.desc
570                 });
571             }
572             //println(props.toSource());
573             // we need to output:
574             //classname => {
575             //    propname => 
576             //        type=>
577             //        desc=>
578             //    }
579     
580             var ret = {
581                 props : props,
582                 events: events
583             };
584             return ret;
585             
586             
587             
588             // b) methods
589             // c) events
590             
591             
592         },
593         srcFileRelName : function(sourceFile)
594         {
595            // print(JSON.stringify([ sourceFile, this.baseDir ] ));
596             return sourceFile.substring(this.baseDir.length+1);
597         },
598         srcFileFlatName: function(sourceFile)
599         {
600             var name = sourceFile; //this.srcFileRelName(sourceFile);
601            // print("NAME: " + name);
602             name = name.replace(/\.\.?[\\\/]/g, "").replace(/[\\\/]/g, "_");
603             return name.replace(/\:/g, "_") + '.html'; //??;
604             
605         },
606         
607         makeSrcFile: function(sourceFile) 
608         {
609             // this stuff works...
610          
611             
612             var name = this.srcFileFlatName(this.srcFileRelName(sourceFile)) ;
613             
614             this.LOG.inform("Write Source file : " + this.target+"/symbols/src/" + name + " FROM: "  + sourceFile);
615             
616             var pretty = imports.PrettyPrint.toPretty(File.read(  sourceFile));
617             
618             File.write(this.target+"/symbols/src/" + name, 
619                 '<html><head>' +
620                 '<title>' + sourceFile + '</title>' +
621                 '<link rel="stylesheet" type="text/css" href="' + this.roojs + '/css/highlight-js.css"/>' + 
622                 '</head><body class="highlightpage">' +
623                 pretty +
624                 '</body></html>');
625         },
626         /**
627          * used by JSON output to generate a function skeleton
628          */
629         makeFuncSkel :function(params) {
630             if (!params) return "function ()\n{\n\n}";
631             return "function (" +
632                 params.filter(
633                     function($) {
634                         return $.name.indexOf(".") == -1; // don't show config params in signature
635                     }
636                 ).map( function($) { return $.name == 'this' ? '_self' : $.name; } ).join(", ") +
637             ")\n{\n\n}";
638         }
639         
640      
641         
642     }
643 );
644   
645
646
647
648
649
650  
651
652
653
654