|
@ -9,7 +9,10 @@ var Promise = require('bluebird'), |
|
|
mdFootnote = require('markdown-it-footnote'), |
|
|
mdFootnote = require('markdown-it-footnote'), |
|
|
mdExternalLinks = require('markdown-it-external-links'), |
|
|
mdExternalLinks = require('markdown-it-external-links'), |
|
|
mdExpandTabs = require('markdown-it-expand-tabs'), |
|
|
mdExpandTabs = require('markdown-it-expand-tabs'), |
|
|
|
|
|
mdAttrs = require('markdown-it-attrs'), |
|
|
|
|
|
hljs = require('highlight.js'), |
|
|
slug = require('slug'), |
|
|
slug = require('slug'), |
|
|
|
|
|
cheerio = require('cheerio'), |
|
|
_ = require('lodash'); |
|
|
_ = require('lodash'); |
|
|
|
|
|
|
|
|
// Load plugins
|
|
|
// Load plugins
|
|
@ -17,7 +20,15 @@ var Promise = require('bluebird'), |
|
|
var mkdown = md({ |
|
|
var mkdown = md({ |
|
|
html: true, |
|
|
html: true, |
|
|
linkify: true, |
|
|
linkify: true, |
|
|
typography: true |
|
|
|
|
|
|
|
|
typography: true, |
|
|
|
|
|
highlight: function (str, lang) { |
|
|
|
|
|
if (lang && hljs.getLanguage(lang)) { |
|
|
|
|
|
try { |
|
|
|
|
|
return '<pre class="hljs"><code>' + hljs.highlight(lang, str, true).value + '</code></pre>'; |
|
|
|
|
|
} catch (__) {} |
|
|
|
|
|
} |
|
|
|
|
|
return '<pre class="hljs"><code>' + hljs.highlightAuto(str).value + '</code></pre>'; |
|
|
|
|
|
} |
|
|
}) |
|
|
}) |
|
|
.use(mdEmoji) |
|
|
.use(mdEmoji) |
|
|
.use(mdTaskLists) |
|
|
.use(mdTaskLists) |
|
@ -33,7 +44,8 @@ var mkdown = md({ |
|
|
}) |
|
|
}) |
|
|
.use(mdExpandTabs, { |
|
|
.use(mdExpandTabs, { |
|
|
tabWidth: 4 |
|
|
tabWidth: 4 |
|
|
}); |
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
.use(mdAttrs); |
|
|
|
|
|
|
|
|
// Rendering rules
|
|
|
// Rendering rules
|
|
|
|
|
|
|
|
@ -41,13 +53,23 @@ mkdown.renderer.rules.emoji = function(token, idx) { |
|
|
return '<i class="twa twa-' + token[idx].markup + '"></i>'; |
|
|
return '<i class="twa twa-' + token[idx].markup + '"></i>'; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// Parse markdown headings tree
|
|
|
|
|
|
|
|
|
mkdown.inline.ruler.push('internal_link', (state) => { |
|
|
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Parse markdown content and build TOC tree |
|
|
|
|
|
* |
|
|
|
|
|
* @param {(Function|string)} content Markdown content |
|
|
|
|
|
* @return {Array} TOC tree |
|
|
|
|
|
*/ |
|
|
const parseTree = (content) => { |
|
|
const parseTree = (content) => { |
|
|
|
|
|
|
|
|
let tokens = md().parse(content, {}); |
|
|
let tokens = md().parse(content, {}); |
|
|
let tocArray = []; |
|
|
let tocArray = []; |
|
|
|
|
|
|
|
|
|
|
|
//-> Extract headings and their respective levels
|
|
|
|
|
|
|
|
|
for (let i = 0; i < tokens.length; i++) { |
|
|
for (let i = 0; i < tokens.length; i++) { |
|
|
if (tokens[i].type !== "heading_close") { |
|
|
if (tokens[i].type !== "heading_close") { |
|
|
continue |
|
|
continue |
|
@ -75,6 +97,12 @@ const parseTree = (content) => { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//-> Exclude levels deeper than 2
|
|
|
|
|
|
|
|
|
|
|
|
_.remove(tocArray, (n) => { return n.level > 2; }); |
|
|
|
|
|
|
|
|
|
|
|
//-> Build tree from flat array
|
|
|
|
|
|
|
|
|
return _.reduce(tocArray, (tree, v) => { |
|
|
return _.reduce(tocArray, (tree, v) => { |
|
|
let treeLength = tree.length - 1; |
|
|
let treeLength = tree.length - 1; |
|
|
if(v.level < 2) { |
|
|
if(v.level < 2) { |
|
@ -98,23 +126,42 @@ const parseTree = (content) => { |
|
|
}; |
|
|
}; |
|
|
let lastNodePath = GetNodePath(); |
|
|
let lastNodePath = GetNodePath(); |
|
|
let lastNode = _.get(tree[treeLength], lastNodePath); |
|
|
let lastNode = _.get(tree[treeLength], lastNodePath); |
|
|
lastNode.push({ |
|
|
|
|
|
content: v.content, |
|
|
|
|
|
anchor: v.anchor, |
|
|
|
|
|
nodes: [] |
|
|
|
|
|
}); |
|
|
|
|
|
_.set(tree[treeLength], lastNodePath, lastNode); |
|
|
|
|
|
|
|
|
if(lastNode) { |
|
|
|
|
|
lastNode.push({ |
|
|
|
|
|
content: v.content, |
|
|
|
|
|
anchor: v.anchor, |
|
|
|
|
|
nodes: [] |
|
|
|
|
|
}); |
|
|
|
|
|
_.set(tree[treeLength], lastNodePath, lastNode); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
return tree; |
|
|
return tree; |
|
|
}, []); |
|
|
}, []); |
|
|
|
|
|
|
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Parse markdown content to HTML |
|
|
|
|
|
* |
|
|
|
|
|
* @param {String} content Markdown content |
|
|
|
|
|
* @return {String} HTML formatted content |
|
|
|
|
|
*/ |
|
|
|
|
|
const parseContent = (content) => { |
|
|
|
|
|
|
|
|
|
|
|
let output = mkdown.render(content); |
|
|
|
|
|
let cr = cheerio.load(output); |
|
|
|
|
|
cr('table').addClass('table is-bordered is-striped is-narrow'); |
|
|
|
|
|
output = cr.html(); |
|
|
|
|
|
|
|
|
|
|
|
return output; |
|
|
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
module.exports = { |
|
|
module.exports = { |
|
|
|
|
|
|
|
|
parse(content) { |
|
|
parse(content) { |
|
|
return { |
|
|
return { |
|
|
html: mkdown.render(content), |
|
|
|
|
|
|
|
|
html: parseContent(content), |
|
|
tree: parseTree(content) |
|
|
tree: parseTree(content) |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|