simbo/metalsmith-robotskirt

View on GitHub
lib/index.js

Summary

Maintainability
A
2 hrs
Test Coverage
'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>';
}