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