intern-Assignment/Node-Assignments/practice-2/node_modules/hbs/lib/hbs.js
2025-01-31 15:20:39 +05:30

332 lines
8 KiB
JavaScript

var fs = require('fs');
var path = require('path');
var walk = require('walk').walk;
var async = require('./async');
function Instance(handlebars) {
if (!(this instanceof Instance)) {
return new Instance(handlebars);
}
// expose handlebars, allows users to use their versions
// by overriding this early in their apps
var self = this;
self.handlebars = handlebars || require('handlebars').create();
// cache for templates, express 3.x doesn't do this for us
self.cache = {};
self.__express = middleware.bind(this);
// queue for partials registration
self._queue = null
// DEPRECATED, kept for backwards compatibility
self.SafeString = this.handlebars.SafeString;
self.Utils = this.handlebars.Utils;
};
// express 3.x template engine compliance
function middleware(filename, options, cb) {
var self = this;
if (self._queue) {
self._queue.push(middleware.bind.apply(middleware, [this].concat(Array.prototype.slice.call(arguments))))
return
}
var cache = self.cache;
var handlebars = self.handlebars;
self.async = async();
// grab extension from filename
// if we need a layout, we will look for one matching out extension
var extension = path.extname(filename);
// Default handlebars runtime options
var handlebarsOpts = {
allowProtoMethodsByDefault: true,
allowProtoPropertiesByDefault: true
}
// If passing the locals as data, create the handlebars options object now
if (self.__localsAsData) {
handlebarsOpts.data = options._locals
}
// render the original file
// cb(err, str)
function render_file(locals, cb) {
// cached?
var template = cache[filename];
if (template) {
try {
var res = template(locals, handlebarsOpts)
self.async.done(function (values) {
Object.keys(values).forEach(function (id) {
res = res.replace(id, values[id])
})
cb(null, res)
})
} catch (err) {
cb(prependFilenameToError(filename, err))
}
return
}
fs.readFile(filename, 'utf8', function(err, str){
if (err) {
return cb(err);
}
var template = handlebars.compile(str);
if (locals.cache) {
cache[filename] = template;
}
try {
var res = template(locals, handlebarsOpts);
self.async.done(function(values) {
Object.keys(values).forEach(function(id) {
res = res.replace(id, values[id]);
});
cb(null, res);
});
} catch (err) {
cb(prependFilenameToError(filename, err))
}
});
}
// render with a layout
function render_with_layout (filename, template, locals, cb) {
render_file(locals, function(err, str) {
if (err) {
return cb(err);
}
locals.body = str;
try {
var res = template(locals, handlebarsOpts)
self.async.done(function (values) {
Object.keys(values).forEach(function (id) {
res = res.replace(id, values[id])
})
cb(null, res)
})
} catch (err) {
cb(prependFilenameToError(filename, err))
}
});
}
var layout = options.layout;
// user did not specify a layout in the locals
// check global layout state
if (layout === undefined && options.settings && options.settings['view options']) {
layout = options.settings['view options'].layout;
}
// user explicitly request no layout
// either by specifying false for layout: false in locals
// or by settings the false view options
if (layout !== undefined && !layout) {
return render_file(options, cb);
}
var view_dirs = options.settings.views;
var layout_filename = [].concat(view_dirs).map(function (view_dir) {
var view_path = path.join(view_dir, layout || 'layout');
if (!path.extname(view_path)) {
view_path += extension;
}
return view_path;
});
for (var i = 0; i < layout_filename.length; i++) {
var layout_template = cache[layout_filename[i]]
if (layout_template) {
return render_with_layout(layout_filename[i], layout_template, options, cb)
}
}
// TODO check if layout path has .hbs extension
function prependFilenameToError (filename, err) {
// prepend to the message
if (typeof err.message === 'string') {
err.message = filename + ': ' + err.message
}
return err
}
function cacheAndCompile(filename, str) {
var layout_template = handlebars.compile(str);
if (options.cache) {
cache[filename] = layout_template;
}
render_with_layout(filename, layout_template, options, cb)
}
function tryReadFileAndCache(templates) {
var template = templates.shift();
fs.readFile(template, 'utf8', function(err, str) {
if (err) {
if (layout && templates.length === 0) {
// Only return error if user explicitly asked for layout.
return cb(err);
}
if (templates.length > 0) {
return tryReadFileAndCache(templates);
}
return render_file(options, cb);
}
cacheAndCompile(template, str);
});
}
tryReadFileAndCache(layout_filename);
}
// express 2.x template engine compliance
Instance.prototype.compile = function (str) {
if (typeof str !== 'string') {
return str;
}
var template = this.handlebars.compile(str);
return function (locals) {
return template(locals, {
helpers: locals.blockHelpers,
partials: null,
data: null
});
};
};
Instance.prototype.registerHelper = function () {
this.handlebars.registerHelper.apply(this.handlebars, arguments);
};
Instance.prototype.registerPartial = function () {
this.handlebars.registerPartial.apply(this.handlebars, arguments);
};
Instance.prototype.registerPartials = function (directory, options, done) {
var self = this
if (this._queue) {
self._queue.unshift(self.registerPartials.bind.apply(self.registerPartials,
[this].concat(Array.prototype.slice.call(arguments))))
return
} else {
self._queue = []
}
var callback
var handlebars = self.handlebars
var opts = options || {}
if (done || typeof options !== 'function') {
callback = done
} else {
callback = options
opts = {}
}
var rename = opts.rename !== undefined ? opts.rename : function (name) {
return name.replace(/\-/g, '_')
}
var w = walk(directory)
w.on('file', function (root, stat, done) {
var filepath = path.join(root, stat.name)
var isValidTemplate = /\.(html|hbs)$/.test(filepath);
if (!isValidTemplate) {
return done(null);
}
fs.readFile(filepath, 'utf8', function(err, data) {
if (!err) {
var extname = path.extname(filepath)
var name = path.relative(directory, filepath)
.slice(0, -(extname.length))
.replace(/\\/g, '/')
handlebars.registerPartial(rename(name).replace(/ /g, '_'), data)
}
done(err);
});
})
w.on('end', function () {
if (self._queue) {
var q = self._queue
self._queue = null
for (var i = 0; i < q.length; i++) {
q[i]()
}
}
})
if (callback) {
w.on('end', callback)
}
};
Instance.prototype.registerAsyncHelper = function(name, fn) {
var self = this;
self.handlebars.registerHelper(name, function() {
return self.async.resolve(fn.bind(this), arguments)
});
};
Instance.prototype.localsAsTemplateData = function(app) {
// Set a flag to indicate we should pass locals as data
this.__localsAsData = true;
app.render = (function(render) {
return function(view, options, callback) {
if (typeof options === "function") {
callback = options;
options = {};
}
// Mix response.locals (options._locals) with app.locals (this.locals)
options._locals = options._locals || {};
for (var key in this.locals) {
options._locals[key] = this.locals[key];
}
return render.call(this, view, options, callback);
};
})(app.render);
};
module.exports = new Instance();
module.exports.create = function(handlebars) {
return new Instance(handlebars);
};