From 56fa78573ea96ddfa6462b5e98b7995eb91a0abf Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 19 Aug 2012 15:17:56 -0700 Subject: [PATCH 01/10] keep track of which VM is running JSDoc --- jsdoc.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/jsdoc.js b/jsdoc.js index 21eaa02c..4824693a 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -21,6 +21,12 @@ env = { finish: null }, + /** + The type of VM that is executing jsdoc. + @type string + */ + vm: '', + /** The command line arguments passed into jsdoc. @type Array @@ -120,6 +126,23 @@ function exit(n) { java.lang.System.exit(n); } +/** + Detect the type of VM running jsdoc. + **Note**: Rhino is the only VM that is currently supported. + @return {string} rhino|node + */ +function detectVm() { + if (typeof Packages === "object" && + Object.prototype.toString.call(Packages) === "[object JavaPackage]") { + return "rhino"; + } else if ( require && require.main && module && (require.main === module) ) { + return "node"; + } else { + // unknown VM + return; + } +} + function installPlugins(plugins, p) { var dictionary = require('jsdoc/tag/dictionary'), parser = p || app.jsdoc.parser; @@ -196,6 +219,8 @@ function main() { env.opts = jsdoc.opts.parser.parse(env.args); + env.vm = detectVm(); + try { env.conf = new Config( fs.readFileSync( env.opts.configure || env.dirname + '/conf.json' ) ).get(); } From d091675cfab28de4d20d94f39018f9445973e103 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 19 Aug 2012 15:25:02 -0700 Subject: [PATCH 02/10] make 'fs' and 'path' modules more consistent with Node.js --- rhino_modules/fs.js | 82 +++++++++--------------- rhino_modules/jsdoc/src/scanner.js | 2 +- rhino_modules/jsdoc/tutorial/resolver.js | 9 +-- rhino_modules/path.js | 77 ++++++++++++---------- templates/default/publish.js | 4 +- test/spec-collection.js | 2 +- test/specs/path.js | 35 ++++++++++ 7 files changed, 120 insertions(+), 91 deletions(-) create mode 100644 test/specs/path.js diff --git a/rhino_modules/fs.js b/rhino_modules/fs.js index a32eff3b..5eb42552 100644 --- a/rhino_modules/fs.js +++ b/rhino_modules/fs.js @@ -1,12 +1,17 @@ /*global Packages: true */ +var path = require('path'); + exports.readFileSync = function(filename, encoding) { encoding = encoding || 'utf-8'; return readFile(filename, encoding); }; -var stat = exports.stat = exports.statSync = function(path, encoding) { - var f = new java.io.File(path); +// in node 0.8, path.exists() and path.existsSync() moved to the "fs" module +exports.existsSync = path.existsSync; + +var statSync = exports.statSync = function(_path) { + var f = new java.io.File(_path); return { isFile: function() { return f.isFile(); @@ -15,14 +20,13 @@ var stat = exports.stat = exports.statSync = function(path, encoding) { return f.isDirectory(); } }; - }; -var readdirSync = exports.readdirSync = function(path) { +var readdirSync = exports.readdirSync = function(_path) { var dir, files; - dir = new java.io.File(path); + dir = new java.io.File(_path); if (!dir.directory) { return [String(dir)]; } files = dir.list(); @@ -35,6 +39,8 @@ var readdirSync = exports.readdirSync = function(path) { return files; }; +// TODO: not part of node's "fs" module +// for node, could use wrench.readdirSyncRecursive(), although it doesn't take a 'recurse' param var ls = exports.ls = function(dir, recurse, _allFiles, _path) { var files, file; @@ -47,7 +53,7 @@ var ls = exports.ls = function(dir, recurse, _allFiles, _path) { if (_path.length === 0) { return _allFiles; } if (typeof recurse === 'undefined') { recurse = 1; } - if ( stat(dir).isFile(dir) ) { + if ( statSync(dir).isFile(dir) ) { files = [dir]; } else { @@ -77,57 +83,33 @@ var ls = exports.ls = function(dir, recurse, _allFiles, _path) { return _allFiles; }; -var toDir = exports.toDir = function(path) { - var f = new java.io.File(path); +// TODO: not part of node's "fs" module +var toDir = exports.toDir = function(_path) { + var f = new java.io.File(_path); if (f.isDirectory()){ - return path; - } - - var parts = path.split(/[\\\/]/); - parts.pop(); - - return parts.join('/'); -}; - -function makeDir(/**string*/ path) { - var dirPath = toDir(path); - (new java.io.File(dirPath)).mkdir(); -} - -function exists(path) { - var f = new java.io.File(path); - - if (f.isDirectory()){ - return true; - } - if (!f.exists()){ - return false; - } - if (!f.canRead()){ - return false; - } - return true; -} - -exports.mkPath = function(/**Array*/ path) { - if (path.constructor != Array){path = path.split(/[\\\/]/);} - var make = ""; - for (var i = 0, l = path.length; i < l; i++) { - make += path[i] + '/'; - if (! exists(make)) { - makeDir(make); - } + return _path; + } else { + return path.dirname(_path); } }; -var toFile = exports.toFile = function(path) { - var parts = path.split(/[\\\/]/); - return parts.pop(); +var mkdirSync = exports.mkdirSync = function(/**string*/ _path) { + var dir_path = toDir(_path); + (new java.io.File(dir_path)).mkdir(); }; -exports.copyFile = function(inFile, outDir, fileName) { - if (fileName == null){fileName = toFile(inFile);} +// TODO: not part of node's "fs" module +// for node, could use: https://github.com/substack/node-mkdirp +exports.mkPath = function(/**Array*/ _path) { + if (_path.constructor == Array) { _path = _path.join(""); } + + (new java.io.File(_path)).mkdirs(); +}; + +// TODO: not part of node's "fs" module +exports.copyFileSync = function(inFile, outDir, fileName) { + if (fileName == null){fileName = path.basename(inFile);} outDir = toDir(outDir); diff --git a/rhino_modules/jsdoc/src/scanner.js b/rhino_modules/jsdoc/src/scanner.js index ea47ce89..0a73b1fe 100644 --- a/rhino_modules/jsdoc/src/scanner.js +++ b/rhino_modules/jsdoc/src/scanner.js @@ -37,7 +37,7 @@ exports.Scanner.prototype.scan = function(searchPaths, depth, filter) { searchPaths.forEach(function($) { var filepath = decodeURIComponent($); - if ( fs.stat(filepath).isFile() ) { + if ( fs.statSync(filepath).isFile() ) { filePaths.push(filepath); } else { diff --git a/rhino_modules/jsdoc/tutorial/resolver.js b/rhino_modules/jsdoc/tutorial/resolver.js index d83e9417..6f8a717b 100644 --- a/rhino_modules/jsdoc/tutorial/resolver.js +++ b/rhino_modules/jsdoc/tutorial/resolver.js @@ -10,6 +10,7 @@ var tutorial = require('jsdoc/tutorial'), fs = require('fs'), + path = require('path'), hasOwnProp = Object.prototype.hasOwnProperty, conf = {}, tutorials = {}, @@ -39,15 +40,15 @@ exports.root.getByName = function(name) { }; /** Load tutorials from given path. - @param {string} path - Tutorials directory. + @param {string} _path - Tutorials directory. */ -exports.load = function(path) { +exports.load = function(_path) { var match, type, name, content, current, - files = fs.ls(path); + files = fs.ls(_path); // tutorials handling files.forEach(function(file) { @@ -55,7 +56,7 @@ exports.load = function(path) { // any filetype that can apply to tutorials if (match) { - name = fs.toFile(match[1]); + name = path.basename(match[1]); content = fs.readFileSync(file); switch (match[2].toLowerCase()) { diff --git a/rhino_modules/path.js b/rhino_modules/path.js index d21b375e..f15f16b8 100644 --- a/rhino_modules/path.js +++ b/rhino_modules/path.js @@ -1,37 +1,48 @@ var isWindows = java.lang.System.getProperty("os.name").toLowerCase().contains("windows"); -var fileSeparator = java.lang.System.getProperty("file.separator"); +var fileSeparator = exports.sep = java.lang.System.getProperty("file.separator"); /** * Returns everything on a path except for the last item * e.g. if the path was 'path/to/something', the return value would be 'path/to' */ -exports.basename = function(path) { - var parts = path.split(fileSeparator); +exports.dirname = function(_path) { + var parts = _path.split(fileSeparator); parts.pop(); - path = parts.join(fileSeparator); - return path; + _path = parts.join(fileSeparator); + return _path; }; /** * Returns the last item on a path */ -exports.filename = function(path) { - var parts = path.split(fileSeparator); +exports.basename = function(_path, ext) { + var base, + idx, + parts = _path.split(fileSeparator); if (parts.length > 0) { - return parts.pop(); + base = parts.pop(); + idx = ext ? base.indexOf(ext) : -1; + if (idx !== -1) { + base = Array.prototype.slice.call(base, 0, base.length - ext.length).join(""); + } } - return null; + return base; }; -exports.existsSync = function(path) { - var file = new java.io.File(path); +exports.existsSync = function(_path) { + var f = new java.io.File(_path); - if (file.isFile()) { - return true; + if (f.isDirectory()){ + return true; } - - return false; + if (!f.exists()){ + return false; + } + if (!f.canRead()){ + return false; + } + return true; }; //Code below taken from node @@ -73,8 +84,8 @@ if (isWindows) { /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?([\s\S]*?)$/; // windows version - exports.normalize = function(path) { - var result = splitDeviceRe.exec(path), + exports.normalize = function(_path) { + var result = splitDeviceRe.exec(_path), device = result[1] || '', isUnc = device && device.charAt(1) !== ':', isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute @@ -102,44 +113,44 @@ if (isWindows) { return p && typeof p === 'string'; } - var paths = Array.prototype.slice.call(arguments, 0).filter(f); - var joined = paths.join('\\'); + var _paths = Array.prototype.slice.call(arguments, 0).filter(f); + var joined = _paths.join('\\'); // Make sure that the joined path doesn't start with two slashes // - it will be mistaken for an unc path by normalize() - - // unless the paths[0] also starts with two slashes - if (/^[\\\/]{2}/.test(joined) && !/^[\\\/]{2}/.test(paths[0])) { + // unless the _paths[0] also starts with two slashes + if (/^[\\\/]{2}/.test(joined) && !/^[\\\/]{2}/.test(_paths[0])) { joined = joined.slice(1); } return exports.normalize(joined); }; } else { - // path.normalize(path) + // path.normalize(_path) // posix version - exports.normalize = function(path) { - var isAbsolute = path.charAt(0) === '/', - trailingSlash = path.slice(-1) === '/'; + exports.normalize = function(_path) { + var isAbsolute = _path.charAt(0) === '/', + trailingSlash = _path.slice(-1) === '/'; // Normalize the path - path = normalizeArray(path.split('/').filter(function(p) { + _path = normalizeArray(_path.split('/').filter(function(p) { return !!p; }), !isAbsolute).join('/'); - if (!path && !isAbsolute) { - path = '.'; + if (!_path && !isAbsolute) { + _path = '.'; } - if (path && trailingSlash) { - path += '/'; + if (_path && trailingSlash) { + _path += '/'; } - return (isAbsolute ? '/' : '') + path; + return (isAbsolute ? '/' : '') + _path; }; // posix version exports.join = function() { - var paths = Array.prototype.slice.call(arguments, 0); - return exports.normalize(paths.filter(function(p, index) { + var _paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(_paths.filter(function(p, index) { return p && typeof p === 'string'; }).join('/')); }; diff --git a/templates/default/publish.js b/templates/default/publish.js index c9e97bc2..794b2820 100644 --- a/templates/default/publish.js +++ b/templates/default/publish.js @@ -175,7 +175,7 @@ mixins = find({kind: 'mixin'}), namespaces = find({kind: 'namespace'}); - var outdir = opts.destination; + var outdir = env.dirname + '/' + opts.destination; if (packageInfo && packageInfo.name) { outdir += '/' + packageInfo.name + '/' + packageInfo.version + '/'; } @@ -188,7 +188,7 @@ staticFiles.forEach(function(fileName) { var toDir = fs.toDir(fileName.replace(fromDir, outdir)); fs.mkPath(toDir); - fs.copyFile(fileName, toDir); + fs.copyFileSync(fileName, toDir); }); function linkto(longname, linktext) { diff --git a/test/spec-collection.js b/test/spec-collection.js index 627c89bc..e50930d5 100644 --- a/test/spec-collection.js +++ b/test/spec-collection.js @@ -36,7 +36,7 @@ exports.load = function(loadpath, matcher, clear) { var file = path.join(env.dirname, loadpath, wannaBeSpecs[i]); try { if (fs.statSync(file).isFile()) { - if (!/.*nodejs_modules.*/.test(file) && matcher.test(path.filename(file))) { + if (!/.*nodejs_modules.*/.test(file) && matcher.test(path.basename(file))) { specs.push(createSpecObj(file)); } } diff --git a/test/specs/path.js b/test/specs/path.js new file mode 100644 index 00000000..942f3a73 --- /dev/null +++ b/test/specs/path.js @@ -0,0 +1,35 @@ +/*global describe: true, expect: true, it: true */ +describe("path", function() { + // TODO: more tests + var path = require('path'); + + var pathChunks = [ + "foo", + "bar", + "baz", + "qux.html" + ]; + var joinedPath = path.join.apply(this, pathChunks); + + describe("basename", function() { + it("should exist", function() { + expect(path.basename).toBeDefined(); + }); + + it("should be a function", function() { + expect(typeof path.basename).toEqual("function"); + }); + + it("should work correctly without an 'ext' parameter", function() { + expect( path.basename(joinedPath) ).toEqual( pathChunks[pathChunks.length - 1] ); + }); + + it("should work correctly with an 'ext' parameter", function() { + var fn = pathChunks[pathChunks.length - 1], + ext = Array.prototype.slice.call( fn, fn.indexOf(".") ).join(""); + bn = Array.prototype.slice.call( fn, 0, fn.indexOf(".") ).join(""); + + expect( path.basename(joinedPath, ext) ).toEqual(bn); + }); + }); +}); \ No newline at end of file From 75071e98750127b2eb1fdcc31fc46789bdba4083 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 19 Aug 2012 19:14:54 -0700 Subject: [PATCH 03/10] allow @augments to point at an undocumented dependency --- rhino_modules/jsdoc/augment.js | 13 ++++++------- test/fixtures/augmentstag2.js | 6 +++--- test/specs/jsdoc/augment.js | 29 +---------------------------- test/specs/tags/augmentstag.js | 20 +++++++++++++++----- 4 files changed, 25 insertions(+), 43 deletions(-) diff --git a/rhino_modules/jsdoc/augment.js b/rhino_modules/jsdoc/augment.js index 291cc6e4..4a2ec590 100644 --- a/rhino_modules/jsdoc/augment.js +++ b/rhino_modules/jsdoc/augment.js @@ -1,7 +1,6 @@ -var doop = require("jsdoc/util/doop").doop; - (function() { - var hasOwnProp = Object.prototype.hasOwnProperty; + var doop = require("jsdoc/util/doop").doop, + hasOwnProp = Object.prototype.hasOwnProperty; exports.addInherited = function(docs) { var dependencies = mapDependencies(docs.index); @@ -43,6 +42,10 @@ var doop = require("jsdoc/util/doop").doop; function getAdditions(doclets, docs) { var additions = []; var doc, parents, members, member, parts; + + // doclets will be undefined if the inherited symbol isn't documented + doclets = doclets || []; + for (var i=0, ii=doclets.length; i Date: Wed, 22 Aug 2012 08:57:58 -0700 Subject: [PATCH 04/10] Merge pull request #166 from hegemonic/exports-publish --- jsdoc | 4 +- jsdoc.js | 27 +- templates/default/publish.js | 846 +++++++++++++++++------------------ templates/haruki/publish.js | 381 ++++++++-------- 4 files changed, 628 insertions(+), 630 deletions(-) diff --git a/jsdoc b/jsdoc index 4c82ee53..ee9c4edf 100755 --- a/jsdoc +++ b/jsdoc @@ -16,7 +16,9 @@ if test "$1" = "--debug" then echo "Running Debug" CMD="org.mozilla.javascript.tools.debugger.Main -debug" - shift + # strip --debug argument + length=$(($#-1)) + ARGS=${@:2:$length} else CMD="org.mozilla.javascript.tools.shell.Main" fi diff --git a/jsdoc.js b/jsdoc.js index 4824693a..95633368 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -202,7 +202,7 @@ app = { /** - Run the jsoc application. + Run the jsdoc application. */ function main() { var sourceFiles, @@ -325,17 +325,32 @@ function main() { env.opts.template = env.opts.template || 'templates/default'; - // should define a global "publish" function - include(env.opts.template + '/publish.js'); + // templates should include a publish.js file that exports a "publish" function + var template = require(env.opts.template + '/publish'); - if (typeof publish === 'function') { - publish( + if (template.publish && typeof template.publish === 'function') { + template.publish( new (require('typicaljoe/taffy'))(docs), env.opts, resolver.root ); } - else { // TODO throw no publish warning? + else { + // old templates define a global "publish" function, which is deprecated + include(env.opts.template + '/publish.js'); + if (publish && typeof publish === 'function') { + console.log( env.opts.template + ' uses a global "publish" function, which is ' + + 'deprecated and may not be supported in future versions. ' + + 'Please update the template so that it exports a "publish" function.' ); + publish( + new (require('typicaljoe/taffy'))(docs), + env.opts, + resolver.root + ); + } + else { + throw new Error( env.opts.template + " does not export a 'publish' function." ); + } } } } diff --git a/templates/default/publish.js b/templates/default/publish.js index 794b2820..c7d7bcc6 100644 --- a/templates/default/publish.js +++ b/templates/default/publish.js @@ -1,323 +1,322 @@ -/*global env: true, publish: true */ -(function() { +/*global env: true */ +var template = require('jsdoc/template'), + fs = require('fs'), + helper = require('jsdoc/util/templateHelper'), + scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' }, + hasOwnProp = Object.prototype.hasOwnProperty; - var template = require('jsdoc/template'), - fs = require('fs'), - helper = require('jsdoc/util/templateHelper'), - scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' }, - hasOwnProp = Object.prototype.hasOwnProperty; +/** + @param {TAFFY} data See . + @param {object} opts + @param {Tutorial} tutorials + */ +exports.publish = function(data, opts, tutorials) { + var defaultTemplatePath = 'templates/default', + templatePath = (opts.template) ? opts.template : defaultTemplatePath, + view = new template.Template(env.dirname + '/' + templatePath + '/tmpl'); - /** - @global - @param {TAFFY} data See . - @param {object} opts - @param {Tutorial} tutorials - */ - publish = function(data, opts, tutorials) { - var defaultTemplatePath = 'templates/default'; - var templatePath = (opts.template) ? opts.template : defaultTemplatePath; - var out = '', - view = new template.Template(env.dirname + '/' + templatePath + '/tmpl'); - - // set up templating - view.layout = 'layout.tmpl'; + // set up templating + view.layout = 'layout.tmpl'; - // set up tutorials for helper - helper.setTutorials(tutorials); + // set up tutorials for helper + helper.setTutorials(tutorials); - function find(spec) { - return data.get( data.find(spec) ); - } - - function htmlsafe(str) { - return str.replace(/'+p.name+''); } - else { pnames.push(p.name); } - } - }); - } - - f.signature = (f.signature || '') + '('+pnames.join(', ')+')'; - } - - function generateAncestry(thisdoc) { - var ancestors = [], - doc = thisdoc; - - while (doc = doc.memberof) { - doc = find({longname: doc}); - if (doc) { doc = doc[0]; } - if (!doc) { break; } - ancestors.unshift( linkto(doc.longname, (scopeToPunc[doc.scope] || '') + doc.name) ); - } - if (ancestors.length) { - ancestors[ancestors.length-1] += (scopeToPunc[thisdoc.scope] || ''); - } - return ancestors; - } - - function addSignatureReturns(f) { - var returnTypes = []; - - if (f.returns) { - f.returns.forEach(function(r) { - if (r.type && r.type.names) { - if (! returnTypes.length) { returnTypes = r.type.names; } - } - }); - } - - if (returnTypes && returnTypes.length) { - returnTypes = returnTypes.map(function(r) { - return linkto(r); - }); - } - f.signature = ''+(f.signature || '') + '' + ''+(returnTypes.length? ' → {'+returnTypes.join('|')+'}' : '')+''; - } - - function addSignatureType(f) { - var types = []; - - if (f.type && f.type.names) { - types = f.type.names; - } - - if (types && types.length) { - types = types.map(function(t) { - return linkto(t, htmlsafe(t)); - }); - } - - f.signature = (f.signature || '') + ''+(types.length? ' :'+types.join('|') : '')+''; - } - - function addAttribs(f) { - var attribs = []; - - if (f.virtual) { - attribs.push('virtual'); - } - - if (f.access && f.access !== 'public') { - attribs.push(f.access); - } - - if (f.scope && f.scope !== 'instance' && f.scope !== 'global') { - if (f.kind == 'function' || f.kind == 'member' || f.kind == 'constant') { - attribs.push(f.scope); - } - } - - if (f.readonly === true) { - if (f.kind == 'member') { - attribs.push('readonly'); - } - } - - if (f.kind === 'constant') { - attribs.push('constant'); - f.kind = 'member'; - } - - f.attribs = ''+htmlsafe(attribs.length? '<'+attribs.join(', ')+'> ' : '')+''; - } - - data.remove({undocumented: true}); - data.remove({ignore: true}); - if (!opts.private) { data.remove({access: 'private'}); } - data.remove({memberof: ''}); - - var packageInfo = (find({kind: 'package'}) || []) [0]; - - //function renderLinks(text) { - // return helper.resolveLinks(text); - //} - - data.forEach(function(doclet) { - doclet.attribs = ''; - - - if (doclet.examples) { - doclet.examples = doclet.examples.map(function(example) { - var caption, code; - - if (example.match(/^\s*([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { - caption = RegExp.$1; - code = RegExp.$3; - } - - return { - caption: caption || '', - code: code || example - }; - }); - } - if (doclet.see) { - doclet.see.forEach(function(seeItem, i) { - doclet.see[i] = hashToLink(doclet, seeItem); - }); - } - }); - - data.orderBy(['longname', 'version', 'since']); - - // kinds of containers - var globals = find( {kind: ['member', 'function', 'constant', 'typedef'], memberof: {isUndefined: true}} ), - modules = find({kind: 'module'}), - externals = find({kind: 'external'}), - mixins = find({kind: 'mixin'}), - namespaces = find({kind: 'namespace'}); - - var outdir = env.dirname + '/' + opts.destination; - if (packageInfo && packageInfo.name) { - outdir += '/' + packageInfo.name + '/' + packageInfo.version + '/'; - } - fs.mkPath(outdir); - - // copy static files to outdir - var fromDir = env.dirname + '/' + templatePath + '/static', - staticFiles = fs.ls(fromDir, 3); - - staticFiles.forEach(function(fileName) { - var toDir = fs.toDir(fileName.replace(fromDir, outdir)); - fs.mkPath(toDir); - fs.copyFileSync(fileName, toDir); - }); - - function linkto(longname, linktext) { - var url = helper.longnameToUrl[longname]; - return url? ''+(linktext || longname)+'' : (linktext || longname); - } - - function tutoriallink(tutorial) { - return helper.toTutorial(tutorial); - } - - var containers = ['class', 'module', 'external', 'namespace', 'mixin']; - - data.forEach(function(doclet) { - var url = helper.createLink(doclet); - helper.registerLink(doclet.longname, url); - }); - - data.forEach(function(doclet) { - var url = helper.longnameToUrl[doclet.longname]; - - if (url.indexOf('#') > -1) { - doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); - } - else { - doclet.id = doclet.name; - } - - if (doclet.kind === 'function' || doclet.kind === 'class') { - addSignatureParams(doclet); - addSignatureReturns(doclet); - addAttribs(doclet); - } - }); - - // do this after the urls have all been generated - data.forEach(function(doclet) { - doclet.ancestors = generateAncestry(doclet); - - doclet.signature = ''; - - if (doclet.kind === 'member') { - addSignatureType(doclet); - addAttribs(doclet); - } - - if (doclet.kind === 'constant') { - addSignatureType(doclet); - addAttribs(doclet); - } - }); - - var nav = '

Index

', - seen = {}; - - var moduleNames = find({kind: 'module'}); - moduleNames.sort(function(a, b) { - return a.name > b.name; - }); - if (moduleNames.length) { - nav += '

Modules

    '; - moduleNames.forEach(function(m) { - if ( !hasOwnProp.call(seen, m.longname) ) { - nav += '
  • '+linkto(m.longname, m.name)+'
  • '; - } - seen[m.longname] = true; - }); - - nav += '
'; - } - - var externalNames = find({kind: 'external'}); - externalNames.sort(function(a, b) { - return a.name > b.name; - }); - if (externalNames.length) { - nav += '

Externals

    '; - externalNames.forEach(function(e) { - if ( !hasOwnProp.call(seen, e.longname) ) { - nav += '
  • '+linkto( e.longname, e.name.replace(/(^"|"$)/g, '') )+'
  • '; - } - seen[e.longname] = true; - }); - - nav += '
'; - } + function find(spec) { + return data.get( data.find(spec) ); + } - var classNames = find({kind: 'class'}); - classNames.sort(function(a, b) { - return a.name > b.name; - }); - if (classNames.length) { - var moduleClasses = 0; - classNames.forEach(function(c) { - var moduleSameName = find({kind: 'module', longname: c.longname}); - if (moduleSameName.length) { - c.name = c.name.replace('module:', 'require("')+'")'; - moduleClasses++; - moduleSameName[0].module = c; + function htmlsafe(str) { + return str.replace(/'+hash+''; + } + + function addSignatureParams(f) { + var pnames = []; + if (f.params) { + f.params.forEach(function(p) { + if (p.name && p.name.indexOf('.') === -1) { + if (p.optional) { pnames.push(''+p.name+''); } + else { pnames.push(p.name); } } - if (moduleClasses !== -1 && moduleClasses < classNames.length) { - nav += '

Classes

    '; - moduleClasses = -1; - } - if ( !hasOwnProp.call(seen, c.longname) ) { - nav += '
  • '+linkto(c.longname, c.name)+'
  • '; - } - seen[c.longname] = true; }); - - nav += '
'; } - var namespaceNames = find({kind: 'namespace'}); - namespaceNames.sort(function(a, b) { - return a.name > b.name; - }); - if (namespaceNames.length) { - nav += '

Namespaces

    '; - namespaceNames.forEach(function(n) { - if ( !hasOwnProp.call(seen, n.longname) ) { - nav += '
  • '+linkto(n.longname, n.name)+'
  • '; + f.signature = (f.signature || '') + '('+pnames.join(', ')+')'; + } + + function generateAncestry(thisdoc) { + var ancestors = [], + doc = thisdoc.memberof; + + while (doc) { + doc = find({longname: doc}); + if (doc) { doc = doc[0]; } + if (!doc) { break; } + ancestors.unshift( linkto(doc.longname, (scopeToPunc[doc.scope] || '') + doc.name) ); + doc = doc.memberof; + } + if (ancestors.length) { + ancestors[ancestors.length-1] += (scopeToPunc[thisdoc.scope] || ''); + } + return ancestors; + } + + function addSignatureReturns(f) { + var returnTypes = []; + + if (f.returns) { + f.returns.forEach(function(r) { + if (r.type && r.type.names) { + if (! returnTypes.length) { returnTypes = r.type.names; } } - seen[n.longname] = true; }); - - nav += '
'; } + if (returnTypes && returnTypes.length) { + returnTypes = returnTypes.map(function(r) { + return linkto(r); + }); + } + f.signature = ''+(f.signature || '') + '' + ''+(returnTypes.length? ' → {'+returnTypes.join('|')+'}' : '')+''; + } + + function addSignatureType(f) { + var types = []; + + if (f.type && f.type.names) { + types = f.type.names; + } + + if (types && types.length) { + types = types.map(function(t) { + return linkto(t, htmlsafe(t)); + }); + } + + f.signature = (f.signature || '') + ''+(types.length? ' :'+types.join('|') : '')+''; + } + + function addAttribs(f) { + var attribs = []; + + if (f.virtual) { + attribs.push('virtual'); + } + + if (f.access && f.access !== 'public') { + attribs.push(f.access); + } + + if (f.scope && f.scope !== 'instance' && f.scope !== 'global') { + if (f.kind == 'function' || f.kind == 'member' || f.kind == 'constant') { + attribs.push(f.scope); + } + } + + if (f.readonly === true) { + if (f.kind == 'member') { + attribs.push('readonly'); + } + } + + if (f.kind === 'constant') { + attribs.push('constant'); + f.kind = 'member'; + } + + f.attribs = ''+htmlsafe(attribs.length? '<'+attribs.join(', ')+'> ' : '')+''; + } + + data.remove({undocumented: true}); + data.remove({ignore: true}); + if (!opts.private) { data.remove({access: 'private'}); } + data.remove({memberof: ''}); + + var packageInfo = (find({kind: 'package'}) || []) [0]; + + //function renderLinks(text) { + // return helper.resolveLinks(text); + //} + + data.forEach(function(doclet) { + doclet.attribs = ''; + + + if (doclet.examples) { + doclet.examples = doclet.examples.map(function(example) { + var caption, code; + + if (example.match(/^\s*([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { + caption = RegExp.$1; + code = RegExp.$3; + } + + return { + caption: caption || '', + code: code || example + }; + }); + } + if (doclet.see) { + doclet.see.forEach(function(seeItem, i) { + doclet.see[i] = hashToLink(doclet, seeItem); + }); + } + }); + + data.orderBy(['longname', 'version', 'since']); + + var globals = find( {kind: ['member', 'function', 'constant', 'typedef'], memberof: {isUndefined: true}} ); + + var outdir = env.dirname + '/' + opts.destination; + if (packageInfo && packageInfo.name) { + outdir += '/' + packageInfo.name + '/' + packageInfo.version + '/'; + } + fs.mkPath(outdir); + + // copy static files to outdir + var fromDir = env.dirname + '/' + templatePath + '/static', + staticFiles = fs.ls(fromDir, 3); + + staticFiles.forEach(function(fileName) { + var toDir = fs.toDir(fileName.replace(fromDir, outdir)); + fs.mkPath(toDir); + fs.copyFileSync(fileName, toDir); + }); + + function linkto(longname, linktext) { + var url = helper.longnameToUrl[longname]; + return url? ''+(linktext || longname)+'' : (linktext || longname); + } + + function tutoriallink(tutorial) { + return helper.toTutorial(tutorial); + } + + data.forEach(function(doclet) { + var url = helper.createLink(doclet); + helper.registerLink(doclet.longname, url); + }); + + data.forEach(function(doclet) { + var url = helper.longnameToUrl[doclet.longname]; + + if (url.indexOf('#') > -1) { + doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); + } + else { + doclet.id = doclet.name; + } + + if (doclet.kind === 'function' || doclet.kind === 'class') { + addSignatureParams(doclet); + addSignatureReturns(doclet); + addAttribs(doclet); + } + }); + + // do this after the urls have all been generated + data.forEach(function(doclet) { + doclet.ancestors = generateAncestry(doclet); + + doclet.signature = ''; + + if (doclet.kind === 'member') { + addSignatureType(doclet); + addAttribs(doclet); + } + + if (doclet.kind === 'constant') { + addSignatureType(doclet); + addAttribs(doclet); + } + }); + + var nav = '

Index

', + seen = {}; + + var moduleNames = find({kind: 'module'}); + moduleNames.sort(function(a, b) { + return a.name > b.name; + }); + if (moduleNames.length) { + nav += '

Modules

    '; + moduleNames.forEach(function(m) { + if ( !hasOwnProp.call(seen, m.longname) ) { + nav += '
  • '+linkto(m.longname, m.name)+'
  • '; + } + seen[m.longname] = true; + }); + + nav += '
'; + } + + var externalNames = find({kind: 'external'}); + externalNames.sort(function(a, b) { + return a.name > b.name; + }); + if (externalNames.length) { + nav += '

Externals

    '; + externalNames.forEach(function(e) { + if ( !hasOwnProp.call(seen, e.longname) ) { + nav += '
  • '+linkto( e.longname, e.name.replace(/(^"|"$)/g, '') )+'
  • '; + } + seen[e.longname] = true; + }); + + nav += '
'; + } + + var classNames = find({kind: 'class'}); + classNames.sort(function(a, b) { + return a.name > b.name; + }); + if (classNames.length) { + var moduleClasses = 0; + classNames.forEach(function(c) { + var moduleSameName = find({kind: 'module', longname: c.longname}); + if (moduleSameName.length) { + c.name = c.name.replace('module:', 'require("')+'")'; + moduleClasses++; + moduleSameName[0].module = c; + } + if (moduleClasses !== -1 && moduleClasses < classNames.length) { + nav += '

Classes

    '; + moduleClasses = -1; + } + if ( !hasOwnProp.call(seen, c.longname) ) { + nav += '
  • '+linkto(c.longname, c.name)+'
  • '; + } + seen[c.longname] = true; + }); + + nav += '
'; + } + + var namespaceNames = find({kind: 'namespace'}); + namespaceNames.sort(function(a, b) { + return a.name > b.name; + }); + if (namespaceNames.length) { + nav += '

Namespaces

    '; + namespaceNames.forEach(function(n) { + if ( !hasOwnProp.call(seen, n.longname) ) { + nav += '
  • '+linkto(n.longname, n.name)+'
  • '; + } + seen[n.longname] = true; + }); + + nav += '
'; + } + // var constantNames = find({kind: 'constants'}); // if (constantNames.length) { // nav += '

Constants

    '; @@ -330,140 +329,129 @@ // // nav += '
'; // } - - var mixinNames = find({kind: 'mixin'}); - mixinNames.sort(function(a, b) { - return a.name > b.name; + + var mixinNames = find({kind: 'mixin'}); + mixinNames.sort(function(a, b) { + return a.name > b.name; + }); + if (mixinNames.length) { + nav += '

Mixins

    '; + mixinNames.forEach(function(m) { + if ( !hasOwnProp.call(seen, m.longname) ) { + nav += '
  • '+linkto(m.longname, m.name)+'
  • '; + } + seen[m.longname] = true; }); - if (mixinNames.length) { - nav += '

    Mixins

      '; - mixinNames.forEach(function(m) { - if ( !hasOwnProp.call(seen, m.longname) ) { - nav += '
    • '+linkto(m.longname, m.name)+'
    • '; - } - seen[m.longname] = true; - }); - - nav += '
    '; - } - - if (tutorials.children.length) { - nav += '

    Tutorials

      '; - tutorials.children.forEach(function(t) { - nav += '
    • '+tutoriallink(t.name)+'
    • '; - }); - - nav += '
    '; - } - var globalNames = find({kind: ['member', 'function', 'constant', 'typedef'], 'memberof': {'isUndefined': true}}); - globalNames.sort(function(a, b) { - return a.name > b.name; + nav += '
'; + } + + if (tutorials.children.length) { + nav += '

Tutorials

    '; + tutorials.children.forEach(function(t) { + nav += '
  • '+tutoriallink(t.name)+'
  • '; }); - if (globalNames.length) { - nav += '

    Global

      '; - - globalNames.forEach(function(g) { - if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) { - nav += '
    • '+linkto(g.longname, g.name)+'
    • '; - } - seen[g.longname] = true; - }); - - nav += '
    '; - } - // add template helpers - view.find = find; - view.linkto = linkto; - view.tutoriallink = tutoriallink; - view.htmlsafe = htmlsafe; - // once for all - view.nav = nav; + nav += '
'; + } + + var globalNames = find({kind: ['member', 'function', 'constant', 'typedef'], 'memberof': {'isUndefined': true}}); + globalNames.sort(function(a, b) { + return a.name > b.name; + }); + if (globalNames.length) { + nav += '

Global

    '; + + globalNames.forEach(function(g) { + if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) { + nav += '
  • '+linkto(g.longname, g.name)+'
  • '; + } + seen[g.longname] = true; + }); + + nav += '
'; + } + + // add template helpers + view.find = find; + view.linkto = linkto; + view.tutoriallink = tutoriallink; + view.htmlsafe = htmlsafe; + // once for all + view.nav = nav; - function generate(title, docs, filename) { - var data = { - title: title, - docs: docs - }; - - var path = outdir + '/' + filename, - html = view.render('container.tmpl', data); - - html = helper.resolveLinks(html); // turn {@link foo} into foo - - fs.writeFileSync(path, html); - } + function generate(title, docs, filename) { + var data = { + title: title, + docs: docs + }; - for (var longname in helper.longnameToUrl) { - if ( hasOwnProp.call(helper.longnameToUrl, longname) ) { - var classes = find({kind: 'class', longname: longname}); - if (classes.length) { generate('Class: '+classes[0].name, classes, helper.longnameToUrl[longname]); } + var path = outdir + '/' + filename, + html = view.render('container.tmpl', data); + + html = helper.resolveLinks(html); // turn {@link foo} into foo + + fs.writeFileSync(path, html); + } + + for (var longname in helper.longnameToUrl) { + if ( hasOwnProp.call(helper.longnameToUrl, longname) ) { + var classes = find({kind: 'class', longname: longname}); + if (classes.length) { generate('Class: '+classes[0].name, classes, helper.longnameToUrl[longname]); } + + var modules = find({kind: 'module', longname: longname}); + if (modules.length) { generate('Module: '+modules[0].name, modules, helper.longnameToUrl[longname]); } + + var namespaces = find({kind: 'namespace', longname: longname}); + if (namespaces.length) { generate('Namespace: '+namespaces[0].name, namespaces, helper.longnameToUrl[longname]); } - var modules = find({kind: 'module', longname: longname}); - if (modules.length) { generate('Module: '+modules[0].name, modules, helper.longnameToUrl[longname]); } - - var namespaces = find({kind: 'namespace', longname: longname}); - if (namespaces.length) { generate('Namespace: '+namespaces[0].name, namespaces, helper.longnameToUrl[longname]); } - // var constants = find({kind: 'constant', longname: longname}); // if (constants.length) { generate('Constant: '+constants[0].name, constants, helper.longnameToUrl[longname]); } - var mixins = find({kind: 'mixin', longname: longname}); - if (mixins.length) { generate('Mixin: '+mixins[0].name, mixins, helper.longnameToUrl[longname]); } - - var externals = find({kind: 'external', longname: longname}); - if (externals.length) { generate('External: '+externals[0].name, externals, helper.longnameToUrl[longname]); } - } - } - - if (globals.length) { generate('Global', [{kind: 'globalobj'}], 'global.html'); } - - // index page displays information from package.json and lists files - var files = find({kind: 'file'}), - packages = find({kind: 'package'}); - - generate('Index', - packages.concat( - [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] - ).concat(files) - , 'index.html'); - - - function generateTutorial(title, tutorial, filename) { - var data = { - title: title, - header: tutorial.title, - content: tutorial.parse(), - children: tutorial.children - }; - - var path = outdir + '/' + filename, - html = view.render('tutorial.tmpl', data); - - // yes, you can use {@link} in tutorials too! - html = helper.resolveLinks(html); // turn {@link foo} into foo - - fs.writeFileSync(path, html); - } - - // tutorials can have only one parent so there is no risk for loops - function saveChildren(node) { - node.children.forEach(function(child) { - generateTutorial('Tutorial: '+child.title, child, helper.tutorialToUrl(child.name)); - saveChildren(child); - }); - } - saveChildren(tutorials); - }; + var mixins = find({kind: 'mixin', longname: longname}); + if (mixins.length) { generate('Mixin: '+mixins[0].name, mixins, helper.longnameToUrl[longname]); } - function hashToLink(doclet, hash) { - if ( !/^(#.+)/.test(hash) ) { return hash; } + var externals = find({kind: 'external', longname: longname}); + if (externals.length) { generate('External: '+externals[0].name, externals, helper.longnameToUrl[longname]); } + } + } + + if (globals.length) { generate('Global', [{kind: 'globalobj'}], 'global.html'); } + + // index page displays information from package.json and lists files + var files = find({kind: 'file'}), + packages = find({kind: 'package'}); + + generate('Index', + packages.concat( + [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] + ).concat(files), + 'index.html'); + + + function generateTutorial(title, tutorial, filename) { + var data = { + title: title, + header: tutorial.title, + content: tutorial.parse(), + children: tutorial.children + }; - var url = helper.createLink(doclet); + var path = outdir + '/' + filename, + html = view.render('tutorial.tmpl', data); - url = url.replace(/(#.+|$)/, hash); - return ''+hash+''; + // yes, you can use {@link} in tutorials too! + html = helper.resolveLinks(html); // turn {@link foo} into foo + + fs.writeFileSync(path, html); } -}()); + // tutorials can have only one parent so there is no risk for loops + function saveChildren(node) { + node.children.forEach(function(child) { + generateTutorial('Tutorial: '+child.title, child, helper.tutorialToUrl(child.name)); + saveChildren(child); + }); + } + saveChildren(tutorials); +}; diff --git a/templates/haruki/publish.js b/templates/haruki/publish.js index 706eae75..b1dacb6f 100644 --- a/templates/haruki/publish.js +++ b/templates/haruki/publish.js @@ -1,214 +1,207 @@ -/*global publish: true */ /** @overview Builds a tree-like JSON string from the doclet data. - @version 0.0.1 + @version 0.0.2 @example ./jsdoc scratch/jsdoc_test.js -t templates/haruki -d console -q format=xml */ -(function() { - - /** - @global - @param {TAFFY} data - @param {object} opts - */ - publish = function(data, opts) { - - var root = {}, - docs; - - data.remove({undocumented: true}); - docs = data.get(); // <-- an array of Doclet objects - - graft(root, docs); - - if (opts.destination === 'console') { - if (opts.query && opts.query.format === 'xml') { - var xml = require('goessner/json2xml'); - console.log( '\n' + xml.convert(root) + '\n' ); - } - else { - console.log(root); +function graft(parentNode, childNodes, parentLongname, parentName) { + childNodes + .filter(function (element) { + return (element.memberof === parentLongname); + }) + .forEach(function (element, i) { + if (element.kind === 'namespace') { + if (! parentNode.namespaces) { + parentNode.namespaces = { }; } + + var thisNamespace = parentNode.namespaces[element.name] = { + 'name': element.name, + 'description': element.description || '', + 'access': element.access || '', + 'virtual': !!element.virtual + }; + + graft(thisNamespace, childNodes, element.longname, element.name); } - else { - console.log('The only -d destination option currently supported is "console"!'); + else if (element.kind === 'mixin') { + if (! parentNode.mixins) { + parentNode.mixins = { }; + } + + var thisMixin = parentNode.mixins[element.name] = { + 'name': element.name, + 'description': element.description || '', + 'access': element.access || '', + 'virtual': !!element.virtual + }; + + graft(thisMixin, childNodes, element.longname, element.name); } - - }; - - function graft(parentNode, childNodes, parentLongname, parentName) { - childNodes - .filter(function (element) { - return (element.memberof === parentLongname); - }) - .forEach(function (element, i) { - if (element.kind === 'namespace') { - if (! parentNode.namespaces) { - parentNode.namespaces = { }; - } - - var thisNamespace = parentNode.namespaces[element.name] = { - 'name': element.name, - 'description': element.description || '', - 'access': element.access || '', - 'virtual': !!element.virtual - }; - - graft(thisNamespace, childNodes, element.longname, element.name); + else if (element.kind === 'function') { + if (! parentNode.functions) { + parentNode.functions = { }; } - else if (element.kind === 'mixin') { - if (! parentNode.mixins) { - parentNode.mixins = { }; - } - - var thisMixin = parentNode.mixins[element.name] = { - 'name': element.name, - 'description': element.description || '', - 'access': element.access || '', - 'virtual': !!element.virtual - }; - - graft(thisMixin, childNodes, element.longname, element.name); - } - else if (element.kind === 'function') { - if (! parentNode.functions) { - parentNode.functions = { }; - } - - var thisFunction = parentNode.functions[element.name] = { - 'name': element.name, - 'access': element.access || '', - 'virtual': !!element.virtual, - 'description': element.description || '', - 'parameters': [ ], - 'examples': [] - }; - - if (element.returns) { - parentNode.functions[element.name].returns = { - 'type': element.returns[0].type? (element.returns[0].type.names.length === 1? element.returns[0].type.names[0] : element.returns[0].type.names) : '', - 'description': element.returns[0].description || '' - }; - } - - if (element.examples) { - for (var i = 0, len = element.examples.length; i < len; i++) { - parentNode.functions[element.name].examples.push(element.examples[i]); - } - } - - if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { - thisFunction.parameters.push({ - 'name': element.params[i].name, - 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', - 'description': element.params[i].description || '', - 'default': element.params[i].defaultvalue || '', - 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', - 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' - }); - } - } - } - else if (element.kind === 'member') { - if (! parentNode.properties) { - parentNode.properties = { }; - } - parentNode.properties[element.name] = { - 'name': element.name, - 'access': element.access || '', - 'virtual': !!element.virtual, - 'description': element.description || '', - 'type': element.type? (element.type.length === 1? element.type[0] : element.type) : '' + + var thisFunction = parentNode.functions[element.name] = { + 'name': element.name, + 'access': element.access || '', + 'virtual': !!element.virtual, + 'description': element.description || '', + 'parameters': [ ], + 'examples': [] + }; + + if (element.returns) { + parentNode.functions[element.name].returns = { + 'type': element.returns[0].type? (element.returns[0].type.names.length === 1? element.returns[0].type.names[0] : element.returns[0].type.names) : '', + 'description': element.returns[0].description || '' }; } - else if (element.kind === 'event') { - if (! parentNode.events) { - parentNode.events = { }; - } - - var thisEvent = parentNode.events[element.name] = { - 'name': element.name, - 'access': element.access || '', - 'virtual': !!element.virtual, - 'description': element.description || '', - 'parameters': [], - 'examples': [] - }; - - if (element.returns) { - parentNode.events[element.name].returns = { - 'type': element.returns.type? (element.returns.type.names.length === 1? element.returns.type.names[0] : element.returns.type.names) : '', - 'description': element.returns.description || '' - }; - } - - if (element.examples) { - for (var i = 0, len = element.examples.length; i < len; i++) { - thisEvent.examples.push(element.examples[i]); - } - } - - if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { - thisEvent.parameters.push({ - 'name': element.params[i].name, - 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', - 'description': element.params[i].description || '', - 'default': element.params[i].defaultvalue || '', - 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', - 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' - }); - } + if (element.examples) { + for (var i = 0, len = element.examples.length; i < len; i++) { + parentNode.functions[element.name].examples.push(element.examples[i]); } } - else if (element.kind === 'class') { - if (! parentNode.classes) { - parentNode.classes = { }; + + if (element.params) { + for (var i = 0, len = element.params.length; i < len; i++) { + thisFunction.parameters.push({ + 'name': element.params[i].name, + 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', + 'description': element.params[i].description || '', + 'default': element.params[i].defaultvalue || '', + 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', + 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' + }); } - - var thisClass = parentNode.classes[element.name] = { - 'name': element.name, - 'description': element.classdesc || '', - 'extends': element.augments || [], - 'access': element.access || '', - 'virtual': !!element.virtual, - 'fires': element.fires || '', - 'constructor': { - 'name': element.name, - 'description': element.description || '', - 'parameters': [ - ], - 'examples': [] - } + } + } + else if (element.kind === 'member') { + if (! parentNode.properties) { + parentNode.properties = { }; + } + parentNode.properties[element.name] = { + 'name': element.name, + 'access': element.access || '', + 'virtual': !!element.virtual, + 'description': element.description || '', + 'type': element.type? (element.type.length === 1? element.type[0] : element.type) : '' + }; + } + + else if (element.kind === 'event') { + if (! parentNode.events) { + parentNode.events = { }; + } + + var thisEvent = parentNode.events[element.name] = { + 'name': element.name, + 'access': element.access || '', + 'virtual': !!element.virtual, + 'description': element.description || '', + 'parameters': [], + 'examples': [] + }; + + if (element.returns) { + parentNode.events[element.name].returns = { + 'type': element.returns.type? (element.returns.type.names.length === 1? element.returns.type.names[0] : element.returns.type.names) : '', + 'description': element.returns.description || '' }; - - if (element.examples) { - for (var i = 0, len = element.examples.length; i < len; i++) { - thisClass.constructor.examples.push(element.examples[i]); - } + } + + if (element.examples) { + for (var i = 0, len = element.examples.length; i < len; i++) { + thisEvent.examples.push(element.examples[i]); } - - if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { - thisClass.constructor.parameters.push({ - 'name': element.params[i].name, - 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', - 'description': element.params[i].description || '', - 'default': element.params[i].defaultvalue || '', - 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', - 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' - }); - } + } + + if (element.params) { + for (var i = 0, len = element.params.length; i < len; i++) { + thisEvent.parameters.push({ + 'name': element.params[i].name, + 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', + 'description': element.params[i].description || '', + 'default': element.params[i].defaultvalue || '', + 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', + 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' + }); } - - graft(thisClass, childNodes, element.longname, element.name); - } - }); + } + } + else if (element.kind === 'class') { + if (! parentNode.classes) { + parentNode.classes = { }; + } + + var thisClass = parentNode.classes[element.name] = { + 'name': element.name, + 'description': element.classdesc || '', + 'extends': element.augments || [], + 'access': element.access || '', + 'virtual': !!element.virtual, + 'fires': element.fires || '', + 'constructor': { + 'name': element.name, + 'description': element.description || '', + 'parameters': [ + ], + 'examples': [] + } + }; + + if (element.examples) { + for (var i = 0, len = element.examples.length; i < len; i++) { + thisClass.constructor.examples.push(element.examples[i]); + } + } + + if (element.params) { + for (var i = 0, len = element.params.length; i < len; i++) { + thisClass.constructor.parameters.push({ + 'name': element.params[i].name, + 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', + 'description': element.params[i].description || '', + 'default': element.params[i].defaultvalue || '', + 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', + 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' + }); + } + } + + graft(thisClass, childNodes, element.longname, element.name); + } + }); +} + +/** + @param {TAFFY} data + @param {object} opts + */ +exports.publish = function(data, opts) { + + var root = {}, + docs; + + data.remove({undocumented: true}); + docs = data.get(); // <-- an array of Doclet objects + + graft(root, docs); + + if (opts.destination === 'console') { + if (opts.query && opts.query.format === 'xml') { + var xml = require('goessner/json2xml'); + console.log( '\n' + xml.convert(root) + '\n' ); + } + else { + console.log(root); + } } - -}()); - + else { + console.log('The only -d destination option currently supported is "console"!'); + } + +}; From ef6f2e92d518dad8946d41d14db1e389fd4b1dfd Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Wed, 22 Aug 2012 20:07:33 -0700 Subject: [PATCH 05/10] use arrays instead of objects in Haruki template. fixes #153. (thanks to @kpozin for the fix!) --- templates/haruki/publish.js | 44 +++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/templates/haruki/publish.js b/templates/haruki/publish.js index b1dacb6f..c188b24e 100644 --- a/templates/haruki/publish.js +++ b/templates/haruki/publish.js @@ -1,6 +1,6 @@ /** @overview Builds a tree-like JSON string from the doclet data. - @version 0.0.2 + @version 0.0.3 @example ./jsdoc scratch/jsdoc_test.js -t templates/haruki -d console -q format=xml */ @@ -13,38 +13,42 @@ function graft(parentNode, childNodes, parentLongname, parentName) { .forEach(function (element, i) { if (element.kind === 'namespace') { if (! parentNode.namespaces) { - parentNode.namespaces = { }; + parentNode.namespaces = []; } - var thisNamespace = parentNode.namespaces[element.name] = { + var thisNamespace = { 'name': element.name, 'description': element.description || '', 'access': element.access || '', 'virtual': !!element.virtual }; + + parentNode.namespaces.push(thisNamespace); graft(thisNamespace, childNodes, element.longname, element.name); } else if (element.kind === 'mixin') { if (! parentNode.mixins) { - parentNode.mixins = { }; + parentNode.mixins = []; } - var thisMixin = parentNode.mixins[element.name] = { + var thisMixin = { 'name': element.name, 'description': element.description || '', 'access': element.access || '', 'virtual': !!element.virtual }; + + parentNode.mixins.push(thisMixin); graft(thisMixin, childNodes, element.longname, element.name); } else if (element.kind === 'function') { if (! parentNode.functions) { - parentNode.functions = { }; + parentNode.functions = []; } - var thisFunction = parentNode.functions[element.name] = { + var thisFunction = { 'name': element.name, 'access': element.access || '', 'virtual': !!element.virtual, @@ -53,8 +57,10 @@ function graft(parentNode, childNodes, parentLongname, parentName) { 'examples': [] }; + parentNode.functions.push(thisFunction); + if (element.returns) { - parentNode.functions[element.name].returns = { + thisFunction.returns = { 'type': element.returns[0].type? (element.returns[0].type.names.length === 1? element.returns[0].type.names[0] : element.returns[0].type.names) : '', 'description': element.returns[0].description || '' }; @@ -62,7 +68,7 @@ function graft(parentNode, childNodes, parentLongname, parentName) { if (element.examples) { for (var i = 0, len = element.examples.length; i < len; i++) { - parentNode.functions[element.name].examples.push(element.examples[i]); + thisFunction.examples.push(element.examples[i]); } } @@ -81,23 +87,23 @@ function graft(parentNode, childNodes, parentLongname, parentName) { } else if (element.kind === 'member') { if (! parentNode.properties) { - parentNode.properties = { }; + parentNode.properties = []; } - parentNode.properties[element.name] = { + parentNode.properties.push({ 'name': element.name, 'access': element.access || '', 'virtual': !!element.virtual, 'description': element.description || '', 'type': element.type? (element.type.length === 1? element.type[0] : element.type) : '' - }; + }); } else if (element.kind === 'event') { if (! parentNode.events) { - parentNode.events = { }; + parentNode.events = []; } - var thisEvent = parentNode.events[element.name] = { + var thisEvent = { 'name': element.name, 'access': element.access || '', 'virtual': !!element.virtual, @@ -105,9 +111,11 @@ function graft(parentNode, childNodes, parentLongname, parentName) { 'parameters': [], 'examples': [] }; + + parentNode.events.push(thisEvent); if (element.returns) { - parentNode.events[element.name].returns = { + thisEvent.returns = { 'type': element.returns.type? (element.returns.type.names.length === 1? element.returns.type.names[0] : element.returns.type.names) : '', 'description': element.returns.description || '' }; @@ -134,10 +142,10 @@ function graft(parentNode, childNodes, parentLongname, parentName) { } else if (element.kind === 'class') { if (! parentNode.classes) { - parentNode.classes = { }; + parentNode.classes = []; } - var thisClass = parentNode.classes[element.name] = { + var thisClass = { 'name': element.name, 'description': element.classdesc || '', 'extends': element.augments || [], @@ -152,6 +160,8 @@ function graft(parentNode, childNodes, parentLongname, parentName) { 'examples': [] } }; + + parentNode.classes.push(thisClass); if (element.examples) { for (var i = 0, len = element.examples.length; i < len; i++) { From 358dceac2cf73987676efa6927866eb085575bfb Mon Sep 17 00:00:00 2001 From: Allan Jardine Date: Mon, 3 Sep 2012 10:23:55 +0100 Subject: [PATCH 06/10] Fix jsdoc issue #172: Remove second call to unwrap. --- rhino_modules/jsdoc/doclet.js | 1 - 1 file changed, 1 deletion(-) diff --git a/rhino_modules/jsdoc/doclet.js b/rhino_modules/jsdoc/doclet.js index 49c69814..2326eca1 100644 --- a/rhino_modules/jsdoc/doclet.js +++ b/rhino_modules/jsdoc/doclet.js @@ -104,7 +104,6 @@ function toTags(docletSrc) { var tagSrcs, tags = []; - docletSrc = unwrap(docletSrc); tagSrcs = split(docletSrc); for (var i = 0, l = tagSrcs.length; i < l; i++) { From f5e3f0f31dbda794dcdefad57d5ddde1b2858efe Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 3 Sep 2012 10:23:25 -0700 Subject: [PATCH 07/10] save output in the current working directory, not the JSDoc directory --- templates/default/publish.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/default/publish.js b/templates/default/publish.js index c7d7bcc6..69d6ac66 100644 --- a/templates/default/publish.js +++ b/templates/default/publish.js @@ -176,7 +176,7 @@ exports.publish = function(data, opts, tutorials) { var globals = find( {kind: ['member', 'function', 'constant', 'typedef'], memberof: {isUndefined: true}} ); - var outdir = env.dirname + '/' + opts.destination; + var outdir = opts.destination; if (packageInfo && packageInfo.name) { outdir += '/' + packageInfo.name + '/' + packageInfo.version + '/'; } From 7675640537ac725173b05a6348a351639ea89caf Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 8 Sep 2012 18:00:16 -0700 Subject: [PATCH 08/10] improve error message when template is missing (closes #171), plus minor cleanup --- jsdoc.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/jsdoc.js b/jsdoc.js index 95633368..69d1f7ec 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -1,4 +1,4 @@ -/*global app: true, args: true, env: true, publish: true */ +/*global app: true, args: true, env: true, Packages: true, publish: true */ /** * @project jsdoc * @author Michael Mathews @@ -325,9 +325,15 @@ function main() { env.opts.template = env.opts.template || 'templates/default'; - // templates should include a publish.js file that exports a "publish" function - var template = require(env.opts.template + '/publish'); + var template; + try { + template = require(env.opts.template + '/publish'); + } + catch(e) { + throw new Error("Unable to load template: Couldn't find publish.js in " + env.opts.template); + } + // templates should include a publish.js file that exports a "publish" function if (template.publish && typeof template.publish === 'function') { template.publish( new (require('typicaljoe/taffy'))(docs), @@ -341,7 +347,7 @@ function main() { if (publish && typeof publish === 'function') { console.log( env.opts.template + ' uses a global "publish" function, which is ' + 'deprecated and may not be supported in future versions. ' + - 'Please update the template so that it exports a "publish" function.' ); + 'Please update the template to use "exports.publish" instead.' ); publish( new (require('typicaljoe/taffy'))(docs), env.opts, From d22d59b3158ddcb0f6c229e025000bf83d87e3b0 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 8 Sep 2012 18:15:20 -0700 Subject: [PATCH 09/10] improve error message when "-d console" isn't used (closes #174); JSHint cleanup --- templates/haruki/publish.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/templates/haruki/publish.js b/templates/haruki/publish.js index c188b24e..00c47648 100644 --- a/templates/haruki/publish.js +++ b/templates/haruki/publish.js @@ -10,7 +10,10 @@ function graft(parentNode, childNodes, parentLongname, parentName) { .filter(function (element) { return (element.memberof === parentLongname); }) - .forEach(function (element, i) { + .forEach(function (element, index) { + var i, + len; + if (element.kind === 'namespace') { if (! parentNode.namespaces) { parentNode.namespaces = []; @@ -67,13 +70,13 @@ function graft(parentNode, childNodes, parentLongname, parentName) { } if (element.examples) { - for (var i = 0, len = element.examples.length; i < len; i++) { + for (i = 0, len = element.examples.length; i < len; i++) { thisFunction.examples.push(element.examples[i]); } } if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { + for (i = 0, len = element.params.length; i < len; i++) { thisFunction.parameters.push({ 'name': element.params[i].name, 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', @@ -122,13 +125,13 @@ function graft(parentNode, childNodes, parentLongname, parentName) { } if (element.examples) { - for (var i = 0, len = element.examples.length; i < len; i++) { + for (i = 0, len = element.examples.length; i < len; i++) { thisEvent.examples.push(element.examples[i]); } } if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { + for (i = 0, len = element.params.length; i < len; i++) { thisEvent.parameters.push({ 'name': element.params[i].name, 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', @@ -164,13 +167,13 @@ function graft(parentNode, childNodes, parentLongname, parentName) { parentNode.classes.push(thisClass); if (element.examples) { - for (var i = 0, len = element.examples.length; i < len; i++) { + for (i = 0, len = element.examples.length; i < len; i++) { thisClass.constructor.examples.push(element.examples[i]); } } if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { + for (i = 0, len = element.params.length; i < len; i++) { thisClass.constructor.parameters.push({ 'name': element.params[i].name, 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', @@ -211,7 +214,7 @@ exports.publish = function(data, opts) { } } else { - console.log('The only -d destination option currently supported is "console"!'); + console.log('This template only supports output to the console. Use the option "-d console" when you run JSDoc.'); } }; From e59dc9023aaa34c49c5f70a75ace22538edc41a3 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 9 Sep 2012 07:18:13 -0700 Subject: [PATCH 10/10] tests for doclets with Markdown asterisks --- test/fixtures/doclet.js | 23 +++++++++++++++++++++++ test/specs/jsdoc/doclet.js | 23 +++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/doclet.js diff --git a/test/fixtures/doclet.js b/test/fixtures/doclet.js new file mode 100644 index 00000000..abba2473 --- /dev/null +++ b/test/fixtures/doclet.js @@ -0,0 +1,23 @@ +/** + Markdown asterisks in a doclet that does not use leading asterisks. + **Strong** is strong. + + * List item 1. + * List item 2. + @param {string} thingy - The thingy. + */ +function test1(thingy) { + +} + +/** + * Markdown asterisks in a doclet that uses leading asterisks. + * **Strong** is strong. + * + * * List item 1. + * * List item 2. + * @param {string} thingy - The thingy. + */ +function test2(thingy) { + +} diff --git a/test/specs/jsdoc/doclet.js b/test/specs/jsdoc/doclet.js index 1bb1500f..ad9345dd 100644 --- a/test/specs/jsdoc/doclet.js +++ b/test/specs/jsdoc/doclet.js @@ -1,3 +1,22 @@ +/*global describe: true, env: true, expect: true, it: true, jasmine: true, xit: true */ describe("jsdoc/doclet", function() { - //TODO -}); \ No newline at end of file + // TODO: more tests + + var docSet = jasmine.getDocSetFromFile('test/fixtures/doclet.js'), + test1 = docSet.getByLongname('test1')[0], + test2 = docSet.getByLongname('test2')[0]; + + var expectStrong = "**Strong** is strong"; + var expectList = "* List item 1"; + + // TODO: reenable the test and make it pass, or remove the test + xit('does not mangle Markdown in a description that does not use leading asterisks', function() { + expect(test1.description.indexOf(expectStrong)).toBeGreaterThan(-1); + expect(test1.description.indexOf(expectList)).toBeGreaterThan(-1); + }); + + it('does not mangle Markdown in a description that uses leading asterisks', function() { + expect(test2.description.indexOf(expectStrong)).toBeGreaterThan(-1); + expect(test2.description.indexOf(expectList)).toBeGreaterThan(-1); + }); +});