grunt-md2html
Version:
Small Grunt MultiTask to convert Markdown files to HTML
145 lines (125 loc) • 5.63 kB
JavaScript
/**
* grunt-md2html - Small Grunt MultiTask to convert Markdown files to HTML.
* https://github.com/bylexus/grunt-md2html
*
* Copyright (c) 2013-2020 Alexander Schenkel
* Licensed under the MIT license.
*/
module.exports = function (grunt) {
const helpers = require('../lib/helpers.js');
const marked = require('marked');
const path = require('path');
const hl = require('highlight.js');
grunt.registerMultiTask('md2html', 'Small Grunt MultiTask to convert Markdown files to HTML', function () {
// Merge task-specific and/or target-specific options with these defaults.
let options = this.options({
separator: '\n\n',
basePath: null,
layout: null,
markedOptions: {},
highlightjs: {
enabled: false,
style: 'monokai',
compressStyle: true,
options: {}
},
templateData: {}
});
let done = this.async();
let layoutFile = options.layout;
let layout = null;
// Enable and configure highlight.js, if required:
if (options.highlightjs && options.highlightjs.enabled) {
let stylePath = path.join(
path.dirname(require.resolve('highlight.js')),
'..',
'styles',
(options.highlightjs.style || 'monokai') + '.css'
),
CleanCSS = require('clean-css');
// load css style file and compress it, if required:
// the style string is available in the template as highlightjs-style var:
if (grunt.file.exists(stylePath)) {
options.templateData.highlightjs_style = grunt.file.read(stylePath);
if (options.highlightjs.compressStyle) {
options.templateData.highlightjs_style = new CleanCSS().minify(
options.templateData.highlightjs_style
).styles;
}
}
// enable marked's synced highlight callback to apply highlightjs:
options.markedOptions.highlight = (code) => {
hl.configure(options.highlightjs.options || {});
return hl.highlightAuto(code).value;
};
}
marked.setOptions(options.markedOptions);
if (layoutFile && grunt.file.exists(layoutFile)) {
grunt.log.writeln('Using layout file: ' + layoutFile);
layout = grunt.file.read(layoutFile);
}
// Iterate over all specified file groups.
let promises = [];
this.files.forEach((f) => {
// Create hoisted copy of template data, as we modify it in this function
let templateData = Object.assign({}, options.templateData);
let relPath = path.relative(path.dirname(f.dest), options.basePath || f.orig.dest);
let collectedSrcFiles = [];
if (relPath.length === 0) {
relPath = '.';
}
let layoutHtml = layout;
templateData.basepath = relPath;
templateData.destination = f.dest;
// Concat specified files.
let src = f.src
.filter((filepath) => {
// Warn on and remove invalid source files (if nonull was set).
if (!grunt.file.exists(filepath)) {
grunt.log.warn('Source file "' + filepath + '" not found.');
return false;
} else {
return true;
}
})
.map((filepath) => {
// Read file source.
let content = grunt.file.read(filepath);
templateData.src = filepath;
collectedSrcFiles.push(filepath);
return grunt.template.process(content, {
data: templateData
});
})
.join(grunt.util.normalizelf(options.separator));
// join source files: if we concated multiple files
// into one output file, we have multiple sources here:
// so templateData.src is an array for the layout file:
templateData.src = collectedSrcFiles;
// PlantUML extraction: This is async, so
// the rest of the task must wait:
let promise = helpers.extractPlantUMLContent(src, f.dest, options, grunt).then((src) => {
// Handle options.
// src already contains the string containing the whole src content
let html = marked.parse(src);
// Check if we have to wrap a layout:
if (!layoutHtml) {
layoutHtml = '{DOC}';
}
templateData.document = html;
layoutHtml = grunt.template.process(layoutHtml, {
data: templateData
});
layoutHtml = layoutHtml.replace(/\{DOC\}/g, html);
layoutHtml = layoutHtml.replace(/\{BASEPATH\}/g, relPath);
layoutHtml = layoutHtml.replace(/\{DEST\}/g, f.dest);
// Write the destination file.
grunt.file.write(f.dest, layoutHtml);
// Print a success message.
grunt.log.writeln('File "' + f.dest + '" created.');
});
promises.push(promise);
});
Promise.all(promises).then(done).catch(done);
});
};