@se-oss/template
v1.0.0
Published
High-performance, async-first templating engine with built-in filters and TypeScript type safety.
Maintainers
Readme
@se-oss/template is a high-performance, safe, and highly extensible templating engine supporting Mustache syntax, pipelines, and native asynchronous execution.
Benefits
- Performance First: Automated compiled function caching using a dynamic LRU cache.
- Async Architecture: Native support for asynchronous filters, block iterations, and partial resolution.
- Filter Pipelines: Chainable built-in and custom filters with parameter support.
- Fail-Safe Operators: Integrated nullish coalescing (
??) and logical OR (||) operators. - Whitespace Control: Automatic standalone tag detection and whitespace slurping.
- Type-Level Magic: Zero-config TypeScript schema inference directly from template string literals.
- World-Class Errors: Informative compiler errors coupled with syntax-highlighted code frames.
- Secure by Default: Active protection preventing prototype pollution and unsafe property access.
📦 Installation
pnpm add @se-oss/templatenpm
npm install @se-oss/templateyarn
yarn add @se-oss/template📖 Usage
Basic Usage
import { render } from '@se-oss/template';
const result = render('Hello {{name}}!', { name: 'Shahrad' });
// Output: Hello Shahrad!HTML Escaping & Raw Outputs
By default, HTML characters are escaped. Use triple curlies or the ampersand operator to output raw HTML.
// Escaped (Default)
render('Hello {{name}}', { name: '<b>Shahrad</b>' });
// Output: Hello <b>Shahrad</b>
// Raw (Triple Curlies)
render('Hello {{{name}}}', { name: '<b>Shahrad</b>' });
// Output: Hello <b>Shahrad</b>
// Raw (Ampersand)
render('Hello {{&name}}', { name: '<b>Shahrad</b>' });
// Output: Hello <b>Shahrad</b>Null Coalescing & Logical Fallbacks
Safely resolve undefined or empty values inside expressions.
// Nullish Coalescing (??)
render('Hello {{user.nickname ?? user.name ?? "Guest"}}', {
user: { nickname: null, name: 'Shahrad' },
});
// Output: Hello Shahrad
// Logical OR (||)
render('Hello {{user.name || "Guest"}}', { user: { name: '' } });
// Output: Hello GuestConditionals
Handle logical flows with standard conditional blocks.
const tpl = '{{#if active}}Active{{else}}Inactive{{/if}}';
render(tpl, { active: true }); // Output: Active
render(tpl, { active: false }); // Output: Inactive// Inverse conditionals using "unless"
render('{{#unless active}}Inactive{{/unless}}', { active: false });
// Output: InactiveLoops & Iteration
Iterate arrays and objects while exposing index metadata.
// Array Iteration
const tpl =
'{{#each users}}{{this}} (index: {{@index}}, first: {{@first}}, last: {{@last}}){{/each}}';
render(tpl, { users: ['A', 'B'] });
// Output: A (index: 0, first: true, last: false)B (index: 1, first: false, last: true)
// Object Key-Value Iteration
render('{{#each user}}{{@key}}: {{.}}, {{/each}}', {
user: { name: 'Shahrad', role: 'admin' },
});
// Output: name: Shahrad, role: admin,Mustache-Style Sections
Seamless generic sections supporting scope-shifting, looping, and inverted fallbacks.
// Scope shifting and nested lookup
const tpl = '{{#user}}{{name}} is {{role}}{{/user}}';
render(tpl, { user: { name: 'Shahrad', role: 'admin' } });
// Output: Shahrad is admin
// Inverted section (executes if array/value is empty or falsy)
render('{{^items}}Empty{{/items}}', { items: [] });
// Output: EmptyBuilt-in & Custom Filters
Chain multiple filters using the pipe syntax.
// Built-in pipeline with arguments
render('{{name | trim | capitalize | default("Guest")}}', {
name: ' shahrad ',
});
// Output: Shahrad
// Custom local filters
render('{{name | reverse}}', { name: 'shahrad' }, undefined, {
filters: {
reverse: (val) => String(val).split('').reverse().join(''),
},
});
// Output: darhahsAsync Resolution
Perfect for rendering templates depending on async API calls or database lookups.
import { renderAsync } from '@se-oss/template';
const asyncFilter = async (val) => {
return new Promise((resolve) =>
setTimeout(() => resolve(String(val).toUpperCase()), 10)
);
};
await renderAsync(
'Hello {{name | toUpper}}!',
{ name: 'shahrad' },
undefined,
{
filters: { toUpper: asyncFilter },
}
);
// Output: Hello SHAHRAD!Partials
Modularize your templates. Partials compile recursively and resolve dynamically.
const partials = {
userCard: 'Name: {{name}} (Role: {{> roleTag}}) | ',
roleTag: '<u>{{role}}</u>',
};
render(
'{{#each users}}{{> userCard}}{{/each}}',
{
users: [
{ name: 'Shahrad', role: 'Admin' },
{ name: 'Alice', role: 'User' },
],
},
partials
);
// Output: Name: Shahrad (Role: <u>Admin</u>) | Name: Alice (Role: <u>User</u>) |Async Partial Resolvers
Dynamically fetch partials as needed during asynchronous renders.
await renderAsync('Welcome, {{> header}}!', { name: 'Shahrad' }, undefined, {
resolvePartial: async (name) => {
return name === 'header' ? '<b>{{name}}</b>' : '';
},
});
// Output: Welcome, <b>Shahrad</b>!Global Configuration
Register global options, filters, partials, or strict-mode checks.
import { configure } from '@se-oss/template';
configure({
strict: true, // Throw error on undefined variables
cacheSize: 1000, // Dynamic LRU cache limit
filters: {
globalUpper: (val) => String(val).toUpperCase(),
},
partials: {
baseLayout: 'Layout: {{> content}}',
},
});TypeScript Typings
Automatic compile-time schema inference. Your IDE will know the exact fields your template expects.
import { compile } from '@se-oss/template';
const myTemplate = compile('Hello {{user.name}}, you are {{age}}');
// TypeScript errors if properties are missing or of incorrect types
myTemplate({
user: { name: 'Shahrad' },
age: 25,
});Error Reporting
Get immediate, pinpoint accuracy on compilation and parsing errors.
try {
render('Hello {{#if active}}World{{/each}}', { active: true });
} catch (err) {
console.log(err.message);
}
/*
Error: Mismatched block end: expected "if" but found "each"
at template:1:21
> 1 | {{#if active}}Hello{{/each}}
| ^
*/🚀 Migration Guidelines
Migrating from another template engine? We have detailed step-by-step migration guides to help you transition smoothly:
📚 Documentation
For all configuration options, please see the API docs.
🤝 Contributing
Want to contribute? Awesome! To show your support is to star the project, or to raise issues on GitHub.
Thanks again for your support, it is much appreciated! 🙏
License
MIT © Shahrad Elahi and contributors.
