koa-ejs
Version:
ejs render middleware for koa
145 lines (120 loc) • 3.2 kB
JavaScript
/*!
* koa-ejs - index.js
* Copyright(c) 2017 dead_horse <dead_horse@qq.com>
* MIT Licensed
*/
;
/**
* Module dependencies.
*/
const debug = require('debug')('koa-ejs');
const fs = require('mz').fs;
const path = require('path');
const ejs = require('ejs');
/**
* Temp assigned for override later
*/
const parentResolveInclude = ejs.resolveInclude;
/**
* default render options
* @type {Object}
*/
const defaultSettings = {
cache: true,
layout: 'layout',
viewExt: 'html',
locals: {},
compileDebug: false,
debug: false,
writeResp: true,
async: false
};
/**
* set app.context.render
*
* usage:
* ```
* await ctx.render('user', {name: 'dead_horse'});
* ```
* @param {Application} app koa application instance
* @param {Object} settings user settings
*/
exports = module.exports = function (app, settings) {
if (app.context.render) {
return;
}
if (!settings || !settings.root) {
throw new Error('settings.root required');
}
settings.root = path.resolve(process.cwd(), settings.root);
/**
* cache the generate package
* @type {Object}
*/
const cache = Object.create(null);
settings = Object.assign({}, defaultSettings, settings);
settings.viewExt = settings.viewExt
? '.' + settings.viewExt.replace(/^\./, '')
: '';
// override `ejs` node_module `resolveInclude` function
ejs.resolveInclude = function(name, filename, isDir) {
if (!path.extname(name)) {
name += settings.viewExt;
}
return parentResolveInclude(name, filename, isDir);
}
/**
* generate html with view name and options
* @param {String} view
* @param {Object} options
* @return {String} html
*/
async function render(view, options) {
view += settings.viewExt;
const viewPath = path.join(settings.root, view);
debug(`render: ${viewPath}`);
// get from cache
if (settings.cache && cache[viewPath]) {
return cache[viewPath].call(options.scope, options);
}
const tpl = await fs.readFile(viewPath, 'utf8');
const fn = ejs.compile(tpl, {
filename: viewPath,
_with: settings._with,
compileDebug: settings.debug && settings.compileDebug,
debug: settings.debug,
delimiter: settings.delimiter,
cache: settings.cache,
async: settings.async,
outputFunctionName: settings.outputFunctionName
});
if (settings.cache) {
cache[viewPath] = fn;
}
return fn.call(options.scope, options);
}
app.context.render = async function (view, _context) {
const ctx = this;
const context = Object.assign({}, ctx.state, _context);
let html = await render(view, context);
const layout = context.layout === false ? false : (context.layout || settings.layout);
if (layout) {
// if using layout
context.body = html;
html = await render(layout, context);
}
const writeResp = context.writeResp === false ? false : (context.writeResp || settings.writeResp);
if (writeResp) {
// normal operation
ctx.type = 'html';
ctx.body = html;
} else {
// only return the html
return html;
}
};
};
/**
* Expose ejs
*/
exports.ejs = ejs;