1 | const u = require('unist-builder');
|
2 | const remark = require('remark');
|
3 | const mergeConfig = require('../merge_config');
|
4 | const toc = require('remark-toc');
|
5 | const links = require('remark-reference-links');
|
6 | const hljs = require('highlight.js');
|
7 | const GithubSlugger = require('github-slugger');
|
8 | const LinkerStack = require('./util/linker_stack');
|
9 | const rerouteLinks = require('./util/reroute_links');
|
10 | const _formatType = require('./util/format_type');
|
11 |
|
12 | const DEFAULT_LANGUAGE = 'javascript';
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | function markdownAST(comments, args) {
|
27 | return mergeConfig(args).then(config => buildMarkdownAST(comments, config));
|
28 | }
|
29 |
|
30 | function buildMarkdownAST(comments, config) {
|
31 |
|
32 | const hljsOptions = config.hljs || {};
|
33 | hljs.configure(hljsOptions);
|
34 |
|
35 | const linkerStack = new LinkerStack(config).namespaceResolver(
|
36 | comments,
|
37 | namespace => {
|
38 | const slugger = new GithubSlugger();
|
39 | return '#' + slugger.slug(namespace);
|
40 | }
|
41 | );
|
42 |
|
43 | const formatType = _formatType.bind(undefined, linkerStack.link);
|
44 |
|
45 | const generatorComment = [
|
46 | u(
|
47 | 'html',
|
48 | '<!-- Generated by documentation.js. Update this documentation by updating the source code. -->'
|
49 | )
|
50 | ];
|
51 |
|
52 | const tableOfContentsHeading = [
|
53 | u('heading', { depth: 3 }, [u('text', 'Table of Contents')])
|
54 | ];
|
55 |
|
56 | |
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | function generate(depth, comment) {
|
65 | function typeSection(comment) {
|
66 | return (
|
67 | comment.type &&
|
68 | u('paragraph', [u('text', 'Type: ')].concat(formatType(comment.type)))
|
69 | );
|
70 | }
|
71 |
|
72 | function paramList(params) {
|
73 | if (params.length === 0) return [];
|
74 | return u(
|
75 | 'list',
|
76 | { ordered: false },
|
77 | params.map(param =>
|
78 | u(
|
79 | 'listItem',
|
80 | [
|
81 | u(
|
82 | 'paragraph',
|
83 | [
|
84 | u('inlineCode', param.name),
|
85 | u('text', ' '),
|
86 | !!param.type && u('strong', formatType(param.type)),
|
87 | u('text', ' ')
|
88 | ]
|
89 | .concat(param.description ? param.description.children : [])
|
90 | .concat([
|
91 | !!param.default &&
|
92 | u('paragraph', [
|
93 | u('text', ' (optional, default '),
|
94 | u('inlineCode', param.default),
|
95 | u('text', ')')
|
96 | ])
|
97 | ])
|
98 | .filter(Boolean)
|
99 | )
|
100 | ]
|
101 | .concat(param.properties && paramList(param.properties))
|
102 | .filter(Boolean)
|
103 | )
|
104 | )
|
105 | );
|
106 | }
|
107 |
|
108 | function paramSection(comment) {
|
109 | return (
|
110 | comment.params.length > 0 && [
|
111 | u('heading', { depth: depth + 1 }, [u('text', 'Parameters')]),
|
112 | paramList(comment.params)
|
113 | ]
|
114 | );
|
115 | }
|
116 |
|
117 | function propertySection(comment) {
|
118 | return (
|
119 | comment.properties.length > 0 && [
|
120 | u('heading', { depth: depth + 1 }, [u('text', 'Properties')]),
|
121 | propertyList(comment.properties)
|
122 | ]
|
123 | );
|
124 | }
|
125 |
|
126 | function propertyList(properties) {
|
127 | return u(
|
128 | 'list',
|
129 | { ordered: false },
|
130 | properties.map(property =>
|
131 | u(
|
132 | 'listItem',
|
133 | [
|
134 | u(
|
135 | 'paragraph',
|
136 | [
|
137 | u('inlineCode', property.name),
|
138 | u('text', ' '),
|
139 | u('strong', formatType(property.type)),
|
140 | u('text', ' ')
|
141 | ]
|
142 | .concat(
|
143 | property.description ? property.description.children : []
|
144 | )
|
145 | .filter(Boolean)
|
146 | ),
|
147 | property.properties && propertyList(property.properties)
|
148 | ].filter(Boolean)
|
149 | )
|
150 | )
|
151 | );
|
152 | }
|
153 |
|
154 | function examplesSection(comment) {
|
155 | return (
|
156 | comment.examples.length > 0 &&
|
157 | [u('heading', { depth: depth + 1 }, [u('text', 'Examples')])].concat(
|
158 | comment.examples.reduce(function(memo, example) {
|
159 | const language = hljsOptions.highlightAuto
|
160 | ? hljs.highlightAuto(example.description).language
|
161 | : DEFAULT_LANGUAGE;
|
162 | return memo
|
163 | .concat(
|
164 | example.caption
|
165 | ? [u('paragraph', [u('emphasis', example.caption)])]
|
166 | : []
|
167 | )
|
168 | .concat([u('code', { lang: language }, example.description)]);
|
169 | }, [])
|
170 | )
|
171 | );
|
172 | }
|
173 |
|
174 | function returnsSection(comment) {
|
175 | return (
|
176 | comment.returns.length > 0 &&
|
177 | comment.returns.map(returns =>
|
178 | u(
|
179 | 'paragraph',
|
180 | [
|
181 | u('text', 'Returns '),
|
182 | u('strong', formatType(returns.type)),
|
183 | u('text', ' ')
|
184 | ].concat(returns.description ? returns.description.children : [])
|
185 | )
|
186 | )
|
187 | );
|
188 | }
|
189 |
|
190 | function throwsSection(comment) {
|
191 | return (
|
192 | comment.throws.length > 0 &&
|
193 | u(
|
194 | 'list',
|
195 | { ordered: false },
|
196 | comment.throws.map(returns =>
|
197 | u('listItem', [
|
198 | u(
|
199 | 'paragraph',
|
200 | [
|
201 | u('text', 'Throws '),
|
202 | u('strong', formatType(returns.type)),
|
203 | u('text', ' ')
|
204 | ].concat(
|
205 | returns.description ? returns.description.children : []
|
206 | )
|
207 | )
|
208 | ])
|
209 | )
|
210 | )
|
211 | );
|
212 | }
|
213 |
|
214 | function augmentsLink(comment) {
|
215 | return (
|
216 | comment.augments.length > 0 &&
|
217 | u('paragraph', [
|
218 | u('strong', [
|
219 | u('text', 'Extends '),
|
220 | u('text', comment.augments.map(tag => tag.name).join(', '))
|
221 | ])
|
222 | ])
|
223 | );
|
224 | }
|
225 |
|
226 | function seeLink(comment) {
|
227 | return (
|
228 | comment.sees.length > 0 &&
|
229 | u(
|
230 | 'list',
|
231 | { ordered: false },
|
232 | comment.sees.map(see =>
|
233 | u('listItem', [
|
234 | u('strong', [u('text', 'See: ')].concat(see.children))
|
235 | ])
|
236 | )
|
237 | )
|
238 | );
|
239 | }
|
240 |
|
241 | function githubLink(comment) {
|
242 | return (
|
243 | comment.context &&
|
244 | comment.context.github &&
|
245 | u('paragraph', [
|
246 | u(
|
247 | 'link',
|
248 | {
|
249 | title: 'Source code on GitHub',
|
250 | url: comment.context.github.url
|
251 | },
|
252 | [
|
253 | u(
|
254 | 'text',
|
255 | comment.context.github.path +
|
256 | ':' +
|
257 | comment.context.loc.start.line +
|
258 | '-' +
|
259 | comment.context.loc.end.line
|
260 | )
|
261 | ]
|
262 | )
|
263 | ])
|
264 | );
|
265 | }
|
266 |
|
267 | function metaSection(comment) {
|
268 | const meta = [
|
269 | 'version',
|
270 | 'since',
|
271 | 'copyright',
|
272 | 'author',
|
273 | 'license',
|
274 | 'deprecated'
|
275 | ].filter(tag => comment[tag]);
|
276 | return (
|
277 | !!meta.length &&
|
278 | [u('strong', [u('text', 'Meta')])].concat(
|
279 | u(
|
280 | 'list',
|
281 | { ordered: false },
|
282 | meta.map(tag => {
|
283 | let metaContent;
|
284 | if (tag === 'copyright' || tag === 'deprecated') {
|
285 | metaContent = comment[tag];
|
286 | } else {
|
287 | metaContent = u('text', comment[tag]);
|
288 | }
|
289 | return u('listItem', [
|
290 | u('paragraph', [
|
291 | u('strong', [u('text', tag)]),
|
292 | u('text', ': '),
|
293 | metaContent
|
294 | ])
|
295 | ]);
|
296 | })
|
297 | )
|
298 | )
|
299 | );
|
300 | }
|
301 |
|
302 | if (comment.kind === 'note') {
|
303 | return [u('heading', { depth }, [u('text', comment.name || '')])]
|
304 | .concat(comment.description)
|
305 | .concat(
|
306 | !!comment.members.static.length &&
|
307 | comment.members.static.reduce(
|
308 | (memo, child) => memo.concat(generate(depth + 1, child)),
|
309 | []
|
310 | )
|
311 | )
|
312 | .filter(Boolean);
|
313 | }
|
314 |
|
315 | return [u('heading', { depth }, [u('text', comment.name || '')])]
|
316 | .concat(githubLink(comment))
|
317 | .concat(augmentsLink(comment))
|
318 | .concat(seeLink(comment))
|
319 | .concat(comment.description ? comment.description.children : [])
|
320 | .concat(typeSection(comment))
|
321 | .concat(paramSection(comment))
|
322 | .concat(propertySection(comment))
|
323 | .concat(examplesSection(comment))
|
324 | .concat(throwsSection(comment))
|
325 | .concat(returnsSection(comment))
|
326 | .concat(metaSection(comment))
|
327 | .concat(
|
328 | !!comment.members.global.length &&
|
329 | comment.members.global.reduce(
|
330 | (memo, child) => memo.concat(generate(depth + 1, child)),
|
331 | []
|
332 | )
|
333 | )
|
334 | .concat(
|
335 | !!comment.members.instance.length &&
|
336 | comment.members.instance.reduce(
|
337 | (memo, child) => memo.concat(generate(depth + 1, child)),
|
338 | []
|
339 | )
|
340 | )
|
341 | .concat(
|
342 | !!comment.members.static.length &&
|
343 | comment.members.static.reduce(
|
344 | (memo, child) => memo.concat(generate(depth + 1, child)),
|
345 | []
|
346 | )
|
347 | )
|
348 | .concat(
|
349 | !!comment.members.inner.length &&
|
350 | comment.members.inner.reduce(
|
351 | (memo, child) => memo.concat(generate(depth + 1, child)),
|
352 | []
|
353 | )
|
354 | )
|
355 | .filter(Boolean);
|
356 | }
|
357 |
|
358 | let root = rerouteLinks(
|
359 | linkerStack.link,
|
360 | u(
|
361 | 'root',
|
362 | generatorComment
|
363 | .concat(config.markdownToc ? tableOfContentsHeading : [])
|
364 | .concat(
|
365 | comments.reduce(
|
366 | (memo, comment) => memo.concat(generate(2, comment)),
|
367 | []
|
368 | )
|
369 | )
|
370 | )
|
371 | );
|
372 |
|
373 | const pluginRemark = remark();
|
374 | if (config.markdownToc)
|
375 | pluginRemark.use(toc, {
|
376 | tight: true,
|
377 | maxDepth: config.markdownTocMaxDepth
|
378 | });
|
379 | if (config.noReferenceLinks !== true) pluginRemark.use(links);
|
380 | root = pluginRemark.run(root);
|
381 |
|
382 | return Promise.resolve(root);
|
383 | }
|
384 |
|
385 | module.exports = markdownAST;
|