From 8105cef501dbf1d5ae3c70ccb4ea45e69f676355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Wrzeszcz?= Date: Thu, 15 Dec 2011 12:06:49 +0100 Subject: [PATCH] Tutorials parsing. --- Jake/templates/package.json.tmpl | 4 + jsdoc.js | 9 +- package.json | 8 +- rhino_modules/fs.js | 2 +- rhino_modules/jsdoc/opts/parser.js | 2 +- rhino_modules/jsdoc/tutorial.js | 56 ++++++++---- rhino_modules/jsdoc/tutorial/resolver.js | 107 ++++++++++++++++++++--- 7 files changed, 153 insertions(+), 35 deletions(-) diff --git a/Jake/templates/package.json.tmpl b/Jake/templates/package.json.tmpl index 2754bcf6b..092cc3bcb 100644 --- a/Jake/templates/package.json.tmpl +++ b/Jake/templates/package.json.tmpl @@ -21,6 +21,10 @@ { "name": "Michael Mathews", "email": "micmath@gmail.com" + }, + { + "name": "Rafa\u0105 Wrzeszcz", + "email": "rafal.wrzeszcz@wrzasq.pl" } ], "maintainers": [ diff --git a/jsdoc.js b/jsdoc.js index 42656b372..a0077d336 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -241,11 +241,14 @@ function main() { exit(0); } + // load this module anyway to ensure root instance exists + // it's not a problem since without tutorials root node will have empty children list resolver = require('jsdoc/tutorial/resolver'); - var Tutorial = require('jsdoc/tutorial').Tutorial; - // tutorials handling - //TODO + if (env.opts.tutorials) { + resolver.load(env.opts.tutorials); + resolver.resolve(); + } env.opts.template = env.opts.template || 'templates/default'; diff --git a/package.json b/package.json index 2257aeb59..ed6f2d267 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "JSDoc", "version": "3.0.0alpha", - "revision": "1319671194463", + "revision": "1323947228470", "description": "An automatic documentation generator for javascript.", "keywords": [ "documentation", "javascript" ], "licenses": [ @@ -21,6 +21,10 @@ { "name": "Michael Mathews", "email": "micmath@gmail.com" + }, + { + "name": "Rafa\u0105 Wrzeszcz", + "email": "rafal.wrzeszcz@wrzasq.pl" } ], "maintainers": [ @@ -29,4 +33,4 @@ "email": "micmath@gmail.com" } ] -} \ No newline at end of file +} diff --git a/rhino_modules/fs.js b/rhino_modules/fs.js index 8022dd6e0..5b1c38ce5 100644 --- a/rhino_modules/fs.js +++ b/rhino_modules/fs.js @@ -133,7 +133,7 @@ exports.copyFile = function(inFile, outDir, fileName) { bis.close(); }; -function toFile(path) { +var toFile = exports.toFile = function(path) { var parts = path.split(/[\\\/]/); return parts.pop(); } diff --git a/rhino_modules/jsdoc/opts/parser.js b/rhino_modules/jsdoc/opts/parser.js index 4c015b8eb..5af5b15f6 100644 --- a/rhino_modules/jsdoc/opts/parser.js +++ b/rhino_modules/jsdoc/opts/parser.js @@ -25,7 +25,7 @@ argParser.addOption('r', 'recurse', false, 'Recurse into subdirectories when argParser.addOption('h', 'help', false, 'Print this message and quit.'); argParser.addOption('X', 'explain', false, 'Dump all found doclet internals to console and quit.'); argParser.addOption('q', 'query', true, 'Provide a querystring to define custom variable names/values to add to the options hash.'); -argParser.addOption('u', 'tutorials', false, 'Directory in which JSDoc should search for tutorials.'); +argParser.addOption('u', 'tutorials', true, 'Directory in which JSDoc should search for tutorials.'); // TODO [-R, recurseonly] = a number representing the depth to recurse diff --git a/rhino_modules/jsdoc/tutorial.js b/rhino_modules/jsdoc/tutorial.js index f82ae93ad..d042ceeba 100644 --- a/rhino_modules/jsdoc/tutorial.js +++ b/rhino_modules/jsdoc/tutorial.js @@ -1,11 +1,13 @@ /** @overview @author Rafał Wrzeszcz - @license Apache License 2.0 - See file 'LICENSE.md' in this project. + @license Apache License 2.0 - See file 'LICENSE.md' in this project. */ +var mdParser = require('evilstreak/markdown'); + /** - @module jsdoc/tutorial + @module jsdoc/tutorial */ /** @@ -13,27 +15,20 @@ @classdesc Represents a single JSDoc tutorial. @param {string} name - Tutorial name. @param {string} content - Text content. + @param {number} type - Source formating. */ -exports.Tutorial = function(name, content) { +exports.Tutorial = function(name, content, type) { this.title = this.name = name; this.content = content; + this.type = type; // default values this.parent = null; this.children = []; - this.type = exports.Tutorial.HTML; -}; - -/** Tutorial source types. - @enum {string} - */ -exports.Tutorial.TYPES = { - HTML: "html", - MD: "markdown" }; /** Moves children from current parent to different one. - @param {Tutorial} parent + @param {Tutorial} parent - New parent. */ exports.Tutorial.prototype.setParent = function(parent) { // removes node from old parent @@ -46,7 +41,7 @@ exports.Tutorial.prototype.setParent = function(parent) { }; /** Removes children from current node. - @param {Tutorial} child + @param {Tutorial} child - Old child. */ exports.Tutorial.prototype.removeChild = function(child) { var index = this.children.indexOf(child); @@ -56,8 +51,39 @@ exports.Tutorial.prototype.removeChild = function(child) { }; /** Adds new children to current node. - @param {Tutorial} child + @param {Tutorial} child - New child. */ exports.Tutorial.prototype.addChild = function(child) { this.children.push(child); }; + +/** Prepares source. + @return {string} HTML source. + */ +exports.Tutorial.prototype.parse = function() { + switch (this.type) { + // nothing to do + case exports.TYPES.HTML: + return this.content; + + // markdown + case exports.TYPES.MARKDOWN: + return mdParser.toHTML(this.content) + .replace(/&/g, '&') // because markdown escapes these + .replace(/</g, '<') + .replace(/>/g, '>'); + + // uhm... should we react somehow? + // if not then this case can be merged with TYPES.HTML + default: + return this.content; + } +}; + +/** Tutorial source types. + @enum {number} + */ +exports.TYPES = { + HTML: 1, + MARKDOWN: 2 +}; diff --git a/rhino_modules/jsdoc/tutorial/resolver.js b/rhino_modules/jsdoc/tutorial/resolver.js index 807873fe9..baf115c41 100644 --- a/rhino_modules/jsdoc/tutorial/resolver.js +++ b/rhino_modules/jsdoc/tutorial/resolver.js @@ -1,27 +1,27 @@ /** @overview @author Rafał Wrzeszcz - @license Apache License 2.0 - See file 'LICENSE.md' in this project. + @license Apache License 2.0 - See file 'LICENSE.md' in this project. */ /** - @module jsdoc/tutorial/resolver + @module jsdoc/tutorial/resolver */ -var tutorial = require('jsdoc/tutorial'); - -var tutorials = {}, - length = 0; +var tutorial = require('jsdoc/tutorial'), + fs = require('fs'), + conf = {}, + tutorials = {}, + finder = /^(.*)\.(x(?:ht)?ml|html?|md|markdown|js(?:on)?)$/i; /** Adds new tutorial. - @param {tutorial.Tutorial} tutorial - New tutorial. + @param {tutorial.Tutorial} current - New tutorial. */ -exports.addTutorial = function(tutorial) { - tutorials[tutorial.name] = tutorial; - ++length; +exports.addTutorial = function(current) { + tutorials[current.name] = current; // default temporary parent - tutorial.setParent(exports.root); + current.setParent(exports.root); }; /** Root tutorial. @@ -29,9 +29,90 @@ exports.addTutorial = function(tutorial) { */ exports.root = new tutorial.Tutorial('', ''); +/** Load tutorials from given path. + @param {string} path - Tutorials directory. + */ +exports.load = function(path) { + var match, + type, + name, + current, + files = fs.ls(path); + + // tutorials handling + files.forEach(function(file) { + match = file.match(finder); + + // any filetype that can apply to tutorials + if (match) { + name = fs.toFile(match[1]); + content = fs.readFileSync(file); + + switch (match[2].toLowerCase()) { + // HTML type + case 'xml': + case 'xhtml': + case 'html': + case 'htm': + type = tutorial.TYPES.HTML; + break; + + // Markdown typs + case 'md': + case 'markdown': + type = tutorial.TYPES.MARKDOWN; + break; + + // configuration file + case 'js': + case 'json': + conf[name] = JSON.parse(content); + + // how can it be? check `finder' regexp + default: + // not a file we want to work with + return; + } + + current = new tutorial.Tutorial(name, content, type); + exports.addTutorial(current); + } + }); +}; + /** Resolves hierarchical structure. @param {object} map - Contents map. */ -exports.resolve = function(map) { - //TODO +exports.resolve = function() { + var item, + current; + for (var name in conf) { + // should we be restrictive here? + // what is someone just wants to keep sample sources in same directory with tutorials? + // I've decided to leave such cases alone + if (!(name in tutorials)) { + continue; + } + + item = conf[name]; + current = tutorials[name] + + // set title + if (item.title) { + current.title = item.title; + } + + // add children + if (item.children) { + item.children.forEach(function(child) { + // I really didn't want to throw you an exception in most cases + // but now, user, you pissed me off ;) + if (!(child in tutorials)) { + throw new Error("Missing child tutorial: " + child); + } + + tutorials[child].setParent(current); + }); + } + } };