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/docs/installation.md
DELETED
|
@@ -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
|
-
|
package/docs/integrations.md
DELETED
|
@@ -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/`.
|
package/docs/partialdef.md
DELETED
|
@@ -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`.
|