miki-template 1.2.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 (73) hide show
  1. package/.github/workflows/ci.yml +54 -0
  2. package/AGENT.md +71 -0
  3. package/API_REFERENCE.md +314 -0
  4. package/CHANGELOG.md +97 -0
  5. package/CODE_OF_CONDUCT.md +14 -0
  6. package/CONTRIBUTING.md +27 -0
  7. package/README.md +304 -0
  8. package/ROADMAP.md +40 -0
  9. package/benchmarks/report.json +17 -0
  10. package/benchmarks/run.js +49 -0
  11. package/benchmarks/templates/large.dtpl +7 -0
  12. package/benchmarks/templates/medium.dtpl +3 -0
  13. package/benchmarks/templates/small.dtpl +7 -0
  14. package/context/component.md +109 -0
  15. package/context/prd.md +131 -0
  16. package/context/project-structure.md +33 -0
  17. package/docs/README.md +18 -0
  18. package/docs/advanced_usage.md +71 -0
  19. package/docs/api.md +102 -0
  20. package/docs/filters.md +540 -0
  21. package/docs/installation.md +106 -0
  22. package/docs/overview.md +57 -0
  23. package/docs/partialdef.md +41 -0
  24. package/docs/security.md +27 -0
  25. package/docs/tags.md +610 -0
  26. package/docs/usage.md +599 -0
  27. package/eslint.config.mjs +34 -0
  28. package/miki-template-1.2.0.vsix +0 -0
  29. package/miki-template-extension/LICENSE +21 -0
  30. package/miki-template-extension/README.md +82 -0
  31. package/miki-template-extension/icon.png +0 -0
  32. package/miki-template-extension/icon.svg +10 -0
  33. package/miki-template-extension/package.json +46 -0
  34. package/miki-template-extension/snippets/miki-template.json +177 -0
  35. package/miki-template-extension/syntaxes/language-configuration.json +26 -0
  36. package/miki-template-extension/syntaxes/miki-template.tmLanguage.json +146 -0
  37. package/package.json +31 -0
  38. package/snippets/miki-template.json +177 -0
  39. package/src/asyncRender.js +21 -0
  40. package/src/cache.js +41 -0
  41. package/src/context.js +122 -0
  42. package/src/context_processors.js +41 -0
  43. package/src/esm.mjs +72 -0
  44. package/src/filters.js +527 -0
  45. package/src/i18n.js +171 -0
  46. package/src/index.js +454 -0
  47. package/src/lexer.js +92 -0
  48. package/src/libraries.js +240 -0
  49. package/src/parser.js +250 -0
  50. package/src/security.js +51 -0
  51. package/src/tags/control.js +591 -0
  52. package/src/tags/helpers.js +27 -0
  53. package/src/tags/i18n.js +230 -0
  54. package/src/tags/inheritance.js +216 -0
  55. package/src/tags/registry.js +18 -0
  56. package/src/tags/util.js +322 -0
  57. package/src/types.d.ts +107 -0
  58. package/syntaxes/language-configuration.json +26 -0
  59. package/syntaxes/miki-template.tmLanguage.json +146 -0
  60. package/tests/asyncRender.test.js +17 -0
  61. package/tests/base.html +6 -0
  62. package/tests/child.html +3 -0
  63. package/tests/context_processors.test.js +13 -0
  64. package/tests/esm.test.mjs +26 -0
  65. package/tests/filters.test.js +99 -0
  66. package/tests/include_security.test.js +9 -0
  67. package/tests/lexer.test.js +45 -0
  68. package/tests/parser.test.js +55 -0
  69. package/tests/partial.html +1 -0
  70. package/tests/partialdef.test.js +40 -0
  71. package/tests/production_checks.js +57 -0
  72. package/tests/security.test.js +28 -0
  73. package/tests/tags.test.js +203 -0
package/README.md ADDED
@@ -0,0 +1,304 @@
1
+ # miki-template
2
+ ![npm version](https://img.shields.io/npm/v/miki-template.svg) ![CI](https://github.com/your-repo/miki-template/workflows/ci.yml/badge.svg)
3
+ A robust, production-ready template engine that brings **Django's template language** features and syntax to Node.js and Express, fully compliant with modern JavaScript (ES6+), CommonJS, and **ESM** (`import`) support.
4
+
5
+ ---
6
+
7
+ ## πŸš€ Features
8
+
9
+ - **Full Syntax Parity**: Supports variables, dotted lookups, filters (`|`), and block tags (`{% %}`).
10
+ - **Template Inheritance**: Multi-level inheritance with `extends`, block overrides, and `{{ block.super }}` support.
11
+ - **Express Integration**: Simple, zero-config integration via `app.engine()`.
12
+ - **ESM & CommonJS**: Works seamlessly with both `import` and `require` syntax.
13
+ - **Security by Default**: Auto-escaping enabled by default with a `SafeString` wrapper.
14
+ - **CSRF & CSP Support**: Native tags for `{% csrf_token %}` and `{% csp_nonce_attr %}` to keep apps secure out-of-the-box.
15
+ - **Block Partials**: Render a single block from a compiled template via `compiled.renderBlock('block_name')`.
16
+ - **Async Rendering**: Support for async filters/tags with `asyncRender()`.
17
+ - **Extensible API**: Easy registration for custom tags and filters.
18
+ - **No Unsafe Code Execution**: Evaluates expressions securely without using `eval()`.
19
+ - **Editor Support**: First-class syntax highlighting and snippets for VS Code, Sublime Text, Atom, and TextMate-compatible editors.
20
+
21
+ ### VS Code
22
+
23
+ #### Option A: Install the official extension (recommended)
24
+
25
+ Search for **miki-template** in the VS Code Marketplace, or install from the command line:
26
+
27
+ ```bash
28
+ code --install-extension miki-template
29
+ ```
30
+
31
+ #### Option B: Manual install from this repo
32
+
33
+ 1. Copy the `syntaxes/` and `snippets/` folders from this repo.
34
+ 2. In VS Code, run **Preferences: Configure File Associations** and associate `*.miki` with `miki-template`.
35
+ 3. Or add a workspace-level `.vscode/settings.json`:
36
+
37
+ ```json
38
+ {
39
+ "files.associations": {
40
+ "*.miki": "miki-template"
41
+ }
42
+ }
43
+ ```
44
+
45
+ ### Sublime Text / Atom / TextMate
46
+
47
+ Drop the `syntaxes/miki-template.tmLanguage.json` file into your editor’s `Packages/User/` folder and associate it with the `.miki` extension.
48
+
49
+ ---
50
+
51
+ ## πŸ“š Documentation
52
+
53
+ - [Installation](docs/installation.md)
54
+ - [Usage Guide](docs/usage.md)
55
+ - [Tags Reference](docs/tags.md)
56
+ - [Filters Reference](docs/filters.md)
57
+ - [Security](docs/security.md)
58
+ - [API Reference](docs/api.md)
59
+ - [Partial Definitions](docs/partialdef.md)
60
+ - [Contributing](docs/advanced_usage.md)
61
+
62
+ ---
63
+
64
+ ## πŸ“¦ Installation
65
+
66
+ ```bash
67
+ npm install miki-template
68
+ ```
69
+
70
+ ---
71
+
72
+ ## πŸ› οΈ Quick Start
73
+
74
+ ### CommonJS (require)
75
+
76
+ ```javascript
77
+ const { render, compile, __express, SafeString, markSafe } = require('miki-template');
78
+
79
+ const template = 'Hello {{ user.name|title }}! Roles: {{ user.roles|join:", " }}';
80
+ const context = {
81
+ user: {
82
+ name: 'miki coder',
83
+ roles: ['admin', 'developer']
84
+ }
85
+ };
86
+
87
+ const result = render(template, context);
88
+ console.log(result); // Output: "Hello Miki Coder! Roles: admin, developer"
89
+ ```
90
+
91
+ ### ES Modules (import)
92
+
93
+ ```javascript
94
+ // Named imports
95
+ import { render, compile, __express, SafeString, markSafe } from 'miki-template';
96
+
97
+ // Or default import (gets all exports)
98
+ import miki from 'miki-template';
99
+ const { render: mikiRender } = miki;
100
+
101
+ const template = 'Hello {{ user.name|title }}!';
102
+ const result = render(template, { user: { name: 'world' } });
103
+ console.log(result); // Output: "Hello World!"
104
+ ```
105
+
106
+ > **Note:** For ESM in Node.js, either name your files `.mjs` or add `"type": "module"` to your `package.json`.
107
+
108
+ ### Express Integration
109
+
110
+ **CommonJS:**
111
+ ```javascript
112
+ const express = require('express');
113
+ const { __express: renderDtpl } = require('miki-template');
114
+
115
+ const app = express();
116
+
117
+ // Register both .html and .miki extensions
118
+ app.engine('html', renderDtpl);
119
+ app.engine('miki', renderDtpl);
120
+ app.set('view engine', 'miki');
121
+ app.set('views', './views');
122
+
123
+ app.get('/', (req, res) => {
124
+ res.render('home', {
125
+ title: 'Django Templates in Node!',
126
+ items: ['Apple', 'Banana', 'Orange']
127
+ });
128
+ });
129
+
130
+ app.listen(3000, () => console.log('App listening on port 3000'));
131
+ ```
132
+
133
+ **ESM:**
134
+ ```javascript
135
+ import express from 'express';
136
+ import { __express as renderDtpl } from 'miki-template';
137
+
138
+ const app = express();
139
+ app.engine('html', renderDtpl);
140
+ app.set('view engine', 'html');
141
+ app.set('views', './views');
142
+
143
+ app.get('/', (req, res) => {
144
+ res.render('home', {
145
+ title: 'Django Templates with ESM!',
146
+ items: ['Apple', 'Banana', 'Orange']
147
+ });
148
+ });
149
+
150
+ app.listen(3000, () => console.log('App listening on port 3000'));
151
+ ```
152
+
153
+ ---
154
+
155
+ ## πŸ“– Template Syntax & Parity
156
+
157
+ ### Variables & Dotted Lookups
158
+ Resolve properties dynamically on nested objects or arrays. If the resolved value is a callable/function, it is automatically executed with zero arguments.
159
+ ```html
160
+ {{ user.profile.name }}
161
+ {{ items.0 }} <!-- Array indexing -->
162
+ {{ user.getFullName }} <!-- Function resolution -->
163
+ ```
164
+
165
+ ### Built-in Filters
166
+ Apply filters using pipes (`|`). Arguments are passed after a colon (`:`).
167
+ - **Text**: `upper`, `lower`, `title`, `capfirst`, `slugify`, `wordcount`, `striptags`, `linebreaks`, `linebreaksbr`, `truncatewords:N`, `truncatechars:N`.
168
+ - **HTML**: `safe`, `escape`.
169
+ - **List**: `length`, `join:","`, `slice:"start:end"`, `dictsort:"key"`, `dictsortreversed:"key"`.
170
+ - **Default**: `default:"fallback"`, `default_if_none:"fallback"`.
171
+ - **Date/Time**: `date:"Y-m-d"`, `time:"H:i"`, `timesince`, `timeuntil`.
172
+ - **Numeric**: `add:5`, `divisibleby:2`, `floatformat:2`.
173
+ - **Misc**: `yesno:"yes,no,maybe"`, `pluralize:"suffix"`, `filesizeformat`.
174
+
175
+ ### Built-in Control Tags
176
+ - **if / elif / else / endif**: Supports conditional expressions with operators: `==`, `!=`, `<`, `<=`, `>`, `>=`, `in`, `not in`, `and`, `or`, `not`.
177
+ ```html
178
+ {% if user.role == 'admin' or user.is_staff %}
179
+ <p>Access Granted</p>
180
+ {% elif user.age >= 18 %}
181
+ <p>Standard Access</p>
182
+ {% else %}
183
+ <p>Access Denied</p>
184
+ {% endif %}
185
+ ```
186
+ - **for / empty / endfor**: Loop over arrays and objects. Injects `forloop` meta tracking.
187
+ ```html
188
+ {% for item in items %}
189
+ <li>{{ forloop.counter }}: {{ item }}</li>
190
+ {% empty %}
191
+ <li>No items found</li>
192
+ {% endfor %}
193
+ ```
194
+ - **with / endwith**: Scopes localized variables.
195
+ ```html
196
+ {% with user.profile.address as addr %}
197
+ <p>{{ addr.city }}, {{ addr.zip }}</p>
198
+ {% endwith %}
199
+ ```
200
+ - **cycle**: Cycle through values sequentially.
201
+ ```html
202
+ {% for row in rows %}
203
+ <tr class="{% cycle 'row-odd' 'row-even' %}">...</tr>
204
+ {% endfor %}
205
+ ```
206
+ - **autoescape on/off**: Control auto-escaping block behavior.
207
+ - **verbatim / endverbatim**: Treat raw text inside literally.
208
+ - **comment / endcomment**: Block comment ignored during parse.
209
+
210
+ ### Security Tags
211
+ - **csrf_token**: Automatically outputs a hidden input carrying the CSRF token from the context variable `csrf_token`.
212
+ ```html
213
+ <form method="post">
214
+ {% csrf_token %}
215
+ ...
216
+ </form>
217
+ ```
218
+ - **csp_nonce_attr**: Dynamically outputs `nonce="value"` if the variable `csp_nonce` is in the context.
219
+ ```html
220
+ <script {% csp_nonce_attr %} src="app.js"></script>
221
+ ```
222
+
223
+ ### Inheritance & Block Rendering
224
+ Inherit structure from parent templates.
225
+ - `base.html`:
226
+ ```html
227
+ <html>
228
+ <body>
229
+ {% block content %}Default Content{% endblock %}
230
+ </body>
231
+ </html>
232
+ ```
233
+ - `child.html`:
234
+ ```html
235
+ {% extends "base.html" %}
236
+ {% block content %}
237
+ <h1>Child Content</h1>
238
+ {{ block.super }} <!-- Renders parent's default content -->
239
+ {% endblock %}
240
+ ```
241
+
242
+ #### Rendering a Block-Level Partial (Django 5.1+ / HTMX Style)
243
+ You can compile a template and choose to render *only a specific block* (useful for AJAX or HTMX requests):
244
+ ```javascript
245
+ const compiled = compile(childTemplateStr, { views: './templates' });
246
+ const partialHtml = compiled.renderBlock('content', context);
247
+ console.log(partialHtml); // Output: "<h1>Child Content</h1> Default Content"
248
+ ```
249
+
250
+ ---
251
+
252
+ ## πŸ”§ Extensibility API
253
+
254
+ ### Register a Custom Filter
255
+
256
+ **CommonJS:**
257
+ ```javascript
258
+ const { registerFilter } = require('miki-template');
259
+
260
+ registerFilter('reverse', (val) => {
261
+ return String(val).split('').reverse().join('');
262
+ });
263
+ ```
264
+
265
+ **ESM:**
266
+ ```javascript
267
+ import { registerFilter } from 'miki-template';
268
+
269
+ registerFilter('reverse', (val) => {
270
+ return String(val).split('').reverse().join('');
271
+ });
272
+ ```
273
+
274
+ ### Register a Custom Tag
275
+
276
+ **CommonJS:**
277
+ ```javascript
278
+ const { registerTag } = require('miki-template');
279
+
280
+ // Custom tag parser returning an AST Node
281
+ registerTag('hello', (tagContent, parser) => {
282
+ return {
283
+ render: (context) => 'Hello World!'
284
+ };
285
+ });
286
+ ```
287
+
288
+ **ESM:**
289
+ ```javascript
290
+ import { registerTag } from 'miki-template';
291
+
292
+ registerTag('hello', (tagContent, parser) => {
293
+ return {
294
+ render: (context) => 'Hello World!'
295
+ };
296
+ });
297
+ ```
298
+
299
+ ---
300
+
301
+ ## πŸ”’ Security
302
+ - **HTML Auto-escaping**: Enabled by default to guard against Cross-Site Scripting (XSS).
303
+ - **SafeString Wrapper**: Explicitly bypass escaping using the `|safe` filter or marking variables via `markSafe(val)`.
304
+ - **No eval() Execution**: Parser evaluates logic statements securely using standard tokens mapping.
package/ROADMAP.md ADDED
@@ -0,0 +1,40 @@
1
+ # Project Roadmap - Django-Style Template Engine
2
+
3
+ Our release and packaging timeline.
4
+
5
+ ---
6
+
7
+ ## 🏁 Phase 1 - Core Delivery (Day 1-2)
8
+ - [x] High-performance tokenizing Lexer.
9
+ - [x] AST parser mapping nested blocks.
10
+ - [x] Context Stack Manager supporting dotted lookup and function calls.
11
+ - [x] HTML Auto-escaping using `he` library and `SafeString` wrappers.
12
+ - [x] Complete suite of built-in filters (25+ filters).
13
+ - [x] Core control flow tags (`if`, `for`, `with`, `cycle`, `autoescape`, `comment`, `verbatim`).
14
+ - [x] Dynamic inheritance (`extends`, `block`, `include` with parameter scope).
15
+ - [x] Express layout adapter (`__express`).
16
+ - [x] Complete test suite verification.
17
+
18
+ ---
19
+
20
+ ## πŸš€ Phase 2 - Advanced Performance & Ecosystem (Next Month)
21
+ - [x] **CI/CD Pipeline** – linting, testing, and automated npm publishing via GitHub Actions.
22
+ - [x] **AST Caching** – cache compiled ASTs for static templates (implemented).
23
+ - [x] **Custom Tag Helpers** – simplify registration of custom block tags (implemented via helpers).
24
+ - [x] **Async Render Options** – support async filters/tags for DB lookups (implemented).
25
+ - [x] **Extended Date Syntax** – integrate `date-fns` for full date formatting parity (implemented).
26
+ - [x] **Advanced Usage Docs** – examples covering partialdef, block rendering, and API (added).
27
+ - [x] **Performance Benchmarks** – baseline measurements and optimization guide (added).
28
+
29
+ ---
30
+
31
+ ## πŸ“¦ Phase 3 - Ecosystem & Tooling (Q4)
32
+ - [ ] VS Code Extension: Syntax highlighting for `.dtpl` or `.html` Django templates.
33
+ - [ ] Web Playground: An interactive sandbox to experiment with the engine.
34
+ - [ ] CLI Compiler: Render templates from the command line.
35
+
36
+ ---
37
+
38
+ ## πŸ“š Documentation
39
+ - [x] Comprehensive docs in `docs/` covering installation, usage, tags, filters, partialdef, security, and API.
40
+ - [x] Advanced examples and performance guide (added).
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "name": "small",
4
+ "syncAvgMs": "0.04",
5
+ "asyncAvgMs": "0.03"
6
+ },
7
+ {
8
+ "name": "medium",
9
+ "syncAvgMs": "0.00",
10
+ "asyncAvgMs": "0.01"
11
+ },
12
+ {
13
+ "name": "large",
14
+ "syncAvgMs": "0.00",
15
+ "asyncAvgMs": "0.01"
16
+ }
17
+ ]
@@ -0,0 +1,49 @@
1
+ // benchmarks/run.js
2
+ // Simple benchmark for sync vs async rendering
3
+ const { compile, asyncRender } = require('../src');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { performance } = require('perf_hooks');
7
+
8
+ function loadTemplate(name) {
9
+ const filePath = path.join(__dirname, 'templates', `${name}.dtpl`);
10
+ return fs.readFileSync(filePath, 'utf8');
11
+ }
12
+
13
+ function benchRender(name, iterations = 20) {
14
+ const tmplStr = loadTemplate(name);
15
+ const compiled = compile(tmplStr);
16
+ // warm up cache
17
+ compiled.render({});
18
+ compiled.asyncRender({});
19
+
20
+ const syncTimes = [];
21
+ const asyncTimes = [];
22
+ for (let i = 0; i < iterations; i++) {
23
+ const t0 = performance.now();
24
+ compiled.render({});
25
+ syncTimes.push(performance.now() - t0);
26
+
27
+ const t1 = performance.now();
28
+ asyncRender(tmplStr, {});
29
+ asyncTimes.push(performance.now() - t1);
30
+ }
31
+ const avg = arr => arr.reduce((a,b)=>a+b,0)/arr.length;
32
+ return {
33
+ name,
34
+ syncAvgMs: avg(syncTimes).toFixed(2),
35
+ asyncAvgMs: avg(asyncTimes).toFixed(2)
36
+ };
37
+ }
38
+
39
+ function main() {
40
+ const results = [];
41
+ ['small','medium','large'].forEach(name => {
42
+ results.push(benchRender(name));
43
+ });
44
+ console.log('Benchmark results:', results);
45
+ const outPath = path.join(__dirname, 'report.json');
46
+ fs.writeFileSync(outPath, JSON.stringify(results, null, 2));
47
+ }
48
+
49
+ main();
@@ -0,0 +1,7 @@
1
+ // large.dtpl
2
+ {% for i in range(0, 1000) %}
3
+ <div class="item">
4
+ <h2>Item {{ i }}</h2>
5
+ <p>{{ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " | repeat:5 }}</p>
6
+ </div>
7
+ {% endfor %}
@@ -0,0 +1,3 @@
1
+ // medium.dtpl
2
+ {% for i in range(0, 100) %}Item {{ i }}
3
+ {% endfor %}
@@ -0,0 +1,7 @@
1
+ // small.dtpl
2
+ Hello {{ name|upper }}!
3
+ {% if showList %}
4
+ <ul>
5
+ {% for item in items %}<li>{{ item }}</li>{% endfor %}
6
+ </ul>
7
+ {% endif %}
@@ -0,0 +1,109 @@
1
+ # Component Hierarchy β€” Django-Style Template Engine (Node.js/Express)
2
+
3
+ ## πŸ“– Purpose
4
+ This document defines the **modules, responsibilities, and relationships** for the template engine. It ensures clarity in implementation and maintainability.
5
+
6
+ ---
7
+
8
+ ## 🧩 Core Components
9
+
10
+ ### 1. **Lexer**
11
+ - **Responsibility**: Tokenize template strings into `TEXT`, `VAR`, `BLOCK`.
12
+ - **Inputs**: Raw template string.
13
+ - **Outputs**: Token stream.
14
+ - **Dependencies**: None.
15
+
16
+ ### 2. **Parser**
17
+ - **Responsibility**: Convert tokens into an AST (Abstract Syntax Tree).
18
+ - **Inputs**: Token stream.
19
+ - **Outputs**: AST nodes (`Text`, `Var`, `If`, `For`, `Block`, etc.).
20
+ - **Dependencies**: Lexer.
21
+
22
+ ### 3. **Renderer**
23
+ - **Responsibility**: Walk AST, evaluate expressions, apply filters, render output.
24
+ - **Inputs**: AST + context.
25
+ - **Outputs**: Final HTML string.
26
+ - **Dependencies**: Parser, Filter Registry, Tag Handlers.
27
+
28
+ ---
29
+
30
+ ## πŸ”§ Supporting Modules
31
+
32
+ ### 4. **Filter Registry**
33
+ - **Responsibility**: Store and apply filters.
34
+ - **Built-in Filters**: `upper`, `lower`, `date`, `truncatechars`, `safe`, `escape`, etc.
35
+ - **Extensibility**: Developers can register custom filters.
36
+ - **Dependencies**: Renderer.
37
+
38
+ ### 5. **Tag Handlers**
39
+ - **Responsibility**: Implement logic for each tag.
40
+ - **Control Flow**: `if`, `elif`, `else`, `for`, `empty`, `with`, `cycle`.
41
+ - **Inheritance**: `extends`, `block`, `include`.
42
+ - **Utilities**: `url`, `static`, `regroup`, `spaceless`, `comment`, `verbatim`.
43
+ - **Security**: `autoescape`, `csrf_token`, `csp_nonce_attr`.
44
+ - **Dependencies**: Renderer, Context.
45
+
46
+ ### 6. **Inheritance System**
47
+ - **Responsibility**: Manage parent/child templates, block overrides.
48
+ - **Mechanism**: Block registry + AST merging.
49
+ - **Dependencies**: Parser, Renderer.
50
+
51
+ ### 7. **Context Manager**
52
+ - **Responsibility**: Provide variables to templates.
53
+ - **Features**: Dotted lookups (`user.name`), querystring injection, context processors.
54
+ - **Dependencies**: Renderer.
55
+
56
+ ---
57
+
58
+ ## πŸ” Security Components
59
+
60
+ ### 8. **Escaping Engine**
61
+ - **Responsibility**: Autoescape HTML by default.
62
+ - **Features**: `safe` filter disables escaping, `escape` forces escaping.
63
+ - **Dependencies**: Renderer, Filter Registry.
64
+
65
+ ---
66
+
67
+ ## πŸ“¦ Integration Layer
68
+
69
+ ### 9. **Express Adapter**
70
+ - **Responsibility**: Integrate engine with Express.
71
+ - **API**: `app.engine('dtpl', renderFile)`.
72
+ - **Dependencies**: Renderer, Context Manager.
73
+
74
+ ---
75
+
76
+ ## πŸ› οΈ Extensibility Components
77
+
78
+ ### 10. **Custom Tag API**
79
+ - **Responsibility**: Allow developers to register new tags.
80
+ - **Mechanism**: Tag registry with handler functions.
81
+
82
+ ### 11. **Custom Filter API**
83
+ - **Responsibility**: Allow developers to register new filters.
84
+ - **Mechanism**: Filter registry extension.
85
+
86
+ ### 12. **Context Processors**
87
+ - **Responsibility**: Inject global variables (e.g., `user`, `request`).
88
+ - **Mechanism**: Middleware-like hooks.
89
+
90
+ ---
91
+
92
+ ## πŸ“‹ Relationships Diagram (Textual)
93
+
94
+ - **Lexer β†’ Parser β†’ Renderer**
95
+ - **Renderer β†’ Filter Registry + Tag Handlers + Context Manager**
96
+ - **Tag Handlers β†’ Inheritance System + Escaping Engine**
97
+ - **Express Adapter β†’ Renderer**
98
+ - **Custom APIs β†’ Filter Registry + Tag Handlers**
99
+ - **Context Processors β†’ Context Manager**
100
+
101
+ ---
102
+
103
+ ## 🎯 Deliverables
104
+ - Modular codebase with clear separation of concerns.
105
+ - Each tag/filter implemented as independent handler.
106
+ - Extensible APIs for developers.
107
+ - Secure rendering pipeline with autoescape.
108
+ - Seamless Express integration.
109
+
package/context/prd.md ADDED
@@ -0,0 +1,131 @@
1
+ # Django-Style Template Engine for Node.js/Express
2
+
3
+ ## πŸ“– Overview
4
+ A custom template engine for Express that replicates **Django’s template language** in Node.js. It supports variables, filters, tags, inheritance, partials, escaping, and extensibility β€” providing full parity with Django templates while integrating seamlessly into Express.
5
+
6
+ ---
7
+
8
+ ## 🎯 Goals
9
+ - Full Django template feature parity in Node.js.
10
+ - Simple Express integration via `app.engine`.
11
+ - Extensible API for custom tags, filters, and context processors.
12
+ - Secure by default (autoescape enabled).
13
+ - Production-ready performance (AST caching, error handling).
14
+
15
+ ---
16
+
17
+ ## 🧩 Core Architecture
18
+ 1. **Lexer** β†’ Tokenizes template into `TEXT`, `VAR`, `BLOCK`.
19
+ 2. **Parser** β†’ Builds AST nodes for tags/filters.
20
+ 3. **Renderer** β†’ Walks AST, evaluates expressions, applies filters.
21
+ 4. **Filter Registry** β†’ Built-in + custom filters.
22
+ 5. **Tag Handlers** β†’ Functions for each tag (`if`, `for`, `block`, etc.).
23
+ 6. **Inheritance System** β†’ Block registry + parent merging.
24
+ 7. **Express Integration** β†’ `app.engine('dtpl', renderFile)`.
25
+
26
+ ---
27
+
28
+ ## 🏷️ Built-in Template Tags
29
+
30
+ | Tag | Purpose | Node.js Implementation |
31
+ |-----|---------|------------------------|
32
+ | **autoescape** | Toggle HTML escaping. | Maintain `context.autoescape` flag. |
33
+ | **block** | Define overridable content. | Store block AST in registry. |
34
+ | **extends** | Template inheritance. | Load parent file, merge blocks. |
35
+ | **include** | Insert partial template. | Load file, render with context. |
36
+ | **if/elif/else** | Conditional rendering. | Evaluate JS expression safely. |
37
+ | **for/empty** | Loop over iterable. | Inject `forloop` vars (`counter`, `first`, `last`). |
38
+ | **with** | Assign temporary variable. | Extend context object. |
39
+ | **cycle** | Alternate values. | Maintain cycle state per loop. |
40
+ | **comment** | Ignore enclosed content. | Strip from AST. |
41
+ | **verbatim** | Raw output. | Treat enclosed text as literal. |
42
+ | **csrf_token** | CSRF protection. | Insert token from context. |
43
+ | **csp_nonce_attr** | CSP nonce attribute. | Render `nonce="value"`. |
44
+ | **url** | Generate route URL. | Integrate with Express router. |
45
+ | **static** | Reference static files. | Map to Express static dir. |
46
+ | **regroup** | Group list by attribute. | Use JS `reduce`. |
47
+ | **spaceless** | Strip whitespace. | Regex replace in output. |
48
+
49
+ ---
50
+
51
+ ## πŸ”§ Built-in Filters
52
+
53
+ | Filter | Purpose | Node.js Implementation |
54
+ |--------|---------|------------------------|
55
+ | **Text** | `upper`, `lower`, `title`, `capfirst`, `truncatewords`, `truncatechars`, `wordcount`, `linebreaks`, `linebreaksbr`, `striptags`, `slugify` | JS string methods, regex, libraries. |
56
+ | **HTML** | `safe`, `escape` | Use `he` for escaping; safe flag. |
57
+ | **List** | `length`, `join`, `slice`, `dictsort`, `dictsortreversed` | JS array methods. |
58
+ | **Date/Time** | `date`, `time`, `timesince`, `timeuntil` | Wrap JS `Date` with `Intl.DateTimeFormat`. |
59
+ | **Numeric** | `add`, `divisibleby`, `floatformat` | Arithmetic ops; `toFixed`. |
60
+ | **Default** | `default`, `default_if_none` | Fallback values. |
61
+ | **Misc** | `pluralize`, `yesno`, `filesizeformat` | String transformations; filesize units. |
62
+
63
+ ---
64
+
65
+ ## πŸ” Escaping & Security
66
+ - Autoescape enabled by default.
67
+ - `safe` filter disables escaping.
68
+ - `escape` filter forces escaping.
69
+ - Use `he` library for HTML entity encoding.
70
+
71
+ ---
72
+
73
+ ## πŸ› οΈ Extensibility
74
+ - **Custom filters**: `filters[name] = fn`.
75
+ - **Custom tags**: Register handler functions in tag registry.
76
+ - **Context processors**: Middleware to inject globals (e.g., `user`, `request`).
77
+
78
+ ---
79
+
80
+ ## πŸ“‹ Development Roadmap
81
+
82
+ ### Phase 1 β€” Core Foundations
83
+ - Lexer, parser, renderer.
84
+ - Express integration.
85
+
86
+ ### Phase 2 β€” Variables & Filters
87
+ - Variables with dotted lookups.
88
+ - Full filter system (text, list, date, numeric, misc).
89
+ - Custom filter API.
90
+
91
+ ### Phase 3 β€” Control Flow Tags
92
+ - `if/elif/else`, `for/empty`, `with`, `cycle`, `comment`, `verbatim`.
93
+
94
+ ### Phase 4 β€” Template Inheritance
95
+ - `extends`, `block`, nested blocks.
96
+ - `include` for partials.
97
+
98
+ ### Phase 5 β€” Utilities & Helpers
99
+ - `static`, `url`, `regroup`, `spaceless`.
100
+
101
+ ### Phase 6 β€” Security
102
+ - Autoescape, safe strings, escape filter.
103
+
104
+ ### Phase 7 β€” Extensibility
105
+ - Custom tags/filters.
106
+ - Context processors.
107
+
108
+ ### Phase 8 β€” Advanced Features
109
+ - Querystring/context passing.
110
+ - Template partials with context overrides.
111
+ - Error handling.
112
+ - Performance optimizations (AST caching).
113
+
114
+ ### Phase 9 β€” Developer Experience
115
+ - Documentation.
116
+ - Testing suite.
117
+ - Packaging as npm module.
118
+ - Example apps.
119
+
120
+ ---
121
+
122
+ ## 🎯 Final Deliverables
123
+ - **Engine core**: Lexer, parser, renderer.
124
+ - **Filter library**: All Django filters.
125
+ - **Tag handlers**: All Django tags.
126
+ - **Inheritance system**: Full block/extends support.
127
+ - **Express integration**: `app.engine('dtpl', ...)`.
128
+ - **Extensibility API**: Custom tags/filters/context processors.
129
+ - **Security**: Autoescape + safe strings.
130
+ - **Docs & tests**: Full coverage.
131
+