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.
- package/.github/workflows/ci.yml +13 -37
- package/.github/workflows/docs.yml +105 -0
- package/.github/workflows/release.yml +6 -0
- package/README.md +69 -14
- package/assets/logo.png +0 -0
- package/benchmarks/ejs-results.json +4 -4
- package/benchmarks/handlebars-results.json +6 -6
- package/benchmarks/miki-results.json +4 -4
- package/benchmarks/pug-results.json +4 -4
- 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/mkdocs.yml +217 -0
- package/overrides/main.html +26 -0
- package/overrides/partials/footer.html +9 -0
- package/package.json +4 -2
- package/requirements-docs.txt +1 -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/integrations.md +0 -214
- 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
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Get up and running with miki-template in under a minute.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- **Node.js** 18.x or 20.x (or later, including Bun)
|
|
8
|
+
- **npm** 9+, **pnpm**, or **yarn**
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
=== "npm"
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install miki-template
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
=== "pnpm"
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add miki-template
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
=== "yarn"
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
yarn add miki-template
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Example: Render a Template String
|
|
31
|
+
|
|
32
|
+
The simplest way to use miki-template is the `render()` convenience function. It compiles the template, applies any registered context processors, and returns the HTML — all in one call.
|
|
33
|
+
|
|
34
|
+
=== "CommonJS (require)"
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const { render } = require('miki-template');
|
|
38
|
+
|
|
39
|
+
const html = render('Hello {{ name|title }}!', { name: 'alice' });
|
|
40
|
+
console.log(html); // "Hello Alice!"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
=== "ES Modules (import)"
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
import { render } from 'miki-template';
|
|
47
|
+
|
|
48
|
+
const html = render('Hello {{ name|title }}!', { name: 'alice' });
|
|
49
|
+
console.log(html); // "Hello Alice!"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Example: Express App
|
|
53
|
+
|
|
54
|
+
The real power of miki-template comes with `setupExpress()` — a single function that registers the view engine, configures the views directory, and patches `res.render` so you can render partials with the `view#partial` syntax.
|
|
55
|
+
|
|
56
|
+
=== "CommonJS (require)"
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const express = require('express');
|
|
60
|
+
const miki = require('miki-template');
|
|
61
|
+
|
|
62
|
+
const app = express();
|
|
63
|
+
miki.setupExpress(app, { extension: 'html', views: './views' });
|
|
64
|
+
|
|
65
|
+
app.get('/', (req, res) => res.render('home', { user: req.user }));
|
|
66
|
+
|
|
67
|
+
app.listen(3000, () => console.log('Listening on :3000'));
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
=== "ES Modules (import)"
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
import express from 'express';
|
|
74
|
+
import miki from 'miki-template';
|
|
75
|
+
|
|
76
|
+
const app = express();
|
|
77
|
+
miki.setupExpress(app, { extension: 'html', views: './views' });
|
|
78
|
+
|
|
79
|
+
app.get('/', (req, res) => res.render('home', { user: req.user }));
|
|
80
|
+
|
|
81
|
+
app.listen(3000, () => console.log('Listening on :3000'));
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
=== "Bun"
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { setupExpress } from 'miki-template';
|
|
88
|
+
import express from 'express';
|
|
89
|
+
|
|
90
|
+
const app = express();
|
|
91
|
+
// Named import works; default import also works (`import miki from ...`)
|
|
92
|
+
setupExpress(app, { extension: 'html', views: './views' });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Next Steps
|
|
96
|
+
|
|
97
|
+
- [What is miki-template?](./what-is-miki-template)
|
|
98
|
+
- [Why miki-template?](./why-miki-template)
|
|
99
|
+
- [Installation Guide](./installation)
|
|
100
|
+
- [Quick Start](./quick-start)
|
|
101
|
+
- [Template Syntax & Tags](./tags)
|
|
102
|
+
- [Filters](./filters)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
Complete guide to installing and verifying miki-template in different environments.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- **Node.js** 18.x or 20.x (Node 18+ required for `URL`, `fetch`, and other Web API globals used by the engine)
|
|
8
|
+
- **npm** 9+, **pnpm**, or **yarn**
|
|
9
|
+
- **Bun** (optional) — miki-template is fully compatible with Bun
|
|
10
|
+
|
|
11
|
+
## Install via npm
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install miki-template
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Install via pnpm
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add miki-template
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Install via yarn
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
yarn add miki-template
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Install via Bun
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bun add miki-template
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Package.json `"type"` Considerations
|
|
36
|
+
|
|
37
|
+
miki-template ships a dual CommonJS/ESM package:
|
|
38
|
+
|
|
39
|
+
- **CommonJS** entry: `src/index.js` — importable via `require('miki-template')` or `import` (Node auto-detects the `import` condition).
|
|
40
|
+
- **ESM** entry: `src/esm.mjs` — importable via `import ... from 'miki-template'`.
|
|
41
|
+
|
|
42
|
+
| Your project uses | How to import |
|
|
43
|
+
|---|---|
|
|
44
|
+
| CommonJS (`"type": "commonjs"` or no `type` field) | `const miki = require('miki-template')` |
|
|
45
|
+
| ES Modules (`"type": "module"`) | `import miki from 'miki-template'` or `import { render } from 'miki-template'` |
|
|
46
|
+
| TypeScript /Bun | Same as ESM — `import` syntax works directly |
|
|
47
|
+
|
|
48
|
+
> **Tip:** If your project is ESM-only (no `"type"` field but using `.mjs` files), use named imports: `import { render, compile } from 'miki-template'`.
|
|
49
|
+
|
|
50
|
+
## Verifying the Installation
|
|
51
|
+
|
|
52
|
+
=== "CommonJS"
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
const miki = require('miki-template');
|
|
56
|
+
console.log(miki.render('Hello {{ name }}!', { name: 'World' }));
|
|
57
|
+
// Output: Hello World!
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
=== "ES Modules"
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
import { render } from 'miki-template';
|
|
64
|
+
console.log(render('Hello {{ name }}!', { name: 'World' }));
|
|
65
|
+
// Output: Hello World!
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
=== "Bun / TypeScript"
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { render } from 'miki-template';
|
|
72
|
+
console.log(render('Hello {{ name }}!', { name: 'World' }));
|
|
73
|
+
// Output: Hello World!
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Troubleshooting
|
|
77
|
+
|
|
78
|
+
### "Cannot find module 'miki-template'"
|
|
79
|
+
|
|
80
|
+
Ensure the package is installed in the correct `node_modules` directory. If you're working in a monorepo, run `npm install` from the package root.
|
|
81
|
+
|
|
82
|
+
### Auto-escaping produces `&` where you expect `&`
|
|
83
|
+
|
|
84
|
+
This is by design — miki-template escapes all variables by default to prevent XSS. Use `|safe` or `markSafe()` for trusted HTML:
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
{{ htmlContent|safe }}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
See [Security](./security) for details.
|
|
91
|
+
|
|
92
|
+
## Next Steps
|
|
93
|
+
|
|
94
|
+
- [Quick Start](./quick-start)
|
|
95
|
+
- [What is miki-template?](./what-is-miki-template)
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# Partial Templates
|
|
2
|
+
|
|
3
|
+
Partial templates let you define reusable UI chunks once and render them anywhere. This is especially powerful with HTMX, Turbo, or any AJAX-style partial response pattern.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Defining Partials](#defining-partials)
|
|
8
|
+
- [Rendering Partials by Name](#rendering-partials-by-name)
|
|
9
|
+
- [Nested Partials](#nested-partials)
|
|
10
|
+
- [Partials with Context](#partials-with-context)
|
|
11
|
+
- [Partials with Include](#partials-with-include)
|
|
12
|
+
- [Rendering Partials Programmatically](#rendering-partials-programmatically)
|
|
13
|
+
- [Express Partial Rendering](#express-partial-rendering)
|
|
14
|
+
- [Common Pitfalls](#common-pitfalls)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Defining Partials
|
|
19
|
+
|
|
20
|
+
Use `{% partialdef %}` to define a named partial inside any template:
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<!-- views/home.html -->
|
|
24
|
+
{% partialdef card %}
|
|
25
|
+
<div class="card">
|
|
26
|
+
<h3>{{ title|default:"Untitled" }}</h3>
|
|
27
|
+
<p>{{ body|truncatewords:30 }}</p>
|
|
28
|
+
{% if featured %}<em>Featured</em>{% endif %}
|
|
29
|
+
</div>
|
|
30
|
+
{% endpartialdef %}
|
|
31
|
+
|
|
32
|
+
{% for entry in entries %}
|
|
33
|
+
{% partial card with title=entry.title body=entry.body featured=entry.featured %}
|
|
34
|
+
{% endfor %}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Inline Partials
|
|
38
|
+
|
|
39
|
+
A `{% partialdef %}` block renders its body inline where it is defined **and** registers itself for later use:
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
{% partialdef notice %}
|
|
43
|
+
<div class="alert">{{ message }}</div>
|
|
44
|
+
{% endpartialdef %}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This means you get immediate output and a reusable partial in one declaration.
|
|
48
|
+
|
|
49
|
+
Use the `inline` option explicitly:
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
{% partialdef greeting inline %}
|
|
53
|
+
Hello {{ name }}!
|
|
54
|
+
{% endpartialdef %}
|
|
55
|
+
<!-- Above line ALSO outputs "Hello World!" when rendered -->
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Rendering Partials by Name
|
|
59
|
+
|
|
60
|
+
Once defined, you can render a partial by name from your routes using the `view#partial` syntax:
|
|
61
|
+
|
|
62
|
+
=== "CommonJS"
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
const express = require('express');
|
|
66
|
+
const miki = require('miki-template');
|
|
67
|
+
|
|
68
|
+
const app = express();
|
|
69
|
+
miki.setupExpress(app, { extension: 'html', views: './views' });
|
|
70
|
+
|
|
71
|
+
app.get('/card/:id', (req, res) =>
|
|
72
|
+
res.render(`home#card`, { title: 'Hello', body: 'World...', featured: true })
|
|
73
|
+
);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
=== "ES Modules"
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
import express from 'express';
|
|
80
|
+
import miki from 'miki-template';
|
|
81
|
+
|
|
82
|
+
const app = express();
|
|
83
|
+
miki.setupExpress(app, { extension: 'html', views: './views' });
|
|
84
|
+
|
|
85
|
+
app.get('/card/:id', (req, res) =>
|
|
86
|
+
res.render(`home#card`, { title: 'Hello', body: 'World...', featured: true })
|
|
87
|
+
);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The syntax is `viewName#partialName`. The engine resolves the file, extracts the named partial, and renders only that block.
|
|
91
|
+
|
|
92
|
+
**Real-world HTMX example:**
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<!-- views/products.html -->
|
|
96
|
+
{% partialdef product_card %}
|
|
97
|
+
<div class="product-card" id="product-{{ product.id }}">
|
|
98
|
+
<img src="{{ product.image|static }}" alt="{{ product.name }}">
|
|
99
|
+
<h3>{{ product.name|capfirst }}</h3>
|
|
100
|
+
<p class="price">${{ product.price|floatformat:2 }}</p>
|
|
101
|
+
<button hx-post="/cart/add/{{ product.id }}" hx-swap="outerHTML">
|
|
102
|
+
Add to Cart
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
{% endpartialdef %}
|
|
106
|
+
|
|
107
|
+
{% for product in products %}
|
|
108
|
+
{% partial product_card with product=product %}
|
|
109
|
+
{% endfor %}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// The entire page renders all cards
|
|
114
|
+
app.get('/shop', (req, res) =>
|
|
115
|
+
res.render('products', { products: catalog })
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// HTMX swaps just one card after an action
|
|
119
|
+
app.post('/cart/add/:id', (req, res) =>
|
|
120
|
+
res.render('products#product_card', {
|
|
121
|
+
product: catalog.find(p => p.id == req.params.id)
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Nested Partials
|
|
127
|
+
|
|
128
|
+
Partials can call other partials:
|
|
129
|
+
|
|
130
|
+
```html
|
|
131
|
+
{% partialdef header %}
|
|
132
|
+
<div class="card-header">
|
|
133
|
+
<h3>{{ title }}</h3>
|
|
134
|
+
</div>
|
|
135
|
+
{% endpartialdef %}
|
|
136
|
+
|
|
137
|
+
{% partialdef card %}
|
|
138
|
+
<div class="card">
|
|
139
|
+
{% partial header with title=title %}
|
|
140
|
+
<p>{{ body }}</p>
|
|
141
|
+
</div>
|
|
142
|
+
{% endpartialdef %}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Partials with Context
|
|
146
|
+
|
|
147
|
+
By default, partials inherit the parent context. Use `with` to pass explicit values:
|
|
148
|
+
|
|
149
|
+
```html
|
|
150
|
+
{% partial card with title="Hello" body="World" %}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
You can also pass context variables:
|
|
154
|
+
|
|
155
|
+
```html
|
|
156
|
+
{% partial card with title=entry.title body=entry.body %}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Partials with Include
|
|
160
|
+
|
|
161
|
+
You can include a partial from another template file using the `#partialName` syntax:
|
|
162
|
+
|
|
163
|
+
```html
|
|
164
|
+
{% include "header.html#partial_name" %}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This loads `header.html`, registers all its partials, and renders only the named one.
|
|
168
|
+
|
|
169
|
+
**Real-world navigation include:**
|
|
170
|
+
|
|
171
|
+
```html
|
|
172
|
+
<!-- views/nav.html -->
|
|
173
|
+
{% partialdef navigation %}
|
|
174
|
+
<nav>
|
|
175
|
+
{% for link in links %}
|
|
176
|
+
<a href="{{ link.url }}" class="{% if link.active %}current{% endif %}">{{ link.label }}</a>
|
|
177
|
+
{% endfor %}
|
|
178
|
+
</nav>
|
|
179
|
+
{% endpartialdef %}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```html
|
|
183
|
+
<!-- In any template -->
|
|
184
|
+
{% include "nav.html#navigation" with links=nav_links %}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Rendering Partials Programmatically
|
|
188
|
+
|
|
189
|
+
### renderPartialFromSource
|
|
190
|
+
|
|
191
|
+
Render a named partial from a template source string:
|
|
192
|
+
|
|
193
|
+
=== "CommonJS"
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
const { renderPartialFromSource } = require('miki-template');
|
|
197
|
+
|
|
198
|
+
const source = `{% partialdef card %}<div>{{ title }}</div>{% endpartialdef %}`;
|
|
199
|
+
const html = renderPartialFromSource(source, 'card', { title: 'Hello' });
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
=== "ES Modules"
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
import { renderPartialFromSource } from 'miki-template';
|
|
206
|
+
|
|
207
|
+
const source = `{% partialdef card %}<div>{{ title }}</div>{% endpartialdef %}`;
|
|
208
|
+
const html = renderPartialFromSource(source, 'card', { title: 'Hello' });
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### renderPartialFromFile
|
|
212
|
+
|
|
213
|
+
Render a named partial from a template file:
|
|
214
|
+
|
|
215
|
+
=== "CommonJS"
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
const { renderPartialFromFile } = require('miki-template');
|
|
219
|
+
|
|
220
|
+
const html = renderPartialFromFile('home', 'card', { title: 'Hello' }, { views: './views' });
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
=== "ES Modules"
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
import { renderPartialFromFile } from 'miki-template';
|
|
227
|
+
|
|
228
|
+
const html = renderPartialFromFile('home', 'card', { title: 'Hello' }, { views: './views' });
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### compiled.renderPartial
|
|
232
|
+
|
|
233
|
+
Render a partial from a compiled template:
|
|
234
|
+
|
|
235
|
+
=== "CommonJS"
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
const { compile } = require('miki-template');
|
|
239
|
+
|
|
240
|
+
const compiled = compile('<h1>{{ title }}</h1>', { views: './templates' });
|
|
241
|
+
const html = compiled.renderPartial('card', { title: 'Hello' });
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
=== "ES Modules"
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
import { compile } from 'miki-template';
|
|
248
|
+
|
|
249
|
+
const compiled = compile('<h1>{{ title }}</h1>', { views: './templates' });
|
|
250
|
+
const html = compiled.renderPartial('card', { title: 'Hello' });
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### compiled.renderBlock
|
|
254
|
+
|
|
255
|
+
Render a single block from a compiled template — useful for AJAX responses:
|
|
256
|
+
|
|
257
|
+
=== "CommonJS"
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
const { compile } = require('miki-template');
|
|
261
|
+
|
|
262
|
+
const compiled = compile(childTemplate, { views: './templates' });
|
|
263
|
+
const html = compiled.renderBlock('content', context);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
=== "ES Modules"
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
import { compile } from 'miki-template';
|
|
270
|
+
|
|
271
|
+
const compiled = compile(childTemplate, { views: './templates' });
|
|
272
|
+
const html = compiled.renderBlock('content', context);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Express Partial Rendering
|
|
276
|
+
|
|
277
|
+
### res.render with `#partial`
|
|
278
|
+
|
|
279
|
+
When using `setupExpress()`, you can render partials directly:
|
|
280
|
+
|
|
281
|
+
=== "CommonJS"
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
app.get('/card/:id', (req, res) =>
|
|
285
|
+
res.render(`home#card`, { title: 'Hello', body: '...' })
|
|
286
|
+
);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
=== "ES Modules"
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
app.get('/card/:id', (req, res) =>
|
|
293
|
+
res.render(`home#card`, { title: 'Hello', body: '...' })
|
|
294
|
+
);
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### res.renderPartial middleware
|
|
298
|
+
|
|
299
|
+
If you don't want to patch `res.render`, add the partial renderer middleware instead:
|
|
300
|
+
|
|
301
|
+
=== "CommonJS"
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
const express = require('express');
|
|
305
|
+
const miki = require('miki-template');
|
|
306
|
+
|
|
307
|
+
const app = express();
|
|
308
|
+
app.use(miki.expressPartialRenderer());
|
|
309
|
+
|
|
310
|
+
app.get('/card', (req, res) =>
|
|
311
|
+
res.renderPartial('home#card', { user: req.user })
|
|
312
|
+
);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
=== "ES Modules"
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
import express from 'express';
|
|
319
|
+
import miki from 'miki-template';
|
|
320
|
+
|
|
321
|
+
const app = express();
|
|
322
|
+
app.use(miki.expressPartialRenderer());
|
|
323
|
+
|
|
324
|
+
app.get('/card', (req, res) =>
|
|
325
|
+
res.renderPartial('home#card', { user: req.user })
|
|
326
|
+
);
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Partial API Reference
|
|
330
|
+
|
|
331
|
+
### renderPartialFromSource(fileContent, partialName, contextObj, options, filePath?)
|
|
332
|
+
|
|
333
|
+
Render a named partial from a template source string.
|
|
334
|
+
|
|
335
|
+
| Parameter | Type | Description |
|
|
336
|
+
|-----------|------|-------------|
|
|
337
|
+
| `fileContent` | `string` | Template source string |
|
|
338
|
+
| `partialName` | `string` | Name of the partial to render |
|
|
339
|
+
| `contextObj` | `object` | Variables to inject |
|
|
340
|
+
| `options` | `object` | Options |
|
|
341
|
+
| `filePath` | `string?` | Optional file path for error messages |
|
|
342
|
+
|
|
343
|
+
### renderPartialFromFile(fileName, partialName, contextObj, options)
|
|
344
|
+
|
|
345
|
+
Render a named partial from a template file.
|
|
346
|
+
|
|
347
|
+
| Parameter | Type | Description |
|
|
348
|
+
|-----------|------|-------------|
|
|
349
|
+
| `fileName` | `string` | Template file name (without extension) |
|
|
350
|
+
| `partialName` | `string` | Name of the partial to render |
|
|
351
|
+
| `contextObj` | `object` | Variables to inject |
|
|
352
|
+
| `options` | `object` | Options including `views` directories |
|
|
353
|
+
|
|
354
|
+
## Common Pitfalls
|
|
355
|
+
|
|
356
|
+
| Issue | Symptom | Fix |
|
|
357
|
+
|-------|---------|-----|
|
|
358
|
+
| Missing partial name | `{% partial %}` renders nothing | Ensure the name matches a defined `partialdef`. |
|
|
359
|
+
| Variable not found | Appears empty | Variables are resolved in the current context; use `with` to pass explicit values. |
|
|
360
|
+
| Inline vs non-inline confusion | Duplicate output | Use `inline` only when you want immediate rendering at the declaration site. |
|
|
361
|
+
| Partial leaks across includes | Unexpected partials available | `include "file#partial"` isolates partials; `include "file"` (full) makes all partials available. |
|
|
362
|
+
|
|
363
|
+
## Next Steps
|
|
364
|
+
|
|
365
|
+
- [Template Inheritance](./template-inheritance)
|
|
366
|
+
- [Tags: partialdef and partial](./tags#partial-tags)
|
|
367
|
+
- [API Reference: renderPartial](../api/render-partial)
|