miki-template 2.0.1 → 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 +13 -37
- package/.github/workflows/docs.yml +105 -0
- package/.github/workflows/npm-publish-github-packages.yml +36 -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
package/.github/workflows/ci.yml
CHANGED
|
@@ -7,60 +7,36 @@ on:
|
|
|
7
7
|
branches: [ main ]
|
|
8
8
|
workflow_dispatch:
|
|
9
9
|
|
|
10
|
-
jobs:
|
|
11
|
-
test:
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
strategy:
|
|
14
|
-
matrix:
|
|
15
|
-
node-version: [18.x, 20.x]
|
|
16
|
-
steps:
|
|
17
|
-
- uses: actions/checkout@v4
|
|
18
|
-
- name: Use Node.js
|
|
19
|
-
uses: actions/setup-node@v4
|
|
20
|
-
with:
|
|
21
|
-
node-version: ${{ matrix.node-version }}
|
|
22
|
-
- name: Install
|
|
23
|
-
run: npm ci
|
|
24
|
-
- name: Run unit tests
|
|
25
|
-
run: npm test
|
|
26
|
-
- name: Run integration smoke-test
|
|
27
|
-
if: github.event_name == 'workflow_dispatch'
|
|
28
|
-
run: node live-test/integrations/smoke-test.js
|
|
29
|
-
# CI Workflow — lint + test on every push and PR.
|
|
30
|
-
#
|
|
31
|
-
# Releases are handled by .github/workflows/release.yml, which triggers
|
|
32
|
-
# automatically when a `chore(release):` commit lands on main.
|
|
33
|
-
|
|
34
|
-
name: CI
|
|
35
|
-
|
|
36
|
-
on:
|
|
37
|
-
push:
|
|
38
|
-
branches: [ main ]
|
|
39
|
-
pull_request:
|
|
40
|
-
branches: [ main ]
|
|
41
|
-
|
|
42
10
|
jobs:
|
|
43
11
|
lint:
|
|
44
|
-
runs-on:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
45
13
|
steps:
|
|
46
14
|
- uses: actions/checkout@v4
|
|
47
15
|
- name: Setup Node.js
|
|
48
16
|
uses: actions/setup-node@v4
|
|
49
17
|
with:
|
|
50
18
|
node-version: '20'
|
|
19
|
+
cache: 'npm'
|
|
51
20
|
- run: npm ci
|
|
52
21
|
- name: Lint code
|
|
53
22
|
run: npx eslint src/**/*.js
|
|
54
23
|
|
|
55
24
|
test:
|
|
56
|
-
runs-on:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
57
26
|
needs: lint
|
|
27
|
+
strategy:
|
|
28
|
+
matrix:
|
|
29
|
+
node-version: [18.x, 20.x]
|
|
58
30
|
steps:
|
|
59
31
|
- uses: actions/checkout@v4
|
|
60
|
-
- name:
|
|
32
|
+
- name: Use Node.js
|
|
61
33
|
uses: actions/setup-node@v4
|
|
62
34
|
with:
|
|
63
|
-
node-version:
|
|
35
|
+
node-version: ${{ matrix.node-version }}
|
|
36
|
+
cache: 'npm'
|
|
64
37
|
- run: npm ci
|
|
65
|
-
- name: Run
|
|
38
|
+
- name: Run unit tests
|
|
66
39
|
run: npm test
|
|
40
|
+
- name: Run integration smoke-test
|
|
41
|
+
if: github.event_name == 'workflow_dispatch'
|
|
42
|
+
run: node live-test/integrations/smoke-test.js
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: Deploy Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
pages: write
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: "pages"
|
|
17
|
+
cancel-in-progress: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
deploy-docs:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout repository
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Setup Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.12'
|
|
30
|
+
cache: 'pip'
|
|
31
|
+
cache-dependency-path: 'requirements-docs.txt'
|
|
32
|
+
|
|
33
|
+
- name: Install MkDocs Material with imaging support
|
|
34
|
+
run: |
|
|
35
|
+
python -m pip install --upgrade pip
|
|
36
|
+
pip install -r requirements-docs.txt
|
|
37
|
+
|
|
38
|
+
- name: Verify docs build
|
|
39
|
+
run: mkdocs build --strict
|
|
40
|
+
|
|
41
|
+
- name: Deploy docs to GitHub Pages
|
|
42
|
+
run: mkdocs gh-deploy --force --message "chore: deploy docs to GitHub Pages [skip ci]"
|
|
43
|
+
|
|
44
|
+
- name: Output Pages URL
|
|
45
|
+
run: echo "Docs deployed to https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/"
|
|
46
|
+
|
|
47
|
+
sync-wiki:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
needs: deploy-docs
|
|
50
|
+
steps:
|
|
51
|
+
- name: Checkout repository
|
|
52
|
+
uses: actions/checkout@v4
|
|
53
|
+
with:
|
|
54
|
+
fetch-depth: 0
|
|
55
|
+
|
|
56
|
+
- name: Setup Git config
|
|
57
|
+
run: |
|
|
58
|
+
git config --global user.name "github-actions[bot]"
|
|
59
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
60
|
+
|
|
61
|
+
- name: Create wiki clone directory
|
|
62
|
+
run: mkdir -p wiki-repo
|
|
63
|
+
|
|
64
|
+
- name: Clone wiki repository
|
|
65
|
+
run: |
|
|
66
|
+
git clone https://github.com/${{ github.repository }}.wiki.git wiki-repo || true
|
|
67
|
+
|
|
68
|
+
- name: Sync docs to wiki
|
|
69
|
+
if: hashFiles('wiki-repo/.git/config') != ''
|
|
70
|
+
run: |
|
|
71
|
+
cd wiki-repo
|
|
72
|
+
|
|
73
|
+
# Copy all markdown docs preserving directory structure
|
|
74
|
+
cp -r ../docs/*.md .
|
|
75
|
+
cp -r ../docs/guide/*.md ./guide/ 2>/dev/null || true
|
|
76
|
+
cp -r ../docs/api/*.md ./api/ 2>/dev/null || true
|
|
77
|
+
cp -r ../docs/integrations/*.md ./integrations/ 2>/dev/null || true
|
|
78
|
+
|
|
79
|
+
# Remove index.md if it exists (GitHub wiki uses README.md as home page)
|
|
80
|
+
if [ -f index.md ]; then
|
|
81
|
+
if [ -f README.md ]; then
|
|
82
|
+
rm index.md
|
|
83
|
+
else
|
|
84
|
+
mv index.md README.md
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Create Home.md from index.md if README.md doesn't exist
|
|
89
|
+
if [ ! -f README.md ] && [ ! -f Home.md ] && [ -f docs/index.md ]; then
|
|
90
|
+
cp docs/index.md Home.md
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Commit and push if there are changes
|
|
94
|
+
git add -A
|
|
95
|
+
git diff --cached --quiet || {
|
|
96
|
+
git commit -m "docs: sync documentation to wiki"
|
|
97
|
+
git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.wiki.git master
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
- name: Wiki initialization hint
|
|
101
|
+
if: hashFiles('wiki-repo/.git/config') == ''
|
|
102
|
+
run: |
|
|
103
|
+
echo "::warning::Wiki repository not found. Enable the wiki in repository settings first."
|
|
104
|
+
echo "Go to https://github.com/${{ github.repository }}/settings/wiki and click 'Create the first page'"
|
|
105
|
+
continue-on-error: true
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Node.js Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 20
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm test
|
|
20
|
+
|
|
21
|
+
publish-gpr:
|
|
22
|
+
needs: build
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
packages: write
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: 20
|
|
32
|
+
registry-url: https://npm.pkg.github.com/
|
|
33
|
+
- run: npm ci
|
|
34
|
+
- run: npm publish
|
|
35
|
+
env:
|
|
36
|
+
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
package/README.md
CHANGED
|
@@ -75,14 +75,13 @@ miki-template is built for real-world apps. Its compiled-AST engine is especiall
|
|
|
75
75
|
|
|
76
76
|
## 📚 Documentation
|
|
77
77
|
|
|
78
|
-
- [
|
|
79
|
-
- [
|
|
80
|
-
- [
|
|
81
|
-
- [
|
|
82
|
-
- [
|
|
83
|
-
- [
|
|
84
|
-
- [
|
|
85
|
-
- [Contributing](docs/advanced_usage.md)
|
|
78
|
+
- **[Documentation](https://alainmiki.github.io/miki-template/)** — Full docs site
|
|
79
|
+
- [Getting Started](https://alainmiki.github.io/miki-template/guide/getting-started)
|
|
80
|
+
- [Installation](https://alainmiki.github.io/miki-template/guide/installation)
|
|
81
|
+
- [Quick Start](https://alainmiki.github.io/miki-template/guide/quick-start)
|
|
82
|
+
- [API Reference](https://alainmiki.github.io/miki-template/api/)
|
|
83
|
+
- [Integrations](https://alainmiki.github.io/miki-template/integrations/)
|
|
84
|
+
- [Performance](https://alainmiki.github.io/miki-template/performance)
|
|
86
85
|
|
|
87
86
|
---
|
|
88
87
|
|
|
@@ -252,13 +251,14 @@ Resolve properties dynamically on nested objects or arrays. If the resolved valu
|
|
|
252
251
|
|
|
253
252
|
### Built-in Filters
|
|
254
253
|
Apply filters using pipes (`|`). Arguments are passed after a colon (`:`).
|
|
255
|
-
- **Text**: `upper`, `lower`, `title`, `capfirst`, `slugify`, `wordcount`, `striptags`, `linebreaks`, `linebreaksbr`, `truncatewords:N`, `truncatechars:N`.
|
|
254
|
+
- **Text**: `upper`, `lower`, `title`, `capfirst`, `slugify`, `wordcount`, `striptags`, `linebreaks`, `linebreaksbr`, `truncatewords:N`, `truncatechars:N`, `truncatechars_html:N`.
|
|
256
255
|
- **HTML**: `safe`, `escape`.
|
|
257
|
-
- **List**: `length`, `join:","`, `slice:"start:end"`, `dictsort:"key"`, `dictsortreversed:"key"`.
|
|
258
|
-
- **Default**: `default:"fallback"`, `default_if_none:"fallback"`.
|
|
259
|
-
- **Date/Time**: `date:"Y-m-d"`, `time:"H:i"`, `timesince`, `timeuntil`.
|
|
260
|
-
- **Numeric**: `add:5`, `divisibleby:2`, `floatformat:2`.
|
|
261
|
-
- **
|
|
256
|
+
- **List**: `length`, `join:","`, `slice:"start:end"`, `dictsort:"key"`, `dictsortreversed:"key"`, `sort`, `unique`, `random`, `reverse`, `split:","`, `replace:"old,new"`.
|
|
257
|
+
- **Default**: `default:"fallback"`, `default_if_none:"fallback"`, `firstof:v1 v2 v3`.
|
|
258
|
+
- **Date/Time**: `date:"Y-m-d"`, `time:"H:i"`, `date_format:"yyyy-MM-dd"`, `strftime:"PPpp"`, `timesince`, `timeuntil`, `ago`, `until`, `time_diff:other_date`.
|
|
259
|
+
- **Numeric**: `add:5`, `sub:3`, `mult:2`, `divisibleby:2`, `mod:3`, `floatformat:2`, `square`, `sqrt`, `abs`, `round:2`, `floor`, `ceil`, `min:10`, `max:100`, `sum`, `average`.
|
|
260
|
+
- **Currency/Data**: `currency:"$"`, `phone_number`, `email`, `url`, `mask:"*"`, `whatsapp_link:"msg"`, `credit_card`, `ssn`, `ip_address`, `uuid`, `filesizeformat`, `yesno:"yes,no,maybe"`, `pluralize:"s"`, `urlencode`, `escapeuri`, `stringformat:"%s"`, `cut:"text"`, `addslashes`, `removetags:"p,div"`, `trans`, `regroup:"attr"`, `json`, `urlize`.
|
|
261
|
+
- **Encoding**: `base64_encode`, `base64_decode`.
|
|
262
262
|
|
|
263
263
|
### Built-in Control Tags
|
|
264
264
|
- **if / elif / else / endif**: Supports conditional expressions with operators: `==`, `!=`, `<`, `<=`, `>`, `>=`, `in`, `not in`, `and`, `or`, `not`.
|
|
@@ -291,9 +291,64 @@ Apply filters using pipes (`|`). Arguments are passed after a colon (`:`).
|
|
|
291
291
|
<tr class="{% cycle 'row-odd' 'row-even' %}">...</tr>
|
|
292
292
|
{% endfor %}
|
|
293
293
|
```
|
|
294
|
+
- **firstof**: Return the first truthy value.
|
|
295
|
+
```html
|
|
296
|
+
{% firstof var1 var2 var3 "fallback" %}
|
|
297
|
+
```
|
|
298
|
+
- **set**: Assign variables.
|
|
299
|
+
```html
|
|
300
|
+
{% set total = price * quantity %}
|
|
301
|
+
{% set greeting %}Hello {{ name }}{% endset %}
|
|
302
|
+
```
|
|
303
|
+
- **ifchanged / endifchanged**: Render only when value changes.
|
|
304
|
+
```html
|
|
305
|
+
{% for item in items %}
|
|
306
|
+
{% ifchanged item.category %}
|
|
307
|
+
<h2>{{ item.category }}</h2>
|
|
308
|
+
{% endifchanged %}
|
|
309
|
+
{% endfor %}
|
|
310
|
+
```
|
|
311
|
+
- **now**: Output current date/time.
|
|
312
|
+
```html
|
|
313
|
+
{% now "Y-m-d H:i:s" %}
|
|
314
|
+
```
|
|
315
|
+
- **static**: Generate static file URLs.
|
|
316
|
+
```html
|
|
317
|
+
{% static "css/style.css" %}
|
|
318
|
+
```
|
|
319
|
+
- **url**: Build URLs from route names.
|
|
320
|
+
```html
|
|
321
|
+
{% url 'user.profile' user.id %}
|
|
322
|
+
```
|
|
323
|
+
- **regroup**: Group lists by attribute.
|
|
324
|
+
```html
|
|
325
|
+
{% for group in items|regroup:"category" %}
|
|
326
|
+
<h3>{{ group.grouper }}</h3>
|
|
327
|
+
{% endfor %}
|
|
328
|
+
```
|
|
329
|
+
- **spaceless**: Remove whitespace between tags.
|
|
330
|
+
```html
|
|
331
|
+
{% spaceless %}<div> <span>hi</span> </div>{% endspaceless %}
|
|
332
|
+
```
|
|
333
|
+
- **widthratio**: Calculate ratios.
|
|
334
|
+
```html
|
|
335
|
+
{% widthratio value max max_width %}
|
|
336
|
+
```
|
|
337
|
+
- **debug**: Dump template context.
|
|
338
|
+
```html
|
|
339
|
+
{% debug %}
|
|
340
|
+
```
|
|
294
341
|
- **autoescape on/off**: Control auto-escaping block behavior.
|
|
295
342
|
- **verbatim / endverbatim**: Treat raw text inside literally.
|
|
296
343
|
- **comment / endcomment**: Block comment ignored during parse.
|
|
344
|
+
- **load**: Activate template libraries.
|
|
345
|
+
```html
|
|
346
|
+
{% load lorem humanize %}
|
|
347
|
+
```
|
|
348
|
+
- **templatetag**: Output literal template tag tokens.
|
|
349
|
+
```html
|
|
350
|
+
{% templatetag openblock %} if user.is_admin {% templatetag closeblock %}
|
|
351
|
+
```
|
|
297
352
|
|
|
298
353
|
### Security Tags
|
|
299
354
|
- **csrf_token**: Automatically outputs a hidden input carrying the CSRF token from the context variable `csrf_token`.
|
package/assets/logo.png
ADDED
|
Binary file
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "ejs:medium",
|
|
9
|
-
"medianMs": 0.
|
|
10
|
-
"rps":
|
|
9
|
+
"medianMs": 0.033799999999985175,
|
|
10
|
+
"rps": 29586
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "ejs:large",
|
|
14
|
-
"medianMs": 3.
|
|
15
|
-
"rps":
|
|
14
|
+
"medianMs": 3.5704000000000633,
|
|
15
|
+
"rps": 280
|
|
16
16
|
}
|
|
17
17
|
]
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"name": "handlebars:small",
|
|
4
|
-
"medianMs": 0.
|
|
5
|
-
"rps":
|
|
4
|
+
"medianMs": 0.0024999999999977263,
|
|
5
|
+
"rps": 400000
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "handlebars:medium",
|
|
9
|
-
"medianMs": 0.
|
|
10
|
-
"rps":
|
|
9
|
+
"medianMs": 0.019200000000012096,
|
|
10
|
+
"rps": 52083
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "handlebars:large",
|
|
14
|
-
"medianMs": 1.
|
|
15
|
-
"rps":
|
|
14
|
+
"medianMs": 1.5285000000001219,
|
|
15
|
+
"rps": 654
|
|
16
16
|
}
|
|
17
17
|
]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"name": "miki:small",
|
|
4
|
-
"medianMs": 0.
|
|
5
|
-
"rps":
|
|
4
|
+
"medianMs": 0.00899999999998613,
|
|
5
|
+
"rps": 111111
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "miki:medium",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "miki:large",
|
|
14
|
-
"medianMs": 0.
|
|
15
|
-
"rps":
|
|
14
|
+
"medianMs": 0.002200000000016189,
|
|
15
|
+
"rps": 454545
|
|
16
16
|
}
|
|
17
17
|
]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"name": "pug:small",
|
|
4
|
-
"medianMs": 0.
|
|
5
|
-
"rps":
|
|
4
|
+
"medianMs": 0.0006999999999948159,
|
|
5
|
+
"rps": 1428571
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
8
|
"name": "pug:medium",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "pug:large",
|
|
14
|
-
"medianMs": 0.
|
|
15
|
-
"rps":
|
|
14
|
+
"medianMs": 0.33320000000003347,
|
|
15
|
+
"rps": 3001
|
|
16
16
|
}
|
|
17
17
|
]
|
package/benchmarks/stress.mjs
CHANGED
|
@@ -528,7 +528,7 @@ const endRps = 100000 / dur;
|
|
|
528
528
|
|
|
529
529
|
record('endurance: 100k renders complete in < 30s', dur < 30000,
|
|
530
530
|
`${dur.toFixed(0)} ms total, ${Math.round(endRps).toLocaleString()} rps avg`);
|
|
531
|
-
record('endurance: RSS growth <
|
|
531
|
+
record('endurance: RSS growth < 80 MB', rssDelta < 80,
|
|
532
532
|
`before ${beforeRss.toFixed(1)} MB, peak ${endRssMax.toFixed(1)} MB (Δ +${rssDelta.toFixed(1)} MB)`);
|
|
533
533
|
record('endurance: output is still correct at end', (() => {
|
|
534
534
|
const expected = endC.render(enduranceData);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# asyncRender()
|
|
2
|
+
|
|
3
|
+
Async version of `render()`. Returns a Promise. Use this when your templates contain async filters, async custom tags, or async library components.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
asyncRender(templateStr, contextObj = {}, options = {})
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Returns
|
|
12
|
+
|
|
13
|
+
`Promise<string>` — The rendered HTML.
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
Use `asyncRender()` when your templates contain:
|
|
18
|
+
|
|
19
|
+
- Async filters (returning Promises)
|
|
20
|
+
- Async custom tags (render return a Promise)
|
|
21
|
+
- Async library helpers
|
|
22
|
+
- `{% load %}` libraries with async components
|
|
23
|
+
|
|
24
|
+
Using async features with `render()` throws: `Async node encountered during sync render. Use asyncRender() instead.`
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
|
|
28
|
+
### Basic async render
|
|
29
|
+
|
|
30
|
+
=== "CommonJS"
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
const { asyncRender } = require('miki-template');
|
|
34
|
+
|
|
35
|
+
const html = await asyncRender('Hello {{ name }}!', { name: 'World' });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
=== "ES Modules"
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import { asyncRender } from 'miki-template';
|
|
42
|
+
|
|
43
|
+
const html = await asyncRender('Hello {{ name }}!', { name: 'World' });
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### With async tags/filters
|
|
47
|
+
|
|
48
|
+
=== "CommonJS"
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
const { asyncRender } = require('miki-template');
|
|
52
|
+
|
|
53
|
+
const html = await asyncRender(templateWithAsyncHelpers, context, options);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
=== "ES Modules"
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
import { asyncRender } from 'miki-template';
|
|
60
|
+
|
|
61
|
+
const html = await asyncRender(templateWithAsyncHelpers, context, options);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Async partial from file
|
|
65
|
+
|
|
66
|
+
=== "CommonJS"
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
const { asyncRender } = require('miki-template');
|
|
70
|
+
|
|
71
|
+
const html = await asyncRender('home#card', context, { views: './views' });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
=== "ES Modules"
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
import { asyncRender } from 'miki-template';
|
|
78
|
+
|
|
79
|
+
const html = await asyncRender('home#card', context, { views: './views' });
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Related
|
|
83
|
+
|
|
84
|
+
- [render()](./render)
|
|
85
|
+
- [compile()](./compile)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Cache API
|
|
2
|
+
|
|
3
|
+
## clearCache
|
|
4
|
+
|
|
5
|
+
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
|
+
|
|
7
|
+
=== "CommonJS"
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
const { clearCache } = require('miki-template');
|
|
11
|
+
|
|
12
|
+
clearCache();
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
=== "ES Modules"
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
import { clearCache } from 'miki-template';
|
|
19
|
+
|
|
20
|
+
clearCache();
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## How Caching Works
|
|
24
|
+
|
|
25
|
+
- Templates are cached by a key combining the source string and compile options (views, custom settings).
|
|
26
|
+
- The cache is an in-memory LRU cache limited to 100 entries.
|
|
27
|
+
- Cached compiled templates are reused across renders, improving performance for repeated templates.
|
|
28
|
+
- Partials defined via `{% partialdef %}` are cached along with their parent template.
|
|
29
|
+
|
|
30
|
+
## When to Clear Cache
|
|
31
|
+
|
|
32
|
+
- During development when templates change frequently on disk
|
|
33
|
+
- In tests to ensure fresh compilation
|
|
34
|
+
- When dynamically registering custom tags/filters at runtime
|
|
35
|
+
|
|
36
|
+
### Development File Watcher
|
|
37
|
+
|
|
38
|
+
=== "CommonJS"
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
const fs = require('fs');
|
|
42
|
+
const { clearCache } = require('miki-template');
|
|
43
|
+
|
|
44
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
45
|
+
fs.watch('./views', () => {
|
|
46
|
+
clearCache();
|
|
47
|
+
console.log('Template cache cleared');
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
=== "ES Modules"
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
import fs from 'node:fs';
|
|
56
|
+
import { clearCache } from 'miki-template';
|
|
57
|
+
|
|
58
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
59
|
+
fs.watch('./views', () => {
|
|
60
|
+
clearCache();
|
|
61
|
+
console.log('Template cache cleared');
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## cache Library
|
|
67
|
+
|
|
68
|
+
The built-in `cache` library (auto-activated) provides a template tag for caching fragments:
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
{% load cache %}
|
|
72
|
+
|
|
73
|
+
{% cache 300 sidebar_key %}
|
|
74
|
+
<div class="sidebar">
|
|
75
|
+
{% for item in sidebar_items %}
|
|
76
|
+
<a href="{{ item.url }}">{{ item.title }}</a>
|
|
77
|
+
{% endfor %}
|
|
78
|
+
</div>
|
|
79
|
+
{% endcache %}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The first argument is the TTL in seconds. The second is a cache key. Additional arguments serve as key components.
|
|
83
|
+
|
|
84
|
+
## Next Steps
|
|
85
|
+
|
|
86
|
+
- [Advanced Usage: Caching](../guide/advanced-usage#caching)
|
|
87
|
+
- [API Reference](../)
|