miki-template 2.2.3 → 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 (65) hide show
  1. package/.github/workflows/docs.yml +3 -1
  2. package/.github/workflows/release.yml +0 -5
  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/docs/javascripts/extra.js +0 -174
  64. package/docs/stylesheets/extra.css +0 -819
  65. package/overrides/partials/footer.html +0 -9
@@ -1,397 +1,794 @@
1
- # Advanced Usage
1
+ # Advanced Usage
2
+
3
+
2
4
 
3
5
  This guide covers advanced miki-template features: caching, library system, i18n, and more.
4
6
 
7
+
8
+
5
9
  ## Table of Contents
6
10
 
11
+
12
+
7
13
  - [Caching](#caching)
14
+
8
15
  - [Library System](#library-system)
16
+
9
17
  - [i18n / Internationalization](#i18n--internationalization)
18
+
10
19
  - [Template Discovery](#template-discovery)
20
+
11
21
  - [Partial Templates](#partial-templates)
22
+
12
23
  - [Extending the Engine](#extending-the-engine)
13
24
 
25
+
26
+
14
27
  ---
15
28
 
29
+
30
+
16
31
  ## Caching
17
32
 
33
+
34
+
18
35
  miki-template caches compiled templates for performance. The cache is an in-memory LRU with a 100-entry limit.
19
36
 
37
+
38
+
20
39
  ### Clearing the Cache
21
40
 
41
+
42
+
22
43
  === "CommonJS"
23
44
 
45
+
46
+
24
47
  ```javascript
48
+
25
49
  const { clearCache } = require('miki-template');
26
50
 
51
+
52
+
27
53
  clearCache();
54
+
28
55
  ```
29
56
 
57
+
58
+
30
59
  === "ES Modules"
31
60
 
61
+
62
+
32
63
  ```javascript
64
+
33
65
  import { clearCache } from 'miki-template';
34
66
 
67
+
68
+
35
69
  clearCache();
70
+
36
71
  ```
37
72
 
73
+
74
+
38
75
  ### When to Clear Cache
39
76
 
77
+
78
+
40
79
  - **Development** — when templates change frequently on disk
80
+
41
81
  - **Tests** — to ensure fresh compilation
82
+
42
83
  - **Runtime filter/tag registration** — when dynamically registering custom tags/filters
43
84
 
85
+
86
+
44
87
  ```javascript
88
+
45
89
  // Development middleware that clears cache on file changes
90
+
46
91
  const { clearCache } = require('miki-template');
47
92
 
93
+
94
+
48
95
  if (process.env.NODE_ENV !== 'production') {
96
+
49
97
  fs.watch('./views', () => {
98
+
50
99
  clearCache();
100
+
51
101
  console.log('Template cache cleared');
102
+
52
103
  });
104
+
53
105
  }
106
+
54
107
  ```
55
108
 
109
+
110
+
56
111
  ### How Caching Works
57
112
 
113
+
114
+
58
115
  - Templates are cached by source string and compile options.
116
+
59
117
  - The cache key combines the template source and the options object (views, custom settings).
118
+
60
119
  - Cached templates are reused across renders, improving performance for repeated templates.
61
120
 
121
+
122
+
62
123
  ## Library System
63
124
 
125
+
126
+
64
127
  Libraries are bundles of filters, tags, and helpers that can be loaded into templates. Built-in libraries (humanize, cache, lorem, markdown, i18n) are auto-activated.
65
128
 
129
+
130
+
66
131
  ### Registering a Library
67
132
 
133
+
134
+
68
135
  === "CommonJS"
69
136
 
137
+
138
+
70
139
  ```javascript
140
+
71
141
  const { registerLibrary } = require('miki-template');
72
142
 
143
+
144
+
73
145
  registerLibrary('myutils', {
146
+
74
147
  filters: {
148
+
75
149
  shout: (val) => String(val).toUpperCase() + '!',
150
+
76
151
  whisper: (val) => String(val).toLowerCase() + '...'
152
+
77
153
  },
154
+
78
155
  tags: {
156
+
79
157
  timestamp: (tagContent, parser) => ({
158
+
80
159
  render: () => new Date().toISOString()
160
+
81
161
  })
162
+
82
163
  },
164
+
83
165
  helpers: {
166
+
84
167
  formatPrice: (val) => `$${Number(val).toFixed(2)}`
168
+
85
169
  }
170
+
86
171
  });
172
+
87
173
  ```
88
174
 
175
+
176
+
89
177
  === "ES Modules"
90
178
 
179
+
180
+
91
181
  ```javascript
182
+
92
183
  import { registerLibrary } from 'miki-template';
93
184
 
185
+
186
+
94
187
  registerLibrary('myutils', {
188
+
95
189
  filters: {
190
+
96
191
  shout: (val) => String(val).toUpperCase() + '!',
192
+
97
193
  whisper: (val) => String(val).toLowerCase() + '...'
194
+
98
195
  },
196
+
99
197
  tags: {
198
+
100
199
  timestamp: (tagContent, parser) => ({
200
+
101
201
  render: () => new Date().toISOString()
202
+
102
203
  })
204
+
103
205
  },
206
+
104
207
  helpers: {
208
+
105
209
  formatPrice: (val) => `$${Number(val).toFixed(2)}`
210
+
106
211
  }
212
+
107
213
  });
214
+
108
215
  ```
109
216
 
217
+
218
+
110
219
  ### Loading Libraries in Templates
111
220
 
221
+
222
+
112
223
  Once registered, load the library with `{% load %}`:
113
224
 
225
+
226
+
114
227
  ```html
228
+
115
229
  {% load myutils %}
116
230
 
231
+
232
+
117
233
  {{ price|formatPrice }}
234
+
118
235
  {{ message|shout }}
236
+
119
237
  {% timestamp %}
238
+
120
239
  ```
121
240
 
241
+
242
+
122
243
  ### Built-in Libraries
123
244
 
245
+
246
+
124
247
  The following libraries are auto-activated (no `{% load %}` needed):
125
248
 
249
+
250
+
126
251
  | Library | Features |
252
+
127
253
  |---------|----------|
254
+
128
255
  | `humanize` | Natural date formatting, number formatting |
256
+
129
257
  | `cache` | Cache control tags and filters |
258
+
130
259
  | `lorem` | Lorem ipsum placeholder text |
260
+
131
261
  | `markdown` | `{{ content|markdown }}` filter for Markdown→HTML |
262
+
132
263
  | `i18n` | `{% trans %}` and `{% blocktrans %}` for translations |
133
264
 
265
+
266
+
134
267
  ### Deactivating and Re-registering
135
268
 
269
+
270
+
136
271
  === "CommonJS"
137
272
 
273
+
274
+
138
275
  ```javascript
276
+
139
277
  const { unregisterLibrary, activateLibrary } = require('miki-template');
140
278
 
279
+
280
+
141
281
  // Remove a library
282
+
142
283
  unregisterLibrary('lorem');
143
284
 
285
+
286
+
144
287
  // Re-activate
288
+
145
289
  activateLibrary('lorem');
290
+
146
291
  ```
147
292
 
293
+
294
+
148
295
  === "ES Modules"
149
296
 
297
+
298
+
150
299
  ```javascript
300
+
151
301
  import { unregisterLibrary, activateLibrary } from 'miki-template';
152
302
 
303
+
304
+
153
305
  unregisterLibrary('lorem');
306
+
154
307
  activateLibrary('lorem');
308
+
155
309
  ```
156
310
 
311
+
312
+
157
313
  ## i18n / Internationalization
158
314
 
315
+
316
+
159
317
  miki-template includes a built-in i18n system supporting `{% trans %}` and `{% blocktrans %}` tags.
160
318
 
319
+
320
+
161
321
  ### Registering Translations
162
322
 
323
+
324
+
163
325
  === "CommonJS"
164
326
 
327
+
328
+
165
329
  ```javascript
330
+
166
331
  const miki = require('miki-template');
167
332
 
333
+
334
+
168
335
  miki.setLanguage('fr');
336
+
169
337
  miki.registerTranslation('fr', {
338
+
170
339
  'Hello': 'Bonjour',
340
+
171
341
  'Goodbye': 'Au revoir',
342
+
172
343
  'Welcome, {name}!': 'Bienvenue, {name} !'
344
+
173
345
  });
346
+
174
347
  ```
175
348
 
349
+
350
+
176
351
  === "ES Modules"
177
352
 
353
+
354
+
178
355
  ```javascript
356
+
179
357
  import { setLanguage, registerTranslation } from 'miki-template';
180
358
 
359
+
360
+
181
361
  setLanguage('fr');
362
+
182
363
  registerTranslation('fr', {
364
+
183
365
  'Hello': 'Bonjour',
366
+
184
367
  'Goodbye': 'Au revoir',
368
+
185
369
  'Welcome, {name}!': 'Bienvenue, {name} !'
370
+
186
371
  });
372
+
187
373
  ```
188
374
 
375
+
376
+
189
377
  ### Setting Fallback Language
190
378
 
379
+
380
+
191
381
  === "CommonJS"
192
382
 
383
+
384
+
193
385
  ```javascript
386
+
194
387
  const { setLanguage, setFallbackLanguage } = require('miki-template');
195
388
 
389
+
390
+
196
391
  setLanguage('fr');
392
+
197
393
  setFallbackLanguage('en');
394
+
198
395
  ```
199
396
 
397
+
398
+
200
399
  === "ES Modules"
201
400
 
401
+
402
+
202
403
  ```javascript
404
+
203
405
  import { setLanguage, setFallbackLanguage } from 'miki-template';
204
406
 
407
+
408
+
205
409
  setLanguage('fr');
410
+
206
411
  setFallbackLanguage('en');
412
+
207
413
  ```
208
414
 
415
+
416
+
209
417
  ### Template Usage
210
418
 
419
+
420
+
211
421
  ```html
422
+
212
423
  {% trans "Hello" %}
424
+
213
425
  {% blocktrans %}Welcome, {{ name }}!{% endblocktrans %}
426
+
214
427
  ```
215
428
 
429
+
430
+
216
431
  ### Managing Languages
217
432
 
433
+
434
+
218
435
  === "CommonJS"
219
436
 
437
+
438
+
220
439
  ```javascript
440
+
221
441
  const {
442
+
222
443
  registerTranslation,
444
+
223
445
  unregisterTranslation,
446
+
224
447
  setLanguage,
448
+
225
449
  getLanguage,
450
+
226
451
  setFallbackLanguage,
452
+
227
453
  getFallbackLanguage,
454
+
228
455
  getAvailableLanguages
456
+
229
457
  } = require('miki-template');
458
+
230
459
  ```
231
460
 
461
+
462
+
232
463
  === "ES Modules"
233
464
 
465
+
466
+
234
467
  ```javascript
468
+
235
469
  import {
470
+
236
471
  registerTranslation,
472
+
237
473
  unregisterTranslation,
474
+
238
475
  setLanguage,
476
+
239
477
  getLanguage,
478
+
240
479
  setFallbackLanguage,
480
+
241
481
  getFallbackLanguage,
482
+
242
483
  getAvailableLanguages
484
+
243
485
  } from 'miki-template';
486
+
244
487
  ```
245
488
 
489
+
490
+
246
491
  ## Template Discovery
247
492
 
493
+
494
+
248
495
  The `findTemplateInViews()` function intelligently locates templates in nested directories.
249
496
 
497
+
498
+
250
499
  === "CommonJS"
251
500
 
501
+
502
+
252
503
  ```javascript
504
+
253
505
  const { findTemplateInViews, setAppTemplateDirNames } = require('miki-template');
254
506
 
507
+
508
+
255
509
  // Customize which directory names are treated as app template roots
510
+
256
511
  setAppTemplateDirNames(['templates', 'views', 'pages']);
257
512
 
513
+
514
+
258
515
  // Search for a template by name
516
+
259
517
  const found = findTemplateInViews('home', ['./views', './app/templates']);
518
+
260
519
  console.log(found);
520
+
261
521
  // → /absolute/path/to/app/templates/home.html
522
+
262
523
  ```
263
524
 
525
+
526
+
264
527
  === "ES Modules"
265
528
 
529
+
530
+
266
531
  ```javascript
532
+
267
533
  import { findTemplateInViews, setAppTemplateDirNames } from 'miki-template';
268
534
 
535
+
536
+
269
537
  setAppTemplateDirNames(['templates', 'views', 'pages']);
538
+
270
539
  const found = findTemplateInViews('home', ['./views', './app/templates']);
540
+
271
541
  console.log(found);
542
+
272
543
  ```
273
544
 
545
+
546
+
274
547
  ## Partial Templates
275
548
 
549
+
550
+
276
551
  Partials let you define reusable template fragments using `{% partialdef %}` and render them on demand.
277
552
 
553
+
554
+
278
555
  ### Defining and Rendering Partials
279
556
 
557
+
558
+
280
559
  ```html
560
+
281
561
  {% partialdef card %}
562
+
282
563
  <div class="card">
564
+
283
565
  <h3>{{ user.name }}</h3>
566
+
284
567
  <p>{{ user.email }}</p>
568
+
285
569
  </div>
570
+
286
571
  {% endpartialdef %}
572
+
287
573
  ```
288
574
 
575
+
576
+
289
577
  Render a partial:
290
578
 
579
+
580
+
291
581
  === "CommonJS"
292
582
 
583
+
584
+
293
585
  ```javascript
586
+
294
587
  const { render, compile } = require('miki-template');
295
588
 
589
+
590
+
296
591
  // Using render() with file partials:
592
+
297
593
  const html = render('home#card', { user: userData }, { views: './views' });
298
594
 
595
+
596
+
299
597
  // Using compiled.renderPartial():
598
+
300
599
  const compiled = compile(templateString);
600
+
301
601
  const partialHtml = compiled.renderPartial('card', { user: userData });
602
+
302
603
  ```
303
604
 
605
+
606
+
304
607
  === "ES Modules"
305
608
 
609
+
610
+
306
611
  ```javascript
612
+
307
613
  import { render, compile } from 'miki-template';
308
614
 
615
+
616
+
309
617
  const html = render('home#card', { user: userData }, { views: './views' });
310
618
 
619
+
620
+
311
621
  const compiled = compile(templateString);
622
+
312
623
  const partialHtml = compiled.renderPartial('card', { user: userData });
624
+
313
625
  ```
314
626
 
627
+
628
+
315
629
  ### Partial with Context
316
630
 
631
+
632
+
317
633
  ```html
634
+
318
635
  {% partialdef greeting %}
636
+
319
637
  Hello, {{ name }}! You have {{ count }} messages.
638
+
320
639
  {% endpartialdef %}
321
640
 
641
+
642
+
322
643
  {% partial greeting with name="Alice" count=3 %}
644
+
323
645
  ```
324
646
 
647
+
648
+
325
649
  ## Extending the Engine
326
650
 
651
+
652
+
327
653
  ### Registering Custom Tags
328
654
 
655
+
656
+
329
657
  === "CommonJS"
330
658
 
659
+
660
+
331
661
  ```javascript
662
+
332
663
  const { registerTag } = require('miki-template');
333
664
 
665
+
666
+
334
667
  registerTag('markdown', (tagContent, parser) => {
668
+
335
669
  const nodelist = parser.parse(['endmarkdown']);
670
+
336
671
  parser.skipTag();
672
+
337
673
  const { marked } = require('marked');
338
674
 
675
+
676
+
339
677
  return {
678
+
340
679
  render: (context) => {
680
+
341
681
  const body = nodelist.map(n => n.render(context)).join('');
682
+
342
683
  return markSafe(marked(body));
684
+
343
685
  }
686
+
344
687
  };
688
+
345
689
  });
690
+
346
691
  ```
347
692
 
693
+
694
+
348
695
  === "ES Modules"
349
696
 
697
+
698
+
350
699
  ```javascript
700
+
351
701
  import { registerTag, markSafe } from 'miki-template';
702
+
352
703
  import { marked } from 'marked';
353
704
 
705
+
706
+
354
707
  registerTag('markdown', (tagContent, parser) => {
708
+
355
709
  const nodelist = parser.parse(['endmarkdown']);
710
+
356
711
  parser.skipTag();
357
712
 
713
+
714
+
358
715
  return {
716
+
359
717
  render: (context) => {
718
+
360
719
  const body = nodelist.map(n => n.render(context)).join('');
720
+
361
721
  return markSafe(marked(body));
722
+
362
723
  }
724
+
363
725
  };
726
+
364
727
  });
728
+
365
729
  ```
366
730
 
731
+
732
+
367
733
  ### Registering Custom Helpers
368
734
 
735
+
736
+
369
737
  === "CommonJS"
370
738
 
739
+
740
+
371
741
  ```javascript
742
+
372
743
  const { registerHelper } = require('miki-template');
373
744
 
745
+
746
+
374
747
  registerHelper('truncate_words', (str, count) => {
748
+
375
749
  const words = String(str).split(/\s+/);
750
+
376
751
  return words.slice(0, count).join(' ') + (words.length > count ? '...' : '');
752
+
377
753
  });
754
+
378
755
  ```
379
756
 
757
+
758
+
380
759
  === "ES Modules"
381
760
 
761
+
762
+
382
763
  ```javascript
764
+
383
765
  import { registerHelper } from 'miki-template';
384
766
 
767
+
768
+
385
769
  registerHelper('truncate_words', (str, count) => {
770
+
386
771
  const words = String(str).split(/\s+/);
772
+
387
773
  return words.slice(0, count).join(' ') + (words.length > count ? '...' : '');
774
+
388
775
  });
776
+
389
777
  ```
390
778
 
779
+
780
+
391
781
  ## Next Steps
392
782
 
393
- - [Custom Tags](./custom-tags)
394
- - [Custom Filters](./custom-filters)
395
- - [API Reference: Libraries](../api/libraries)
396
- - [API Reference: Cache](../api/cache)
397
- - [API Reference: i18n](../api/i18n)
783
+
784
+
785
+ - [Custom Tags](./custom-tags.md)
786
+
787
+ - [Custom Filters](./custom-filters.md)
788
+
789
+ - [API Reference: Libraries](../api/libraries.md)
790
+
791
+ - [API Reference: Cache](../api/cache.md)
792
+
793
+ - [API Reference: i18n](../api/i18n.md)
794
+