var fs = require('fs')
var remark = require('remark')
var html = require('remark-html')
var slug = require('remark-slug')
var autolinkHeadings = require('remark-autolink-headings')
var com = require('./tmpl/com.part')
var select = require('unist-util-select')
var ssbRef = require('ssb-ref')
const linkSvg = ''
module.exports.doc = function (path) {
var text = fs.readFileSync(path, 'utf-8')
return remark()
.use(slug)
.use(autolinkHeadings, {
attributes: { class: 'anchor' },
template: linkSvg
})
.use(html)
.use(injectTOC)
.use(transformCodeExamples)
.use(transformSsbLinks)
.process(text)
}
// find all h2s and create a dropdown table-of-contents
function injectTOC (remark, options) {
remark.Compiler.prototype.visitors.tableOfContents = renderTOC
return ast => {
var headings = ast.children.filter(node => node.type == 'heading' && node.depth == 2)
if (headings.length > 3)
createTOC(ast, headings)
return ast
}
}
// add the tableOfContents node to the ast
function createTOC (ast, headings) {
ast.children.unshift({ type: 'tableOfContents', children: headings })
}
// render the tableOfContents widget
function renderTOC (node, root) {
return com.tableOfContents(node.children)
}
// find any sections and group them together into our code-examples component
function transformCodeExamples (remark, options) {
remark.Compiler.prototype.visitors.codeExamples = renderCodeExamples
return ast => {
var groups = findCodeGroupings(ast)
createCodeExamples(ast, groups)
return ast
}
}
// locate the contiguous groupings
function findCodeGroupings (ast) {
var groups = [], groupStart = false
ast.children.forEach((node, i) => {
if (groupStart) {
// in a grouping, look for a non-code item
if (node.type !== 'code' || !node.lang) {
groups.push([groupStart, i])
groupStart = false
}
} else {
// not in a grouping, look for a code item
if (node.type === 'code' && node.lang) {
groupStart = i
}
}
})
if (groupStart)
groups.push([groupStart, ast.children.length])
return groups
}
// replace groupings with code-example nodes
function createCodeExamples (ast, groups) {
var offset = 0 // offset to counter the changes introduced by splices
groups.forEach(group => {
var start = group[0], end = group[1]
var len = end - start
ast.children.splice(start-offset, len, {
type: 'codeExamples',
children: ast.children.slice(start-offset, start-offset+len),
position: false // TODO - needed? doesnt look like it
})
offset += len - 1
})
}
// convert from AST to html
function renderCodeExamples (node, root) {
var codes = {}
node.children.forEach(node => {
if (node.type == 'code' && !!node.lang)
codes[node.lang] = node.value
})
return com.code(codes)
}
// add a prefix to links to ssb refs
function transformSsbLinks (remark, options) {
return ast => {
select(ast, "link").forEach(link => {
if (ssbRef.isLink(link.url)) {
link.url = 'https://git.scuttlebot.io/' + link.url
}
})
return ast
}
}