hexo
Version:
A fast, simple & powerful blog framework, powered by Node.js.
221 lines (186 loc) • 5.01 kB
JavaScript
/**
* Render functions.
*
* @class render
* @module hexo
* @static
*/
var async = require('async'),
pathFn = require('path'),
fs = require('graceful-fs'),
_ = require('lodash'),
util = require('../util'),
file = util.file2,
yfm = util.yfm;
var cache = {};
var getExtname = function(str){
return pathFn.extname(str).replace(/^\./, '');
};
/**
* Checks if the given `path` is renderable.
*
* @method isRenderable
* @param {String} path
* @return {Boolean}
* @static
*/
var isRenderable = exports.isRenderable = function(path){
return hexo.extend.renderer.isRenderable(path);
};
/**
* Checks if the given `path` is renderable by synchronized renderer.
*
* @method isRenderableSync
* @param {String} path
* @return {Boolean}
* @static
*/
var isRenderableSync = exports.isRenderableSync = function(path){
return hexo.extend.renderer.isRenderableSync(path);
};
/**
* Gets the output extension name.
*
* @method getOutput
* @param {String} path
* @return {String}
* @static
*/
var getOutput = exports.getOutput = function(path){
return hexo.extend.renderer.getOutput(path);
};
/**
* Renders data.
*
* @method render
* @param {Object} data
* @param {Object} [options]
* @param {Function} [callback]
* @async
* @static
*/
var render = exports.render = function(data, options, callback){
if (!callback){
if (typeof options === 'function'){
callback = options;
options = {};
} else {
callback = function(){};
}
}
async.waterfall([
function(next){
if (typeof data.text !== 'undefined') return next(null, data.text);
if (!data.path) return next(new Error('No input file or string'));
file.readFile(data.path, next);
},
function(text, next){
var ext = data.engine || getExtname(data.path);
if (ext && isRenderable(ext)){
var renderer = hexo.extend.renderer.get(ext);
renderer({
path: data.path,
text: text
}, options, next);
} else {
next(null, text);
}
}
], callback);
};
/**
* Renders data synchronizedly.
*
* @method renderSync
* @param {Object} data
* @param {Object} [options]
* @static
*/
exports.renderSync = function(data, options){
var text = '';
if (typeof data.text !== 'undefined'){
text = data.text;
} else if (data.path){
text = file.readFileSync(data.path);
if (!text) return;
} else {
return;
}
var ext = data.engine || getExtname(data.path);
if (ext && isRenderableSync(ext)){
var renderer = hexo.extend.renderer.get(ext, true);
return renderer({path: data.path, text: text}, options);
} else {
return text;
}
};
/**
* Renders a file. This function supports helpers and layouts.
*
* @method renderFile
* @param {String} source
* @param {Object} [options]
* @param {Function} [callback]
* @async
* @static
*/
var renderFile = exports.renderFile = function(source, options, callback){
if (!callback){
if (typeof options === 'function'){
callback = options;
options = {};
} else {
callback = function(){};
}
}
var helper = hexo.extend.helper.list();
async.waterfall([
// Load cache
function(next){
if (options.cache && cache.hasOwnProperty(source)){
next(null, cache[source]);
} else {
file.readFile(source, function(err, content){
if (err) return callback(err);
var data = cache[source] = yfm(content);
next(null, data);
});
}
},
// Render template
function(data, next){
var layout = data.hasOwnProperty('layout') ? data.layout : options.layout,
locals = _.extend({}, helper, options, _.omit(data, 'layout', '_content')),
extname = pathFn.extname(source),
renderer = hexo.extend.renderer.get(extname);
renderer({path: source, text: data._content}, locals, function(err, result){
if (err) return callback(err);
if (!layout) return callback(null, result);
var layoutPath = '';
// Relative path
layoutPath = pathFn.resolve(source, layout);
if (!pathFn.extname(layoutPath)) layoutPath += extname;
var layoutLocals = _.extend({}, locals, {body: result, layout: false});
fs.exists(layoutPath, function(exist){
if (exist) return next(null, layoutPath, layoutLocals);
var viewDir = options.view_dir || options.settings.views;
if (!exist && !viewDir) return callback(null, result);
// Absolute path
layoutPath = pathFn.join(viewDir, layout);
if (!pathFn.extname(layoutPath)) layoutPath += extname;
fs.exists(layoutPath, function(exist){
if (exist){
next(null, layoutPath, layoutLocals);
} else {
callback(null, result);
}
});
});
});
},
// Wrap the template with layout
function(layoutPath, locals, next){
renderFile(layoutPath, locals, callback);
}
]);
};