diff --git a/fixtures/test6.js b/fixtures/test6.js index 8153624..7a8d2a8 100644 --- a/fixtures/test6.js +++ b/fixtures/test6.js @@ -1,5 +1,5 @@ /** - @overview This sample handles namespaces and interfaces. + @overview This sample handles namespaces, interfaces, and links. @license MIT */ @@ -24,7 +24,7 @@ main.dispose = function() {}; /** - * Definition for a Thing object. + * Definition for a Thing object used by a Worker. See {@link main.Worker}. * @interface */ main.Thing; @@ -52,9 +52,9 @@ main.Worker; /** - * Have a Worker do some Thing. + * Have a Worker do some Thing. See {@link main.Thing}. * - * @param {main.Thing} thing The Thing to do. + * @param {main.Thing} thing The Thing to do. See {@link main.Thing}. */ main.Worker.prototype.do = function(thing) {}; diff --git a/lib/generateMD.js b/lib/generateMD.js index 5702118..1cbfef9 100644 --- a/lib/generateMD.js +++ b/lib/generateMD.js @@ -6,6 +6,55 @@ var Mustache = require('mustache'); var fs = require('fs'); var path = require('path'); +var util = require('util'); + + +/** + * Replaces {@link ...} with `[...](...)`. + * @param {string} str - string to process + * @param {Object} targets - map of targets to use for links (optional) + * @return {string} + */ +function replaceLink(str, targets) { + return str.replace(/\{@link\s+([^}]+)\}/g, function(full, link) { + if (link in targets) { + return util.format('[%s](%s)', link, targets[link]); + } else if (link.match(/^(https?:)?\/\//)) { + return util.format('[%s](%s)', link, link); + } + return link; + }); +} + + +/** + * Processes a tag for Markdown replacements. + * @param {Object} tag - tag to process + * @param {Object} targets - map of targets to use for links (optional) + */ +function processTag(tag, targets) { + if (tag.description) { + tag.description = replaceLink(tag.description, targets); + } + if (tag.params) { + tag.params.forEach(function (param) { + if (param.description) { + param.description = replaceLink(param.description, targets); + } + }); + } + if (tag.members) { + tag.members.forEach(function (member) { + processTag(member, targets); + }); + } + if (tag.methods) { + tag.methods.forEach(function (method) { + processTag(method, targets); + }); + } +} + /** * Renders markdown from the given analyzed AST @@ -24,6 +73,16 @@ module.exports = function(ast, templateDir, isIndex, sort) { templateDir = templateDir.replace(/\\/g, '/'); } + var tags = (ast.modules || []) + .concat(ast.classes || []) + .concat(ast.functions || []); + var targets = {}; + tags.forEach(function (tag) { + if (tag.longname) { + tag.target = targets[tag.longname] = '#' + tag.longname.toLowerCase(); + } + }); + //if ast is an index file, we need to sort the contents and to use the right templates; if (isIndex) { console.log('Now generating index'); @@ -60,6 +119,10 @@ module.exports = function(ast, templateDir, isIndex, sort) { return Mustache.render(templates.index, ast, templates); } + tags.forEach(function (tag) { + processTag(tag, targets); + }); + templates = { file: fs.readFileSync(templateDir + '/file.mustache', 'utf8'), class: fs.readFileSync(templateDir + '/class.mustache', 'utf8'), diff --git a/sample_output/test6.md b/sample_output/test6.md index 1f5085a..bca2bb2 100644 --- a/sample_output/test6.md +++ b/sample_output/test6.md @@ -19,7 +19,7 @@ Disposes everything. ## Class: Thing -Definition for a Thing object. +Definition for a Thing object used by a Worker. See [main.Worker](#main.worker). **name**: `string` , Every Thing has a name. **data**: `* | undefined` , Every Thing might have some data. @@ -29,11 +29,11 @@ Definition for a Worker. ### main.Worker.do(thing) -Have a Worker do some Thing. +Have a Worker do some Thing. See [main.Thing](#main.thing). **Parameters** -**thing**: `main.Thing`, The Thing to do. +**thing**: `main.Thing`, The Thing to do. See [main.Thing](#main.thing). @@ -67,6 +67,6 @@ Run the Bar utility. **License:** MIT -**Overview:** This sample handles namespaces and interfaces. +**Overview:** This sample handles namespaces, interfaces, and links. diff --git a/test/lib/generateMD.js b/test/lib/generateMD.js index cf283dc..0655295 100644 --- a/test/lib/generateMD.js +++ b/test/lib/generateMD.js @@ -82,15 +82,15 @@ describe('generateMD', function() { generateMD(analyzed, null, true, 'standard'); expect(analyzed.functions).to.eql([ - {name: 'four', longname: 'foo.four' }, - {name: 'one', longname: 'foo.one' }, - {name: 'three', longname: 'bar.three' }, - {name: 'two', longname: 'bar.two' }, - {name: 'zero', longname: 'zero' } + {name: 'four', longname: 'foo.four', target: '#foo.four' }, + {name: 'one', longname: 'foo.one', target: '#foo.one' }, + {name: 'three', longname: 'bar.three', target: '#bar.three' }, + {name: 'two', longname: 'bar.two', target: '#bar.two' }, + {name: 'zero', longname: 'zero', target: '#zero' } ]); expect(analyzed.classes).to.eql([ - {name: 'Five', longname: 'Five' }, - {name: 'Six', longname: 'bar.Six' } + {name: 'Five', longname: 'Five', target: '#five' }, + {name: 'Six', longname: 'bar.Six', target: '#bar.six' } ]); }); @@ -112,15 +112,15 @@ describe('generateMD', function() { generateMD(analyzed, null, true, 'namespace'); expect(analyzed.functions).to.eql([ - {name: 'zero', longname: 'zero' }, - {name: 'three', longname: 'bar.three' }, - {name: 'two', longname: 'bar.two' }, - {name: 'four', longname: 'foo.four' }, - {name: 'one', longname: 'foo.one' } + {name: 'zero', longname: 'zero', target: '#zero' }, + {name: 'three', longname: 'bar.three', target: '#bar.three' }, + {name: 'two', longname: 'bar.two', target: '#bar.two' }, + {name: 'four', longname: 'foo.four', target: '#foo.four' }, + {name: 'one', longname: 'foo.one', target: '#foo.one' } ]); expect(analyzed.classes).to.eql([ - {name: 'Five', longname: 'Five' }, - {name: 'Six', longname: 'bar.Six' } + {name: 'Five', longname: 'Five', target: '#five' }, + {name: 'Six', longname: 'bar.Six', target: '#bar.six' } ]); }); @@ -142,15 +142,15 @@ describe('generateMD', function() { generateMD(analyzed, null, true, 'none'); expect(analyzed.functions).to.eql([ - {name: 'zero', longname: 'zero' }, - {name: 'one', longname: 'foo.one' }, - {name: 'two', longname: 'bar.two' }, - {name: 'three', longname: 'bar.three' }, - {name: 'four', longname: 'foo.four' } + {name: 'zero', longname: 'zero', target: '#zero' }, + {name: 'one', longname: 'foo.one', target: '#foo.one' }, + {name: 'two', longname: 'bar.two', target: '#bar.two' }, + {name: 'three', longname: 'bar.three', target: '#bar.three' }, + {name: 'four', longname: 'foo.four', target: '#foo.four' } ]); expect(analyzed.classes).to.eql([ - {name: 'Five', longname: 'Five' }, - {name: 'Six', longname: 'bar.Six' } + {name: 'Five', longname: 'Five', target: '#five' }, + {name: 'Six', longname: 'bar.Six', target: '#bar.six' } ]); }); });