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
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Libraries API
|
|
2
|
+
|
|
3
|
+
## registerLibrary
|
|
4
|
+
|
|
5
|
+
Register a library — a named bundle of filters, tags, and helpers.
|
|
6
|
+
|
|
7
|
+
=== "CommonJS"
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
const { registerLibrary } = require('miki-template');
|
|
11
|
+
|
|
12
|
+
registerLibrary('mylib', {
|
|
13
|
+
filters: {
|
|
14
|
+
shout: (val) => String(val).toUpperCase() + '!'
|
|
15
|
+
},
|
|
16
|
+
tags: {
|
|
17
|
+
hello: (tagContent, parser) => ({
|
|
18
|
+
render: (context) => 'Hello!'
|
|
19
|
+
})
|
|
20
|
+
},
|
|
21
|
+
helpers: {
|
|
22
|
+
bold: (inner, context) => `<b>${inner}</b>`
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
=== "ES Modules"
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import { registerLibrary } from 'miki-template';
|
|
31
|
+
|
|
32
|
+
registerLibrary('mylib', {
|
|
33
|
+
filters: {
|
|
34
|
+
shout: (val) => String(val).toUpperCase() + '!'
|
|
35
|
+
},
|
|
36
|
+
tags: {
|
|
37
|
+
hello: (tagContent, parser) => ({
|
|
38
|
+
render: (context) => 'Hello!'
|
|
39
|
+
})
|
|
40
|
+
},
|
|
41
|
+
helpers: {
|
|
42
|
+
bold: (inner, context) => `<b>${inner}</b>`
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## activateLibrary
|
|
48
|
+
|
|
49
|
+
Activate a library so its filters and tags become available.
|
|
50
|
+
|
|
51
|
+
=== "CommonJS"
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
const { activateLibrary } = require('miki-template');
|
|
55
|
+
|
|
56
|
+
activateLibrary('mylib');
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
=== "ES Modules"
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
import { activateLibrary } from 'miki-template';
|
|
63
|
+
|
|
64
|
+
activateLibrary('mylib');
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Built-in libraries (`humanize`, `cache`, `lorem`, `markdown`, `i18n`) are auto-activated on import.
|
|
68
|
+
|
|
69
|
+
## unregisterLibrary
|
|
70
|
+
|
|
71
|
+
=== "CommonJS"
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
const { unregisterLibrary } = require('miki-template');
|
|
75
|
+
|
|
76
|
+
// Unregister specific library
|
|
77
|
+
unregisterLibrary('mylib');
|
|
78
|
+
|
|
79
|
+
// Unregister all libraries
|
|
80
|
+
unregisterLibrary();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
=== "ES Modules"
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
import { unregisterLibrary } from 'miki-template';
|
|
87
|
+
|
|
88
|
+
unregisterLibrary('mylib');
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## hasLibrary
|
|
92
|
+
|
|
93
|
+
=== "CommonJS"
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
const { hasLibrary } = require('miki-template');
|
|
97
|
+
|
|
98
|
+
if (hasLibrary('humanize')) {
|
|
99
|
+
// library is registered
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
=== "ES Modules"
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import { hasLibrary } from 'miki-template';
|
|
107
|
+
|
|
108
|
+
if (hasLibrary('humanize')) {
|
|
109
|
+
// library is registered
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## getLibrary
|
|
114
|
+
|
|
115
|
+
Retrieve a library by name.
|
|
116
|
+
|
|
117
|
+
=== "CommonJS"
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const { getLibrary } = require('miki-template');
|
|
121
|
+
|
|
122
|
+
const lib = getLibrary('humanize');
|
|
123
|
+
console.log(Object.keys(lib.filters));
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
=== "ES Modules"
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
import { getLibrary } from 'miki-template';
|
|
130
|
+
|
|
131
|
+
const lib = getLibrary('humanize');
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## getLibraryNames
|
|
135
|
+
|
|
136
|
+
=== "CommonJS"
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
const { getLibraryNames } = require('miki-template');
|
|
140
|
+
|
|
141
|
+
console.log(getLibraryNames());
|
|
142
|
+
// ['humanize', 'cache', 'lorem']
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
=== "ES Modules"
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
import { getLibraryNames } from 'miki-template';
|
|
149
|
+
|
|
150
|
+
console.log(getLibraryNames());
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## registerLibraryFromPath
|
|
154
|
+
|
|
155
|
+
Load a library from a JavaScript file on disk. The file must export `{ tags, filters, helpers }`.
|
|
156
|
+
|
|
157
|
+
=== "CommonJS"
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
const { registerLibraryFromPath } = require('miki-template');
|
|
161
|
+
|
|
162
|
+
registerLibraryFromPath('mylib', './libs/mylib.js');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
=== "ES Modules"
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
import { registerLibraryFromPath } from 'miki-template';
|
|
169
|
+
|
|
170
|
+
await registerLibraryFromPath('mylib', './libs/mylib.mjs');
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Built-in Libraries
|
|
174
|
+
|
|
175
|
+
### humanize
|
|
176
|
+
|
|
177
|
+
Provides natural formatting filters: `intcomma`, `intword`, `ordinal`, `naturalday`, `naturaltime`.
|
|
178
|
+
|
|
179
|
+
| Filter | Description |
|
|
180
|
+
|--------|-------------|
|
|
181
|
+
| `intcomma` | `1234567` → `1,234,567` |
|
|
182
|
+
| `intword` | `1234567` → `1.2M` |
|
|
183
|
+
| `ordinal` | `1` → `1st`, `2` → `2nd` |
|
|
184
|
+
| `naturalday` | Format dates as "today", "yesterday" |
|
|
185
|
+
| `naturaltime` | Format times as "just now", "2 hours ago" |
|
|
186
|
+
| `ordinal` | Convert numbers to ordinal (1st, 2nd, 3rd) |
|
|
187
|
+
|
|
188
|
+
### cache
|
|
189
|
+
|
|
190
|
+
Provides `{% cache timeout key %}...{% endcache %}` tag for caching template fragments.
|
|
191
|
+
|
|
192
|
+
### lorem
|
|
193
|
+
|
|
194
|
+
Provides `{% lorem count random_words %}` tag and `lorem` filter for placeholder text.
|
|
195
|
+
|
|
196
|
+
### markdown
|
|
197
|
+
|
|
198
|
+
Provides `markdown` filter for Markdown→HTML conversion.
|
|
199
|
+
|
|
200
|
+
### i18n
|
|
201
|
+
|
|
202
|
+
Provides `{% trans %}`, `{% blocktrans %}`, `{% language %}` tags and translation functions.
|
|
203
|
+
|
|
204
|
+
## Next Steps
|
|
205
|
+
|
|
206
|
+
- [Advanced Usage: Libraries](../guide/advanced-usage#library-system)
|
|
207
|
+
- [API Reference](../)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# renderPartialFromFile / renderPartialFromSource
|
|
2
|
+
|
|
3
|
+
Render a named partial from a template file or source string.
|
|
4
|
+
|
|
5
|
+
## renderPartialFromFile
|
|
6
|
+
|
|
7
|
+
Render a named partial from a template file.
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
renderPartialFromFile(fileName, partialName, contextObj, options)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Parameters
|
|
14
|
+
|
|
15
|
+
| Parameter | Type | Description |
|
|
16
|
+
|-----------|------|-------------|
|
|
17
|
+
| `fileName` | `string` | Template file name (without extension) |
|
|
18
|
+
| `partialName` | `string` | Name of the partial to render |
|
|
19
|
+
| `contextObj` | `object` | Variables to inject |
|
|
20
|
+
| `options` | `object` | Options including `views` directories |
|
|
21
|
+
|
|
22
|
+
### Example
|
|
23
|
+
|
|
24
|
+
=== "CommonJS"
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
const { renderPartialFromFile } = require('miki-template');
|
|
28
|
+
|
|
29
|
+
const html = renderPartialFromFile('home', 'card', { title: 'Hello' }, { views: './views' });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
=== "ES Modules"
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
import { renderPartialFromFile } from 'miki-template';
|
|
36
|
+
|
|
37
|
+
const html = renderPartialFromFile('home', 'card', { title: 'Hello' }, { views: './views' });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## renderPartialFromSource
|
|
41
|
+
|
|
42
|
+
Render a named partial from a template source string.
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
renderPartialFromSource(fileContent, partialName, contextObj, options, filePath?)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Parameters
|
|
49
|
+
|
|
50
|
+
| Parameter | Type | Description |
|
|
51
|
+
|-----------|------|-------------|
|
|
52
|
+
| `fileContent` | `string` | Template source string |
|
|
53
|
+
| `partialName` | `string` | Name of the partial to render |
|
|
54
|
+
| `contextObj` | `object` | Variables to inject |
|
|
55
|
+
| `options` | `object` | Options |
|
|
56
|
+
| `filePath` | `string?` | Optional file path for error messages |
|
|
57
|
+
|
|
58
|
+
### Example
|
|
59
|
+
|
|
60
|
+
=== "CommonJS"
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
const { renderPartialFromSource } = require('miki-template');
|
|
64
|
+
|
|
65
|
+
const source = `{% partialdef card %}<div>{{ title }}</div>{% endpartialdef %}`;
|
|
66
|
+
const html = renderPartialFromSource(source, 'card', { title: 'Hello' });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
=== "ES Modules"
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
import { renderPartialFromSource } from 'miki-template';
|
|
73
|
+
|
|
74
|
+
const source = `{% partialdef card %}<div>{{ title }}</div>{% endpartialdef %}`;
|
|
75
|
+
const html = renderPartialFromSource(source, 'card', { title: 'Hello' });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Related
|
|
79
|
+
|
|
80
|
+
- [render()](./render)
|
|
81
|
+
- [compile()](./compile)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# render()
|
|
2
|
+
|
|
3
|
+
Render a template string or file partial.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
render(templateStr, contextObj = {}, options = {})
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Description |
|
|
14
|
+
|-----------|------|-------------|
|
|
15
|
+
| `templateStr` | `string` | Template string or file path with `#partial` suffix |
|
|
16
|
+
| `contextObj` | `object` | Variables to inject into the template |
|
|
17
|
+
| `options` | `object` | Options including `views` directories and custom settings |
|
|
18
|
+
|
|
19
|
+
## Returns
|
|
20
|
+
|
|
21
|
+
`string` — The rendered HTML.
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
### Render a template string
|
|
26
|
+
|
|
27
|
+
=== "CommonJS"
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
const { render } = require('miki-template');
|
|
31
|
+
|
|
32
|
+
const html = render('Hello {{ name }}!', { name: 'World' });
|
|
33
|
+
// Output: Hello World!
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
=== "ES Modules"
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import { render } from 'miki-template';
|
|
40
|
+
|
|
41
|
+
const html = render('Hello {{ name }}!', { name: 'World' });
|
|
42
|
+
// Output: Hello World!
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Render a partial from file
|
|
46
|
+
|
|
47
|
+
=== "CommonJS"
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
const { render } = require('miki-template');
|
|
51
|
+
|
|
52
|
+
const html = render('home#card', { title: 'Hello' }, { views: './views' });
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
=== "ES Modules"
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import { render } from 'miki-template';
|
|
59
|
+
|
|
60
|
+
const html = render('home#card', { title: 'Hello' }, { views: './views' });
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Render with options
|
|
64
|
+
|
|
65
|
+
=== "CommonJS"
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const { render } = require('miki-template');
|
|
69
|
+
|
|
70
|
+
const html = render(template, context, {
|
|
71
|
+
views: ['./views', './app/templates'],
|
|
72
|
+
staticUrl: '/static',
|
|
73
|
+
urlHelper: (name, ...args) => '/' + name + '/' + args.join('/')
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
=== "ES Modules"
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
import { render } from 'miki-template';
|
|
81
|
+
|
|
82
|
+
const html = render(template, context, {
|
|
83
|
+
views: ['./views', './app/templates'],
|
|
84
|
+
staticUrl: '/static',
|
|
85
|
+
urlHelper: (name, ...args) => '/' + name + '/' + args.join('/')
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Related
|
|
90
|
+
|
|
91
|
+
- [compile()](./compile)
|
|
92
|
+
- [asyncRender()](./async-render)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Security API
|
|
2
|
+
|
|
3
|
+
## markSafe
|
|
4
|
+
|
|
5
|
+
Mark a string as safe (bypass auto-escaping).
|
|
6
|
+
|
|
7
|
+
=== "CommonJS"
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
const { markSafe } = require('miki-template');
|
|
11
|
+
|
|
12
|
+
const html = markSafe('<b>ok</b>');
|
|
13
|
+
// Will not be escaped
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
=== "ES Modules"
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { markSafe } from 'miki-template';
|
|
20
|
+
|
|
21
|
+
const html = markSafe('<b>ok</b>');
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## isSafe
|
|
25
|
+
|
|
26
|
+
Check if a value is marked safe.
|
|
27
|
+
|
|
28
|
+
=== "CommonJS"
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
const { isSafe } = require('miki-template');
|
|
32
|
+
|
|
33
|
+
if (isSafe(value)) {
|
|
34
|
+
// value is marked safe
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
=== "ES Modules"
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import { isSafe } from 'miki-template';
|
|
42
|
+
|
|
43
|
+
if (isSafe(value)) {
|
|
44
|
+
// value is marked safe
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## escapeHtml
|
|
49
|
+
|
|
50
|
+
Escape HTML special characters (`&`, `<`, `>`, `"`, `'`, `` ` ``).
|
|
51
|
+
|
|
52
|
+
=== "CommonJS"
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
const { escapeHtml } = require('miki-template');
|
|
56
|
+
|
|
57
|
+
const escaped = escapeHtml('<script>');
|
|
58
|
+
// Output: <script>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
=== "ES Modules"
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
import { escapeHtml } from 'miki-template';
|
|
65
|
+
|
|
66
|
+
const escaped = escapeHtml('<script>');
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Force-Escape SafeString
|
|
70
|
+
|
|
71
|
+
Pass `true` as the second argument to force-escape a `SafeString` (matching Django's `|escape` filter behavior):
|
|
72
|
+
|
|
73
|
+
=== "CommonJS"
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
const { escapeHtml, SafeString } = require('miki-template');
|
|
77
|
+
|
|
78
|
+
const safe = new SafeString('<b>bold</b>');
|
|
79
|
+
const forced = escapeHtml(safe, true);
|
|
80
|
+
// Output: <b>bold</b>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
=== "ES Modules"
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
import { escapeHtml, SafeString } from 'miki-template';
|
|
87
|
+
|
|
88
|
+
const safe = new SafeString('<b>bold</b>');
|
|
89
|
+
const forced = escapeHtml(safe, true);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## stripExpressContext
|
|
93
|
+
|
|
94
|
+
Strip Express-specific framework keys (`_`, `settings`, `cache`) from a context object.
|
|
95
|
+
|
|
96
|
+
=== "CommonJS"
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
const { stripExpressContext } = require('miki-template');
|
|
100
|
+
|
|
101
|
+
const cleanCtx = stripExpressContext(expressOptions);
|
|
102
|
+
// Removes: _locals, settings, cache, and other _ prefixed keys
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
=== "ES Modules"
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
import { stripExpressContext } from 'miki-template';
|
|
109
|
+
|
|
110
|
+
const cleanCtx = stripExpressContext(expressOptions);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## SafeString Class
|
|
114
|
+
|
|
115
|
+
Create a SafeString instance directly.
|
|
116
|
+
|
|
117
|
+
=== "CommonJS"
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const { SafeString } = require('miki-template');
|
|
121
|
+
|
|
122
|
+
const safe = new SafeString('<b>ok</b>');
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
=== "ES Modules"
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
import { SafeString } from 'miki-template';
|
|
129
|
+
|
|
130
|
+
const safe = new SafeString('<b>ok</b>');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Path Traversal Protection
|
|
134
|
+
|
|
135
|
+
The `extends` and `include` tags validate that resolved template paths stay within configured views directories. Attempting to traverse outside throws an error:
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
{% extends "../../etc/passwd" %} <!-- throws -->
|
|
139
|
+
{% include "../../secrets" %} <!-- throws -->
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Next Steps
|
|
143
|
+
|
|
144
|
+
- [Security Guide](../guide/security)
|
|
145
|
+
- [API Reference](../)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# setupExpress()
|
|
2
|
+
|
|
3
|
+
One-line Express integration that wires the view engine, views directory, and partial responses.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
setupExpress(app, options = {})
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Options
|
|
12
|
+
|
|
13
|
+
| Option | Type | Description |
|
|
14
|
+
|--------|------|-------------|
|
|
15
|
+
| `extension` | `string` | View file extension, default `'html'` |
|
|
16
|
+
| `views` | `string\|string[]` | Views directory path(s) |
|
|
17
|
+
| `async` | `boolean` | Use async engine (`__expressAsync`) for Express 5+ |
|
|
18
|
+
|
|
19
|
+
## What It Does
|
|
20
|
+
|
|
21
|
+
- Calls `app.engine()` with the miki view engine.
|
|
22
|
+
- Sets `app.set('view engine', extension)` if not already set.
|
|
23
|
+
- Sets `app.set('views', views)` if `options.views` is provided.
|
|
24
|
+
- Expands `views` to include nested template directories (app-style `templates/` folders).
|
|
25
|
+
- Patches `res.render` to support `view#partial` syntax for HTMX responses.
|
|
26
|
+
|
|
27
|
+
## Example
|
|
28
|
+
|
|
29
|
+
=== "CommonJS"
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
const express = require('express');
|
|
33
|
+
const miki = require('miki-template');
|
|
34
|
+
|
|
35
|
+
const app = express();
|
|
36
|
+
miki.setupExpress(app, { extension: 'html', views: './views' });
|
|
37
|
+
|
|
38
|
+
app.get('/', (req, res) => res.render('home', { user: req.user }));
|
|
39
|
+
app.get('/card/:id', (req, res) =>
|
|
40
|
+
res.render(`home#card`, { title: 'Hello' })
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
app.listen(3000);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
=== "ES Modules"
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
import express from 'express';
|
|
50
|
+
import miki from 'miki-template';
|
|
51
|
+
|
|
52
|
+
const app = express();
|
|
53
|
+
miki.setupExpress(app, { extension: 'html', views: './views' });
|
|
54
|
+
|
|
55
|
+
app.get('/', (req, res) => res.render('home', { user: req.user }));
|
|
56
|
+
app.get('/card/:id', (req, res) =>
|
|
57
|
+
res.render(`home#card`, { title: 'Hello' })
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
app.listen(3000);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Partial Responses
|
|
64
|
+
|
|
65
|
+
`setupExpress()` patches `res.render` so that any view name containing `#` renders only the named partial:
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
// Renders only the "card" partialdef from home.html
|
|
69
|
+
res.render('home#card', { title: 'Hello' });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This is ideal for HTMX where you only need to update a portion of the page.
|
|
73
|
+
|
|
74
|
+
## Related
|
|
75
|
+
|
|
76
|
+
- [Integrations: Express](../integrations/express)
|