razor-tmpl
Version:
razor style template engine for JavaScript
169 lines (142 loc) • 3.81 kB
JavaScript
/**
* module dependencies
*/
var fs = require('fs');
var pathFn = require('path');
var vm = require('vm');
var util = require('util');
var layoutUtil = require('./layoutUtil');
var razor = module.exports = require('../index');
var Engine = require('../engine');
var Parser = require('../parser');
var Compiler = require('../compiler');
/**
* decide whether enable cache
*
* default not cache when development
*/
var enableCache;
var env = process.env.NODE_ENV || 'developement';
enableCache = env !== 'developement';
/*
* cache
*/
Engine.prototype.cache = {};
/**
* cache control
*/
Engine.prototype.enableCache = enableCache;
/**
* Synchronous version
*/
Engine.prototype.renderFileSync = function(view, locals) {
locals = locals || {};
view = pathFn.resolve(view);
var tmpl = this._readFull(view, locals);
return this.advanceRender(view, locals, tmpl);
};
/**
* express framework support
*
* e.g
* var razor = require('razor-tmpl');
* app.engine('.razor',razor.express);
*/
razor.express = function(view, loals, callback) {
try {
var res = razor.renderFileSync(view, locals);
callback(null, res);
}
catch (e) {
callback(e);
}
};
/**
* read a template & cache
*
* private
*/
Engine.prototype._read = function(view) {
var ret;
// read cache
if (this.enableCache && (ret = this.cache[view])) {
return ret;
}
// read file
ret = fs.readFileSync(view, 'utf8');
// save cache ?
if (this.enableCache) {
this.cache[view] = ret;
}
return ret;
}
/**
* get the full template & cache it
*/
Engine.prototype._readFull = function(view, locals) {
if (this.enableCache && this.cache[view]) {
return this.cache[view];
}
var tmpl = this._read(view);
// find out all @include & replace with correct contents
tmpl = this._handleInclude(view, tmpl);
// find layout
var layout = (function() {
if (locals && locals.layout) return locals.layout;
// search in template
var ret = layoutUtil.get(tmpl, this.symbol);
// found
if (ret) {
tmpl = ret.template;
return ret.layout;
}
// not found;
return null;
}).bind(this)();
if (!layout) {
return tmpl;
}
// append ext
if (pathFn.extname(layout) === '') {
layout += pathFn.extname(view);
}
var layoutPath = pathFn.resolve(pathFn.dirname(view), layout);
var layoutTmpl = this._readFull(layoutPath);
var final = layoutUtil.fill(layoutTmpl, tmpl);
if (this.enableCache) {
this.cache[view] = final;
}
return final;
}
/**
* handle @include in tmpl
*/
Engine.prototype._handleInclude = function(view, tmpl) {
var self = this;
var base = pathFn.dirname(view);
var reg = new RegExp(this.symbol + "include\\(['\\\"]([\\s\\S]*?)['\\\"]\\);?", 'ig');
return tmpl.replace(reg, function(match, group) {
var to = group.trim(); //to be included
// append ext if need
if (pathFn.extname(to) === '') {
to += pathFn.extname(view);
}
to = pathFn.join(base, to);
return self._readFull(to); //to再次include 其他的
});
};
/**
* 支持require,支持__dirname,支持__filename
*/
Engine.prototype.advanceRender = function(view, locals, tmpl) {
locals = locals || {};
var tmpl = this.addVarDef(tmpl, locals);
var tokens = Parser.parse(this.symbol, tmpl);
var codes = Compiler.toCodes(tokens);
var code = codes.join('\n');
var func = new Function(this.localsName, "require", "__dirname", "__filename", code);
var _require = require('./getRequire')(view);
var _dirname = pathFn.dirname(view);
var _filename = view;
return func(locals, _require, _dirname, _filename);
};