Files: ee4ec0e7cbc4ebcdc15047b21b100a11df3bcbe3 / markdown.js
3318 bytesRaw
1 | var fs = require('fs') |
2 | var remark = require('remark') |
3 | var html = require('remark-html') |
4 | var slug = require('remark-slug') |
5 | var autolinkHeadings = require('remark-autolink-headings') |
6 | var com = require('./tmpl/com.part') |
7 | |
8 | const linkSvg = '<svg aria-hidden="true" class="octicon octicon-link" height="16" role="img" version="1.1" viewBox="0 0 16 16" width="16"><path d="M4 9h1v1h-1c-1.5 0-3-1.69-3-3.5s1.55-3.5 3-3.5h4c1.45 0 3 1.69 3 3.5 0 1.41-0.91 2.72-2 3.25v-1.16c0.58-0.45 1-1.27 1-2.09 0-1.28-1.02-2.5-2-2.5H4c-0.98 0-2 1.22-2 2.5s1 2.5 2 2.5z m9-3h-1v1h1c1 0 2 1.22 2 2.5s-1.02 2.5-2 2.5H9c-0.98 0-2-1.22-2-2.5 0-0.83 0.42-1.64 1-2.09v-1.16c-1.09 0.53-2 1.84-2 3.25 0 1.81 1.55 3.5 3 3.5h4c1.45 0 3-1.69 3-3.5s-1.5-3.5-3-3.5z"></path></svg>' |
9 | |
10 | module.exports.doc = function (path) { |
11 | var text = fs.readFileSync(path, 'utf-8') |
12 | return remark() |
13 | .use(slug) |
14 | .use(autolinkHeadings, { |
15 | attributes: { class: 'anchor' }, |
16 | template: linkSvg |
17 | }) |
18 | .use(html) |
19 | .use(injectTOC) |
20 | .use(transformCodeExamples) |
21 | .process(text) |
22 | } |
23 | |
24 | // find all h2s and create a dropdown table-of-contents |
25 | function injectTOC (remark, options) { |
26 | remark.Compiler.prototype.visitors.tableOfContents = renderTOC |
27 | return ast => { |
28 | var headings = ast.children.filter(node => node.type == 'heading' && node.depth == 2) |
29 | if (headings.length > 3) |
30 | createTOC(ast, headings) |
31 | return ast |
32 | } |
33 | } |
34 | |
35 | // add the tableOfContents node to the ast |
36 | function createTOC (ast, headings) { |
37 | ast.children.unshift({ type: 'tableOfContents', children: headings }) |
38 | } |
39 | |
40 | // render the tableOfContents widget |
41 | function renderTOC (node, root) { |
42 | return com.tableOfContents(node.children) |
43 | } |
44 | |
45 | // find any <code> sections and group them together into our code-examples component |
46 | function transformCodeExamples (remark, options) { |
47 | remark.Compiler.prototype.visitors.codeExamples = renderCodeExamples |
48 | return ast => { |
49 | var groups = findCodeGroupings(ast) |
50 | createCodeExamples(ast, groups) |
51 | return ast |
52 | } |
53 | } |
54 | |
55 | // locate the contiguous <code> groupings |
56 | function findCodeGroupings (ast) { |
57 | var groups = [], groupStart = false |
58 | ast.children.forEach((node, i) => { |
59 | if (groupStart) { |
60 | // in a grouping, look for a non-code item |
61 | if (node.type !== 'code' || !node.lang) { |
62 | groups.push([groupStart, i]) |
63 | groupStart = false |
64 | } |
65 | } else { |
66 | // not in a grouping, look for a code item |
67 | if (node.type === 'code' && node.lang) { |
68 | groupStart = i |
69 | } |
70 | } |
71 | }) |
72 | if (groupStart) |
73 | groups.push([groupStart, ast.children.length]) |
74 | return groups |
75 | } |
76 | |
77 | // replace <code> groupings with code-example nodes |
78 | function createCodeExamples (ast, groups) { |
79 | var offset = 0 // offset to counter the changes introduced by splices |
80 | groups.forEach(group => { |
81 | var start = group[0], end = group[1] |
82 | var len = end - start |
83 | ast.children.splice(start-offset, len, { |
84 | type: 'codeExamples', |
85 | children: ast.children.slice(start-offset, start-offset+len), |
86 | position: false // TODO - needed? doesnt look like it |
87 | }) |
88 | offset += len - 1 |
89 | }) |
90 | } |
91 | |
92 | // convert from AST to html |
93 | function renderCodeExamples (node, root) { |
94 | var codes = {} |
95 | node.children.forEach(node => { |
96 | if (node.type == 'code' && !!node.lang) |
97 | codes[node.lang] = node.value |
98 | }) |
99 | |
100 | return com.code(codes) |
101 | } |
102 |
Built with git-ssb-web