miki-template 1.3.3 → 1.3.7
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/.eslintrc.json +16 -0
- package/.github/release-notes/v1.3.1.md +55 -55
- package/.github/release-notes/v1.3.3.md +77 -0
- package/.github/workflows/ci.yml +38 -54
- package/.github/workflows/release.yml +106 -0
- package/AGENT.md +71 -71
- package/API_REFERENCE.md +314 -314
- package/CHANGELOG.md +173 -169
- package/CODE_OF_CONDUCT.md +14 -14
- package/CONTRIBUTING.md +27 -27
- package/README.md +342 -321
- package/ROADMAP.md +40 -40
- package/benchmarks/report.json +16 -16
- package/benchmarks/run.js +49 -49
- package/benchmarks/stress.mjs +647 -647
- package/benchmarks/templates/large.dtpl +7 -7
- package/benchmarks/templates/medium.dtpl +3 -3
- package/benchmarks/templates/small.dtpl +7 -7
- package/context/component.md +109 -109
- package/context/prd.md +131 -131
- package/context/project-structure.md +33 -33
- package/dir/base.html +22 -22
- package/dir/cmpnt.html +10 -10
- package/dir/footer.html +2 -2
- package/dir/home.html +80 -80
- package/dir/index.html +80 -0
- package/dir/navbar.html +8 -8
- package/docs/README.md +18 -18
- package/docs/advanced_usage.md +71 -71
- package/docs/api.md +119 -119
- package/docs/filters.md +708 -708
- package/docs/installation.md +106 -106
- package/docs/overview.md +79 -57
- package/docs/partialdef.md +70 -70
- package/docs/security.md +27 -27
- package/docs/tags.md +673 -673
- package/docs/usage.md +646 -646
- package/eslint.config.mjs +42 -42
- package/ex.mjs +34 -32
- package/live-test/package-lock.json +915 -0
- package/live-test/package.json +9 -0
- package/live-test/server.js +14 -0
- package/live-test/views/base.html +8 -0
- package/live-test/views/child.html +7 -0
- package/live-test/views/index.html +1 -0
- package/miki-template-extension/.github/workflows/ci.yml +116 -116
- package/miki-template-extension/.vscodeignore +7 -7
- package/miki-template-extension/CHANGELOG.md +99 -99
- package/miki-template-extension/LICENSE +21 -21
- package/miki-template-extension/README.md +273 -273
- package/miki-template-extension/extension.js +1013 -1013
- package/miki-template-extension/icon.svg +10 -10
- package/miki-template-extension/package.json +280 -280
- package/miki-template-extension/snippets/miki-template.json +717 -717
- package/miki-template-extension/syntaxes/language-configuration.json +114 -114
- package/miki-template-extension/syntaxes/miki-template.tmLanguage.json +355 -355
- package/miki-template-extension/tests/grammar-tests.json +162 -162
- package/miki-template-extension/tests/run-grammar-tests.js +82 -82
- package/package.json +40 -34
- package/sample-app/package-lock.json +901 -0
- package/sample-app/package.json +9 -0
- package/sample-app/server.js +14 -0
- package/sample-app/views/index.html +1 -0
- package/scripts/build-vsix.js +129 -129
- package/scripts/build-vsix.ps1 +15 -15
- package/snippets/miki-template.json +177 -177
- package/src/asyncRender.js +20 -20
- package/src/cache.js +80 -80
- package/src/context.js +126 -126
- package/src/context_processors.js +48 -48
- package/src/esm.mjs +89 -84
- package/src/filters.js +975 -975
- package/src/i18n.js +171 -171
- package/src/index.js +1112 -940
- package/src/lexer.js +114 -114
- package/src/libraries.js +371 -371
- package/src/parser.js +270 -270
- package/src/security.js +53 -53
- package/src/tags/control.js +719 -719
- package/src/tags/extra.js +154 -154
- package/src/tags/helpers.js +26 -26
- package/src/tags/i18n.js +256 -256
- package/src/tags/inheritance.js +335 -335
- package/src/tags/registry.js +18 -18
- package/src/tags/util.js +400 -400
- package/src/types.d.ts +107 -107
- package/syntaxes/language-configuration.json +26 -26
- package/syntaxes/miki-template.tmLanguage.json +146 -146
- package/tests/asyncRender.test.js +17 -17
- package/tests/base.html +6 -6
- package/tests/child.html +3 -3
- package/tests/context_processors.test.js +13 -13
- package/tests/esm.test.mjs +61 -61
- package/tests/filters.test.js +254 -254
- package/tests/finder-appdirs.test.js +19 -0
- package/tests/finder.test.js +17 -0
- package/tests/fixtures/views/nested/index.html +1 -0
- package/tests/fixtures/views/partial.html +1 -0
- package/tests/fixtures/views/sub/deepfile.html +1 -0
- package/tests/fixtures/views-appdirs/product/site/detail.html +1 -0
- package/tests/include_security.test.js +9 -9
- package/tests/integration/README.md +32 -32
- package/tests/integration/features.test.cjs +1681 -1681
- package/tests/integration/features.test.mjs +1697 -1697
- package/tests/integration/finder.esm.test.mjs +13 -0
- package/tests/integration/templates/base.miki +6 -6
- package/tests/integration/templates/child.miki +6 -6
- package/tests/integration/templates/index.html +17 -17
- package/tests/lexer.test.js +45 -45
- package/tests/parser.test.js +57 -57
- package/tests/partial.html +1 -1
- package/tests/partialdef.test.js +79 -79
- package/tests/production_checks.js +57 -57
- package/tests/security.test.js +28 -28
- package/tests/tags.test.js +233 -233
|
@@ -1,7 +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 %}
|
|
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 %}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
// medium.dtpl
|
|
2
|
-
{% for i in range(0, 100) %}Item {{ i }}
|
|
3
|
-
{% endfor %}
|
|
1
|
+
// medium.dtpl
|
|
2
|
+
{% for i in range(0, 100) %}Item {{ i }}
|
|
3
|
+
{% endfor %}
|
|
@@ -1,7 +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 %}
|
|
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 %}
|
package/context/component.md
CHANGED
|
@@ -1,109 +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
|
-
|
|
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
CHANGED
|
@@ -1,131 +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
|
-
|
|
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
|
+
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
/project-root
|
|
2
|
-
/src
|
|
3
|
-
lexer.js
|
|
4
|
-
parser.js
|
|
5
|
-
renderer.js
|
|
6
|
-
filters.js
|
|
7
|
-
security.js
|
|
8
|
-
cache.js
|
|
9
|
-
context.js
|
|
10
|
-
tags/
|
|
11
|
-
control.js
|
|
12
|
-
inheritance.js
|
|
13
|
-
util.js
|
|
14
|
-
index.js
|
|
15
|
-
/templates
|
|
16
|
-
base.html
|
|
17
|
-
home.html
|
|
18
|
-
header.html
|
|
19
|
-
/examples
|
|
20
|
-
app.js
|
|
21
|
-
/tests
|
|
22
|
-
lexer.test.js
|
|
23
|
-
parser.test.js
|
|
24
|
-
renderer.test.js
|
|
25
|
-
filters.test.js
|
|
26
|
-
tags.test.js
|
|
27
|
-
security.test.js
|
|
28
|
-
README.md
|
|
29
|
-
API_REFERENCE.md
|
|
30
|
-
ROADMAP.md
|
|
31
|
-
CONTRIBUTING.md
|
|
32
|
-
CODE_OF_CONDUCT.md
|
|
33
|
-
package.json
|
|
1
|
+
/project-root
|
|
2
|
+
/src
|
|
3
|
+
lexer.js
|
|
4
|
+
parser.js
|
|
5
|
+
renderer.js
|
|
6
|
+
filters.js
|
|
7
|
+
security.js
|
|
8
|
+
cache.js
|
|
9
|
+
context.js
|
|
10
|
+
tags/
|
|
11
|
+
control.js
|
|
12
|
+
inheritance.js
|
|
13
|
+
util.js
|
|
14
|
+
index.js
|
|
15
|
+
/templates
|
|
16
|
+
base.html
|
|
17
|
+
home.html
|
|
18
|
+
header.html
|
|
19
|
+
/examples
|
|
20
|
+
app.js
|
|
21
|
+
/tests
|
|
22
|
+
lexer.test.js
|
|
23
|
+
parser.test.js
|
|
24
|
+
renderer.test.js
|
|
25
|
+
filters.test.js
|
|
26
|
+
tags.test.js
|
|
27
|
+
security.test.js
|
|
28
|
+
README.md
|
|
29
|
+
API_REFERENCE.md
|
|
30
|
+
ROADMAP.md
|
|
31
|
+
CONTRIBUTING.md
|
|
32
|
+
CODE_OF_CONDUCT.md
|
|
33
|
+
package.json
|
package/dir/base.html
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>
|
|
7
|
-
|
|
8
|
-
{% block title %}
|
|
9
|
-
Document
|
|
10
|
-
{% endblock title %}
|
|
11
|
-
|
|
12
|
-
</title>
|
|
13
|
-
</head>
|
|
14
|
-
<body>
|
|
15
|
-
{% include "navbar.html" %}
|
|
16
|
-
|
|
17
|
-
{% block content %}
|
|
18
|
-
|
|
19
|
-
{% endblock content %}
|
|
20
|
-
{% include "footer.html" %}
|
|
21
|
-
|
|
22
|
-
</body>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>
|
|
7
|
+
|
|
8
|
+
{% block title %}
|
|
9
|
+
Document
|
|
10
|
+
{% endblock title %}
|
|
11
|
+
|
|
12
|
+
</title>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
{% include "navbar.html" %}
|
|
16
|
+
|
|
17
|
+
{% block content %}
|
|
18
|
+
|
|
19
|
+
{% endblock content %}
|
|
20
|
+
{% include "footer.html" %}
|
|
21
|
+
|
|
22
|
+
</body>
|
|
23
23
|
</html>
|
package/dir/cmpnt.html
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
{% partialdef me %}
|
|
2
|
-
<h2>here is some content</h2>
|
|
3
|
-
{% endpartialdef %}
|
|
4
|
-
{% partialdef you %}
|
|
5
|
-
<h2>here is some content for you</h2>
|
|
6
|
-
{% endpartialdef %}
|
|
7
|
-
{% partialdef us inline %}
|
|
8
|
-
{% partial me%}
|
|
9
|
-
{% partial you%}
|
|
10
|
-
<h2>here is some content for us</h2>
|
|
1
|
+
{% partialdef me %}
|
|
2
|
+
<h2>here is some content</h2>
|
|
3
|
+
{% endpartialdef %}
|
|
4
|
+
{% partialdef you %}
|
|
5
|
+
<h2>here is some content for you</h2>
|
|
6
|
+
{% endpartialdef %}
|
|
7
|
+
{% partialdef us inline %}
|
|
8
|
+
{% partial me%}
|
|
9
|
+
{% partial you%}
|
|
10
|
+
<h2>here is some content for us</h2>
|
|
11
11
|
{% endpartialdef %}
|
package/dir/footer.html
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
<footer>
|
|
2
|
-
<h3>some footer content with : {{siteName}}</h3>
|
|
1
|
+
<footer>
|
|
2
|
+
<h3>some footer content with : {{siteName}}</h3>
|
|
3
3
|
</footer>
|