Improve link handling in API docs

This commit is contained in:
ahocevar
2018-10-24 17:49:00 +02:00
parent b11daa8344
commit 453a723005
4 changed files with 112 additions and 7 deletions

View File

@@ -0,0 +1,111 @@
/**
* Modified from JSDoc's plugins/markdown and lib/jsdoc/util/markdown modules
* (see https://github.com/jsdoc3/jsdoc/), which are licensed under the Apache 2
* license (see http://www.apache.org/licenses/LICENSE-2.0).
*
* This version does not protect http(s) urls from being turned into links, and
* works around an issue with `~` characters in module paths by escaping them.
*/
const marked = require('marked');
const format = require('util').format;
const tags = [
'author',
'classdesc',
'description',
'exceptions',
'params',
'properties',
'returns',
'see',
'summary'
];
const hasOwnProp = Object.prototype.hasOwnProperty;
const markedRenderer = new marked.Renderer();
// Allow prettyprint to work on inline code samples
markedRenderer.code = function(code, language) {
const langClass = language ? ' lang-' + language : '';
return format('<pre class="prettyprint source%s"><code>%s</code></pre>',
langClass, escapeCode(code));
};
function escapeCode(source) {
return source.replace(/</g, '&lt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function escapeUnderscoresAndTildes(source) {
return source.replace(/\{@[^}\r\n]+\}/g, function(wholeMatch) {
return wholeMatch
.replace(/(^|[^\\])_/g, '$1\\_')
.replace('~', '&tilde;');
});
}
function unencodeQuotesAndTildes(source) {
return source.replace(/\{@[^}\r\n]+\}/g, function(wholeMatch) {
return wholeMatch
.replace(/&quot;/g, '"')
.replace(/&tilde;/g, '~');
});
}
function parse(source) {
let result;
source = escapeUnderscoresAndTildes(source);
result = marked(source, {renderer: markedRenderer})
.replace(/\s+$/, '')
.replace(/&#39;/g, '\'');
result = unencodeQuotesAndTildes(result);
return result;
}
function shouldProcessString(tagName, text) {
let shouldProcess = true;
// we only want to process `@author` and `@see` tags that contain Markdown links
if ((tagName === 'author' || tagName === 'see') && text.indexOf('[') === -1) {
shouldProcess = false;
}
return shouldProcess;
}
function process(doclet) {
tags.forEach(function(tag) {
if (!hasOwnProp.call(doclet, tag)) {
return;
}
if (typeof doclet[tag] === 'string' && shouldProcessString(tag, doclet[tag])) {
doclet[tag] = parse(doclet[tag]);
} else if (Array.isArray(doclet[tag])) {
doclet[tag].forEach(function(value, index, original) {
const inner = {};
inner[tag] = value;
process(inner);
original[index] = inner[tag];
});
} else if (doclet[tag]) {
process(doclet[tag]);
}
});
}
exports.handlers = {
newDoclet: function(e) {
process(e.doclet);
}
};