markdown-to-html
Version:
Converts markdown text to HTML. A readable stream plus utilities and web demo.
163 lines (142 loc) • 4.86 kB
JavaScript
'use strict';
// Converts a markdown file into an HTML file, writing it to stdout.
// Implemented as a readable stream.
//
// Usage:
// node format_markdown <filename> <context> <github user id>
//
var marked = require('marked');
var pygmentize = require('pygmentize-bundled');
var linkify = require('../lib/gfm-linkify');
var fs = require('fs');
var path = require('path');
var util = require('util');
var Readable = require('stream').Readable;
util.inherits(Markdown, Readable);
function Markdown() {
this.super_ = this.constructor.super_;
this.super_.call(this);
this.debug = false;
this.bufmax = 1024;
this.html = '';
this.setEncoding('utf8');
}
Markdown.prototype.render = function(fileName, opts, onDone) {
var flavour = opts.flavor || 'gfm',
highlight = opts.highlight,
stylesheet = opts.stylesheet,
context = opts.context,
title = opts.title,
titleText = title;
if (title) {
var dirName = path.dirname(fileName),
baseName = path.basename(fileName),
pathName = path.resolve(fileName);
titleText = titleText.replace('$FILENAME', fileName);
titleText = titleText.replace('$DIRNAME', dirName);
titleText = titleText.replace('$BASENAME', baseName);
titleText = titleText.replace('$PATHNAME', pathName);
}
this.done = false;
this.once('error', function(err) {
if (onDone) onDone(err)
else console.error('>>>' + err);
});
if (fileName === null) {
if (onDone) onDone('Missing file name arg');
else console.error('>>>Missing file name arg');
}
//======================================================
// Read the file content.
//======================================================
fs.readFile(fileName, onFileReady.bind(this));
//======================================================
// Process the file content.
//======================================================
function onFileReady(err, data) {
if (err) {
if (onDone) onDone(err);
else console.error('>>>' + err);
return;
}
var fileContent = data.toString();
var options = {
gfm: (flavour === 'gfm') ? true : false,
breaks: true,
tables: true,
sanitize: true
};
if (highlight) {
options['highlight'] = function(code, lang, callback) {
pygmentize({lang: lang, format: 'html'}, code, function(err, result) {
if (err) {
callback(err);
}
callback(err, result.toString());
});
}
}
//======================================================
// Call marked to parse the file content
//======================================================
if (this.debug) console.error('>>>starting to get code');
try {
marked.setOptions(options);
marked(fileContent, getCode.bind(this));
} catch(err) {
if (onDone) onDone(err)
else console.error('>>>' + err);
}
//======================================================
// Enhance the output of marked with optional header
// or trailer. Linkify if context specified.
//======================================================
function getCode(err, code) {
if (err) {
if (onDone) onDone(err)
else console.error('>>>' + err);
return;
}
if (stylesheet || title) {
if (titleText == null) titleText = fileName;
this.cat('<!DOCTYPE html>\n' +
'<html>\n' +
'<head>\n' +
' <title>' + titleText + '</title>\n');
if (stylesheet) {
this.cat(' <link rel="stylesheet" href="' + stylesheet + '">\n');
}
this.cat('</head>\n<body>\n' );
}
if (context) {
this.cat( linkify(code, context) );
} else {
this.cat( code );
}
if (stylesheet || title) {
this.cat( '</body>\n</html>\n' );
}
if (this.debug) console.error('>>>finished getting code');
this.done = true;
if (onDone) onDone();
} // end getCode
} // end onFileReady
} // end render
Markdown.prototype.cat = function(data) {
this.html += data;
}
Markdown.prototype._read = function(size) {
var pushSize = Math.min(size, this.bufmax);
if (this.html.length > 0) {
if (this.debug) console.error('>>>_read size=' + pushSize + ', html="' + this.html.substr(0, pushSize) + '"');
this.push(this.html.substr(0, pushSize));
this.html = this.html.slice(pushSize);
if (this.done && this.html.length === 0) {
this.push(null); // eof
}
} else {
if (this.debug) console.error('>>>_read - nothing to push');
}
return;
}
module.exports = Markdown;