miki-template 2.2.2 → 2.3.0

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 (66) hide show
  1. package/.github/workflows/docs.yml +3 -1
  2. package/.github/workflows/release.yml +1 -0
  3. package/benchmarks/ejs-results.json +6 -6
  4. package/benchmarks/ejs.js +5 -3
  5. package/benchmarks/handlebars-results.json +6 -6
  6. package/benchmarks/handlebars.js +5 -8
  7. package/benchmarks/miki-results.json +6 -6
  8. package/benchmarks/miki.js +6 -3
  9. package/benchmarks/pug-results.json +6 -6
  10. package/benchmarks/pug.js +5 -3
  11. package/docs/api/async-render.md +88 -3
  12. package/docs/api/cache.md +90 -3
  13. package/docs/api/compile.md +131 -3
  14. package/docs/api/context-processors.md +80 -3
  15. package/docs/api/filters.md +223 -3
  16. package/docs/api/finder.md +97 -3
  17. package/docs/api/helpers.md +56 -3
  18. package/docs/api/i18n.md +160 -3
  19. package/docs/api/index.md +82 -28
  20. package/docs/api/libraries.md +210 -3
  21. package/docs/api/render-partial.md +84 -3
  22. package/docs/api/render.md +95 -3
  23. package/docs/api/security.md +148 -3
  24. package/docs/api/setup-express.md +78 -2
  25. package/docs/api/tags.md +138 -4
  26. package/docs/filter.md +0 -0
  27. package/docs/guide/advanced-usage.md +403 -6
  28. package/docs/guide/async-rendering.md +312 -4
  29. package/docs/guide/context-processors.md +261 -4
  30. package/docs/guide/custom-filters.md +315 -4
  31. package/docs/guide/custom-tags.md +275 -4
  32. package/docs/guide/filters.md +675 -3
  33. package/docs/guide/getting-started.md +109 -7
  34. package/docs/guide/installation.md +99 -4
  35. package/docs/guide/partial-templates.md +371 -4
  36. package/docs/guide/quick-start.md +228 -6
  37. package/docs/guide/security.md +348 -3
  38. package/docs/guide/tags.md +789 -6
  39. package/docs/guide/template-discovery.md +174 -4
  40. package/docs/guide/template-inheritance.md +277 -4
  41. package/docs/index.md +24 -42
  42. package/docs/integrations/elysia.md +4 -2
  43. package/docs/integrations/express.md +219 -219
  44. package/docs/integrations/fastify.md +4 -2
  45. package/docs/integrations/hono.md +4 -2
  46. package/docs/integrations/index.md +68 -68
  47. package/docs/integrations/koa.md +4 -2
  48. package/docs/integrations/nestjs.md +4 -2
  49. package/docs/integrations/tsed.md +4 -2
  50. package/docs/performance.md +45 -8
  51. package/ex.mjs +1 -1
  52. package/mkdocs.yml +0 -22
  53. package/overrides/main.html +1 -1
  54. package/package.json +1 -1
  55. package/requirements-docs.txt +2 -1
  56. package/src/codegen.js +905 -0
  57. package/src/context.js +42 -30
  58. package/src/filters.js +16 -0
  59. package/src/index.js +66 -61
  60. package/src/tags/control.js +15 -12
  61. package/src/utils.js +60 -0
  62. package/tests/filters.test.js +9 -0
  63. package/.github/workflows/npm-publish-github-packages.yml +0 -36
  64. package/docs/javascripts/extra.js +0 -174
  65. package/docs/stylesheets/extra.css +0 -819
  66. package/overrides/partials/footer.html +0 -9
@@ -1,367 +1,734 @@
1
- # Partial Templates
1
+ # Partial Templates
2
+
3
+
2
4
 
3
5
  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
6
 
7
+
8
+
5
9
  ## Table of Contents
6
10
 
11
+
12
+
7
13
  - [Defining Partials](#defining-partials)
14
+
8
15
  - [Rendering Partials by Name](#rendering-partials-by-name)
16
+
9
17
  - [Nested Partials](#nested-partials)
18
+
10
19
  - [Partials with Context](#partials-with-context)
20
+
11
21
  - [Partials with Include](#partials-with-include)
22
+
12
23
  - [Rendering Partials Programmatically](#rendering-partials-programmatically)
24
+
13
25
  - [Express Partial Rendering](#express-partial-rendering)
26
+
14
27
  - [Common Pitfalls](#common-pitfalls)
15
28
 
29
+
30
+
16
31
  ---
17
32
 
33
+
34
+
18
35
  ## Defining Partials
19
36
 
37
+
38
+
20
39
  Use `{% partialdef %}` to define a named partial inside any template:
21
40
 
41
+
42
+
22
43
  ```html
44
+
23
45
  <!-- views/home.html -->
46
+
24
47
  {% partialdef card %}
48
+
25
49
  <div class="card">
50
+
26
51
  <h3>{{ title|default:"Untitled" }}</h3>
52
+
27
53
  <p>{{ body|truncatewords:30 }}</p>
54
+
28
55
  {% if featured %}<em>Featured</em>{% endif %}
56
+
29
57
  </div>
58
+
30
59
  {% endpartialdef %}
31
60
 
61
+
62
+
32
63
  {% for entry in entries %}
64
+
33
65
  {% partial card with title=entry.title body=entry.body featured=entry.featured %}
66
+
34
67
  {% endfor %}
68
+
35
69
  ```
36
70
 
71
+
72
+
37
73
  ### Inline Partials
38
74
 
75
+
76
+
39
77
  A `{% partialdef %}` block renders its body inline where it is defined **and** registers itself for later use:
40
78
 
79
+
80
+
41
81
  ```html
82
+
42
83
  {% partialdef notice %}
84
+
43
85
  <div class="alert">{{ message }}</div>
86
+
44
87
  {% endpartialdef %}
88
+
45
89
  ```
46
90
 
91
+
92
+
47
93
  This means you get immediate output and a reusable partial in one declaration.
48
94
 
95
+
96
+
49
97
  Use the `inline` option explicitly:
50
98
 
99
+
100
+
51
101
  ```html
102
+
52
103
  {% partialdef greeting inline %}
104
+
53
105
  Hello {{ name }}!
106
+
54
107
  {% endpartialdef %}
108
+
55
109
  <!-- Above line ALSO outputs "Hello World!" when rendered -->
110
+
56
111
  ```
57
112
 
113
+
114
+
58
115
  ## Rendering Partials by Name
59
116
 
117
+
118
+
60
119
  Once defined, you can render a partial by name from your routes using the `view#partial` syntax:
61
120
 
121
+
122
+
62
123
  === "CommonJS"
63
124
 
125
+
126
+
64
127
  ```javascript
128
+
65
129
  const express = require('express');
130
+
66
131
  const miki = require('miki-template');
67
132
 
133
+
134
+
68
135
  const app = express();
136
+
69
137
  miki.setupExpress(app, { extension: 'html', views: './views' });
70
138
 
139
+
140
+
71
141
  app.get('/card/:id', (req, res) =>
142
+
72
143
  res.render(`home#card`, { title: 'Hello', body: 'World...', featured: true })
144
+
73
145
  );
146
+
74
147
  ```
75
148
 
149
+
150
+
76
151
  === "ES Modules"
77
152
 
153
+
154
+
78
155
  ```javascript
156
+
79
157
  import express from 'express';
158
+
80
159
  import miki from 'miki-template';
81
160
 
161
+
162
+
82
163
  const app = express();
164
+
83
165
  miki.setupExpress(app, { extension: 'html', views: './views' });
84
166
 
167
+
168
+
85
169
  app.get('/card/:id', (req, res) =>
170
+
86
171
  res.render(`home#card`, { title: 'Hello', body: 'World...', featured: true })
172
+
87
173
  );
174
+
88
175
  ```
89
176
 
177
+
178
+
90
179
  The syntax is `viewName#partialName`. The engine resolves the file, extracts the named partial, and renders only that block.
91
180
 
181
+
182
+
92
183
  **Real-world HTMX example:**
93
184
 
185
+
186
+
94
187
  ```html
188
+
95
189
  <!-- views/products.html -->
190
+
96
191
  {% partialdef product_card %}
192
+
97
193
  <div class="product-card" id="product-{{ product.id }}">
194
+
98
195
  <img src="{{ product.image|static }}" alt="{{ product.name }}">
196
+
99
197
  <h3>{{ product.name|capfirst }}</h3>
198
+
100
199
  <p class="price">${{ product.price|floatformat:2 }}</p>
200
+
101
201
  <button hx-post="/cart/add/{{ product.id }}" hx-swap="outerHTML">
202
+
102
203
  Add to Cart
204
+
103
205
  </button>
206
+
104
207
  </div>
208
+
105
209
  {% endpartialdef %}
106
210
 
211
+
212
+
107
213
  {% for product in products %}
214
+
108
215
  {% partial product_card with product=product %}
216
+
109
217
  {% endfor %}
218
+
110
219
  ```
111
220
 
221
+
222
+
112
223
  ```javascript
224
+
113
225
  // The entire page renders all cards
226
+
114
227
  app.get('/shop', (req, res) =>
228
+
115
229
  res.render('products', { products: catalog })
230
+
116
231
  );
117
232
 
233
+
234
+
118
235
  // HTMX swaps just one card after an action
236
+
119
237
  app.post('/cart/add/:id', (req, res) =>
238
+
120
239
  res.render('products#product_card', {
240
+
121
241
  product: catalog.find(p => p.id == req.params.id)
242
+
122
243
  })
244
+
123
245
  );
246
+
124
247
  ```
125
248
 
249
+
250
+
126
251
  ## Nested Partials
127
252
 
253
+
254
+
128
255
  Partials can call other partials:
129
256
 
257
+
258
+
130
259
  ```html
260
+
131
261
  {% partialdef header %}
262
+
132
263
  <div class="card-header">
264
+
133
265
  <h3>{{ title }}</h3>
266
+
134
267
  </div>
268
+
135
269
  {% endpartialdef %}
136
270
 
271
+
272
+
137
273
  {% partialdef card %}
274
+
138
275
  <div class="card">
276
+
139
277
  {% partial header with title=title %}
278
+
140
279
  <p>{{ body }}</p>
280
+
141
281
  </div>
282
+
142
283
  {% endpartialdef %}
284
+
143
285
  ```
144
286
 
287
+
288
+
145
289
  ## Partials with Context
146
290
 
291
+
292
+
147
293
  By default, partials inherit the parent context. Use `with` to pass explicit values:
148
294
 
295
+
296
+
149
297
  ```html
298
+
150
299
  {% partial card with title="Hello" body="World" %}
300
+
151
301
  ```
152
302
 
303
+
304
+
153
305
  You can also pass context variables:
154
306
 
307
+
308
+
155
309
  ```html
310
+
156
311
  {% partial card with title=entry.title body=entry.body %}
312
+
157
313
  ```
158
314
 
315
+
316
+
159
317
  ## Partials with Include
160
318
 
319
+
320
+
161
321
  You can include a partial from another template file using the `#partialName` syntax:
162
322
 
323
+
324
+
163
325
  ```html
326
+
164
327
  {% include "header.html#partial_name" %}
328
+
165
329
  ```
166
330
 
331
+
332
+
167
333
  This loads `header.html`, registers all its partials, and renders only the named one.
168
334
 
335
+
336
+
169
337
  **Real-world navigation include:**
170
338
 
339
+
340
+
171
341
  ```html
342
+
172
343
  <!-- views/nav.html -->
344
+
173
345
  {% partialdef navigation %}
346
+
174
347
  <nav>
348
+
175
349
  {% for link in links %}
350
+
176
351
  <a href="{{ link.url }}" class="{% if link.active %}current{% endif %}">{{ link.label }}</a>
352
+
177
353
  {% endfor %}
354
+
178
355
  </nav>
356
+
179
357
  {% endpartialdef %}
358
+
180
359
  ```
181
360
 
361
+
362
+
182
363
  ```html
364
+
183
365
  <!-- In any template -->
366
+
184
367
  {% include "nav.html#navigation" with links=nav_links %}
368
+
185
369
  ```
186
370
 
371
+
372
+
187
373
  ## Rendering Partials Programmatically
188
374
 
375
+
376
+
189
377
  ### renderPartialFromSource
190
378
 
379
+
380
+
191
381
  Render a named partial from a template source string:
192
382
 
383
+
384
+
193
385
  === "CommonJS"
194
386
 
387
+
388
+
195
389
  ```javascript
390
+
196
391
  const { renderPartialFromSource } = require('miki-template');
197
392
 
393
+
394
+
198
395
  const source = `{% partialdef card %}<div>{{ title }}</div>{% endpartialdef %}`;
396
+
199
397
  const html = renderPartialFromSource(source, 'card', { title: 'Hello' });
398
+
200
399
  ```
201
400
 
401
+
402
+
202
403
  === "ES Modules"
203
404
 
405
+
406
+
204
407
  ```javascript
408
+
205
409
  import { renderPartialFromSource } from 'miki-template';
206
410
 
411
+
412
+
207
413
  const source = `{% partialdef card %}<div>{{ title }}</div>{% endpartialdef %}`;
414
+
208
415
  const html = renderPartialFromSource(source, 'card', { title: 'Hello' });
416
+
209
417
  ```
210
418
 
419
+
420
+
211
421
  ### renderPartialFromFile
212
422
 
423
+
424
+
213
425
  Render a named partial from a template file:
214
426
 
427
+
428
+
215
429
  === "CommonJS"
216
430
 
431
+
432
+
217
433
  ```javascript
434
+
218
435
  const { renderPartialFromFile } = require('miki-template');
219
436
 
437
+
438
+
220
439
  const html = renderPartialFromFile('home', 'card', { title: 'Hello' }, { views: './views' });
440
+
221
441
  ```
222
442
 
443
+
444
+
223
445
  === "ES Modules"
224
446
 
447
+
448
+
225
449
  ```javascript
450
+
226
451
  import { renderPartialFromFile } from 'miki-template';
227
452
 
453
+
454
+
228
455
  const html = renderPartialFromFile('home', 'card', { title: 'Hello' }, { views: './views' });
456
+
229
457
  ```
230
458
 
459
+
460
+
231
461
  ### compiled.renderPartial
232
462
 
463
+
464
+
233
465
  Render a partial from a compiled template:
234
466
 
467
+
468
+
235
469
  === "CommonJS"
236
470
 
471
+
472
+
237
473
  ```javascript
474
+
238
475
  const { compile } = require('miki-template');
239
476
 
477
+
478
+
240
479
  const compiled = compile('<h1>{{ title }}</h1>', { views: './templates' });
480
+
241
481
  const html = compiled.renderPartial('card', { title: 'Hello' });
482
+
242
483
  ```
243
484
 
485
+
486
+
244
487
  === "ES Modules"
245
488
 
489
+
490
+
246
491
  ```javascript
492
+
247
493
  import { compile } from 'miki-template';
248
494
 
495
+
496
+
249
497
  const compiled = compile('<h1>{{ title }}</h1>', { views: './templates' });
498
+
250
499
  const html = compiled.renderPartial('card', { title: 'Hello' });
500
+
251
501
  ```
252
502
 
503
+
504
+
253
505
  ### compiled.renderBlock
254
506
 
507
+
508
+
255
509
  Render a single block from a compiled template — useful for AJAX responses:
256
510
 
511
+
512
+
257
513
  === "CommonJS"
258
514
 
515
+
516
+
259
517
  ```javascript
518
+
260
519
  const { compile } = require('miki-template');
261
520
 
521
+
522
+
262
523
  const compiled = compile(childTemplate, { views: './templates' });
524
+
263
525
  const html = compiled.renderBlock('content', context);
526
+
264
527
  ```
265
528
 
529
+
530
+
266
531
  === "ES Modules"
267
532
 
533
+
534
+
268
535
  ```javascript
536
+
269
537
  import { compile } from 'miki-template';
270
538
 
539
+
540
+
271
541
  const compiled = compile(childTemplate, { views: './templates' });
542
+
272
543
  const html = compiled.renderBlock('content', context);
544
+
273
545
  ```
274
546
 
547
+
548
+
275
549
  ## Express Partial Rendering
276
550
 
551
+
552
+
277
553
  ### res.render with `#partial`
278
554
 
555
+
556
+
279
557
  When using `setupExpress()`, you can render partials directly:
280
558
 
559
+
560
+
281
561
  === "CommonJS"
282
562
 
563
+
564
+
283
565
  ```javascript
566
+
284
567
  app.get('/card/:id', (req, res) =>
568
+
285
569
  res.render(`home#card`, { title: 'Hello', body: '...' })
570
+
286
571
  );
572
+
287
573
  ```
288
574
 
575
+
576
+
289
577
  === "ES Modules"
290
578
 
579
+
580
+
291
581
  ```javascript
582
+
292
583
  app.get('/card/:id', (req, res) =>
584
+
293
585
  res.render(`home#card`, { title: 'Hello', body: '...' })
586
+
294
587
  );
588
+
295
589
  ```
296
590
 
591
+
592
+
297
593
  ### res.renderPartial middleware
298
594
 
595
+
596
+
299
597
  If you don't want to patch `res.render`, add the partial renderer middleware instead:
300
598
 
599
+
600
+
301
601
  === "CommonJS"
302
602
 
603
+
604
+
303
605
  ```javascript
606
+
304
607
  const express = require('express');
608
+
305
609
  const miki = require('miki-template');
306
610
 
611
+
612
+
307
613
  const app = express();
614
+
308
615
  app.use(miki.expressPartialRenderer());
309
616
 
617
+
618
+
310
619
  app.get('/card', (req, res) =>
620
+
311
621
  res.renderPartial('home#card', { user: req.user })
622
+
312
623
  );
624
+
313
625
  ```
314
626
 
627
+
628
+
315
629
  === "ES Modules"
316
630
 
631
+
632
+
317
633
  ```javascript
634
+
318
635
  import express from 'express';
636
+
319
637
  import miki from 'miki-template';
320
638
 
639
+
640
+
321
641
  const app = express();
642
+
322
643
  app.use(miki.expressPartialRenderer());
323
644
 
645
+
646
+
324
647
  app.get('/card', (req, res) =>
648
+
325
649
  res.renderPartial('home#card', { user: req.user })
650
+
326
651
  );
652
+
327
653
  ```
328
654
 
655
+
656
+
329
657
  ## Partial API Reference
330
658
 
659
+
660
+
331
661
  ### renderPartialFromSource(fileContent, partialName, contextObj, options, filePath?)
332
662
 
663
+
664
+
333
665
  Render a named partial from a template source string.
334
666
 
667
+
668
+
335
669
  | Parameter | Type | Description |
670
+
336
671
  |-----------|------|-------------|
672
+
337
673
  | `fileContent` | `string` | Template source string |
674
+
338
675
  | `partialName` | `string` | Name of the partial to render |
676
+
339
677
  | `contextObj` | `object` | Variables to inject |
678
+
340
679
  | `options` | `object` | Options |
680
+
341
681
  | `filePath` | `string?` | Optional file path for error messages |
342
682
 
683
+
684
+
343
685
  ### renderPartialFromFile(fileName, partialName, contextObj, options)
344
686
 
687
+
688
+
345
689
  Render a named partial from a template file.
346
690
 
691
+
692
+
347
693
  | Parameter | Type | Description |
694
+
348
695
  |-----------|------|-------------|
696
+
349
697
  | `fileName` | `string` | Template file name (without extension) |
698
+
350
699
  | `partialName` | `string` | Name of the partial to render |
700
+
351
701
  | `contextObj` | `object` | Variables to inject |
702
+
352
703
  | `options` | `object` | Options including `views` directories |
353
704
 
705
+
706
+
354
707
  ## Common Pitfalls
355
708
 
709
+
710
+
356
711
  | Issue | Symptom | Fix |
712
+
357
713
  |-------|---------|-----|
714
+
358
715
  | Missing partial name | `{% partial %}` renders nothing | Ensure the name matches a defined `partialdef`. |
716
+
359
717
  | Variable not found | Appears empty | Variables are resolved in the current context; use `with` to pass explicit values. |
718
+
360
719
  | Inline vs non-inline confusion | Duplicate output | Use `inline` only when you want immediate rendering at the declaration site. |
720
+
361
721
  | Partial leaks across includes | Unexpected partials available | `include "file#partial"` isolates partials; `include "file"` (full) makes all partials available. |
362
722
 
723
+
724
+
363
725
  ## Next Steps
364
726
 
365
- - [Template Inheritance](./template-inheritance)
366
- - [Tags: partialdef and partial](./tags#partial-tags)
367
- - [API Reference: renderPartial](../api/render-partial)
727
+
728
+
729
+ - [Template Inheritance](./template-inheritance.md)
730
+
731
+ - [Tags: partialdef and partial](./tags.md#partial-tags)
732
+
733
+ - [API Reference: renderPartial](../api/render-partial.md)
734
+