miki-template 2.0.1 → 2.2.3

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 (71) hide show
  1. package/.github/workflows/ci.yml +13 -37
  2. package/.github/workflows/docs.yml +105 -0
  3. package/.github/workflows/release.yml +6 -0
  4. package/README.md +69 -14
  5. package/assets/logo.png +0 -0
  6. package/benchmarks/ejs-results.json +4 -4
  7. package/benchmarks/handlebars-results.json +6 -6
  8. package/benchmarks/miki-results.json +4 -4
  9. package/benchmarks/pug-results.json +4 -4
  10. package/benchmarks/stress.mjs +1 -1
  11. package/docs/api/async-render.md +85 -0
  12. package/docs/api/cache.md +87 -0
  13. package/docs/api/compile.md +128 -0
  14. package/docs/api/context-processors.md +77 -0
  15. package/docs/api/filters.md +217 -0
  16. package/docs/api/finder.md +94 -0
  17. package/docs/api/helpers.md +53 -0
  18. package/docs/api/i18n.md +157 -0
  19. package/docs/api/index.md +54 -0
  20. package/docs/api/libraries.md +207 -0
  21. package/docs/api/render-partial.md +81 -0
  22. package/docs/api/render.md +92 -0
  23. package/docs/api/security.md +145 -0
  24. package/docs/api/setup-express.md +76 -0
  25. package/docs/api/tags.md +134 -0
  26. package/docs/assets/banner.png +0 -0
  27. package/docs/assets/logo.png +0 -0
  28. package/docs/guide/advanced-usage.md +397 -0
  29. package/docs/guide/async-rendering.md +308 -0
  30. package/docs/guide/context-processors.md +257 -0
  31. package/docs/guide/custom-filters.md +311 -0
  32. package/docs/guide/custom-tags.md +271 -0
  33. package/docs/guide/filters.md +642 -0
  34. package/docs/guide/getting-started.md +102 -0
  35. package/docs/guide/installation.md +95 -0
  36. package/docs/guide/partial-templates.md +367 -0
  37. package/docs/guide/quick-start.md +222 -0
  38. package/docs/guide/security.md +345 -0
  39. package/docs/guide/tags.md +783 -0
  40. package/docs/guide/template-discovery.md +170 -0
  41. package/docs/guide/template-inheritance.md +273 -0
  42. package/docs/guide/what-is-miki-template.md +28 -0
  43. package/docs/guide/why-miki-template.md +75 -0
  44. package/docs/index.md +104 -0
  45. package/docs/integrations/elysia.md +78 -0
  46. package/docs/integrations/express.md +219 -0
  47. package/docs/integrations/fastify.md +77 -0
  48. package/docs/integrations/hono.md +78 -0
  49. package/docs/integrations/index.md +68 -0
  50. package/docs/integrations/koa.md +88 -0
  51. package/docs/integrations/nestjs.md +78 -0
  52. package/docs/integrations/tsed.md +81 -0
  53. package/docs/javascripts/extra.js +174 -0
  54. package/docs/performance.md +37 -0
  55. package/docs/stylesheets/extra.css +819 -0
  56. package/mkdocs.yml +217 -0
  57. package/overrides/main.html +26 -0
  58. package/overrides/partials/footer.html +9 -0
  59. package/package.json +4 -2
  60. package/requirements-docs.txt +1 -0
  61. package/docs/README.md +0 -18
  62. package/docs/advanced_usage.md +0 -71
  63. package/docs/api.md +0 -122
  64. package/docs/filters.md +0 -708
  65. package/docs/installation.md +0 -106
  66. package/docs/integrations.md +0 -214
  67. package/docs/overview.md +0 -79
  68. package/docs/partialdef.md +0 -70
  69. package/docs/security.md +0 -27
  70. package/docs/tags.md +0 -673
  71. package/docs/usage.md +0 -646
@@ -1,106 +0,0 @@
1
- # Installation
2
-
3
- ## npm
4
- ```bash
5
- npm install miki-template
6
- ```
7
-
8
- ## Prerequisites
9
- - **Node.js** >= 14 (ES6+ support)
10
- - **npm** (or **yarn**) for package management
11
-
12
- ## Optional dependencies
13
- - **express** – for server‑side rendering integration (recommended).
14
- - **eslint** – for linting your project (dev dependency).
15
-
16
- ## Module System Support
17
-
18
- `miki-template` supports both **CommonJS** (`require`) and **ESM** (`import`).
19
-
20
- ### CommonJS (CJS)
21
-
22
- ```js
23
- const { render, compile, __express, SafeString, markSafe } = require('miki-template');
24
- ```
25
-
26
- ### ES Modules (ESM)
27
-
28
- ```js
29
- // Named imports
30
- import { render, compile, __express, SafeString, markSafe } from 'miki-template';
31
-
32
- // Default import (all exports)
33
- import miki from 'miki-template';
34
- const result = miki.render('Hello {{ name }}', { name: 'World' });
35
- ```
36
-
37
- > **Note:** When using ESM in Node.js, either name your files `.mjs` or set `"type": "module"` in your `package.json`.
38
-
39
- ## Publishing to npm
40
-
41
- This project is configured for automatic npm publishing via GitHub Actions. When you push to `main`, the CI workflow runs tests and, if they pass, publishes the package to npm.
42
-
43
- ### Prerequisites for publishing
44
-
45
- 1. You must have an npm account and be a maintainer of the `miki-template` package on npm.
46
- 2. In your GitHub repository, go to **Settings → Secrets and variables → Actions**.
47
- 3. Add a new repository secret named `NPM_TOKEN` with your npm automation token.
48
- - Generate it at https://www.npmjs.com/settings/YOUR_USERNAME/tokens
49
- - Select **Automation** as the token type.
50
-
51
- The CI workflow will then automatically publish on every push to `main`.
52
-
53
- ### Manual publishing
54
-
55
- ```bash
56
- npm version patch # or minor/major
57
- npm publish --access public
58
- ```
59
-
60
- ---
61
-
62
- ## Quick Start
63
-
64
- ### 1. Add the engine to your project
65
-
66
- **CJS:**
67
- ```js
68
- const { render, compile } = require('miki-template');
69
- ```
70
-
71
- **ESM:**
72
- ```js
73
- import { render, compile } from 'miki-template';
74
- ```
75
-
76
- ### 2. (Express) Register the view engine
77
-
78
- **CJS:**
79
- ```js
80
- const express = require('express');
81
- const { __express: renderDtpl } = require('miki-template');
82
- const app = express();
83
- app.engine('html', renderDtpl);
84
- app.set('view engine', 'html');
85
- app.set('views', './views');
86
- ```
87
-
88
- **ESM:**
89
- ```js
90
- import express from 'express';
91
- import { __express as renderDtpl } from 'miki-template';
92
-
93
- const app = express();
94
- app.engine('html', renderDtpl);
95
- app.set('view engine', 'html');
96
- app.set('views', './views');
97
- ```
98
-
99
- ### 3. Run the test suite to verify
100
-
101
- ```bash
102
- npm test
103
- ```
104
-
105
- ---
106
-
@@ -1,214 +0,0 @@
1
- ## Integrations — miki-template
2
-
3
- This document shows concise examples for integrating `miki-template` with popular Node.js and Bun web frameworks. Use the synchronous `render()` API for CPU-bound sync templates, and `asyncRender()` when using async helpers.
4
-
5
- Notes
6
- - For CommonJS: `const miki = require('miki-template');`
7
- - For ESM / Bun: `import miki from 'miki-template';` or `import * as miki from 'miki-template';`
8
- - When rendering files, pass `options.views` or set framework view roots so the engine can locate templates.
9
-
10
- Setup (install)
11
-
12
- ```bash
13
- # npm
14
- npm install miki-template
15
-
16
- # bun
17
- bun add miki-template
18
- ```
19
-
20
- Express (recommended: use `setupExpress`)
21
-
22
- CommonJS
23
-
24
- ```js
25
- const express = require('express');
26
- const miki = require('miki-template');
27
-
28
- const app = express();
29
-
30
- // One-line setup: wires engine, sets views, and patches res.render to support `view#partial`
31
- miki.setupExpress(app, { extension: 'html', views: './views' });
32
-
33
- app.get('/', (req, res) => {
34
- res.render('index', { user: req.user });
35
- });
36
-
37
- app.get('/partial/:name', (req, res) => {
38
- // Renders only the named partial inside the template
39
- res.render(`index#${req.params.name}`, { user: req.user });
40
- });
41
-
42
- app.listen(3000);
43
- ```
44
-
45
- ESM / Bun (similar)
46
-
47
- ```js
48
- import express from 'express';
49
- import miki from 'miki-template';
50
-
51
- const app = express();
52
- miki.setupExpress(app, { extension: 'html', views: './views' });
53
- app.listen(3000);
54
- ```
55
-
56
- Koa
57
-
58
- ```js
59
- // CommonJS
60
- const Koa = require('koa');
61
- const path = require('path');
62
- const miki = require('miki-template');
63
-
64
- const app = new Koa();
65
-
66
- // Simple render helper attached to context
67
- app.context.render = async function (view, locals = {}) {
68
- const html = await miki.asyncRender(view, locals, { views: path.resolve('./views') });
69
- this.type = 'text/html';
70
- this.body = html;
71
- };
72
-
73
- app.use(async (ctx) => {
74
- await ctx.render('index', { user: ctx.state.user });
75
- });
76
-
77
- app.listen(3000);
78
- ```
79
-
80
- Fastify
81
-
82
- ```js
83
- const Fastify = require('fastify');
84
- const path = require('path');
85
- const miki = require('miki-template');
86
-
87
- const app = Fastify();
88
-
89
- app.get('/', async (request, reply) => {
90
- const html = await miki.asyncRender('index', { user: request.user }, { views: path.resolve('./views') });
91
- reply.type('text/html').send(html);
92
- });
93
-
94
- app.listen(3000);
95
- ```
96
-
97
- NestJS (Express under the hood)
98
-
99
- ```ts
100
- // In main.ts
101
- import { NestFactory } from '@nestjs/core';
102
- import { AppModule } from './app.module';
103
- import * as miki from 'miki-template';
104
-
105
- async function bootstrap() {
106
- const app = await NestFactory.create(AppModule);
107
- // Use the underlying Express instance
108
- const expressApp = app.getHttpAdapter().getInstance();
109
- miki.setupExpress(expressApp, { extension: 'html', views: './views' });
110
- await app.listen(3000);
111
- }
112
- bootstrap();
113
- ```
114
-
115
- Ts.ED
116
-
117
- ```ts
118
- // In server bootstrap
119
- import { ServerLoader } from '@tsed/di';
120
- import * as miki from 'miki-template';
121
-
122
- // Ts.ED also runs on Express/Koa — obtain the underlying app
123
- // and call miki.setupExpress(...) when using the Express adapter.
124
-
125
- // Example when using Express adapter:
126
- // miki.setupExpress(server.rawApp, { extension: 'html', views: './views' });
127
- ```
128
-
129
- Elysia (Bun-friendly)
130
-
131
- ```js
132
- // ESM / Bun example
133
- import { Elysia } from 'elysia';
134
- import * as miki from 'miki-template';
135
- import path from 'path';
136
-
137
- const app = new Elysia();
138
-
139
- app.get('/', async () => {
140
- const html = await miki.asyncRender('index', { }, { views: path.resolve('./views') });
141
- return new Response(html, { headers: { 'Content-Type': 'text/html' } });
142
- });
143
-
144
- app.listen(3000);
145
- ```
146
-
147
- Hono (Edge + Bun)
148
-
149
- ```js
150
- import { Hono } from 'hono';
151
- import * as miki from 'miki-template';
152
- import path from 'path';
153
-
154
- const app = new Hono();
155
-
156
- app.get('/', async (c) => {
157
- const html = await miki.asyncRender('index', { }, { views: path.resolve('./views') });
158
- return c.html(html);
159
- });
160
-
161
- app.listen({ port: 3000 });
162
- ```
163
-
164
- Nifra / other minimal frameworks
165
-
166
- ```js
167
- // Generic handler pattern — works in almost any framework
168
- // (Nifra users can adapt the response API)
169
- const miki = require('miki-template');
170
- const path = require('path');
171
-
172
- async function handler(req, res) {
173
- const html = await miki.asyncRender('index', { }, { views: path.resolve('./views') });
174
- res.setHeader('Content-Type', 'text/html');
175
- res.end(html);
176
- }
177
- ```
178
-
179
- Bun-specific notes
180
- - Bun is ESM-first; import `miki-template` using `import miki from 'miki-template'`.
181
- - Use `bun add miki-template` to install.
182
- - When using Bun's native servers, call `miki.asyncRender(...)` and return/send the Response object accordingly.
183
-
184
- Tips and best practices
185
- - Prefer `miki.setupExpress()` for Express-based apps — it wires partial rendering and view expansion.
186
- - For non-Express frameworks, call `miki.render()` (sync) or `miki.asyncRender()` (async) and set `options.views` to your views root (or pass absolute file paths resolved with your framework).
187
- - To support Django-style app templates (e.g. `packages/*/templates/...`), call `miki.setAppTemplateDirNames(['templates','app_templates'])` early in your app startup if you use a custom folder name.
188
-
189
- Engine usage & partial rendering
190
-
191
- Use the engine APIs directly when you don't want framework-specific wiring or when you need fine-grained control over `views` roots.
192
-
193
- ```js
194
- const miki = require('miki-template');
195
- const path = require('path');
196
-
197
- // Sync render of a named partial inside a template file
198
- const html = miki.render('home#card', { user: 'Alice', title: 'Card' }, { views: path.resolve('./views') });
199
-
200
- // Async render when templates use async helpers
201
- const htmlAsync = await miki.asyncRender('home#card', { user: 'Bob' }, { views: path.resolve('./views') });
202
-
203
- // If your project arranges templates under custom folder names, configure
204
- // what constitutes an "app template" directory before rendering:
205
- miki.setAppTemplateDirNames(['templates', 'app_templates']);
206
-
207
- // To locate a template file programmatically without rendering, use the
208
- // exported finder helper:
209
- const found = miki.findTemplateInViews('home', [path.resolve('./views')]);
210
- if (found) console.log('Resolved to', found);
211
- ```
212
-
213
- Further reading
214
- - See the main API docs for `setupExpress`, `render`, and `asyncRender` in `docs/api.md`.
package/docs/overview.md DELETED
@@ -1,79 +0,0 @@
1
- # Overview
2
-
3
- Welcome to **miki-template** – a production‑ready, Django‑style template engine for Node.js and Express. This documentation mirrors the layout of popular open‑source libraries (e.g., Django, Jinja2, Mustache) and provides a clear, hierarchical guide for developers of all skill levels.
4
-
5
- - **Project structure** – quick glance at the repository layout.
6
- - **Feature list** – exhaustive rundown of supported tags, filters, security helpers, and the new `partialdef` system.
7
- - **Getting started** – installation, basic rendering, and Express integration.
8
- - **Advanced usage** – inheritance, block rendering, custom tags/filters, and performance tips.
9
-
10
- ---
11
-
12
- ## Repository layout
13
-
14
- ```
15
- 📦 miki-template/
16
- ├─ 📁 src/ # Core engine source files
17
- │ ├─ index.js # Entry point, compile/render APIs
18
- │ ├─ lexer.js # Tokenizer
19
- │ ├─ parser.js # AST builder
20
- │ ├─ context.js # Scope & partial registry
21
- │ └─ tags/ # Built‑in tag parsers (control, inheritance, util)
22
- │ ├─ control.js # if, for, with, cycle, partialdef, …
23
- │ ├─ inheritance.js # extends, block, super
24
- │ └─ util.js # comment, verbatim, etc.
25
- ├─ 📁 filters/ # Built‑in filter implementations
26
- ├─ 📁 tests/ # Jest‑style test suite
27
- ├─ 📁 docs/ # 📖 Documentation (this folder)
28
- ├─ README.md # Project landing page (high‑level intro)
29
- ├─ AGENT.md # Agent guardrails (internal)
30
- ├─ ROADMAP.md # Future roadmap & milestones
31
- └─ package.json # npm package definition
32
- ```
33
-
34
- Each module is deliberately **single‑responsibility** and fully typed via JSDoc comments, making it easy to extend.
35
-
36
- ---
37
-
38
- ## Where to start
39
-
40
- - **Installation** – see `docs/installation.md`.
41
- - **Basic rendering** – see `docs/usage.md`.
42
- - **Tag reference** – see `docs/tags.md`.
43
- - **Filter reference** – see `docs/filters.md`.
44
- - **Partial definitions** – see `docs/partialdef.md`.
45
- - **Security considerations** – see `docs/security.md`.
46
-
47
- ## Recursive and app-style template discovery
48
-
49
- `miki-template` now supports Django-style recursive template discovery. When you configure your views directory (via `miki.setupExpress(app, { views: './views' })` or by passing `views` to `render()`), the engine will:
50
-
51
- - Resolve direct paths like `nested/index` relative to each `views` directory.
52
- - If a bare template name (e.g. `card`) is used, recursively scan subfolders of the configured `views` directories to find `card.html` or `card.miki`.
53
- - Discover app-style `templates` directories located under application packages (e.g. `project/apps/product/templates/...`) and include them in the search.
54
-
55
- Configuration:
56
-
57
- - Programmatically set which folder names should be treated as app template roots via the API:
58
-
59
- - `setAppTemplateDirNames(['templates', 'site_templates'])` — sets the list of folder names that will be discovered under the views root.
60
- - `getAppTemplateDirNames()` — returns the current list.
61
-
62
- Examples:
63
-
64
- - `render('home#card', ctx, { views: './views' })` will search `./views` and any `templates/` subfolders for `home.html` or `home.miki`, and render the `card` partial.
65
- - If your project places templates under `packages/product/templates/detail.html`, `render('detail', ..., { views: './views' })` will find it automatically.
66
-
67
- This behavior is opt‑out by simply clearing the app-dir names: `setAppTemplateDirNames([])` will disable app-style discovery.
68
-
69
- For API‑level details (e.g., `compile().renderPartial`) check `docs/api.md`.
70
-
71
- ---
72
-
73
- ## Contributing
74
-
75
- We follow the standard open‑source workflow. Details are in `docs/contributing.md`.
76
-
77
- ---
78
-
79
- > **Tip**: All documentation files are located under `c:/Users/Coder Miki/Desktop/miki-template/docs/`.
@@ -1,70 +0,0 @@
1
- # Partial Definition (`partialdef`)
2
-
3
- `partialdef` is the cornerstone feature that brings Django‑style **named template fragments** to Node.js. It allows you to define a reusable block once and render it multiple times, optionally **inline** for immediate output. Combined with the `setupExpress` helper, partials can be served as standalone HTTP responses for HTMX-style UIs.
4
-
5
- ## Syntax
6
- ```html
7
- {% partialdef name [inline] %}
8
- ...template code...
9
- {% endpartialdef %}
10
- ```
11
- - `name` – identifier used with `{% partial name %}`.
12
- - Optional `inline` – if present, the block is rendered **where it is defined**; no separate `{% partial %}` call is required.
13
-
14
- ## Rendering a Partial
15
- ```html
16
- {% partial greeting %}
17
- ```
18
- The engine looks up the definition in the current rendering **Context** (`context.partialDefs`) and injects the rendered output.
19
-
20
- ## Including a Partial From Another File
21
- Use the `file#partial` syntax to include just a single named partial:
22
- ```html
23
- {% include "home.html#card" with title="Hi" %}
24
- ```
25
-
26
- ## API Usage
27
- ```js
28
- const tpl = `{% partialdef api %}API {{ data }}{% endpartialdef %}`;
29
- const compiled = compile(tpl);
30
- const out = compiled.renderPartial('api', { data: 123 }); // "API 123"
31
- ```
32
-
33
- `renderPartialFromSource` and `renderPartialFromFile` are also exported at the top level:
34
- ```js
35
- const miki = require('miki-template');
36
- miki.renderPartialFromFile('views/home.html', 'card', { user: req.user });
37
- miki.renderPartialFromSource(src, 'card', { user: req.user }, { views: 'views' });
38
- ```
39
-
40
- ## Serving a Partial Over HTTP (HTMX)
41
- With `miki.setupExpress(app, { extension: 'html', views: './views' })`, the same `res.render(...)` call you use for full pages also serves a single partial by appending `#partialName` to the view name:
42
-
43
- ```javascript
44
- app.get('/partials/:name', (req, res) =>
45
- res.render(`home#${req.params.name}`, { user: req.user })
46
- );
47
- ```
48
-
49
- The same effect can be obtained via the lighter `expressPartialRenderer()` middleware:
50
- ```javascript
51
- app.use(miki.expressPartialRenderer());
52
- app.get('/card', (req, res) => res.renderPartial('home#card', { user: req.user }));
53
- ```
54
-
55
- ## Features
56
- - **Full tag parity** – conditionals (`if`), loops (`for`), variable scoping (`with`) work inside a `partialdef`.
57
- - **Nested partials** – you can define a partial inside another; inner definitions are registered first and can be used by the outer.
58
- - **Scope isolation** – each rendering of a partial receives its own scope, mirroring Django’s behavior.
59
- - **Inline rendering** – render inline without an extra `{% partial %}` tag (`{% partialdef foo inline %}…{% endpartialdef %}`).
60
- - **`with` arguments** – bind extra context values when rendering: `{% partial card with title="Hello" description="World" %}`.
61
- - **Performance** – partials are compiled once per template; subsequent renders reuse the compiled AST.
62
-
63
- ## Common Pitfalls
64
- | Issue | Symptom | Fix |
65
- |-------|---------|-----|
66
- | Missing partial name | `{% partial %}` renders nothing | Ensure the name matches a defined `partialdef`. |
67
- | Variable not found | Appears empty | Variables are resolved in the current context; use `{% with %}` inside the partial if you need a local alias. |
68
- | Inline vs non‑inline confusion | Duplicate output | Use `inline` only when you want immediate rendering. |
69
-
70
- > Implementation lives in `src/tags/control.js` (class `PartialDefNode` and `PartialNode`).
package/docs/security.md DELETED
@@ -1,27 +0,0 @@
1
- # Security
2
-
3
- `miki-template` is built with **secure defaults**. All variables are **auto‑escaped** unless explicitly marked safe.
4
-
5
- ## Auto‑escaping
6
- - Every string output goes through `escapeHtml` before being concatenated.
7
- - Use the `|safe` filter or `markSafe(value)` to bypass escaping when you trust the data.
8
-
9
- ## CSRF Protection
10
- - The `{% csrf_token %}` tag renders a hidden `<input>` containing the `csrf_token` value from the rendering context.
11
- - Example:
12
- ```html
13
- <form method="post">{% csrf_token %} ... </form>
14
- ```
15
- - It is a thin wrapper; you must generate and store `csrf_token` in your Express middleware.
16
-
17
- ## CSP Nonce
18
- - `{% csp_nonce_attr %}` injects `nonce="{{ csp_nonce }}"` when `csp_nonce` is present in the context.
19
- - Useful for inline scripts when you have a CSP policy with `script-src 'nonce-...';`.
20
-
21
- ## SafeString Wrapper
22
- - Filters returning `SafeString` bypass auto‑escaping. The wrapper is applied automatically by the `safe` filter.
23
-
24
- ## No `eval`
25
- - Template expressions are parsed into an AST and evaluated using a sandboxed evaluator that **never calls `eval` or `new Function`**.
26
-
27
- > Security‑related code lives in `src/security.js` and the tag implementations in `src/tags/control.js`.