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 partial templates, including bulk registration from directories with custom naming transformations.
Register individual partial templates by name and content.
/**
* Register single partial template
* @param name - Partial name to use in templates
* @param template - Template string content
*/
function registerPartial(name, template);Usage Examples:
const hbs = require('hbs');
// Simple partial
hbs.registerPartial('header', '<header><h1>{{title}}</h1></header>');
// Partial with variables
hbs.registerPartial('user_card', `
<div class="user-card">
<h3>{{name}}</h3>
<p>{{email}}</p>
<span class="role">{{role}}</span>
</div>
`);
// Partial using other partials
hbs.registerPartial('page_layout', `
{{> header}}
<main>{{> content}}</main>
{{> footer}}
`);In Templates:
<!-- Use registered partials -->
{{> header title="Welcome"}}
{{> user_card name="Alice" email="alice@example.com" role="Admin"}}
<!-- Partials inherit current context -->
{{#each users}}
{{> user_card}}
{{/each}}Automatically register all partial templates from a directory with optional naming customization.
/**
* Register all partials from directory
* @param directory - Path to directory containing partial files
* @param options - Optional configuration object
* @param callback - Optional completion callback
*/
function registerPartials(directory, options, callback);Basic Usage:
const hbs = require('hbs');
const path = require('path');
// Register all partials from directory
hbs.registerPartials(path.join(__dirname, 'views/partials'));
// With callback
hbs.registerPartials(path.join(__dirname, 'views/partials'), (err) => {
if (err) {
console.error('Failed to register partials:', err);
} else {
console.log('All partials registered successfully');
}
});Directory Structure Example:
views/partials/
├── header.hbs → {{> header}}
├── footer.hbs → {{> footer}}
├── user-card.html → {{> user_card}}
├── nav menu.hbs → {{> nav_menu}}
├── social/
│ ├── twitter.hbs → {{> social/twitter}}
│ └── facebook.hbs → {{> social/facebook}}
└── forms/
├── login-form.hbs → {{> forms/login_form}}
└── contact.hbs → {{> forms/contact}}Customize how filenames are transformed into partial names using the rename option.
/**
* Partial registration options
* @typedef {Object} PartialOptions
* @property {Function} rename - Function to transform filename to partial name
*/
/**
* Register partials with custom naming
* @param directory - Path to directory containing partial files
* @param options - Configuration with rename function
* @param callback - Optional completion callback
*/
function registerPartials(directory, options, callback);Usage Examples:
const hbs = require('hbs');
// Default naming (spaces and hyphens become underscores)
hbs.registerPartials('./partials');
// user-profile.hbs → user_profile
// contact form.hbs → contact_form
// Custom naming: camelCase
hbs.registerPartials('./partials', {
rename: function(name) {
return name.replace(/[\s-_]+(.)/g, (match, letter) => letter.toUpperCase());
}
});
// user-profile.hbs → userProfile
// contact form.hbs → contactForm
// Custom naming: preserve hyphens, remove spaces
hbs.registerPartials('./partials', {
rename: function(name) {
return name.replace(/\s+/g, '');
}
});
// user-profile.hbs → user-profile
// contact form.hbs → contactform
// Custom naming: uppercase with prefix
hbs.registerPartials('./partials', {
rename: function(name) {
return 'PARTIAL_' + name.toUpperCase().replace(/\W/g, '_');
}
});
// user-profile.hbs → PARTIAL_USER_PROFILE
// contact form.hbs → PARTIAL_CONTACT_FORMUnderstanding how files are discovered and processed during directory registration.
File Discovery Rules:
.html or .hbs extensionsNaming Transformation Process:
rename function if providedExample Transformation:
Directory: /app/views/partials
File: /app/views/partials/admin/user-form.hbs
1. Relative path: admin/user-form.hbs
2. Remove extension: admin/user-form
3. Convert slashes: admin/user-form
4. Custom rename: admin/userForm (if camelCase rename)
5. Final cleanup: admin/userForm
Partial name: {{> admin/userForm}}Partial registration is asynchronous and non-blocking, but templates wait for completion before rendering.
const hbs = require('hbs');
// Multiple registration calls are queued
hbs.registerPartials('./partials/global');
hbs.registerPartials('./partials/admin');
hbs.registerPartials('./partials/components');
// All registrations complete before template rendering
app.get('/', (req, res) => {
// This render will wait for all partials to be registered
res.render('index');
});Registration Queue:
registerPartials calls are queued if called simultaneouslyPartial registration handles various error conditions gracefully.
Error Scenarios:
Example Error Handling:
hbs.registerPartials('./nonexistent-directory', (err) => {
if (err) {
console.error('Partial registration failed:', err.message);
// ENOENT: no such file or directory, open './nonexistent-directory'
}
});
// Handle partial registration in application startup
function initializePartials() {
return new Promise((resolve, reject) => {
hbs.registerPartials('./views/partials', (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
// Use in async startup
async function startApp() {
try {
await initializePartials();
console.log('Partials loaded successfully');
app.listen(3000, () => {
console.log('Server ready');
});
} catch (err) {
console.error('Failed to start app:', err);
process.exit(1);
}
}Partials inherit the current template context and can receive additional data.
Context Inheritance:
<!-- Template data: { user: { name: "Alice", role: "Admin" }, title: "Dashboard" } -->
{{> header}}
<!-- header partial has access to {{user.name}}, {{title}}, etc. -->
{{#with user}}
{{> user_profile}}
<!-- user_profile partial has access to {{name}}, {{role}} directly -->
{{/with}}Passing Additional Data:
<!-- Pass additional variables to partials -->
{{> button_primary text="Click Me" size="large"}}
<!-- Combine context and additional data -->
{{> user_card role="Guest"}}
<!-- user_card gets both inherited context AND role="Guest" -->Partials can include other partials, creating reusable component hierarchies.
Example Nested Structure:
<!-- page_layout partial -->
<!DOCTYPE html>
<html>
{{> head}}
<body>
{{> header}}
<main>
{{> content}}
</main>
{{> footer}}
</body>
</html>
<!-- header partial -->
<header>
{{> navigation}}
{{> breadcrumbs}}
</header>
<!-- navigation partial -->
<nav>
{{#each menuItems}}
{{> nav_item}}
{{/each}}
</nav>