documentation
Version:
a documentation generator
197 lines (179 loc) • 6 kB
JavaScript
import doctrine from 'doctrine-temporary-fork';
const Syntax = doctrine.Syntax;
import { u } from 'unist-builder';
/**
* Shortcut to create a new text node
*
* @param {string} text contents
* @returns {Object} remark AST node
*/
function t(text) {
return u('text', text);
}
/**
* Helper used to automatically link items to global JS documentation or to internal
* documentation.
*
* @param {string} text - text to potentially link
* @param {function} [getHref] - a function that tries
* to find a URL to point a named link to
* @param {string} description text that will be shown to the user, if this
* is a two-part link with both target and text
* @returns {Object} [mdast](https://www.npmjs.com/package/mdast) node
* @example
* link('string').url // => 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String'
*/
function link(text, getHref, description) {
const href = getHref(text);
if (href) {
// TODO: this is a temporary fix until we drop remark 3.x support,
// and then we should remove the 'href' property and only
// have the url property of links
return u(
'link',
{
href,
url: href
},
[t(description || text)]
);
}
return t(text);
}
/**
* Given a list of types, a method to get a link location, and start,
* end, and separator strings, format a list of potential types. This is
* used for optional arrays, like where either a string or number is
* accepted as an input.
*
* @param {Function} getHref a method that resolves a namepath to a path
* @param {Array<Object>} items a list of doctrine-formatted type objects
* @param {string} start string to prefix the output
* @param {string} end string to suffix the output
* @param {string} sep string between items
* @returns {Array<Object>} formatted remark AST
*/
function commaList(getHref, items, start, end, sep) {
let res = [];
if (start) {
res.push(t(start));
}
for (let i = 0, iz = items.length; i < iz; ++i) {
res = res.concat(formatType(getHref, items[i]));
if (i + 1 !== iz) {
res.push(t(sep || ', '));
}
}
if (end) {
res.push(t(end));
}
return res;
}
/**
* Add a string after and potentially before a formatted type definition
*
* @param {Array<Object>} formatted remark AST of a type definition
* @param {string} str postfix
* @param {boolean} prefix string to put after the type comment
* @returns {Array<Object>} suffixed and potentially prefixed type
*/
function decorate(formatted, str, prefix) {
if (prefix) {
return [t(str)].concat(formatted);
}
return formatted.concat(t(str));
}
/**
* Helper used to format JSDoc-style type definitions into HTML or Markdown.
*
* @name formatType
* @param {function} getHref - a function that tries
* to find a URL to point a named link to
* @param {Object} node - type object in doctrine style
* @returns {Object[]} array of [mdast](https://www.npmjs.com/package/mdast) syntax trees
* @example
* formatType({ type: 'NameExpression', name: 'String' })[0].url
* // => 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String'
*/
export default function formatType(getHref, node) {
let result = [];
if (!node) {
return [t('any')];
}
switch (node.type) {
case Syntax.NullableLiteral:
return [t('?')];
case Syntax.AllLiteral:
return [t('any')];
case Syntax.NullLiteral:
return [t('null')];
case Syntax.VoidLiteral:
return [t('void')];
case Syntax.UndefinedLiteral:
return [link('undefined', getHref)];
case Syntax.NameExpression:
return [link(node.name, getHref)];
case Syntax.ParameterType:
if (node.name) {
result.push(t(node.name + ': '));
}
return result.concat(formatType(getHref, node.expression));
case Syntax.TypeApplication:
return formatType(getHref, node.expression).concat(
commaList(getHref, node.applications, '<', '>')
);
case Syntax.UnionType:
return commaList(getHref, node.elements, '(', ')', ' | ');
case Syntax.ArrayType:
return commaList(getHref, node.elements, '[', ']');
case Syntax.RecordType:
return commaList(getHref, node.fields, '{', '}');
case Syntax.FieldType:
if (node.value) {
return [t(node.key + ': ')].concat(formatType(getHref, node.value));
}
return [t(node.key)];
case Syntax.FunctionType:
result = [t('function (')];
if (node['this']) {
if (node['new']) {
result.push(t('new: '));
} else {
result.push(t('this: '));
}
result = result.concat(formatType(getHref, node['this']));
if (node.params.length !== 0) {
result.push(t(', '));
}
}
result = result.concat(commaList(getHref, node.params, '', ')'));
if (node.result) {
result = result.concat(
[t(': ')].concat(formatType(getHref, node.result))
);
}
return result;
case Syntax.RestType:
// note that here we diverge from doctrine itself, which
// lets the expression be omitted.
return decorate(formatType(getHref, node.expression), '...', true);
case Syntax.OptionalType:
if (node.default) {
return decorate(formatType(getHref, node.expression), '?').concat(
t('= ' + node.default)
);
}
return decorate(formatType(getHref, node.expression), '?');
case Syntax.NonNullableType:
return decorate(formatType(getHref, node.expression), '!', node.prefix);
case Syntax.NullableType:
return decorate(formatType(getHref, node.expression), '?');
case Syntax.StringLiteralType:
return [u('inlineCode', JSON.stringify(node.value))];
case Syntax.NumericLiteralType:
case Syntax.BooleanLiteralType:
return [u('inlineCode', String(node.value))];
default:
throw new Error('Unknown type ' + node.type);
}
}