doctool.js (3463B)
1 /* 2 Process a single doc file 3 4 argv[2] = template file 5 argv[3] = input file 6 argv[4] = output file 7 */ 8 9 var fs = require("fs"); 10 var path = require("path"); 11 var markdown = require("./lib/markdown"); 12 var argv = process.argv; 13 var argc = argv.length; 14 var template = fs.readFileSync(argv[2], "utf8"); 15 16 17 function formatIdString( str ) { 18 str = str 19 .replace(/\([^)}]*\)/gmi, "") 20 .replace(/[^A-Za-z0-9_.]+/gmi, "_"); 21 return str.substr(0, 1).toLowerCase() + str.substr(1); 22 } 23 24 25 function generateToc(data) { 26 var last_level = 0; 27 var first_level = 0; 28 var toc = [ '<div id="toc">', '<h2>Table Of Contents</h2>' ]; 29 30 data.replace(/(^#+)\W+([^$\n]+)/gmi, function(src, level, text) { 31 level = level.length; 32 33 if (first_level == 0) first_level = level; 34 35 if (level <= last_level) toc.push("</li>"); 36 37 if (level > last_level) { 38 toc.push("<ul>"); 39 } else if (level < last_level) { 40 for (var c = last_level-level; 0 < c ; c--) { 41 toc.push("</ul>"); 42 toc.push("</li>"); 43 } 44 } 45 46 toc.push("<li>"); 47 toc.push('<a href="#'+formatIdString(text)+'">'+text+'</a>'); 48 49 last_level = level; 50 }); 51 52 for (var c = last_level-first_level; 0 <= c ; c--) { 53 toc.push("</li>"); 54 toc.push("</ul>"); 55 } 56 57 toc.push("<hr />") 58 toc.push("</div>"); 59 60 return toc.join(""); 61 } 62 63 64 var includeExpr = /^@include\s+([A-Za-z0-9-_]+)(?:\.)?([a-zA-Z]*)$/gmi; 65 // Allow including other pages in the data. 66 function loadIncludes(data, current_file) { 67 return data.replace(includeExpr, function(src, name, ext) { 68 try { 69 var include_path = 70 path.join(current_file, "../", name+"."+(ext || "markdown")) 71 return loadIncludes(fs.readFileSync(include_path, "utf8"), current_file); 72 } catch(e) { 73 return ""; 74 } 75 }); 76 } 77 78 79 function convertData(data) { 80 // Convert it to HTML from Markdown 81 var html = markdown.toHTML(markdown.parse(data), {xhtml:true}) 82 .replace(/<hr><\/hr>/g, "<hr />") 83 .replace(/\.md/g, ".html") 84 .replace(/(\<h[2-6])\>([^<]+)(\<\/h[1-6]\>)/gmi, function(o, ts, c, te) { 85 return ts+' id="'+formatIdString(c)+'">'+c+te; 86 }); 87 88 return html; 89 } 90 91 92 if (argc > 3) { 93 var filename = argv[3]; 94 var output = template; 95 var version = require(__dirname + '/../package.json').version; 96 var html; 97 98 fs.readFile(filename, "utf8", function(err, data) { 99 if (err) throw err; 100 101 // go recursion. 102 data = loadIncludes(data, filename); 103 // go markdown. 104 html = convertData(data); 105 filename = path.basename(filename, '.md'); 106 107 if (filename != "_toc" && filename != "index") { 108 if (data) { 109 html = generateToc(data) + "\n" + html; 110 } 111 112 var nfilename = 113 filename.replace(/[_-]/g, ' ').replace(/\w\S*/g, function(txt){ 114 return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase() 115 }); 116 output = output.replace(/{{section}}/g, nfilename) 117 } else { 118 output = output.replace(/{{section}}/g, ""); 119 output = output.replace(/<body([^>]*)>/, 120 '<body class="'+filename+'" $1>'); 121 } 122 123 if (html.length == 0) { 124 html = "Sorry, this section is currently undocumented, \ 125 but we'll be working on it."; 126 } 127 output = output.replace(/{{content}}/g, html); 128 129 output = output.replace(/{{version}}/g, 'v' + version); 130 131 if (argc > 4) { 132 fs.writeFileSync(argv[4], output); 133 } else { 134 process.stdout.write(output); 135 } 136 }); 137 }