An open API service indexing awesome lists of open source software.

https://github.com/shahradelahi/ts-template

⚡ High-performance, async-first templating engine with built-in filters and TypeScript type safety
https://github.com/shahradelahi/ts-template

async-rendering compiler filters mustache performance template-engine type-safe typescript

Last synced: 26 days ago
JSON representation

⚡ High-performance, async-first templating engine with built-in filters and TypeScript type safety

Awesome Lists containing this project

README

          


@se-oss/template


CI
NPM Version
MIT License
npm bundle size
Install Size

_@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.

---

- [Benefits](#benefits)
- [Installation](#-installation)
- [Usage](#-usage)
- [Migration Guidelines](#-migration-guidelines)
- [Documentation](#-documentation)
- [Contributing](#-contributing)
- [License](#license)

## 📦 Installation

```bash
pnpm add @se-oss/template
```

Install using your favorite package manager

**npm**

```bash
npm install @se-oss/template
```

**yarn**

```bash
yarn add @se-oss/template
```

## 📖 Usage

### Basic Usage

```ts
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.

```ts
// Escaped (Default)
render('Hello {{name}}', { name: 'Shahrad' });
// Output: Hello <b>Shahrad</b>

// Raw (Triple Curlies)
render('Hello {{{name}}}', { name: 'Shahrad' });
// Output: Hello Shahrad

// Raw (Ampersand)
render('Hello {{&name}}', { name: 'Shahrad' });
// Output: Hello Shahrad
```

### Null Coalescing & Logical Fallbacks

Safely resolve undefined or empty values inside expressions.

```ts
// 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 Guest
```

### Conditionals

Handle logical flows with standard conditional blocks.

```ts
const tpl = '{{#if active}}Active{{else}}Inactive{{/if}}';

render(tpl, { active: true }); // Output: Active
render(tpl, { active: false }); // Output: Inactive
```

```ts
// Inverse conditionals using "unless"
render('{{#unless active}}Inactive{{/unless}}', { active: false });
// Output: Inactive
```

### Loops & Iteration

Iterate arrays and objects while exposing index metadata.

```ts
// 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.

```ts
// 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: Empty
```

### Built-in & Custom Filters

Chain multiple filters using the pipe syntax.

```ts
// 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: darhahs
```

### Async Resolution

Perfect for rendering templates depending on async API calls or database lookups.

```ts
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.

```ts
const partials = {
userCard: 'Name: {{name}} (Role: {{> roleTag}}) | ',
roleTag: '{{role}}',
};

render(
'{{#each users}}{{> userCard}}{{/each}}',
{
users: [
{ name: 'Shahrad', role: 'Admin' },
{ name: 'Alice', role: 'User' },
],
},
partials
);
// Output: Name: Shahrad (Role: Admin) | Name: Alice (Role: User) |
```

#### Async Partial Resolvers

Dynamically fetch partials as needed during asynchronous renders.

```ts
await renderAsync('Welcome, {{> header}}!', { name: 'Shahrad' }, undefined, {
resolvePartial: async (name) => {
return name === 'header' ? '{{name}}' : '';
},
});
// Output: Welcome, Shahrad!
```

### Global Configuration

Register global options, filters, partials, or strict-mode checks.

```ts
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.

```ts
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.

```ts
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:

- [Migrating from mustache.js](migration/mustache.md)
- [Migrating from Handlebars.js](migration/handlebars.md)

## 📚 Documentation

For all configuration options, please see [the API docs](https://www.jsdocs.io/package/@se-oss/template).

## 🤝 Contributing

Want to contribute? Awesome! To show your support is to star the project, or to raise issues on [GitHub](https://github.com/shahradelahi/ts-template).

Thanks again for your support, it is much appreciated! 🙏

## License

[MIT](/LICENSE) © [Shahrad Elahi](https://github.com/shahradelahi) and [contributors](https://github.com/shahradelahi/ts-template/graphs/contributors).