Express.js template engine plugin for Handlebars
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Register and manage Handlebars helper functions, including asynchronous helpers with callback support for complex operations.
Register standard Handlebars helpers that return values immediately.
/**
* Register synchronous helper function
* @param name - Helper name to use in templates
* @param helper - Helper function that returns a value
*/
function registerHelper(name, helper);Usage Examples:
const hbs = require('hbs');
// Simple string helper
hbs.registerHelper('uppercase', function(str) {
return str.toUpperCase();
});
// Helper with multiple parameters
hbs.registerHelper('link_to', function(title, url) {
return '<a href="' + url + '">' + title + '</a>';
});
// Block helper
hbs.registerHelper('list', function(items, options) {
let out = '<ul>';
for (let i = 0; i < items.length; i++) {
out += '<li>' + options.fn(items[i]) + '</li>';
}
return out + '</ul>';
});
// Helper with context access
hbs.registerHelper('conditional', function(v1, operator, v2, options) {
switch (operator) {
case '==':
return (v1 == v2) ? options.fn(this) : options.inverse(this);
case '!=':
return (v1 != v2) ? options.fn(this) : options.inverse(this);
default:
return options.inverse(this);
}
});In Templates:
<!-- Simple helper -->
{{uppercase "hello world"}}
<!-- Output: HELLO WORLD -->
<!-- Helper with parameters -->
{{{link_to "Google" "https://google.com"}}}
<!-- Output: <a href="https://google.com">Google</a> -->
<!-- Block helper -->
{{#list people}}
{{name}} - {{age}}
{{/list}}
<!-- Conditional helper -->
{{#conditional score ">=" 90}}
Grade: A
{{else}}
Grade: B or lower
{{/conditional}}Register helpers that perform asynchronous operations like API calls, file I/O, or database queries.
/**
* Register asynchronous helper function
* @param name - Helper name to use in templates
* @param helper - Helper function that calls callback with result
*/
function registerAsyncHelper(name, helper);Usage Examples:
const hbs = require('hbs');
const fs = require('fs');
// File reading helper
hbs.registerAsyncHelper('readFile', function(filename, callback) {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) {
callback('Error reading file');
} else {
callback(data);
}
});
});
// API call helper
hbs.registerAsyncHelper('fetchUserName', function(userId, callback) {
// Simulate API call
setTimeout(() => {
const users = { 1: 'Alice', 2: 'Bob', 3: 'Charlie' };
callback(users[userId] || 'Unknown User');
}, 100);
});
// Helper with multiple parameters
hbs.registerAsyncHelper('formatDate', function(date, format, callback) {
// Simulate async date formatting
process.nextTick(() => {
const formatted = new Date(date).toLocaleDateString();
callback(formatted);
});
});
// Helper with context access
hbs.registerAsyncHelper('checkPermission', function(action, callback) {
// this.user would be available from template context
const user = this.user || {};
// Simulate permission check
setTimeout(() => {
const hasPermission = user.permissions && user.permissions.includes(action);
callback(hasPermission ? 'Allowed' : 'Denied');
}, 50);
});In Templates:
<!-- File content helper -->
<pre>{{readFile "config.txt"}}</pre>
<!-- User data helper -->
<p>Welcome, {{fetchUserName userId}}!</p>
<!-- Date formatting helper -->
<span>Created: {{formatDate createdAt "short"}}</span>
<!-- Permission helper -->
<div class="status">{{checkPermission "admin"}}</div>Direct access to the underlying Handlebars instance for advanced operations.
/**
* Direct access to Handlebars instance
* @type HandlebarsStatic
*/
handlebars;Usage Examples:
const hbs = require('hbs');
// Access Handlebars directly
const SafeString = hbs.handlebars.SafeString;
const Utils = hbs.handlebars.Utils;
// Use Handlebars SafeString in helpers
hbs.registerHelper('safeHtml', function(html) {
return new SafeString(html);
});
// Use Handlebars utilities
hbs.registerHelper('escapeExpression', function(str) {
return hbs.handlebars.Utils.escapeExpression(str);
});
// Register helper on Handlebars directly (not recommended)
hbs.handlebars.registerHelper('directHelper', function() {
return 'Registered directly';
});All helpers receive the current template context as this and access to Handlebars options.
hbs.registerHelper('contextExample', function() {
// Access current template data
console.log('Current context:', this);
// Access specific properties
return this.someProperty || 'default';
});Block helpers receive an options parameter with additional functionality:
hbs.registerHelper('blockExample', function(options) {
// options.fn - renders the block content
// options.inverse - renders the {{else}} content
// options.hash - contains hash parameters
// options.data - contains @index, @first, @last, etc.
return options.fn(this); // Render block content
});Helpers can accept named parameters via hash notation:
hbs.registerHelper('withDefaults', function(options) {
const defaults = {
color: 'blue',
size: 'medium',
...options.hash // Override with provided values
};
return `<div class="${defaults.color} ${defaults.size}">Content</div>`;
});In Template:
{{withDefaults color="red" size="large"}}
<!-- Output: <div class="red large">Content</div> -->Asynchronous helpers are automatically coordinated so templates wait for all async operations to complete before rendering.
Async Flow:
Multiple Async Helpers:
hbs.registerAsyncHelper('slowHelper1', function(callback) {
setTimeout(() => callback('Result 1'), 100);
});
hbs.registerAsyncHelper('slowHelper2', function(callback) {
setTimeout(() => callback('Result 2'), 200);
});In Template:
<p>{{slowHelper1}} and {{slowHelper2}}</p>
<!-- Template waits for both helpers, then outputs: -->
<!-- <p>Result 1 and Result 2</p> -->