pretty-remarkable
Version:
Plugin for prettifying markdown with Remarkable using custom renderer rules.
151 lines (127 loc) • 3.31 kB
JavaScript
/*!
* pretty-remarkable <https://github.com/jonschlinkert/pretty-remarkable>
*
* Copyright (c) 2014-2018, present, Jon Schlinkert.
* Released under the MIT License.
*/
;
const rules = require('./lib/rules');
/**
* Register as a plugin by passing `prettify` to remarkable's
* `.use()` method.
*
* ```js
* const md = new Remarkable();
* md.use(prettify);
* const result = md.render(str);
* ```
*
* @param {Object} `options`
* @return {String}
*/
function prettify(md) {
ast(md);
const render = md.render;
md.render = function(str, options) {
if (typeof str !== 'string') {
throw new TypeError('expected a string');
}
str = str.split(/\]\[\]\s*\n\s*\[/).join('][]\n\n[');
return render.call(md, str, options);
};
md.renderer.renderInline = function(tokens, options, env) {
const _rules = rules;
let len = tokens.length, i = 0;
let str = '';
while (len--) {
str += _rules[tokens[i].type](tokens, i++, options, env, this);
}
return str;
};
md.renderer.render = function(tokens, options, env) {
let _rules = rules;
let len = tokens.length, i = -1;
let str = '';
while (++i < len) {
let token = tokens[i];
if (token.type === 'inline') {
str += this.renderInline(token.children, options, env);
} else {
str += _rules[token.type](tokens, i, options, env, this);
}
}
if (options.condense !== false) {
str = str.split(/(?:\r\n|\n){2,}/).join('\n\n');
}
let newline = '\n';
if (options.newline === false) {
newline = '';
}
if (typeof options.newline === 'string') {
newline = options.newline;
}
str = str.trim() + newline;
return str;
};
}
function ast(md) {
let parse = md.parse;
md.parse = function(str, options) {
let tokens = parse.apply(md, arguments);
let ast = {type: 'root', nodes: []};
let nodes = [ast];
let stack = [];
function last() {
return stack.length ? stack[stack.length - 1] : nodes[nodes.length - 1];
}
visit({nodes: tokens}, function(tok) {
if (tok.children) {
define(tok, 'children', tok.children);
}
let prev = last();
let match = parseType(tok);
if (match) {
if (match[2] === 'open') {
let node = {type: match[1], nodes: [tok]};
define(tok, 'parent', node);
define(node, 'parent', prev);
prev.nodes.push(node);
stack.push(node);
} else {
let parent = stack.pop();
define(tok, 'parent', parent);
parent.nodes.push(tok);
}
} else {
define(tok, 'parent', prev);
if (tok.type !== 'inline') {
prev.nodes.push(tok);
}
}
});
tokens.ast = ast;
return tokens;
};
}
function parseType(tok) {
return /(.*?)_(open|close)$/.exec(tok.type);
}
function visit(node, fn) {
fn(node);
if (node.nodes || node.children) {
mapVisit(node, fn);
}
}
function mapVisit(node, fn) {
var nodes = node.nodes || node.children;
for (var i = 0; i < nodes.length; i++) {
visit(nodes[i], fn);
}
}
function define(obj, key, value) {
Reflect.defineProperty(obj, key, { value });
}
/**
* expose `prettify`
*/
module.exports = prettify;