miki-template 2.0.0 → 2.2.2
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.
- package/.github/workflows/ci.yml +14 -10
- package/.github/workflows/docs.yml +105 -0
- package/.github/workflows/npm-publish-github-packages.yml +36 -0
- package/README.md +142 -26
- package/assets/logo.png +0 -0
- package/benchmarks/ejs-results.json +17 -0
- package/benchmarks/ejs.js +36 -0
- package/benchmarks/handlebars-results.json +17 -0
- package/benchmarks/handlebars.js +48 -0
- package/benchmarks/miki-results.json +17 -0
- package/benchmarks/miki.js +36 -0
- package/benchmarks/pug-results.json +17 -0
- package/benchmarks/pug.js +36 -0
- package/benchmarks/run.js +69 -37
- package/benchmarks/stress.mjs +1 -1
- package/docs/api/async-render.md +85 -0
- package/docs/api/cache.md +87 -0
- package/docs/api/compile.md +128 -0
- package/docs/api/context-processors.md +77 -0
- package/docs/api/filters.md +217 -0
- package/docs/api/finder.md +94 -0
- package/docs/api/helpers.md +53 -0
- package/docs/api/i18n.md +157 -0
- package/docs/api/index.md +54 -0
- package/docs/api/libraries.md +207 -0
- package/docs/api/render-partial.md +81 -0
- package/docs/api/render.md +92 -0
- package/docs/api/security.md +145 -0
- package/docs/api/setup-express.md +76 -0
- package/docs/api/tags.md +134 -0
- package/docs/assets/banner.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/guide/advanced-usage.md +397 -0
- package/docs/guide/async-rendering.md +308 -0
- package/docs/guide/context-processors.md +257 -0
- package/docs/guide/custom-filters.md +311 -0
- package/docs/guide/custom-tags.md +271 -0
- package/docs/guide/filters.md +642 -0
- package/docs/guide/getting-started.md +102 -0
- package/docs/guide/installation.md +95 -0
- package/docs/guide/partial-templates.md +367 -0
- package/docs/guide/quick-start.md +222 -0
- package/docs/guide/security.md +345 -0
- package/docs/guide/tags.md +783 -0
- package/docs/guide/template-discovery.md +170 -0
- package/docs/guide/template-inheritance.md +273 -0
- package/docs/guide/what-is-miki-template.md +28 -0
- package/docs/guide/why-miki-template.md +75 -0
- package/docs/index.md +104 -0
- package/docs/integrations/elysia.md +78 -0
- package/docs/integrations/express.md +219 -0
- package/docs/integrations/fastify.md +77 -0
- package/docs/integrations/hono.md +78 -0
- package/docs/integrations/index.md +68 -0
- package/docs/integrations/koa.md +88 -0
- package/docs/integrations/nestjs.md +78 -0
- package/docs/integrations/tsed.md +81 -0
- package/docs/javascripts/extra.js +174 -0
- package/docs/performance.md +37 -0
- package/docs/stylesheets/extra.css +819 -0
- package/live-test/integrations/elysia-example.js +16 -0
- package/live-test/integrations/express-example.js +24 -0
- package/live-test/integrations/fastify-example.js +20 -0
- package/live-test/integrations/hono-example.js +16 -0
- package/live-test/integrations/koa-example.js +30 -0
- package/live-test/integrations/nestjs-example.js +25 -0
- package/live-test/integrations/smoke-test.js +166 -0
- package/live-test/integrations/tsed-example.js +23 -0
- package/live-test/package-lock.json +235 -0
- package/live-test/package.json +4 -0
- package/live-test/views/home.html +17 -0
- package/mkdocs.yml +217 -0
- package/overrides/main.html +26 -0
- package/overrides/partials/footer.html +9 -0
- package/package.json +16 -6
- package/requirements-docs.txt +1 -0
- package/tests/integration/partial-render.test.cjs +13 -0
- package/docs/README.md +0 -18
- package/docs/advanced_usage.md +0 -71
- package/docs/api.md +0 -122
- package/docs/filters.md +0 -708
- package/docs/installation.md +0 -106
- package/docs/overview.md +0 -79
- package/docs/partialdef.md +0 -70
- package/docs/security.md +0 -27
- package/docs/tags.md +0 -673
- package/docs/usage.md +0 -646
package/docs/api/tags.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Tags API
|
|
2
|
+
|
|
3
|
+
## registerTag
|
|
4
|
+
|
|
5
|
+
Register a custom tag callable from templates as `{% tag_name content %}...{% endtag_name %}`.
|
|
6
|
+
|
|
7
|
+
=== "CommonJS"
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
const { registerTag } = require('miki-template');
|
|
11
|
+
|
|
12
|
+
registerTag('hello', (tagContent, parser) => {
|
|
13
|
+
return {
|
|
14
|
+
render: (context) => 'Hello World!'
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
=== "ES Modules"
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import { registerTag } from 'miki-template';
|
|
23
|
+
|
|
24
|
+
registerTag('hello', (tagContent, parser) => {
|
|
25
|
+
return {
|
|
26
|
+
render: (context) => 'Hello World!'
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Tag Parser Signature
|
|
32
|
+
|
|
33
|
+
- `tagContent` — The full text after the tag name, as a string.
|
|
34
|
+
- `parser` — The Parser instance, providing `parser.parse()`, `parser.peek()`, `parser.skipTag()`.
|
|
35
|
+
|
|
36
|
+
Returns a **Node** object with a `render(context)` method. The render method receives the `Context` object and returns a string.
|
|
37
|
+
|
|
38
|
+
## Built-in Tags
|
|
39
|
+
|
|
40
|
+
### Control Flow
|
|
41
|
+
|
|
42
|
+
| Tag | Description |
|
|
43
|
+
|-----|-------------|
|
|
44
|
+
| `if / elif / else / endif` | Conditional blocks |
|
|
45
|
+
| `for / empty / endfor` | Loop over arrays/objects |
|
|
46
|
+
| `with / endwith` | Create a scoped context |
|
|
47
|
+
| `cycle` | Cycle through values |
|
|
48
|
+
| `firstof` | Output first non-empty value |
|
|
49
|
+
| `ifchanged / endifchanged` | Only output if value changed |
|
|
50
|
+
|
|
51
|
+
### Variable Assignment
|
|
52
|
+
|
|
53
|
+
| Tag | Description |
|
|
54
|
+
|-----|-------------|
|
|
55
|
+
| `set var = expr` | Inline variable assignment |
|
|
56
|
+
| `set var %}...{% endset` | Capture block output into a variable |
|
|
57
|
+
|
|
58
|
+
### Date and Time
|
|
59
|
+
|
|
60
|
+
| Tag | Description |
|
|
61
|
+
|-----|-------------|
|
|
62
|
+
| `now "Y-m-d H:i:s"` | Output current date/time |
|
|
63
|
+
|
|
64
|
+
### Utility
|
|
65
|
+
|
|
66
|
+
| Tag | Description |
|
|
67
|
+
|-----|-------------|
|
|
68
|
+
| `static "path"` | Resolve static asset path |
|
|
69
|
+
| `url 'route.name' arg1 arg2` | Generate URL by route name |
|
|
70
|
+
| `regroup list by attr as name` | Regroup a list by an attribute |
|
|
71
|
+
| `spaceless / endspaceless` | Remove whitespace between HTML tags |
|
|
72
|
+
| `widthratio value max max_width` | Calculate CSS width ratio |
|
|
73
|
+
| `debug` | Output debugging context information |
|
|
74
|
+
|
|
75
|
+
### Security
|
|
76
|
+
|
|
77
|
+
| Tag | Description |
|
|
78
|
+
|-----|-------------|
|
|
79
|
+
| `csrf_token` | Output CSRF hidden input |
|
|
80
|
+
| `csp_nonce_attr` | Output `nonce` attribute for CSP |
|
|
81
|
+
|
|
82
|
+
### Comments and Raw Output
|
|
83
|
+
|
|
84
|
+
| Tag | Description |
|
|
85
|
+
|-----|-------------|
|
|
86
|
+
| `comment / endcomment` | Comment out content |
|
|
87
|
+
| `verbatim / endverbatim` | Disable tag parsing within |
|
|
88
|
+
|
|
89
|
+
### Autoescape
|
|
90
|
+
|
|
91
|
+
| Tag | Description |
|
|
92
|
+
|-----|-------------|
|
|
93
|
+
| `autoescape on / off / endautoescape` | Toggle HTML escaping |
|
|
94
|
+
|
|
95
|
+
### Library Loading
|
|
96
|
+
|
|
97
|
+
| Tag | Description |
|
|
98
|
+
|-----|-------------|
|
|
99
|
+
| `load library_name` | Load a registered library |
|
|
100
|
+
|
|
101
|
+
### Template Tags
|
|
102
|
+
|
|
103
|
+
| Tag | Description |
|
|
104
|
+
|-----|-------------|
|
|
105
|
+
| `templatetag token` | Output a template syntax character (e.g. `{% templatetag openpercentblock %}`) |
|
|
106
|
+
|
|
107
|
+
### Inheritance
|
|
108
|
+
|
|
109
|
+
| Tag | Description |
|
|
110
|
+
|-----|-------------|
|
|
111
|
+
| `extends "parent.html"` | Inherit from a parent template |
|
|
112
|
+
| `block name / endblock` | Define/overriding inheritable block |
|
|
113
|
+
| `include "file.html"` | Include another template |
|
|
114
|
+
|
|
115
|
+
### Partials
|
|
116
|
+
|
|
117
|
+
| Tag | Description |
|
|
118
|
+
|-----|-------------|
|
|
119
|
+
| `partialdef name / endpartialdef` | Define a named partial |
|
|
120
|
+
| `partial name with k=v` | Render a defined partial |
|
|
121
|
+
|
|
122
|
+
### i18n
|
|
123
|
+
|
|
124
|
+
| Tag | Description |
|
|
125
|
+
|-----|-------------|
|
|
126
|
+
| `trans "key"` | Translate a string |
|
|
127
|
+
| `blocktrans / endblocktrans` | Translate with variables |
|
|
128
|
+
| `language "xx" / endlanguage` | Switch language for a block |
|
|
129
|
+
|
|
130
|
+
## Next Steps
|
|
131
|
+
|
|
132
|
+
- [Tags Guide](../guide/tags)
|
|
133
|
+
- [Custom Tags](../guide/custom-tags)
|
|
134
|
+
- [API Reference](../)
|
|
Binary file
|
|
Binary file
|
|
@@ -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)
|