miki-template 1.3.3 → 1.3.7

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.
Files changed (115) hide show
  1. package/.eslintrc.json +16 -0
  2. package/.github/release-notes/v1.3.1.md +55 -55
  3. package/.github/release-notes/v1.3.3.md +77 -0
  4. package/.github/workflows/ci.yml +38 -54
  5. package/.github/workflows/release.yml +106 -0
  6. package/AGENT.md +71 -71
  7. package/API_REFERENCE.md +314 -314
  8. package/CHANGELOG.md +173 -169
  9. package/CODE_OF_CONDUCT.md +14 -14
  10. package/CONTRIBUTING.md +27 -27
  11. package/README.md +342 -321
  12. package/ROADMAP.md +40 -40
  13. package/benchmarks/report.json +16 -16
  14. package/benchmarks/run.js +49 -49
  15. package/benchmarks/stress.mjs +647 -647
  16. package/benchmarks/templates/large.dtpl +7 -7
  17. package/benchmarks/templates/medium.dtpl +3 -3
  18. package/benchmarks/templates/small.dtpl +7 -7
  19. package/context/component.md +109 -109
  20. package/context/prd.md +131 -131
  21. package/context/project-structure.md +33 -33
  22. package/dir/base.html +22 -22
  23. package/dir/cmpnt.html +10 -10
  24. package/dir/footer.html +2 -2
  25. package/dir/home.html +80 -80
  26. package/dir/index.html +80 -0
  27. package/dir/navbar.html +8 -8
  28. package/docs/README.md +18 -18
  29. package/docs/advanced_usage.md +71 -71
  30. package/docs/api.md +119 -119
  31. package/docs/filters.md +708 -708
  32. package/docs/installation.md +106 -106
  33. package/docs/overview.md +79 -57
  34. package/docs/partialdef.md +70 -70
  35. package/docs/security.md +27 -27
  36. package/docs/tags.md +673 -673
  37. package/docs/usage.md +646 -646
  38. package/eslint.config.mjs +42 -42
  39. package/ex.mjs +34 -32
  40. package/live-test/package-lock.json +915 -0
  41. package/live-test/package.json +9 -0
  42. package/live-test/server.js +14 -0
  43. package/live-test/views/base.html +8 -0
  44. package/live-test/views/child.html +7 -0
  45. package/live-test/views/index.html +1 -0
  46. package/miki-template-extension/.github/workflows/ci.yml +116 -116
  47. package/miki-template-extension/.vscodeignore +7 -7
  48. package/miki-template-extension/CHANGELOG.md +99 -99
  49. package/miki-template-extension/LICENSE +21 -21
  50. package/miki-template-extension/README.md +273 -273
  51. package/miki-template-extension/extension.js +1013 -1013
  52. package/miki-template-extension/icon.svg +10 -10
  53. package/miki-template-extension/package.json +280 -280
  54. package/miki-template-extension/snippets/miki-template.json +717 -717
  55. package/miki-template-extension/syntaxes/language-configuration.json +114 -114
  56. package/miki-template-extension/syntaxes/miki-template.tmLanguage.json +355 -355
  57. package/miki-template-extension/tests/grammar-tests.json +162 -162
  58. package/miki-template-extension/tests/run-grammar-tests.js +82 -82
  59. package/package.json +40 -34
  60. package/sample-app/package-lock.json +901 -0
  61. package/sample-app/package.json +9 -0
  62. package/sample-app/server.js +14 -0
  63. package/sample-app/views/index.html +1 -0
  64. package/scripts/build-vsix.js +129 -129
  65. package/scripts/build-vsix.ps1 +15 -15
  66. package/snippets/miki-template.json +177 -177
  67. package/src/asyncRender.js +20 -20
  68. package/src/cache.js +80 -80
  69. package/src/context.js +126 -126
  70. package/src/context_processors.js +48 -48
  71. package/src/esm.mjs +89 -84
  72. package/src/filters.js +975 -975
  73. package/src/i18n.js +171 -171
  74. package/src/index.js +1112 -940
  75. package/src/lexer.js +114 -114
  76. package/src/libraries.js +371 -371
  77. package/src/parser.js +270 -270
  78. package/src/security.js +53 -53
  79. package/src/tags/control.js +719 -719
  80. package/src/tags/extra.js +154 -154
  81. package/src/tags/helpers.js +26 -26
  82. package/src/tags/i18n.js +256 -256
  83. package/src/tags/inheritance.js +335 -335
  84. package/src/tags/registry.js +18 -18
  85. package/src/tags/util.js +400 -400
  86. package/src/types.d.ts +107 -107
  87. package/syntaxes/language-configuration.json +26 -26
  88. package/syntaxes/miki-template.tmLanguage.json +146 -146
  89. package/tests/asyncRender.test.js +17 -17
  90. package/tests/base.html +6 -6
  91. package/tests/child.html +3 -3
  92. package/tests/context_processors.test.js +13 -13
  93. package/tests/esm.test.mjs +61 -61
  94. package/tests/filters.test.js +254 -254
  95. package/tests/finder-appdirs.test.js +19 -0
  96. package/tests/finder.test.js +17 -0
  97. package/tests/fixtures/views/nested/index.html +1 -0
  98. package/tests/fixtures/views/partial.html +1 -0
  99. package/tests/fixtures/views/sub/deepfile.html +1 -0
  100. package/tests/fixtures/views-appdirs/product/site/detail.html +1 -0
  101. package/tests/include_security.test.js +9 -9
  102. package/tests/integration/README.md +32 -32
  103. package/tests/integration/features.test.cjs +1681 -1681
  104. package/tests/integration/features.test.mjs +1697 -1697
  105. package/tests/integration/finder.esm.test.mjs +13 -0
  106. package/tests/integration/templates/base.miki +6 -6
  107. package/tests/integration/templates/child.miki +6 -6
  108. package/tests/integration/templates/index.html +17 -17
  109. package/tests/lexer.test.js +45 -45
  110. package/tests/parser.test.js +57 -57
  111. package/tests/partial.html +1 -1
  112. package/tests/partialdef.test.js +79 -79
  113. package/tests/production_checks.js +57 -57
  114. package/tests/security.test.js +28 -28
  115. package/tests/tags.test.js +233 -233
package/API_REFERENCE.md CHANGED
@@ -1,314 +1,314 @@
1
- # API Reference - miki-template
2
-
3
- This document provides complete details for the public APIs exported by the **miki-template** engine — a Django-style template engine for Node.js and Express.
4
-
5
- ---
6
-
7
- ## Core Exports
8
-
9
- ```javascript
10
- const {
11
- compile,
12
- render,
13
- asyncRender,
14
- __express,
15
- clearCache,
16
- registerTag,
17
- registerFilter,
18
- registerHelper,
19
- registerContextProcessor,
20
- SafeString,
21
- markSafe,
22
- isSafe,
23
- escapeHtml
24
- } = require('miki-template');
25
- ```
26
-
27
- ---
28
-
29
- ### `compile(templateStr, options)`
30
-
31
- Compiles a raw template string into a reusable compiled template object. The compiled template caches its AST internally.
32
-
33
- **Parameters**:
34
- - `templateStr` (string): The raw template string to compile.
35
- - `options` (object): Config parameters:
36
- - `views` (string|string[]): Directories to search for templates when using `extends` or `include`.
37
- - `staticUrl` (string): Prefix for the `{% static %}` tag. Defaults to `/static/`.
38
- - `urlHelper` (function): Custom URL resolver for `{% url %}` tag.
39
-
40
- **Returns**: An object containing:
41
- - `render(context)` — Synchronously renders the template.
42
- - `asyncRender(context)` — Returns a Promise for async rendering.
43
- - `renderBlock(blockName, context)` — Renders a specific block by name.
44
- - `renderPartial(partialName, context)` — Renders a defined partial by name.
45
-
46
- **Example**:
47
- ```javascript
48
- const { compile } = require('miki-template');
49
-
50
- const template = compile('Hello {{ name }}!');
51
- const output = template.render({ name: 'World' });
52
- console.log(output); // Hello World!
53
- ```
54
-
55
- ---
56
-
57
- ### `render(templateStr, context, options)`
58
-
59
- One-step convenience function that compiles and renders a template.
60
-
61
- **Parameters**:
62
- - `templateStr` (string): Raw template string.
63
- - `context` (object): Variables available to the template.
64
- - `options` (object): Same as `compile()` options.
65
-
66
- **Returns**: Rendered HTML string.
67
-
68
- ---
69
-
70
- ### `asyncRender(templateStr, context, options)`
71
-
72
- Asynchronous rendering for templates with async helpers or async context processors.
73
-
74
- **Parameters**: Same as `render()`.
75
-
76
- **Returns**: `Promise<string>` — resolved rendered HTML.
77
-
78
- **Example**:
79
- ```javascript
80
- const { asyncRender } = require('miki-template');
81
- const html = await asyncRender('Hello {{ name }}!', { name: 'Async' });
82
- ```
83
-
84
- ---
85
-
86
- ### `__express(filePath, options, callback)`
87
-
88
- Express-compatible view engine adapter. Use with `app.engine()`.
89
-
90
- **Parameters**:
91
- - `filePath` (string): Absolute path to the template file.
92
- - `options` (object): Express `res.render()` context (view engine strips `_locals`, `settings`, and other Express internals).
93
- - `callback` (function): Node.js callback `(err, html)`.
94
-
95
- **Example**:
96
- ```javascript
97
- const express = require('express');
98
- const { __express } = require('miki-template');
99
-
100
- const app = express();
101
- app.engine('html', __express);
102
- app.set('view engine', 'html');
103
- app.set('views', './views');
104
-
105
- app.get('/', (req, res) => {
106
- res.render('home', { title: 'Home Page' });
107
- });
108
- ```
109
-
110
- ---
111
-
112
- ### `registerTag(name, parserFn)`
113
-
114
- Registers a custom block tag parser. Must be called **before** compiling templates that use the tag.
115
-
116
- **Parameters**:
117
- - `name` (string): Tag identifier word (e.g. `mytag` for `{% mytag %}`).
118
- - `parserFn` (function): `(tagContent: string, parser: Parser) => ASTNode`. Receives the raw tag content and the parser instance.
119
-
120
- **Returns**: `undefined`.
121
-
122
- **Example**:
123
- ```javascript
124
- const { registerTag } = require('miki-template');
125
-
126
- registerTag('greet', (tagContent, parser) => {
127
- const name = tagContent.slice(5).trim(); // strip 'greet'
128
- return {
129
- render: (context) => `Hello ${context.get(name) || 'Guest'}!`
130
- };
131
- });
132
-
133
- // In template: {% greet user.name %}
134
- ```
135
-
136
- ---
137
-
138
- ### `registerFilter(name, filterFn)`
139
-
140
- Registers a custom filter function.
141
-
142
- **Parameters**:
143
- - `name` (string): Filter name used after the pipe `|`.
144
- - `filterFn` (function): `(value: any, arg?: any) => any`. Receives the filtered value and optional argument.
145
-
146
- **Returns**: `undefined`.
147
-
148
- **Example**:
149
- ```javascript
150
- const { registerFilter } = require('miki-template');
151
-
152
- registerFilter('reverse', (val) => {
153
- return String(val).split('').reverse().join('');
154
- });
155
-
156
- // In template: {{ name|reverse }}
157
- ```
158
-
159
- ---
160
-
161
- ### `registerHelper(name, fn)`
162
-
163
- Registers a custom block helper tag. The helper receives the **rendered** inner content as a string.
164
-
165
- **Parameters**:
166
- - `name` (string): Tag name (e.g. `markdown` creates `{% markdown %}...{% endmarkdown %}`).
167
- - `fn` (function): `(innerContent: string, context: Context) => string | Promise<string>`.
168
-
169
- **Returns**: `undefined`.
170
-
171
- **Example**:
172
- ```javascript
173
- const { registerHelper } = require('miki-template');
174
- const markdownIt = require('markdown-it')();
175
- registerHelper('markdown', (content) => markdownIt.render(content));
176
-
177
- // In template:
178
- // {% markdown %}
179
- // # Hello
180
- // {% endmarkdown %}
181
- ```
182
-
183
- ---
184
-
185
- ### `registerContextProcessor(fn)`
186
-
187
- Adds a context processor function. Similar to Django's custom context processors.
188
-
189
- **Parameters**:
190
- - `fn` (function): `(context: object) => object | undefined`. Receives the render context and returns extra key-value pairs to merge in.
191
-
192
- **Returns**: `undefined`.
193
-
194
- **Example**:
195
- ```javascript
196
- const { registerContextProcessor } = require('miki-template');
197
-
198
- registerContextProcessor((ctx) => ({
199
- siteName: 'MySite',
200
- currentYear: new Date().getFullYear()
201
- }));
202
-
203
- // Available in all templates as {{ siteName }} and {{ currentYear }}
204
- ```
205
-
206
- ---
207
-
208
- ### `SafeString`
209
-
210
- Class for values that should **bypass HTML auto-escaping**. Construct directly or use `markSafe()`.
211
-
212
- **Example**:
213
- ```javascript
214
- const { SafeString } = require('miki-template');
215
-
216
- const html = new SafeString('<b>Bold</b>');
217
- // {{ html }} renders as <b>Bold</b>, NOT &lt;b&gt;Bold&lt;/b&gt;
218
- ```
219
-
220
- ---
221
-
222
- ### `markSafe(value)`
223
-
224
- Wraps any value in a `SafeString`, instructing the engine to skip HTML escaping for that value.
225
-
226
- **Parameters**:
227
- - `value` (any): Value to mark as safe.
228
-
229
- **Returns**: `SafeString`.
230
-
231
- **Example**:
232
- ```javascript
233
- const { markSafe } = require('miki-template');
234
-
235
- const html = markSafe('<script>alert("xss")</script>');
236
- // {{ html }} outputs the script tag literally (use with caution)
237
- ```
238
-
239
- ---
240
-
241
- ### `isSafe(value)`
242
-
243
- Checks whether a value is a `SafeString` instance.
244
-
245
- **Parameters**:
246
- - `value` (any): Value to check.
247
-
248
- **Returns**: `boolean`.
249
-
250
- ---
251
-
252
- ### `escapeHtml(str)`
253
-
254
- Programmatically escapes HTML special characters (`<`, `>`, `&`, `"`, `'`).
255
-
256
- **Parameters**:
257
- - `str` (string): String to escape.
258
-
259
- **Returns**: `string`.
260
-
261
- ---
262
-
263
- ### `clearCache()`
264
-
265
- Clears the in-memory AST cache. Useful for development or when templates change at runtime.
266
-
267
- **Example**:
268
- ```javascript
269
- const { clearCache } = require('miki-template');
270
-
271
- app.on('restart', () => clearCache());
272
- ```
273
-
274
- ---
275
-
276
- ## Compiled Template API
277
-
278
- `compile()` returns an object with these methods:
279
-
280
- ### `compiled.render(context)`
281
-
282
- Synchronously renders the template with the given context.
283
-
284
- ```javascript
285
- const compiled = compile('Hello {{ name }}!');
286
- compiled.render({ name: 'World' }); // "Hello World!"
287
- ```
288
-
289
- ### `compiled.asyncRender(context)`
290
-
291
- Renders asynchronously, awaiting any Promise-returning helpers.
292
-
293
- ```javascript
294
- const html = await compiled.asyncRender({ name: 'Async' });
295
- ```
296
-
297
- ### `compiled.renderBlock(blockName, context)`
298
-
299
- Renders **only** the named block. Useful for HTMX or AJAX partial responses.
300
-
301
- ```javascript
302
- // Template: {% extends "base.html" %}
303
- // {% block content %}Main{% endblock %}
304
- compiled.renderBlock('content', {}); // "Main"
305
- ```
306
-
307
- ### `compiled.renderPartial(partialName, context)`
308
-
309
- Renders a `{% partialdef %}` block by name.
310
-
311
- ```javascript
312
- // Template: {% partialdef header %}My Header{% endpartialdef %}
313
- compiled.renderPartial('header', {}); // "My Header"
314
- ```
1
+ # API Reference - miki-template
2
+
3
+ This document provides complete details for the public APIs exported by the **miki-template** engine — a Django-style template engine for Node.js and Express.
4
+
5
+ ---
6
+
7
+ ## Core Exports
8
+
9
+ ```javascript
10
+ const {
11
+ compile,
12
+ render,
13
+ asyncRender,
14
+ __express,
15
+ clearCache,
16
+ registerTag,
17
+ registerFilter,
18
+ registerHelper,
19
+ registerContextProcessor,
20
+ SafeString,
21
+ markSafe,
22
+ isSafe,
23
+ escapeHtml
24
+ } = require('miki-template');
25
+ ```
26
+
27
+ ---
28
+
29
+ ### `compile(templateStr, options)`
30
+
31
+ Compiles a raw template string into a reusable compiled template object. The compiled template caches its AST internally.
32
+
33
+ **Parameters**:
34
+ - `templateStr` (string): The raw template string to compile.
35
+ - `options` (object): Config parameters:
36
+ - `views` (string|string[]): Directories to search for templates when using `extends` or `include`.
37
+ - `staticUrl` (string): Prefix for the `{% static %}` tag. Defaults to `/static/`.
38
+ - `urlHelper` (function): Custom URL resolver for `{% url %}` tag.
39
+
40
+ **Returns**: An object containing:
41
+ - `render(context)` — Synchronously renders the template.
42
+ - `asyncRender(context)` — Returns a Promise for async rendering.
43
+ - `renderBlock(blockName, context)` — Renders a specific block by name.
44
+ - `renderPartial(partialName, context)` — Renders a defined partial by name.
45
+
46
+ **Example**:
47
+ ```javascript
48
+ const { compile } = require('miki-template');
49
+
50
+ const template = compile('Hello {{ name }}!');
51
+ const output = template.render({ name: 'World' });
52
+ console.log(output); // Hello World!
53
+ ```
54
+
55
+ ---
56
+
57
+ ### `render(templateStr, context, options)`
58
+
59
+ One-step convenience function that compiles and renders a template.
60
+
61
+ **Parameters**:
62
+ - `templateStr` (string): Raw template string.
63
+ - `context` (object): Variables available to the template.
64
+ - `options` (object): Same as `compile()` options.
65
+
66
+ **Returns**: Rendered HTML string.
67
+
68
+ ---
69
+
70
+ ### `asyncRender(templateStr, context, options)`
71
+
72
+ Asynchronous rendering for templates with async helpers or async context processors.
73
+
74
+ **Parameters**: Same as `render()`.
75
+
76
+ **Returns**: `Promise<string>` — resolved rendered HTML.
77
+
78
+ **Example**:
79
+ ```javascript
80
+ const { asyncRender } = require('miki-template');
81
+ const html = await asyncRender('Hello {{ name }}!', { name: 'Async' });
82
+ ```
83
+
84
+ ---
85
+
86
+ ### `__express(filePath, options, callback)`
87
+
88
+ Express-compatible view engine adapter. Use with `app.engine()`.
89
+
90
+ **Parameters**:
91
+ - `filePath` (string): Absolute path to the template file.
92
+ - `options` (object): Express `res.render()` context (view engine strips `_locals`, `settings`, and other Express internals).
93
+ - `callback` (function): Node.js callback `(err, html)`.
94
+
95
+ **Example**:
96
+ ```javascript
97
+ const express = require('express');
98
+ const { __express } = require('miki-template');
99
+
100
+ const app = express();
101
+ app.engine('html', __express);
102
+ app.set('view engine', 'html');
103
+ app.set('views', './views');
104
+
105
+ app.get('/', (req, res) => {
106
+ res.render('home', { title: 'Home Page' });
107
+ });
108
+ ```
109
+
110
+ ---
111
+
112
+ ### `registerTag(name, parserFn)`
113
+
114
+ Registers a custom block tag parser. Must be called **before** compiling templates that use the tag.
115
+
116
+ **Parameters**:
117
+ - `name` (string): Tag identifier word (e.g. `mytag` for `{% mytag %}`).
118
+ - `parserFn` (function): `(tagContent: string, parser: Parser) => ASTNode`. Receives the raw tag content and the parser instance.
119
+
120
+ **Returns**: `undefined`.
121
+
122
+ **Example**:
123
+ ```javascript
124
+ const { registerTag } = require('miki-template');
125
+
126
+ registerTag('greet', (tagContent, parser) => {
127
+ const name = tagContent.slice(5).trim(); // strip 'greet'
128
+ return {
129
+ render: (context) => `Hello ${context.get(name) || 'Guest'}!`
130
+ };
131
+ });
132
+
133
+ // In template: {% greet user.name %}
134
+ ```
135
+
136
+ ---
137
+
138
+ ### `registerFilter(name, filterFn)`
139
+
140
+ Registers a custom filter function.
141
+
142
+ **Parameters**:
143
+ - `name` (string): Filter name used after the pipe `|`.
144
+ - `filterFn` (function): `(value: any, arg?: any) => any`. Receives the filtered value and optional argument.
145
+
146
+ **Returns**: `undefined`.
147
+
148
+ **Example**:
149
+ ```javascript
150
+ const { registerFilter } = require('miki-template');
151
+
152
+ registerFilter('reverse', (val) => {
153
+ return String(val).split('').reverse().join('');
154
+ });
155
+
156
+ // In template: {{ name|reverse }}
157
+ ```
158
+
159
+ ---
160
+
161
+ ### `registerHelper(name, fn)`
162
+
163
+ Registers a custom block helper tag. The helper receives the **rendered** inner content as a string.
164
+
165
+ **Parameters**:
166
+ - `name` (string): Tag name (e.g. `markdown` creates `{% markdown %}...{% endmarkdown %}`).
167
+ - `fn` (function): `(innerContent: string, context: Context) => string | Promise<string>`.
168
+
169
+ **Returns**: `undefined`.
170
+
171
+ **Example**:
172
+ ```javascript
173
+ const { registerHelper } = require('miki-template');
174
+ const markdownIt = require('markdown-it')();
175
+ registerHelper('markdown', (content) => markdownIt.render(content));
176
+
177
+ // In template:
178
+ // {% markdown %}
179
+ // # Hello
180
+ // {% endmarkdown %}
181
+ ```
182
+
183
+ ---
184
+
185
+ ### `registerContextProcessor(fn)`
186
+
187
+ Adds a context processor function. Similar to Django's custom context processors.
188
+
189
+ **Parameters**:
190
+ - `fn` (function): `(context: object) => object | undefined`. Receives the render context and returns extra key-value pairs to merge in.
191
+
192
+ **Returns**: `undefined`.
193
+
194
+ **Example**:
195
+ ```javascript
196
+ const { registerContextProcessor } = require('miki-template');
197
+
198
+ registerContextProcessor((ctx) => ({
199
+ siteName: 'MySite',
200
+ currentYear: new Date().getFullYear()
201
+ }));
202
+
203
+ // Available in all templates as {{ siteName }} and {{ currentYear }}
204
+ ```
205
+
206
+ ---
207
+
208
+ ### `SafeString`
209
+
210
+ Class for values that should **bypass HTML auto-escaping**. Construct directly or use `markSafe()`.
211
+
212
+ **Example**:
213
+ ```javascript
214
+ const { SafeString } = require('miki-template');
215
+
216
+ const html = new SafeString('<b>Bold</b>');
217
+ // {{ html }} renders as <b>Bold</b>, NOT &lt;b&gt;Bold&lt;/b&gt;
218
+ ```
219
+
220
+ ---
221
+
222
+ ### `markSafe(value)`
223
+
224
+ Wraps any value in a `SafeString`, instructing the engine to skip HTML escaping for that value.
225
+
226
+ **Parameters**:
227
+ - `value` (any): Value to mark as safe.
228
+
229
+ **Returns**: `SafeString`.
230
+
231
+ **Example**:
232
+ ```javascript
233
+ const { markSafe } = require('miki-template');
234
+
235
+ const html = markSafe('<script>alert("xss")</script>');
236
+ // {{ html }} outputs the script tag literally (use with caution)
237
+ ```
238
+
239
+ ---
240
+
241
+ ### `isSafe(value)`
242
+
243
+ Checks whether a value is a `SafeString` instance.
244
+
245
+ **Parameters**:
246
+ - `value` (any): Value to check.
247
+
248
+ **Returns**: `boolean`.
249
+
250
+ ---
251
+
252
+ ### `escapeHtml(str)`
253
+
254
+ Programmatically escapes HTML special characters (`<`, `>`, `&`, `"`, `'`).
255
+
256
+ **Parameters**:
257
+ - `str` (string): String to escape.
258
+
259
+ **Returns**: `string`.
260
+
261
+ ---
262
+
263
+ ### `clearCache()`
264
+
265
+ Clears the in-memory AST cache. Useful for development or when templates change at runtime.
266
+
267
+ **Example**:
268
+ ```javascript
269
+ const { clearCache } = require('miki-template');
270
+
271
+ app.on('restart', () => clearCache());
272
+ ```
273
+
274
+ ---
275
+
276
+ ## Compiled Template API
277
+
278
+ `compile()` returns an object with these methods:
279
+
280
+ ### `compiled.render(context)`
281
+
282
+ Synchronously renders the template with the given context.
283
+
284
+ ```javascript
285
+ const compiled = compile('Hello {{ name }}!');
286
+ compiled.render({ name: 'World' }); // "Hello World!"
287
+ ```
288
+
289
+ ### `compiled.asyncRender(context)`
290
+
291
+ Renders asynchronously, awaiting any Promise-returning helpers.
292
+
293
+ ```javascript
294
+ const html = await compiled.asyncRender({ name: 'Async' });
295
+ ```
296
+
297
+ ### `compiled.renderBlock(blockName, context)`
298
+
299
+ Renders **only** the named block. Useful for HTMX or AJAX partial responses.
300
+
301
+ ```javascript
302
+ // Template: {% extends "base.html" %}
303
+ // {% block content %}Main{% endblock %}
304
+ compiled.renderBlock('content', {}); // "Main"
305
+ ```
306
+
307
+ ### `compiled.renderPartial(partialName, context)`
308
+
309
+ Renders a `{% partialdef %}` block by name.
310
+
311
+ ```javascript
312
+ // Template: {% partialdef header %}My Header{% endpartialdef %}
313
+ compiled.renderPartial('header', {}); // "My Header"
314
+ ```