UNPKG

documentation

Version:
164 lines (138 loc) 8.09 kB
'use strict'; var u = require('unist-builder'); var remark = require('remark'); var mergeConfig = require('../merge_config'); var toc = require('remark-toc'); var hljs = require('highlight.js'); var GithubSlugger = require('github-slugger'); var LinkerStack = require('./util/linker_stack'); var rerouteLinks = require('./util/reroute_links'); var _formatType = require('./util/format_type'); var DEFAULT_LANGUAGE = 'javascript'; /** * Given a hierarchy-nested set of comments, generate an remark-compatible * Abstract Syntax Tree usable for generating Markdown output * * @param comments nested comment * @param {Object} args currently none accepted * @param {boolean} [args.markdownToc=true] whether to include a table of contents * in markdown output. * @param {Object} [args.hljs={}] config to be passed to highlightjs for code highlighting: * consult hljs.configure for the full list. * @returns {Promise<Object>} returns an eventual Markdown value */ function markdownAST(comments, args) { return mergeConfig(args).then(function (config) { return buildMarkdownAST(comments, config); }); } function buildMarkdownAST(comments, config) { // Configure code highlighting var hljsOptions = config.hljs || {}; hljs.configure(hljsOptions); var linkerStack = new LinkerStack(config).namespaceResolver(comments, function (namespace) { var slugger = new GithubSlugger(); return '#' + slugger.slug(namespace); }); var formatType = _formatType.bind(undefined, linkerStack.link); var generatorComment = [u('html', '<!-- Generated by documentation.js. Update this documentation by updating the source code. -->')]; var tableOfContentsHeading = [u('heading', { depth: 3 }, [u('text', 'Table of Contents')])]; /** * Generate an AST chunk for a comment at a given depth: this is * split from the main function to handle hierarchially nested comments * * @param {number} depth nesting of the comment, starting at 1 * @param {Object} comment a single comment * @returns {Object} remark-compatible AST */ function generate(depth, comment) { function typeSection(comment) { return comment.type && u('paragraph', [u('text', 'Type: ')].concat(formatType(comment.type))); } function paramList(params) { if (params.length === 0) return []; return u('list', { ordered: false }, params.map(function (param) { return u('listItem', [u('paragraph', [u('inlineCode', param.name), u('text', ' '), !!param.type && u('strong', formatType(param.type)), u('text', ' ')].concat(param.description ? param.description.children : []).concat([!!param.default && u('paragraph', [u('text', ' (optional, default '), u('inlineCode', param.default), u('text', ')')])]).filter(Boolean))].concat(param.properties && paramList(param.properties)).filter(Boolean)); })); } function paramSection(comment) { return comment.params.length > 0 && [u('strong', [u('text', 'Parameters')]), paramList(comment.params)]; } function propertySection(comment) { return comment.properties.length > 0 && [u('strong', [u('text', 'Properties')]), propertyList(comment.properties)]; } function propertyList(properties) { return u('list', { ordered: false }, properties.map(function (property) { return u('listItem', [u('paragraph', [u('inlineCode', property.name), u('text', ' '), u('strong', formatType(property.type)), u('text', ' ')].concat(property.description ? property.description.children : []).filter(Boolean)), property.properties && propertyList(property.properties)].filter(Boolean)); })); } function examplesSection(comment) { return comment.examples.length > 0 && [u('strong', [u('text', 'Examples')])].concat(comment.examples.reduce(function (memo, example) { var language = hljsOptions.highlightAuto ? hljs.highlightAuto(example.description).language : DEFAULT_LANGUAGE; return memo.concat(example.caption ? [u('paragraph', [u('emphasis', example.caption)])] : []).concat([u('code', { lang: language }, example.description)]); }, [])); } function returnsSection(comment) { return comment.returns.length > 0 && comment.returns.map(function (returns) { return u('paragraph', [u('text', 'Returns '), u('strong', formatType(returns.type)), u('text', ' ')].concat(returns.description ? returns.description.children : [])); }); } function throwsSection(comment) { return comment.throws.length > 0 && u('list', { ordered: false }, comment.throws.map(function (returns) { return u('listItem', [u('paragraph', [u('text', 'Throws '), u('strong', formatType(returns.type)), u('text', ' ')].concat(returns.description ? returns.description.children : []))]); })); } function augmentsLink(comment) { return comment.augments.length > 0 && u('paragraph', [u('strong', [u('text', 'Extends '), u('text', comment.augments.map(function (tag) { return tag.name; }).join(', '))])]); } function seeLink(comment) { return comment.sees.length > 0 && u('list', { ordered: false }, comment.sees.map(function (see) { return u('listItem', [u('strong', [u('text', 'See: ')].concat(see.children))]); })); } function githubLink(comment) { return comment.context && comment.context.github && u('paragraph', [u('link', { title: 'Source code on GitHub', url: comment.context.github.url }, [u('text', comment.context.github.path + ':' + comment.context.loc.start.line + '-' + comment.context.loc.end.line)])]); } function metaSection(comment) { var meta = ['version', 'since', 'copyright', 'author', 'license', 'deprecated'].filter(function (tag) { return comment[tag]; }); return !!meta.length && [u('strong', [u('text', 'Meta')])].concat(u('list', { ordered: false }, meta.map(function (tag) { var metaContent = void 0; if (tag === 'copyright' || tag === 'deprecated') { metaContent = comment[tag]; } else { metaContent = u('text', comment[tag]); } return u('listItem', [u('paragraph', [u('strong', [u('text', tag)]), u('text', ': '), metaContent])]); }))); } if (comment.kind === 'note') { return [u('heading', { depth }, [u('text', comment.name || '')])].concat(comment.description).concat(!!comment.members.static.length && comment.members.static.reduce(function (memo, child) { return memo.concat(generate(depth + 1, child)); }, [])).filter(Boolean); } return [u('heading', { depth }, [u('text', comment.name || '')])].concat(githubLink(comment)).concat(augmentsLink(comment)).concat(seeLink(comment)).concat(comment.description ? comment.description.children : []).concat(typeSection(comment)).concat(paramSection(comment)).concat(propertySection(comment)).concat(examplesSection(comment)).concat(throwsSection(comment)).concat(returnsSection(comment)).concat(metaSection(comment)).concat(!!comment.members.global.length && comment.members.global.reduce(function (memo, child) { return memo.concat(generate(depth + 1, child)); }, [])).concat(!!comment.members.instance.length && comment.members.instance.reduce(function (memo, child) { return memo.concat(generate(depth + 1, child)); }, [])).concat(!!comment.members.static.length && comment.members.static.reduce(function (memo, child) { return memo.concat(generate(depth + 1, child)); }, [])).concat(!!comment.members.inner.length && comment.members.inner.reduce(function (memo, child) { return memo.concat(generate(depth + 1, child)); }, [])).filter(Boolean); } var root = rerouteLinks(linkerStack.link, u('root', generatorComment.concat(config.markdownToc ? tableOfContentsHeading : []).concat(comments.reduce(function (memo, comment) { return memo.concat(generate(2, comment)); }, [])))); if (config.markdownToc) { root = remark().use(toc, { tight: true }).run(root); } return Promise.resolve(root); } module.exports = markdownAST;