lib/index.js
'use strict';
var _ = require('lodash'),
path = require('path'),
Robotskirt = require('robotskirt'),
highlightjs = require('highlight.js');
/**
* plugin expose function
* @param {object} options custom options
* @return {function} plugin function
*/
module.exports = function(options) {
// default options
var defaults = {
extensions: {
autolink: true,
fenced_code: true,
lax_spacing: true,
no_intra_emphasis: true,
space_headers: true,
strikethrough: true,
superscript: true,
tables: true
},
htmlFlags: {
skip_html: false,
skip_style: false,
skip_images: false,
skip_links: false,
safelink: false,
toc: false,
hard_wrap: false,
use_xhtml: false,
expand_tabs: false,
escape: false
},
renderers: {
blockcode: highlightCodeBlocks
},
smartypants: true
};
// create new object from merged options
options = _.merge({}, defaults, options);
// create robotskirt instances
var renderFunctions = validateRenderFunctions(options.renderers),
renderer = new Robotskirt.HtmlRenderer(convertConfigToRobotskirtFlags(options.htmlFlags, 'HTML'));
_.forEach(renderFunctions, function(func, key) {
renderer[key] = func;
});
var markdown = new Robotskirt.Markdown(renderer, convertConfigToRobotskirtFlags(options.extensions, 'EXT'));
// main plugin function
return function(files, metalsmith, done) {
setImmediate(done);
_.forEach(files, function(file, filename) {
if (!isMarkdownFile(filename)) {
return;
}
var dir = path.dirname(filename),
html = path.basename(filename, path.extname(filename)) + '.html',
contents = markdown.render(file.contents.toString());
if (options.smartypants) {
contents = Robotskirt.smartypantsHtml(contents);
}
file.contents = new Buffer(contents);
if ('.' !== dir) {
html = dir + '/' + html;
}
delete files[filename];
files[html] = file;
});
};
};
/**
* checks if given file has a common markdown extension
* @param {string} file filename or path
* @return {Boolean} test result
*/
function isMarkdownFile(file){
return /\.md|\.markdown/.test(path.extname(file)) ? true : false;
}
/**
* converts configuration object to an array of binary flags for robotskirt
* @param {object} configObj plain object
* @param {string} prefix flag prefix
* @return {array} flags
*/
function convertConfigToRobotskirtFlags(configObj, prefix) {
var robotskirtFlags = [];
prefix = prefix ? prefix + '_' : '';
_.forEach(configObj, function(value, key) {
if (value) {
robotskirtFlags.push(Robotskirt[prefix + key.toUpperCase()]);
}
});
return robotskirtFlags;
}
/**
* validates configuration object of render functions
* @param {object} configObj configuration object
* @return {object} validated object
*/
function validateRenderFunctions(configObj) {
var robotskirtRenderFunctions = {};
_.forEach(configObj, function(func, key) {
if (_.isFunction(func)) {
robotskirtRenderFunctions[key.toLowerCase()] = func;
}
});
return robotskirtRenderFunctions;
}
/**
* syntax highlighting for codeblocks using highlight.js
* @param {string} code content
* @param {string} lang language key
* @return {string} highlighted content
*/
function highlightCodeBlocks(code, lang) {
var validLang = lang && highlightjs.getLanguage(lang.trim()),
highlightedCode = validLang ? highlightjs.highlight(lang, code).value : highlightjs.highlightAuto(code).value,
langClass = validLang ? ' class="lang-' + lang + '"' : '';
return '<pre><code' + langClass + '>' + highlightedCode + '</code></pre>';
}