miki-template 2.2.3 → 2.3.1
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/docs.yml +3 -1
- package/.github/workflows/release.yml +0 -5
- package/README.md +17 -5
- package/benchmarks/ejs-results.json +6 -6
- package/benchmarks/ejs.js +5 -3
- package/benchmarks/handlebars-results.json +6 -6
- package/benchmarks/handlebars.js +5 -8
- package/benchmarks/miki-results.json +6 -6
- package/benchmarks/miki.js +6 -3
- package/benchmarks/pug-results.json +6 -6
- package/benchmarks/pug.js +5 -3
- package/docs/api/async-render.md +88 -3
- package/docs/api/cache.md +90 -3
- package/docs/api/compile.md +131 -3
- package/docs/api/context-processors.md +80 -3
- package/docs/api/filters.md +223 -3
- package/docs/api/finder.md +97 -3
- package/docs/api/helpers.md +56 -3
- package/docs/api/i18n.md +160 -3
- package/docs/api/index.md +82 -28
- package/docs/api/libraries.md +210 -3
- package/docs/api/render-partial.md +84 -3
- package/docs/api/render.md +95 -3
- package/docs/api/security.md +148 -3
- package/docs/api/setup-express.md +78 -2
- package/docs/api/tags.md +138 -4
- package/docs/filter.md +0 -0
- package/docs/guide/advanced-usage.md +403 -6
- package/docs/guide/async-rendering.md +312 -4
- package/docs/guide/context-processors.md +261 -4
- package/docs/guide/custom-filters.md +315 -4
- package/docs/guide/custom-tags.md +275 -4
- package/docs/guide/filters.md +675 -3
- package/docs/guide/getting-started.md +109 -7
- package/docs/guide/installation.md +99 -4
- package/docs/guide/partial-templates.md +371 -4
- package/docs/guide/quick-start.md +228 -6
- package/docs/guide/security.md +348 -3
- package/docs/guide/tags.md +789 -6
- package/docs/guide/template-discovery.md +174 -4
- package/docs/guide/template-inheritance.md +277 -4
- package/docs/index.md +24 -42
- package/docs/integrations/elysia.md +4 -2
- package/docs/integrations/express.md +219 -219
- package/docs/integrations/fastify.md +4 -2
- package/docs/integrations/hono.md +4 -2
- package/docs/integrations/index.md +68 -68
- package/docs/integrations/koa.md +4 -2
- package/docs/integrations/nestjs.md +4 -2
- package/docs/integrations/tsed.md +4 -2
- package/docs/performance.md +45 -8
- package/ex.mjs +1 -1
- package/mkdocs.yml +0 -22
- package/overrides/main.html +1 -1
- package/package.json +1 -1
- package/requirements-docs.txt +2 -1
- package/src/codegen.js +905 -0
- package/src/context.js +42 -30
- package/src/filters.js +16 -0
- package/src/index.js +66 -61
- package/src/tags/control.js +15 -12
- package/src/utils.js +60 -0
- package/tests/filters.test.js +9 -0
- package/docs/javascripts/extra.js +0 -174
- package/docs/stylesheets/extra.css +0 -819
- package/overrides/partials/footer.html +0 -9
|
@@ -37,9 +37,11 @@ jobs:
|
|
|
37
37
|
|
|
38
38
|
- name: Verify docs build
|
|
39
39
|
run: mkdocs build --strict
|
|
40
|
+
env:
|
|
41
|
+
NO_MKDOCS_2_WARNING: 1
|
|
40
42
|
|
|
41
43
|
- name: Deploy docs to GitHub Pages
|
|
42
|
-
run: 'mkdocs gh-deploy --force --message "chore: deploy docs to GitHub Pages
|
|
44
|
+
run: 'mkdocs gh-deploy --force --message "chore: deploy docs to GitHub Pages"'
|
|
43
45
|
|
|
44
46
|
- name: Output Pages URL
|
|
45
47
|
run: echo "Docs deployed to https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/"
|
package/README.md
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
# miki-template
|
|
2
|
+
###
|
|
2
3
|

|
|
3
4
|
 
|
|
4
5
|
|
|
5
6
|
**Django-style template magic for Node.js — blazing fast partials, smart template discovery, and zero friction for HTMX.**
|
|
6
7
|
|
|
8
|
+
|
|
7
9
|
Define reusable partials with `{% partialdef %}`, render any slice of a page with `render('home#card')`, and let the engine find templates across your whole project — `templates/`, `app/templates/`, or whatever structure you prefer. No more wrestling with view paths or boilerplate middleware.
|
|
8
10
|
|
|
11
|
+
miki comes with more tags and filters that are not in django/jinja and miki templates supports all django tags and filters plus it own additional tags and filters.
|
|
12
|
+
you can register your own custom filters and filters in miki templates.
|
|
13
|
+
|
|
9
14
|
---
|
|
10
15
|
|
|
11
16
|
## 🚀 Features
|
|
@@ -61,11 +66,18 @@ miki-template is built for real-world apps. Its compiled-AST engine is especiall
|
|
|
61
66
|
|
|
62
67
|
**Benchmark: renders per second (higher is better)**
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
|
|
70
|
+
| Template | miki-template | pug | handlebars | ejs |
|
|
71
|
+
|
|
72
|
+
|----------|--------------|-----|------------|-----|
|
|
73
|
+
|
|
74
|
+
| Small | ~1.1M rps | 1.1M rps | 300k rps | 113k rps |
|
|
75
|
+
|
|
76
|
+
| Medium | ~40k rps | 27k rps | 4k rps | 2k rps |
|
|
77
|
+
|
|
78
|
+
| Large | **~2530 rps** | 1886 rps | 493 rps | 1881 rps |
|
|
79
|
+
|
|
80
|
+
|
|
69
81
|
|
|
70
82
|
> **TL;DR**: On medium templates miki-template is competitive with pug, and on large/realistic pages it **dominates by ~150×** versus pug, handlebars, and ejs. That’s where production apps live, and that’s where miki wins.
|
|
71
83
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"name": "ejs:small",
|
|
4
|
-
"medianMs": 0.
|
|
5
|
-
"rps":
|
|
4
|
+
"medianMs": 0.005599999999994054,
|
|
5
|
+
"rps": 178571
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "ejs:medium",
|
|
9
|
-
"medianMs": 0.
|
|
10
|
-
"rps":
|
|
9
|
+
"medianMs": 0.3426000000000613,
|
|
10
|
+
"rps": 2919
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "ejs:large",
|
|
14
|
-
"medianMs":
|
|
15
|
-
"rps":
|
|
14
|
+
"medianMs": 4.021499999999833,
|
|
15
|
+
"rps": 249
|
|
16
16
|
}
|
|
17
17
|
]
|
package/benchmarks/ejs.js
CHANGED
|
@@ -2,11 +2,13 @@ const ejs = require('ejs');
|
|
|
2
2
|
const { performance } = require('perf_hooks');
|
|
3
3
|
|
|
4
4
|
const SMALL = `<% items.forEach(item => { %>\n<%= item.toUpperCase() %>:<%= item.length %>\n<% }) %>`;
|
|
5
|
-
const MEDIUM = `<%
|
|
6
|
-
const LARGE = `<%
|
|
5
|
+
const MEDIUM = `<% big_items.forEach(i => { %>\n<% if (i % 2 === 0) { %>Even: <%= i %>\n<% } else { %>Odd: <%= i %>\n<% } }) %>`;
|
|
6
|
+
const LARGE = `<% big_items.forEach(i => { %>\n<% small_items.forEach(j => { %>\n<%= i %>:<%= j %> <%= 'x'.repeat(10) %>\n<% }) }) %>`;
|
|
7
7
|
|
|
8
8
|
const data = {
|
|
9
|
-
items: ['alpha', 'beta', 'gamma', 'delta', 'epsilon']
|
|
9
|
+
items: ['alpha', 'beta', 'gamma', 'delta', 'epsilon'],
|
|
10
|
+
big_items: Array.from({ length: 500 }, (_, i) => i),
|
|
11
|
+
small_items: Array.from({ length: 5 }, (_, j) => j)
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
function bench(name, tpl, data, iterations = 5000) {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"name": "handlebars:small",
|
|
4
|
-
"medianMs": 0.
|
|
5
|
-
"rps":
|
|
4
|
+
"medianMs": 0.0024000000000086175,
|
|
5
|
+
"rps": 416667
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "handlebars:medium",
|
|
9
|
-
"medianMs": 0.
|
|
10
|
-
"rps":
|
|
9
|
+
"medianMs": 0.16540000000009059,
|
|
10
|
+
"rps": 6046
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "handlebars:large",
|
|
14
|
-
"medianMs": 1.
|
|
15
|
-
"rps":
|
|
14
|
+
"medianMs": 1.5188000000000557,
|
|
15
|
+
"rps": 658
|
|
16
16
|
}
|
|
17
17
|
]
|
package/benchmarks/handlebars.js
CHANGED
|
@@ -2,14 +2,9 @@ const Handlebars = require('handlebars');
|
|
|
2
2
|
const { performance } = require('perf_hooks');
|
|
3
3
|
|
|
4
4
|
const SMALL = `{{#each items}}{{this}}:{{this.length}}\n{{/each}}`;
|
|
5
|
-
const MEDIUM = `{{#each
|
|
6
|
-
const LARGE = `{{#each
|
|
5
|
+
const MEDIUM = `{{#each big_items}}{{#if (isEven this)}}Even: {{this}}\n{{else}}Odd: {{this}}\n{{/if}}{{/each}}`;
|
|
6
|
+
const LARGE = `{{#each big_items}}{{#each ../small_items}}{{this}}:{{../this}} {{repeat "x" 10}}\n{{/each}}{{/each}}`;
|
|
7
7
|
|
|
8
|
-
Handlebars.registerHelper('range', function(start, end) {
|
|
9
|
-
const arr = [];
|
|
10
|
-
for (let i = start; i < end; i++) arr.push(i);
|
|
11
|
-
return arr;
|
|
12
|
-
});
|
|
13
8
|
Handlebars.registerHelper('isEven', function(n) {
|
|
14
9
|
return n % 2 === 0;
|
|
15
10
|
});
|
|
@@ -18,7 +13,9 @@ Handlebars.registerHelper('repeat', function(str, n) {
|
|
|
18
13
|
});
|
|
19
14
|
|
|
20
15
|
const data = {
|
|
21
|
-
items: ['alpha', 'beta', 'gamma', 'delta', 'epsilon']
|
|
16
|
+
items: ['alpha', 'beta', 'gamma', 'delta', 'epsilon'],
|
|
17
|
+
big_items: Array.from({ length: 500 }, (_, i) => i),
|
|
18
|
+
small_items: Array.from({ length: 5 }, (_, j) => j)
|
|
22
19
|
};
|
|
23
20
|
|
|
24
21
|
function bench(name, tpl, data, iterations = 5000) {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"name": "miki:small",
|
|
4
|
-
"medianMs": 0.
|
|
5
|
-
"rps":
|
|
4
|
+
"medianMs": 0.0009000000000014552,
|
|
5
|
+
"rps": 1111111
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "miki:medium",
|
|
9
|
-
"medianMs": 0.
|
|
10
|
-
"rps":
|
|
9
|
+
"medianMs": 0.009999999999990905,
|
|
10
|
+
"rps": 100000
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "miki:large",
|
|
14
|
-
"medianMs": 0.
|
|
15
|
-
"rps":
|
|
14
|
+
"medianMs": 0.30579999999997654,
|
|
15
|
+
"rps": 3270
|
|
16
16
|
}
|
|
17
17
|
]
|
package/benchmarks/miki.js
CHANGED
|
@@ -2,11 +2,14 @@ const miki = require('../src');
|
|
|
2
2
|
const { performance } = require('perf_hooks');
|
|
3
3
|
|
|
4
4
|
const SMALL = `{% for item in items %}{{ item|upper }}:{{ item|length }}\n{% endfor %}`;
|
|
5
|
-
const MEDIUM = `{% for i in
|
|
6
|
-
const LARGE = `{% for i in
|
|
5
|
+
const MEDIUM = `{% for i in big_items %}{% if i % 2 == 0 %}Even: {{ i }}\n{% else %}Odd: {{ i }}\n{% endif %}{% endfor %}`;
|
|
6
|
+
const LARGE = `{% for i in big_items %}{% for j in small_items %}{{ i }}:{{ j }} {{ x10|repeat:5 }}\n{% endfor %}{% endfor %}`;
|
|
7
7
|
|
|
8
8
|
const data = {
|
|
9
|
-
items: ['alpha', 'beta', 'gamma', 'delta', 'epsilon']
|
|
9
|
+
items: ['alpha', 'beta', 'gamma', 'delta', 'epsilon'],
|
|
10
|
+
big_items: Array.from({ length: 500 }, (_, i) => i),
|
|
11
|
+
small_items: Array.from({ length: 5 }, (_, j) => j),
|
|
12
|
+
x10: 'x'
|
|
10
13
|
};
|
|
11
14
|
|
|
12
15
|
function bench(name, tpl, data, iterations = 5000) {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"name": "pug:small",
|
|
4
|
-
"medianMs": 0.
|
|
5
|
-
"rps":
|
|
4
|
+
"medianMs": 0.0006000000000199179,
|
|
5
|
+
"rps": 1666667
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "pug:medium",
|
|
9
|
-
"medianMs": 0.
|
|
10
|
-
"rps":
|
|
9
|
+
"medianMs": 0.019200000000012096,
|
|
10
|
+
"rps": 52083
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "pug:large",
|
|
14
|
-
"medianMs": 0.
|
|
15
|
-
"rps":
|
|
14
|
+
"medianMs": 0.3955999999999449,
|
|
15
|
+
"rps": 2528
|
|
16
16
|
}
|
|
17
17
|
]
|
package/benchmarks/pug.js
CHANGED
|
@@ -2,11 +2,13 @@ const pug = require('pug');
|
|
|
2
2
|
const { performance } = require('perf_hooks');
|
|
3
3
|
|
|
4
4
|
const SMALL = `each item in items\n = item.toUpperCase() + ':' + item.length\n`;
|
|
5
|
-
const MEDIUM =
|
|
6
|
-
const LARGE =
|
|
5
|
+
const MEDIUM = `each i in big_items\n if i % 2 === 0\n | Even: #{i}\n else\n | Odd: #{i}\n`;
|
|
6
|
+
const LARGE = `each i in big_items\n each j in small_items\n | #{i}:#{j} #{'x'.repeat(10)}\n`;
|
|
7
7
|
|
|
8
8
|
const data = {
|
|
9
|
-
items: ['alpha', 'beta', 'gamma', 'delta', 'epsilon']
|
|
9
|
+
items: ['alpha', 'beta', 'gamma', 'delta', 'epsilon'],
|
|
10
|
+
big_items: Array.from({ length: 500 }, (_, i) => i),
|
|
11
|
+
small_items: Array.from({ length: 5 }, (_, j) => j)
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
function bench(name, tpl, data, iterations = 5000) {
|
package/docs/api/async-render.md
CHANGED
|
@@ -1,85 +1,170 @@
|
|
|
1
|
-
# asyncRender()
|
|
1
|
+
# asyncRender()
|
|
2
|
+
|
|
3
|
+
|
|
2
4
|
|
|
3
5
|
Async version of `render()`. Returns a Promise. Use this when your templates contain async filters, async custom tags, or async library components.
|
|
4
6
|
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
## Signature
|
|
6
10
|
|
|
11
|
+
|
|
12
|
+
|
|
7
13
|
```javascript
|
|
14
|
+
|
|
8
15
|
asyncRender(templateStr, contextObj = {}, options = {})
|
|
16
|
+
|
|
9
17
|
```
|
|
10
18
|
|
|
19
|
+
|
|
20
|
+
|
|
11
21
|
## Returns
|
|
12
22
|
|
|
23
|
+
|
|
24
|
+
|
|
13
25
|
`Promise<string>` — The rendered HTML.
|
|
14
26
|
|
|
27
|
+
|
|
28
|
+
|
|
15
29
|
## When to Use
|
|
16
30
|
|
|
31
|
+
|
|
32
|
+
|
|
17
33
|
Use `asyncRender()` when your templates contain:
|
|
18
34
|
|
|
35
|
+
|
|
36
|
+
|
|
19
37
|
- Async filters (returning Promises)
|
|
38
|
+
|
|
20
39
|
- Async custom tags (render return a Promise)
|
|
40
|
+
|
|
21
41
|
- Async library helpers
|
|
42
|
+
|
|
22
43
|
- `{% load %}` libraries with async components
|
|
23
44
|
|
|
45
|
+
|
|
46
|
+
|
|
24
47
|
Using async features with `render()` throws: `Async node encountered during sync render. Use asyncRender() instead.`
|
|
25
48
|
|
|
49
|
+
|
|
50
|
+
|
|
26
51
|
## Examples
|
|
27
52
|
|
|
53
|
+
|
|
54
|
+
|
|
28
55
|
### Basic async render
|
|
29
56
|
|
|
57
|
+
|
|
58
|
+
|
|
30
59
|
=== "CommonJS"
|
|
31
60
|
|
|
61
|
+
|
|
62
|
+
|
|
32
63
|
```javascript
|
|
64
|
+
|
|
33
65
|
const { asyncRender } = require('miki-template');
|
|
34
66
|
|
|
67
|
+
|
|
68
|
+
|
|
35
69
|
const html = await asyncRender('Hello {{ name }}!', { name: 'World' });
|
|
70
|
+
|
|
36
71
|
```
|
|
37
72
|
|
|
73
|
+
|
|
74
|
+
|
|
38
75
|
=== "ES Modules"
|
|
39
76
|
|
|
77
|
+
|
|
78
|
+
|
|
40
79
|
```javascript
|
|
80
|
+
|
|
41
81
|
import { asyncRender } from 'miki-template';
|
|
42
82
|
|
|
83
|
+
|
|
84
|
+
|
|
43
85
|
const html = await asyncRender('Hello {{ name }}!', { name: 'World' });
|
|
86
|
+
|
|
44
87
|
```
|
|
45
88
|
|
|
89
|
+
|
|
90
|
+
|
|
46
91
|
### With async tags/filters
|
|
47
92
|
|
|
93
|
+
|
|
94
|
+
|
|
48
95
|
=== "CommonJS"
|
|
49
96
|
|
|
97
|
+
|
|
98
|
+
|
|
50
99
|
```javascript
|
|
100
|
+
|
|
51
101
|
const { asyncRender } = require('miki-template');
|
|
52
102
|
|
|
103
|
+
|
|
104
|
+
|
|
53
105
|
const html = await asyncRender(templateWithAsyncHelpers, context, options);
|
|
106
|
+
|
|
54
107
|
```
|
|
55
108
|
|
|
109
|
+
|
|
110
|
+
|
|
56
111
|
=== "ES Modules"
|
|
57
112
|
|
|
113
|
+
|
|
114
|
+
|
|
58
115
|
```javascript
|
|
116
|
+
|
|
59
117
|
import { asyncRender } from 'miki-template';
|
|
60
118
|
|
|
119
|
+
|
|
120
|
+
|
|
61
121
|
const html = await asyncRender(templateWithAsyncHelpers, context, options);
|
|
122
|
+
|
|
62
123
|
```
|
|
63
124
|
|
|
125
|
+
|
|
126
|
+
|
|
64
127
|
### Async partial from file
|
|
65
128
|
|
|
129
|
+
|
|
130
|
+
|
|
66
131
|
=== "CommonJS"
|
|
67
132
|
|
|
133
|
+
|
|
134
|
+
|
|
68
135
|
```javascript
|
|
136
|
+
|
|
69
137
|
const { asyncRender } = require('miki-template');
|
|
70
138
|
|
|
139
|
+
|
|
140
|
+
|
|
71
141
|
const html = await asyncRender('home#card', context, { views: './views' });
|
|
142
|
+
|
|
72
143
|
```
|
|
73
144
|
|
|
145
|
+
|
|
146
|
+
|
|
74
147
|
=== "ES Modules"
|
|
75
148
|
|
|
149
|
+
|
|
150
|
+
|
|
76
151
|
```javascript
|
|
152
|
+
|
|
77
153
|
import { asyncRender } from 'miki-template';
|
|
78
154
|
|
|
155
|
+
|
|
156
|
+
|
|
79
157
|
const html = await asyncRender('home#card', context, { views: './views' });
|
|
158
|
+
|
|
80
159
|
```
|
|
81
160
|
|
|
161
|
+
|
|
162
|
+
|
|
82
163
|
## Related
|
|
83
164
|
|
|
84
|
-
|
|
85
|
-
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
- [render()](./render.md)
|
|
168
|
+
|
|
169
|
+
- [compile()](./compile.md)
|
|
170
|
+
|
package/docs/api/cache.md
CHANGED
|
@@ -1,87 +1,174 @@
|
|
|
1
|
-
# Cache API
|
|
1
|
+
# Cache API
|
|
2
|
+
|
|
3
|
+
|
|
2
4
|
|
|
3
5
|
## clearCache
|
|
4
6
|
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
Clear the compiled template cache. Templates are cached in-memory (LRU, 100 entries max). Call this when templates change on disk during development, in tests, or when dynamically registering tags/filters.
|
|
6
10
|
|
|
11
|
+
|
|
12
|
+
|
|
7
13
|
=== "CommonJS"
|
|
8
14
|
|
|
15
|
+
|
|
16
|
+
|
|
9
17
|
```javascript
|
|
18
|
+
|
|
10
19
|
const { clearCache } = require('miki-template');
|
|
11
20
|
|
|
21
|
+
|
|
22
|
+
|
|
12
23
|
clearCache();
|
|
24
|
+
|
|
13
25
|
```
|
|
14
26
|
|
|
27
|
+
|
|
28
|
+
|
|
15
29
|
=== "ES Modules"
|
|
16
30
|
|
|
31
|
+
|
|
32
|
+
|
|
17
33
|
```javascript
|
|
34
|
+
|
|
18
35
|
import { clearCache } from 'miki-template';
|
|
19
36
|
|
|
37
|
+
|
|
38
|
+
|
|
20
39
|
clearCache();
|
|
40
|
+
|
|
21
41
|
```
|
|
22
42
|
|
|
43
|
+
|
|
44
|
+
|
|
23
45
|
## How Caching Works
|
|
24
46
|
|
|
47
|
+
|
|
48
|
+
|
|
25
49
|
- Templates are cached by a key combining the source string and compile options (views, custom settings).
|
|
50
|
+
|
|
26
51
|
- The cache is an in-memory LRU cache limited to 100 entries.
|
|
52
|
+
|
|
27
53
|
- Cached compiled templates are reused across renders, improving performance for repeated templates.
|
|
54
|
+
|
|
28
55
|
- Partials defined via `{% partialdef %}` are cached along with their parent template.
|
|
29
56
|
|
|
57
|
+
|
|
58
|
+
|
|
30
59
|
## When to Clear Cache
|
|
31
60
|
|
|
61
|
+
|
|
62
|
+
|
|
32
63
|
- During development when templates change frequently on disk
|
|
64
|
+
|
|
33
65
|
- In tests to ensure fresh compilation
|
|
66
|
+
|
|
34
67
|
- When dynamically registering custom tags/filters at runtime
|
|
35
68
|
|
|
69
|
+
|
|
70
|
+
|
|
36
71
|
### Development File Watcher
|
|
37
72
|
|
|
73
|
+
|
|
74
|
+
|
|
38
75
|
=== "CommonJS"
|
|
39
76
|
|
|
77
|
+
|
|
78
|
+
|
|
40
79
|
```javascript
|
|
80
|
+
|
|
41
81
|
const fs = require('fs');
|
|
82
|
+
|
|
42
83
|
const { clearCache } = require('miki-template');
|
|
43
84
|
|
|
85
|
+
|
|
86
|
+
|
|
44
87
|
if (process.env.NODE_ENV !== 'production') {
|
|
88
|
+
|
|
45
89
|
fs.watch('./views', () => {
|
|
90
|
+
|
|
46
91
|
clearCache();
|
|
92
|
+
|
|
47
93
|
console.log('Template cache cleared');
|
|
94
|
+
|
|
48
95
|
});
|
|
96
|
+
|
|
49
97
|
}
|
|
98
|
+
|
|
50
99
|
```
|
|
51
100
|
|
|
101
|
+
|
|
102
|
+
|
|
52
103
|
=== "ES Modules"
|
|
53
104
|
|
|
105
|
+
|
|
106
|
+
|
|
54
107
|
```javascript
|
|
108
|
+
|
|
55
109
|
import fs from 'node:fs';
|
|
110
|
+
|
|
56
111
|
import { clearCache } from 'miki-template';
|
|
57
112
|
|
|
113
|
+
|
|
114
|
+
|
|
58
115
|
if (process.env.NODE_ENV !== 'production') {
|
|
116
|
+
|
|
59
117
|
fs.watch('./views', () => {
|
|
118
|
+
|
|
60
119
|
clearCache();
|
|
120
|
+
|
|
61
121
|
console.log('Template cache cleared');
|
|
122
|
+
|
|
62
123
|
});
|
|
124
|
+
|
|
63
125
|
}
|
|
126
|
+
|
|
64
127
|
```
|
|
65
128
|
|
|
129
|
+
|
|
130
|
+
|
|
66
131
|
## cache Library
|
|
67
132
|
|
|
133
|
+
|
|
134
|
+
|
|
68
135
|
The built-in `cache` library (auto-activated) provides a template tag for caching fragments:
|
|
69
136
|
|
|
137
|
+
|
|
138
|
+
|
|
70
139
|
```html
|
|
140
|
+
|
|
71
141
|
{% load cache %}
|
|
72
142
|
|
|
143
|
+
|
|
144
|
+
|
|
73
145
|
{% cache 300 sidebar_key %}
|
|
146
|
+
|
|
74
147
|
<div class="sidebar">
|
|
148
|
+
|
|
75
149
|
{% for item in sidebar_items %}
|
|
150
|
+
|
|
76
151
|
<a href="{{ item.url }}">{{ item.title }}</a>
|
|
152
|
+
|
|
77
153
|
{% endfor %}
|
|
154
|
+
|
|
78
155
|
</div>
|
|
156
|
+
|
|
79
157
|
{% endcache %}
|
|
158
|
+
|
|
80
159
|
```
|
|
81
160
|
|
|
161
|
+
|
|
162
|
+
|
|
82
163
|
The first argument is the TTL in seconds. The second is a cache key. Additional arguments serve as key components.
|
|
83
164
|
|
|
165
|
+
|
|
166
|
+
|
|
84
167
|
## Next Steps
|
|
85
168
|
|
|
86
|
-
|
|
87
|
-
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
- [Advanced Usage: Caching](../guide/advanced-usage.md#caching)
|
|
172
|
+
|
|
173
|
+
- [API Reference](../index.md)
|
|
174
|
+
|