miki-template 2.0.1 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.github/workflows/ci.yml +13 -37
  2. package/.github/workflows/docs.yml +105 -0
  3. package/.github/workflows/release.yml +6 -0
  4. package/README.md +69 -14
  5. package/assets/logo.png +0 -0
  6. package/benchmarks/ejs-results.json +4 -4
  7. package/benchmarks/handlebars-results.json +6 -6
  8. package/benchmarks/miki-results.json +4 -4
  9. package/benchmarks/pug-results.json +4 -4
  10. package/benchmarks/stress.mjs +1 -1
  11. package/docs/api/async-render.md +85 -0
  12. package/docs/api/cache.md +87 -0
  13. package/docs/api/compile.md +128 -0
  14. package/docs/api/context-processors.md +77 -0
  15. package/docs/api/filters.md +217 -0
  16. package/docs/api/finder.md +94 -0
  17. package/docs/api/helpers.md +53 -0
  18. package/docs/api/i18n.md +157 -0
  19. package/docs/api/index.md +54 -0
  20. package/docs/api/libraries.md +207 -0
  21. package/docs/api/render-partial.md +81 -0
  22. package/docs/api/render.md +92 -0
  23. package/docs/api/security.md +145 -0
  24. package/docs/api/setup-express.md +76 -0
  25. package/docs/api/tags.md +134 -0
  26. package/docs/assets/banner.png +0 -0
  27. package/docs/assets/logo.png +0 -0
  28. package/docs/guide/advanced-usage.md +397 -0
  29. package/docs/guide/async-rendering.md +308 -0
  30. package/docs/guide/context-processors.md +257 -0
  31. package/docs/guide/custom-filters.md +311 -0
  32. package/docs/guide/custom-tags.md +271 -0
  33. package/docs/guide/filters.md +642 -0
  34. package/docs/guide/getting-started.md +102 -0
  35. package/docs/guide/installation.md +95 -0
  36. package/docs/guide/partial-templates.md +367 -0
  37. package/docs/guide/quick-start.md +222 -0
  38. package/docs/guide/security.md +345 -0
  39. package/docs/guide/tags.md +783 -0
  40. package/docs/guide/template-discovery.md +170 -0
  41. package/docs/guide/template-inheritance.md +273 -0
  42. package/docs/guide/what-is-miki-template.md +28 -0
  43. package/docs/guide/why-miki-template.md +75 -0
  44. package/docs/index.md +104 -0
  45. package/docs/integrations/elysia.md +78 -0
  46. package/docs/integrations/express.md +219 -0
  47. package/docs/integrations/fastify.md +77 -0
  48. package/docs/integrations/hono.md +78 -0
  49. package/docs/integrations/index.md +68 -0
  50. package/docs/integrations/koa.md +88 -0
  51. package/docs/integrations/nestjs.md +78 -0
  52. package/docs/integrations/tsed.md +81 -0
  53. package/docs/javascripts/extra.js +174 -0
  54. package/docs/performance.md +37 -0
  55. package/docs/stylesheets/extra.css +819 -0
  56. package/mkdocs.yml +217 -0
  57. package/overrides/main.html +26 -0
  58. package/overrides/partials/footer.html +9 -0
  59. package/package.json +4 -2
  60. package/requirements-docs.txt +1 -0
  61. package/docs/README.md +0 -18
  62. package/docs/advanced_usage.md +0 -71
  63. package/docs/api.md +0 -122
  64. package/docs/filters.md +0 -708
  65. package/docs/installation.md +0 -106
  66. package/docs/integrations.md +0 -214
  67. package/docs/overview.md +0 -79
  68. package/docs/partialdef.md +0 -70
  69. package/docs/security.md +0 -27
  70. package/docs/tags.md +0 -673
  71. package/docs/usage.md +0 -646
@@ -0,0 +1,397 @@
1
+ # Advanced Usage
2
+
3
+ This guide covers advanced miki-template features: caching, library system, i18n, and more.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Caching](#caching)
8
+ - [Library System](#library-system)
9
+ - [i18n / Internationalization](#i18n--internationalization)
10
+ - [Template Discovery](#template-discovery)
11
+ - [Partial Templates](#partial-templates)
12
+ - [Extending the Engine](#extending-the-engine)
13
+
14
+ ---
15
+
16
+ ## Caching
17
+
18
+ miki-template caches compiled templates for performance. The cache is an in-memory LRU with a 100-entry limit.
19
+
20
+ ### Clearing the Cache
21
+
22
+ === "CommonJS"
23
+
24
+ ```javascript
25
+ const { clearCache } = require('miki-template');
26
+
27
+ clearCache();
28
+ ```
29
+
30
+ === "ES Modules"
31
+
32
+ ```javascript
33
+ import { clearCache } from 'miki-template';
34
+
35
+ clearCache();
36
+ ```
37
+
38
+ ### When to Clear Cache
39
+
40
+ - **Development** — when templates change frequently on disk
41
+ - **Tests** — to ensure fresh compilation
42
+ - **Runtime filter/tag registration** — when dynamically registering custom tags/filters
43
+
44
+ ```javascript
45
+ // Development middleware that clears cache on file changes
46
+ const { clearCache } = require('miki-template');
47
+
48
+ if (process.env.NODE_ENV !== 'production') {
49
+ fs.watch('./views', () => {
50
+ clearCache();
51
+ console.log('Template cache cleared');
52
+ });
53
+ }
54
+ ```
55
+
56
+ ### How Caching Works
57
+
58
+ - Templates are cached by source string and compile options.
59
+ - The cache key combines the template source and the options object (views, custom settings).
60
+ - Cached templates are reused across renders, improving performance for repeated templates.
61
+
62
+ ## Library System
63
+
64
+ Libraries are bundles of filters, tags, and helpers that can be loaded into templates. Built-in libraries (humanize, cache, lorem, markdown, i18n) are auto-activated.
65
+
66
+ ### Registering a Library
67
+
68
+ === "CommonJS"
69
+
70
+ ```javascript
71
+ const { registerLibrary } = require('miki-template');
72
+
73
+ registerLibrary('myutils', {
74
+ filters: {
75
+ shout: (val) => String(val).toUpperCase() + '!',
76
+ whisper: (val) => String(val).toLowerCase() + '...'
77
+ },
78
+ tags: {
79
+ timestamp: (tagContent, parser) => ({
80
+ render: () => new Date().toISOString()
81
+ })
82
+ },
83
+ helpers: {
84
+ formatPrice: (val) => `$${Number(val).toFixed(2)}`
85
+ }
86
+ });
87
+ ```
88
+
89
+ === "ES Modules"
90
+
91
+ ```javascript
92
+ import { registerLibrary } from 'miki-template';
93
+
94
+ registerLibrary('myutils', {
95
+ filters: {
96
+ shout: (val) => String(val).toUpperCase() + '!',
97
+ whisper: (val) => String(val).toLowerCase() + '...'
98
+ },
99
+ tags: {
100
+ timestamp: (tagContent, parser) => ({
101
+ render: () => new Date().toISOString()
102
+ })
103
+ },
104
+ helpers: {
105
+ formatPrice: (val) => `$${Number(val).toFixed(2)}`
106
+ }
107
+ });
108
+ ```
109
+
110
+ ### Loading Libraries in Templates
111
+
112
+ Once registered, load the library with `{% load %}`:
113
+
114
+ ```html
115
+ {% load myutils %}
116
+
117
+ {{ price|formatPrice }}
118
+ {{ message|shout }}
119
+ {% timestamp %}
120
+ ```
121
+
122
+ ### Built-in Libraries
123
+
124
+ The following libraries are auto-activated (no `{% load %}` needed):
125
+
126
+ | Library | Features |
127
+ |---------|----------|
128
+ | `humanize` | Natural date formatting, number formatting |
129
+ | `cache` | Cache control tags and filters |
130
+ | `lorem` | Lorem ipsum placeholder text |
131
+ | `markdown` | `{{ content|markdown }}` filter for Markdown→HTML |
132
+ | `i18n` | `{% trans %}` and `{% blocktrans %}` for translations |
133
+
134
+ ### Deactivating and Re-registering
135
+
136
+ === "CommonJS"
137
+
138
+ ```javascript
139
+ const { unregisterLibrary, activateLibrary } = require('miki-template');
140
+
141
+ // Remove a library
142
+ unregisterLibrary('lorem');
143
+
144
+ // Re-activate
145
+ activateLibrary('lorem');
146
+ ```
147
+
148
+ === "ES Modules"
149
+
150
+ ```javascript
151
+ import { unregisterLibrary, activateLibrary } from 'miki-template';
152
+
153
+ unregisterLibrary('lorem');
154
+ activateLibrary('lorem');
155
+ ```
156
+
157
+ ## i18n / Internationalization
158
+
159
+ miki-template includes a built-in i18n system supporting `{% trans %}` and `{% blocktrans %}` tags.
160
+
161
+ ### Registering Translations
162
+
163
+ === "CommonJS"
164
+
165
+ ```javascript
166
+ const miki = require('miki-template');
167
+
168
+ miki.setLanguage('fr');
169
+ miki.registerTranslation('fr', {
170
+ 'Hello': 'Bonjour',
171
+ 'Goodbye': 'Au revoir',
172
+ 'Welcome, {name}!': 'Bienvenue, {name} !'
173
+ });
174
+ ```
175
+
176
+ === "ES Modules"
177
+
178
+ ```javascript
179
+ import { setLanguage, registerTranslation } from 'miki-template';
180
+
181
+ setLanguage('fr');
182
+ registerTranslation('fr', {
183
+ 'Hello': 'Bonjour',
184
+ 'Goodbye': 'Au revoir',
185
+ 'Welcome, {name}!': 'Bienvenue, {name} !'
186
+ });
187
+ ```
188
+
189
+ ### Setting Fallback Language
190
+
191
+ === "CommonJS"
192
+
193
+ ```javascript
194
+ const { setLanguage, setFallbackLanguage } = require('miki-template');
195
+
196
+ setLanguage('fr');
197
+ setFallbackLanguage('en');
198
+ ```
199
+
200
+ === "ES Modules"
201
+
202
+ ```javascript
203
+ import { setLanguage, setFallbackLanguage } from 'miki-template';
204
+
205
+ setLanguage('fr');
206
+ setFallbackLanguage('en');
207
+ ```
208
+
209
+ ### Template Usage
210
+
211
+ ```html
212
+ {% trans "Hello" %}
213
+ {% blocktrans %}Welcome, {{ name }}!{% endblocktrans %}
214
+ ```
215
+
216
+ ### Managing Languages
217
+
218
+ === "CommonJS"
219
+
220
+ ```javascript
221
+ const {
222
+ registerTranslation,
223
+ unregisterTranslation,
224
+ setLanguage,
225
+ getLanguage,
226
+ setFallbackLanguage,
227
+ getFallbackLanguage,
228
+ getAvailableLanguages
229
+ } = require('miki-template');
230
+ ```
231
+
232
+ === "ES Modules"
233
+
234
+ ```javascript
235
+ import {
236
+ registerTranslation,
237
+ unregisterTranslation,
238
+ setLanguage,
239
+ getLanguage,
240
+ setFallbackLanguage,
241
+ getFallbackLanguage,
242
+ getAvailableLanguages
243
+ } from 'miki-template';
244
+ ```
245
+
246
+ ## Template Discovery
247
+
248
+ The `findTemplateInViews()` function intelligently locates templates in nested directories.
249
+
250
+ === "CommonJS"
251
+
252
+ ```javascript
253
+ const { findTemplateInViews, setAppTemplateDirNames } = require('miki-template');
254
+
255
+ // Customize which directory names are treated as app template roots
256
+ setAppTemplateDirNames(['templates', 'views', 'pages']);
257
+
258
+ // Search for a template by name
259
+ const found = findTemplateInViews('home', ['./views', './app/templates']);
260
+ console.log(found);
261
+ // → /absolute/path/to/app/templates/home.html
262
+ ```
263
+
264
+ === "ES Modules"
265
+
266
+ ```javascript
267
+ import { findTemplateInViews, setAppTemplateDirNames } from 'miki-template';
268
+
269
+ setAppTemplateDirNames(['templates', 'views', 'pages']);
270
+ const found = findTemplateInViews('home', ['./views', './app/templates']);
271
+ console.log(found);
272
+ ```
273
+
274
+ ## Partial Templates
275
+
276
+ Partials let you define reusable template fragments using `{% partialdef %}` and render them on demand.
277
+
278
+ ### Defining and Rendering Partials
279
+
280
+ ```html
281
+ {% partialdef card %}
282
+ <div class="card">
283
+ <h3>{{ user.name }}</h3>
284
+ <p>{{ user.email }}</p>
285
+ </div>
286
+ {% endpartialdef %}
287
+ ```
288
+
289
+ Render a partial:
290
+
291
+ === "CommonJS"
292
+
293
+ ```javascript
294
+ const { render, compile } = require('miki-template');
295
+
296
+ // Using render() with file partials:
297
+ const html = render('home#card', { user: userData }, { views: './views' });
298
+
299
+ // Using compiled.renderPartial():
300
+ const compiled = compile(templateString);
301
+ const partialHtml = compiled.renderPartial('card', { user: userData });
302
+ ```
303
+
304
+ === "ES Modules"
305
+
306
+ ```javascript
307
+ import { render, compile } from 'miki-template';
308
+
309
+ const html = render('home#card', { user: userData }, { views: './views' });
310
+
311
+ const compiled = compile(templateString);
312
+ const partialHtml = compiled.renderPartial('card', { user: userData });
313
+ ```
314
+
315
+ ### Partial with Context
316
+
317
+ ```html
318
+ {% partialdef greeting %}
319
+ Hello, {{ name }}! You have {{ count }} messages.
320
+ {% endpartialdef %}
321
+
322
+ {% partial greeting with name="Alice" count=3 %}
323
+ ```
324
+
325
+ ## Extending the Engine
326
+
327
+ ### Registering Custom Tags
328
+
329
+ === "CommonJS"
330
+
331
+ ```javascript
332
+ const { registerTag } = require('miki-template');
333
+
334
+ registerTag('markdown', (tagContent, parser) => {
335
+ const nodelist = parser.parse(['endmarkdown']);
336
+ parser.skipTag();
337
+ const { marked } = require('marked');
338
+
339
+ return {
340
+ render: (context) => {
341
+ const body = nodelist.map(n => n.render(context)).join('');
342
+ return markSafe(marked(body));
343
+ }
344
+ };
345
+ });
346
+ ```
347
+
348
+ === "ES Modules"
349
+
350
+ ```javascript
351
+ import { registerTag, markSafe } from 'miki-template';
352
+ import { marked } from 'marked';
353
+
354
+ registerTag('markdown', (tagContent, parser) => {
355
+ const nodelist = parser.parse(['endmarkdown']);
356
+ parser.skipTag();
357
+
358
+ return {
359
+ render: (context) => {
360
+ const body = nodelist.map(n => n.render(context)).join('');
361
+ return markSafe(marked(body));
362
+ }
363
+ };
364
+ });
365
+ ```
366
+
367
+ ### Registering Custom Helpers
368
+
369
+ === "CommonJS"
370
+
371
+ ```javascript
372
+ const { registerHelper } = require('miki-template');
373
+
374
+ registerHelper('truncate_words', (str, count) => {
375
+ const words = String(str).split(/\s+/);
376
+ return words.slice(0, count).join(' ') + (words.length > count ? '...' : '');
377
+ });
378
+ ```
379
+
380
+ === "ES Modules"
381
+
382
+ ```javascript
383
+ import { registerHelper } from 'miki-template';
384
+
385
+ registerHelper('truncate_words', (str, count) => {
386
+ const words = String(str).split(/\s+/);
387
+ return words.slice(0, count).join(' ') + (words.length > count ? '...' : '');
388
+ });
389
+ ```
390
+
391
+ ## Next Steps
392
+
393
+ - [Custom Tags](./custom-tags)
394
+ - [Custom Filters](./custom-filters)
395
+ - [API Reference: Libraries](../api/libraries)
396
+ - [API Reference: Cache](../api/cache)
397
+ - [API Reference: i18n](../api/i18n)
@@ -0,0 +1,308 @@
1
+ # Async Rendering
2
+
3
+ miki-template supports async rendering for templates that use async filters, async custom tags, or async library components. Use `asyncRender()` instead of `render()` to await these operations.
4
+
5
+ ## Table of Contents
6
+
7
+ - [When to Use Async Rendering](#when-to-use-async-rendering)
8
+ - [asyncRender()](#asyncrender)
9
+ - [compiled.asyncRender()](#compiledasyncrender)
10
+ - [Async Filters](#async-filters)
11
+ - [Async Custom Tags](#async-custom-tags)
12
+ - [Express Async Engine](#express-async-engine)
13
+
14
+ ---
15
+
16
+ ## When to Use Async Rendering
17
+
18
+ Use `asyncRender()` when your templates contain any of the following:
19
+
20
+ - **Async filters** — filters that return Promises
21
+ - **Async custom tags** — custom tags whose `render()` returns a Promise
22
+ - **Async library components** — i18n translations loaded dynamically
23
+ - **Async helpers** — helpers that perform I/O
24
+
25
+ If you use async features with `render()` or `compiled.render()`, the engine throws:
26
+
27
+ > Async node encountered during sync render. Use asyncRender() instead.
28
+
29
+ ## asyncRender()
30
+
31
+ === "CommonJS"
32
+
33
+ ```javascript
34
+ const { asyncRender } = require('miki-template');
35
+
36
+ const html = await asyncRender('Hello, {{ name }}!', { name: 'World' });
37
+ console.log(html);
38
+ // → "Hello, World!"
39
+ ```
40
+
41
+ === "ES Modules"
42
+
43
+ ```javascript
44
+ import { asyncRender } from 'miki-template';
45
+
46
+ const html = await asyncRender('Hello, {{ name }}!', { name: 'World' });
47
+ console.log(html);
48
+ // → "Hello, World!"
49
+ ```
50
+
51
+ ### With Views and Partials
52
+
53
+ === "CommonJS"
54
+
55
+ ```javascript
56
+ const { asyncRender } = require('miki-template');
57
+
58
+ // Render a single partial from a file
59
+ const html = await asyncRender('home#card', { user: userData }, {
60
+ views: './templates'
61
+ });
62
+ ```
63
+
64
+ === "ES Modules"
65
+
66
+ ```javascript
67
+ import { asyncRender } from 'miki-template';
68
+
69
+ const html = await asyncRender('home#card', { user: userData }, {
70
+ views: './templates'
71
+ });
72
+ ```
73
+
74
+ ## compiled.asyncRender()
75
+
76
+ When you pre-compile a template, the returned object has `asyncRender()` and `asyncRenderWith()` methods:
77
+
78
+ === "CommonJS"
79
+
80
+ ```javascript
81
+ const { compile } = require('miki-template');
82
+
83
+ const template = `
84
+ {% load markdown %}
85
+ {{ content|markdown }}
86
+ `;
87
+
88
+ const compiled = compile(template);
89
+ const html = await compiled.asyncRender({ content: '# Hello World' });
90
+ ```
91
+
92
+ === "ES Modules"
93
+
94
+ ```javascript
95
+ import { compile } from 'miki-template';
96
+
97
+ const template = `
98
+ {% load markdown %}
99
+ {{ content|markdown }}
100
+ `;
101
+
102
+ const compiled = compile(template);
103
+ const html = await compiled.asyncRender({ content: '# Hello World' });
104
+ ```
105
+
106
+ ## Async Filters
107
+
108
+ Filters that return Promises are automatically awaited when using `asyncRender()`:
109
+
110
+ === "CommonJS"
111
+
112
+ ```javascript
113
+ const { registerFilter, asyncRender } = require('miki-template');
114
+
115
+ registerFilter('to_upper', (val) => val.toUpperCase());
116
+ registerFilter('fetch_url', async (url) => {
117
+ const res = await fetch(url);
118
+ return res.text();
119
+ });
120
+ ```
121
+
122
+ === "ES Modules"
123
+
124
+ ```javascript
125
+ import { registerFilter, asyncRender } from 'miki-template';
126
+
127
+ registerFilter('to_upper', (val) => val.toUpperCase());
128
+ registerFilter('fetch_url', async (url) => {
129
+ const res = await fetch(url);
130
+ return res.text();
131
+ });
132
+ ```
133
+
134
+ Usage:
135
+
136
+ ```html
137
+ {{ api_endpoint|fetch_url }}
138
+ ```
139
+
140
+ **Real-world CMS content fetch:**
141
+
142
+ === "CommonJS"
143
+
144
+ ```javascript
145
+ const { registerFilter, asyncRender } = require('miki-template');
146
+
147
+ registerFilter('cms_content', async (id) => {
148
+ const res = await fetch(`https://cms.example.com/api/content/${id}`);
149
+ const data = await res.json();
150
+ return data.html;
151
+ });
152
+
153
+ const html = await asyncRender(
154
+ '{% autoescape off %}{{ page_id|cms_content }}{% endautoescape %}',
155
+ { page_id: 'about' }
156
+ );
157
+ ```
158
+
159
+ === "ES Modules"
160
+
161
+ ```javascript
162
+ import { registerFilter, asyncRender } from 'miki-template';
163
+
164
+ registerFilter('cms_content', async (id) => {
165
+ const res = await fetch(`https://cms.example.com/api/content/${id}`);
166
+ const data = await res.json();
167
+ return data.html;
168
+ });
169
+
170
+ const html = await asyncRender(
171
+ '{% autoescape off %}{{ page_id|cms_content }}{% endautoescape %}',
172
+ { page_id: 'about' }
173
+ );
174
+ ```
175
+
176
+ ## Async Custom Tags
177
+
178
+ Custom tags whose `render()` returns a Promise work with `asyncRender()`:
179
+
180
+ === "CommonJS"
181
+
182
+ ```javascript
183
+ const { registerTag, asyncRender } = require('miki-template');
184
+
185
+ registerTag('api_data', (tagContent, parser) => {
186
+ const endpoint = tagContent.trim();
187
+ return {
188
+ async render(context) {
189
+ const res = await fetch(context.get(endpoint));
190
+ const data = await res.json();
191
+ return JSON.stringify(data, null, 2);
192
+ }
193
+ };
194
+ });
195
+
196
+ const html = await asyncRender(
197
+ '{% api_data api_url %}',
198
+ { api_url: 'https://api.example.com/users' }
199
+ );
200
+ ```
201
+
202
+ === "ES Modules"
203
+
204
+ ```javascript
205
+ import { registerTag, asyncRender } from 'miki-template';
206
+
207
+ registerTag('api_data', (tagContent, parser) => {
208
+ const endpoint = tagContent.trim();
209
+ return {
210
+ async render(context) {
211
+ const res = await fetch(context.get(endpoint));
212
+ const data = await res.json();
213
+ return JSON.stringify(data, null, 2);
214
+ }
215
+ };
216
+ });
217
+
218
+ const html = await asyncRender(
219
+ '{% api_data api_url %}',
220
+ { api_url: 'https://api.example.com/users' }
221
+ );
222
+ ```
223
+
224
+ ## Express Async Engine
225
+
226
+ For Express apps with async templates, use `__expressAsync` or `express({ async: true })`:
227
+
228
+ === "CommonJS"
229
+
230
+ ```javascript
231
+ const express = require('express');
232
+ const miki = require('miki-template');
233
+
234
+ const app = express();
235
+ app.engine('html', miki.express({ async: true }));
236
+ app.set('view engine', 'html');
237
+ app.set('views', './views');
238
+ ```
239
+
240
+ === "ES Modules"
241
+
242
+ ```javascript
243
+ import express from 'express';
244
+ import miki from 'miki-template';
245
+
246
+ const app = express();
247
+ app.engine('html', miki.express({ async: true }));
248
+ app.set('view engine', 'html');
249
+ app.set('views', './views');
250
+ ```
251
+
252
+ ### Express 5+ Native Promise Support
253
+
254
+ If you're using Express 5 (which supports Promise-based view engines), use `__expressAsync` directly:
255
+
256
+ === "CommonJS"
257
+
258
+ ```javascript
259
+ const express = require('express');
260
+ const miki = require('miki-template');
261
+
262
+ app.engine('html', miki.__expressAsync);
263
+ ```
264
+
265
+ === "ES Modules"
266
+
267
+ ```javascript
268
+ import express from 'express';
269
+ import miki from 'miki-template';
270
+
271
+ app.engine('html', miki.__expressAsync);
272
+ ```
273
+
274
+ ## asyncRenderWith()
275
+
276
+ Override compile-time options at render time:
277
+
278
+ === "CommonJS"
279
+
280
+ ```javascript
281
+ const { compile } = require('miki-template');
282
+
283
+ const compiled = compile(template, { views: './views' });
284
+
285
+ const html = await compiled.asyncRenderWith(
286
+ { user: userData },
287
+ { views: './other-views', customOption: true }
288
+ );
289
+ ```
290
+
291
+ === "ES Modules"
292
+
293
+ ```javascript
294
+ import { compile } from 'miki-template';
295
+
296
+ const compiled = compile(template, { views: './views' });
297
+
298
+ const html = await compiled.asyncRenderWith(
299
+ { user: userData },
300
+ { views: './other-views', customOption: true }
301
+ );
302
+ ```
303
+
304
+ ## Next Steps
305
+
306
+ - [Custom Filters: Async Filters](./custom-filters#async-filters)
307
+ - [Custom Tags: Async Custom Tags](./custom-tags#async-custom-tags)
308
+ - [API Reference: asyncRender](../api/async-render)