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
@@ -0,0 +1,54 @@
1
+ # CI/CD Workflow
2
+
3
+ name: CI
4
+
5
+ on:
6
+ push:
7
+ branches: [ main ]
8
+ pull_request:
9
+ branches: [ main ]
10
+ release:
11
+ types: [ created ]
12
+
13
+ jobs:
14
+ lint:
15
+ runs-on: windows-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - name: Setup Node.js
19
+ uses: actions/setup-node@v4
20
+ with:
21
+ node-version: '20'
22
+ - run: npm ci
23
+ - name: Lint code
24
+ run: npx eslint src/**/*.js
25
+
26
+ test:
27
+ runs-on: windows-latest
28
+ needs: lint
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - name: Setup Node.js
32
+ uses: actions/setup-node@v4
33
+ with:
34
+ node-version: '20'
35
+ - run: npm ci
36
+ - name: Run test suite
37
+ run: npm test
38
+
39
+ publish:
40
+ runs-on: windows-latest
41
+ needs: test
42
+ if: github.event_name == 'release' && github.event.action == 'created'
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - name: Setup Node.js
46
+ uses: actions/setup-node@v4
47
+ with:
48
+ node-version: '20'
49
+ registry-url: 'https://registry.npmjs.org'
50
+ - run: npm ci
51
+ - name: Publish package to npm
52
+ run: npm publish --access public
53
+ env:
54
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/AGENT.md ADDED
@@ -0,0 +1,71 @@
1
+ # AGENT.md - Agent Instructions, Guardrails, & Architecture Rules
2
+
3
+ This repository implements a Django-Style Template Engine for Node.js/Express. To ensure high code quality, consistent architecture, and full feature parity with Django without guesswork, all AI agents and developers must strictly follow these instructions and guardrails.
4
+
5
+ ---
6
+
7
+ ## 🎯 Core Objectives
8
+ 1. **Full Feature Parity**: Match Django's template engine rules for rendering variables, dotted lookups, built-in tags, filters, and template inheritance.
9
+ 2. **Deterministic & Secure**: Escape output by default using a safe-string mechanism.
10
+ 3. **No Spaghetti Code**: Maintain a strict separation of Lexer, Parser, Renderer, Context, and Tag/Filter registries.
11
+
12
+ ---
13
+
14
+ ## ⚠️ Strict Guardrails & Anti-Hallucination Rules
15
+
16
+ > [!IMPORTANT]
17
+ > **No Guesswork / Assumptions**
18
+ > - If a tag or filter's behavior is not clearly defined in [prd.md](file:///c:/Users/Coder%20Miki/Desktop/miki-template/context/prd.md) or Django documentation, **DO NOT guess**.
19
+ > - If you notice ambiguities (e.g., how to handle circular extends or nested cycles), raise a question to the user immediately or throw a descriptive compilation error.
20
+
21
+ > [!WARNING]
22
+ > **No Regex-Based Global Search & Replace**
23
+ > - You **must not** implement tags or filters using global regex replacements on the final string. All templates must go through:
24
+ > `Lexer (String -> Tokens) -> Parser (Tokens -> AST) -> Renderer (AST + Context -> HTML)`.
25
+ > - Direct regex replacement on HTML bypasses nesting, escaping, and blocks, which introduces severe bugs.
26
+
27
+ > [!CAUTION]
28
+ > **Security Guardrails**
29
+ > - All variable output must be HTML-escaped by default.
30
+ > - A variable is only safe from escaping if it is marked as a `SafeString` (e.g., using the `safe` filter or internally flagged).
31
+ > - Dynamic expression evaluation (such as in `if` tags) **must not** use direct JS `eval()`. Use a safe AST evaluator or sandboxed parser to prevent remote code execution.
32
+
33
+ ---
34
+
35
+ ## 🧩 Architectural Guidelines
36
+
37
+ ### 1. Lexing (`src/lexer.js`)
38
+ * Tokens must be generated for three primary types:
39
+ - `TEXT`: Plain HTML/text.
40
+ - `VAR`: Variables wrapped in `{{ ... }}`.
41
+ - `BLOCK`: Structural tags wrapped in `{% ... %}`.
42
+ * The Lexer must correctly handle verbatim blocks `{% verbatim %}` and comments `{% comment %}` by suppressing token output or passing raw text tokens.
43
+
44
+ ### 2. Parsing (`src/parser.js`)
45
+ * The Parser takes a stream of tokens and constructs an AST of nodes.
46
+ * Every node must implement a `.render(context)` method (async or sync).
47
+ * For tags that have closing tags (e.g., `{% if %}`...`{% endif %}`), the parser must parse nested tokens recursively until the matching close tag is encountered.
48
+
49
+ ### 3. Context & Scope Isolation (`src/context.js`)
50
+ * Context must support a stack-like structure: `.push()` to create a new scope, and `.pop()` to revert.
51
+ * **Variable Lookups**: Support dotted lookups:
52
+ - Example: `{{ user.profile.name }}` should search `user` in context, then resolve property `profile`, then property `name`.
53
+ - If a resolved value is a function, call it (without arguments, just like Django).
54
+ - If a lookup fails, return an empty string `""` by default, unless configured otherwise.
55
+
56
+ ### 4. Template Inheritance (`src/tags/inheritance.js`)
57
+ * Inheritance works by having a child template load a parent template via `{% extends "parent.html" %}`.
58
+ * The child overrides blocks defined as `{% block block_name %}`.
59
+ * During render, the parent's AST is evaluated, but block nodes are replaced by the child's corresponding block nodes.
60
+ * Support block nesting and the `{{ block.super }}` variable to render parent block content.
61
+
62
+ ---
63
+
64
+ ## 🛠️ Step-by-Step Task Execution Protocol
65
+
66
+ When executing tasks:
67
+ 1. **Analyze Requirements**: Check [prd.md](file:///c:/Users/Coder%20Miki/Desktop/miki-template/context/prd.md) and [component.md](file:///c:/Users/Coder%20Miki/Desktop/miki-template/context/component.md).
68
+ 2. **Check Existing Tests**: Run current test suite using `npm test` or `node --test` to ensure a green state.
69
+ 3. **Write Tests First**: For any new filter, tag, or parser logic, write a corresponding unit test in `tests/` showing the expected template string and its expected output.
70
+ 4. **Implement Modular Code**: Put tag handlers in `src/tags/`, filters in `src/filters.js`, and maintain registry isolation.
71
+ 5. **Verify**: Ensure the test suite passes, check for escaping vulnerabilities, and check that no scope leaks occur.
@@ -0,0 +1,314 @@
1
+ # API Reference - miki-template
2
+
3
+ This document provides complete details for the public APIs exported by the **miki-template** engine — a Django-style template engine for Node.js and Express.
4
+
5
+ ---
6
+
7
+ ## Core Exports
8
+
9
+ ```javascript
10
+ const {
11
+ compile,
12
+ render,
13
+ asyncRender,
14
+ __express,
15
+ clearCache,
16
+ registerTag,
17
+ registerFilter,
18
+ registerHelper,
19
+ registerContextProcessor,
20
+ SafeString,
21
+ markSafe,
22
+ isSafe,
23
+ escapeHtml
24
+ } = require('miki-template');
25
+ ```
26
+
27
+ ---
28
+
29
+ ### `compile(templateStr, options)`
30
+
31
+ Compiles a raw template string into a reusable compiled template object. The compiled template caches its AST internally.
32
+
33
+ **Parameters**:
34
+ - `templateStr` (string): The raw template string to compile.
35
+ - `options` (object): Config parameters:
36
+ - `views` (string|string[]): Directories to search for templates when using `extends` or `include`.
37
+ - `staticUrl` (string): Prefix for the `{% static %}` tag. Defaults to `/static/`.
38
+ - `urlHelper` (function): Custom URL resolver for `{% url %}` tag.
39
+
40
+ **Returns**: An object containing:
41
+ - `render(context)` — Synchronously renders the template.
42
+ - `asyncRender(context)` — Returns a Promise for async rendering.
43
+ - `renderBlock(blockName, context)` — Renders a specific block by name.
44
+ - `renderPartial(partialName, context)` — Renders a defined partial by name.
45
+
46
+ **Example**:
47
+ ```javascript
48
+ const { compile } = require('miki-template');
49
+
50
+ const template = compile('Hello {{ name }}!');
51
+ const output = template.render({ name: 'World' });
52
+ console.log(output); // Hello World!
53
+ ```
54
+
55
+ ---
56
+
57
+ ### `render(templateStr, context, options)`
58
+
59
+ One-step convenience function that compiles and renders a template.
60
+
61
+ **Parameters**:
62
+ - `templateStr` (string): Raw template string.
63
+ - `context` (object): Variables available to the template.
64
+ - `options` (object): Same as `compile()` options.
65
+
66
+ **Returns**: Rendered HTML string.
67
+
68
+ ---
69
+
70
+ ### `asyncRender(templateStr, context, options)`
71
+
72
+ Asynchronous rendering for templates with async helpers or async context processors.
73
+
74
+ **Parameters**: Same as `render()`.
75
+
76
+ **Returns**: `Promise<string>` — resolved rendered HTML.
77
+
78
+ **Example**:
79
+ ```javascript
80
+ const { asyncRender } = require('miki-template');
81
+ const html = await asyncRender('Hello {{ name }}!', { name: 'Async' });
82
+ ```
83
+
84
+ ---
85
+
86
+ ### `__express(filePath, options, callback)`
87
+
88
+ Express-compatible view engine adapter. Use with `app.engine()`.
89
+
90
+ **Parameters**:
91
+ - `filePath` (string): Absolute path to the template file.
92
+ - `options` (object): Express `res.render()` context (view engine strips `_locals`, `settings`, and other Express internals).
93
+ - `callback` (function): Node.js callback `(err, html)`.
94
+
95
+ **Example**:
96
+ ```javascript
97
+ const express = require('express');
98
+ const { __express } = require('miki-template');
99
+
100
+ const app = express();
101
+ app.engine('html', __express);
102
+ app.set('view engine', 'html');
103
+ app.set('views', './views');
104
+
105
+ app.get('/', (req, res) => {
106
+ res.render('home', { title: 'Home Page' });
107
+ });
108
+ ```
109
+
110
+ ---
111
+
112
+ ### `registerTag(name, parserFn)`
113
+
114
+ Registers a custom block tag parser. Must be called **before** compiling templates that use the tag.
115
+
116
+ **Parameters**:
117
+ - `name` (string): Tag identifier word (e.g. `mytag` for `{% mytag %}`).
118
+ - `parserFn` (function): `(tagContent: string, parser: Parser) => ASTNode`. Receives the raw tag content and the parser instance.
119
+
120
+ **Returns**: `undefined`.
121
+
122
+ **Example**:
123
+ ```javascript
124
+ const { registerTag } = require('miki-template');
125
+
126
+ registerTag('greet', (tagContent, parser) => {
127
+ const name = tagContent.slice(5).trim(); // strip 'greet'
128
+ return {
129
+ render: (context) => `Hello ${context.get(name) || 'Guest'}!`
130
+ };
131
+ });
132
+
133
+ // In template: {% greet user.name %}
134
+ ```
135
+
136
+ ---
137
+
138
+ ### `registerFilter(name, filterFn)`
139
+
140
+ Registers a custom filter function.
141
+
142
+ **Parameters**:
143
+ - `name` (string): Filter name used after the pipe `|`.
144
+ - `filterFn` (function): `(value: any, arg?: any) => any`. Receives the filtered value and optional argument.
145
+
146
+ **Returns**: `undefined`.
147
+
148
+ **Example**:
149
+ ```javascript
150
+ const { registerFilter } = require('miki-template');
151
+
152
+ registerFilter('reverse', (val) => {
153
+ return String(val).split('').reverse().join('');
154
+ });
155
+
156
+ // In template: {{ name|reverse }}
157
+ ```
158
+
159
+ ---
160
+
161
+ ### `registerHelper(name, fn)`
162
+
163
+ Registers a custom block helper tag. The helper receives the **rendered** inner content as a string.
164
+
165
+ **Parameters**:
166
+ - `name` (string): Tag name (e.g. `markdown` creates `{% markdown %}...{% endmarkdown %}`).
167
+ - `fn` (function): `(innerContent: string, context: Context) => string | Promise<string>`.
168
+
169
+ **Returns**: `undefined`.
170
+
171
+ **Example**:
172
+ ```javascript
173
+ const { registerHelper } = require('miki-template');
174
+ const markdownIt = require('markdown-it')();
175
+ registerHelper('markdown', (content) => markdownIt.render(content));
176
+
177
+ // In template:
178
+ // {% markdown %}
179
+ // # Hello
180
+ // {% endmarkdown %}
181
+ ```
182
+
183
+ ---
184
+
185
+ ### `registerContextProcessor(fn)`
186
+
187
+ Adds a context processor function. Similar to Django's custom context processors.
188
+
189
+ **Parameters**:
190
+ - `fn` (function): `(context: object) => object | undefined`. Receives the render context and returns extra key-value pairs to merge in.
191
+
192
+ **Returns**: `undefined`.
193
+
194
+ **Example**:
195
+ ```javascript
196
+ const { registerContextProcessor } = require('miki-template');
197
+
198
+ registerContextProcessor((ctx) => ({
199
+ siteName: 'MySite',
200
+ currentYear: new Date().getFullYear()
201
+ }));
202
+
203
+ // Available in all templates as {{ siteName }} and {{ currentYear }}
204
+ ```
205
+
206
+ ---
207
+
208
+ ### `SafeString`
209
+
210
+ Class for values that should **bypass HTML auto-escaping**. Construct directly or use `markSafe()`.
211
+
212
+ **Example**:
213
+ ```javascript
214
+ const { SafeString } = require('miki-template');
215
+
216
+ const html = new SafeString('<b>Bold</b>');
217
+ // {{ html }} renders as <b>Bold</b>, NOT &lt;b&gt;Bold&lt;/b&gt;
218
+ ```
219
+
220
+ ---
221
+
222
+ ### `markSafe(value)`
223
+
224
+ Wraps any value in a `SafeString`, instructing the engine to skip HTML escaping for that value.
225
+
226
+ **Parameters**:
227
+ - `value` (any): Value to mark as safe.
228
+
229
+ **Returns**: `SafeString`.
230
+
231
+ **Example**:
232
+ ```javascript
233
+ const { markSafe } = require('miki-template');
234
+
235
+ const html = markSafe('<script>alert("xss")</script>');
236
+ // {{ html }} outputs the script tag literally (use with caution)
237
+ ```
238
+
239
+ ---
240
+
241
+ ### `isSafe(value)`
242
+
243
+ Checks whether a value is a `SafeString` instance.
244
+
245
+ **Parameters**:
246
+ - `value` (any): Value to check.
247
+
248
+ **Returns**: `boolean`.
249
+
250
+ ---
251
+
252
+ ### `escapeHtml(str)`
253
+
254
+ Programmatically escapes HTML special characters (`<`, `>`, `&`, `"`, `'`).
255
+
256
+ **Parameters**:
257
+ - `str` (string): String to escape.
258
+
259
+ **Returns**: `string`.
260
+
261
+ ---
262
+
263
+ ### `clearCache()`
264
+
265
+ Clears the in-memory AST cache. Useful for development or when templates change at runtime.
266
+
267
+ **Example**:
268
+ ```javascript
269
+ const { clearCache } = require('miki-template');
270
+
271
+ app.on('restart', () => clearCache());
272
+ ```
273
+
274
+ ---
275
+
276
+ ## Compiled Template API
277
+
278
+ `compile()` returns an object with these methods:
279
+
280
+ ### `compiled.render(context)`
281
+
282
+ Synchronously renders the template with the given context.
283
+
284
+ ```javascript
285
+ const compiled = compile('Hello {{ name }}!');
286
+ compiled.render({ name: 'World' }); // "Hello World!"
287
+ ```
288
+
289
+ ### `compiled.asyncRender(context)`
290
+
291
+ Renders asynchronously, awaiting any Promise-returning helpers.
292
+
293
+ ```javascript
294
+ const html = await compiled.asyncRender({ name: 'Async' });
295
+ ```
296
+
297
+ ### `compiled.renderBlock(blockName, context)`
298
+
299
+ Renders **only** the named block. Useful for HTMX or AJAX partial responses.
300
+
301
+ ```javascript
302
+ // Template: {% extends "base.html" %}
303
+ // {% block content %}Main{% endblock %}
304
+ compiled.renderBlock('content', {}); // "Main"
305
+ ```
306
+
307
+ ### `compiled.renderPartial(partialName, context)`
308
+
309
+ Renders a `{% partialdef %}` block by name.
310
+
311
+ ```javascript
312
+ // Template: {% partialdef header %}My Header{% endpartialdef %}
313
+ compiled.renderPartial('header', {}); // "My Header"
314
+ ```
package/CHANGELOG.md ADDED
@@ -0,0 +1,97 @@
1
+ # Changelog
2
+
3
+ ## [1.2.0] - 2026-09-01
4
+ ### Added
5
+ - Full **ESM** support via `src/esm.mjs` wrapper and conditional `package.json` exports.
6
+ - **TypeScript** type definitions (`src/types.d.ts`).
7
+ - **Async Express 5+** adapter: `__expressAsync(filePath, options) → Promise<string>`.
8
+ - **i18n**: `trans` tag/filter, `blocktrans`, `language`, plural rules, `registerTranslation`.
9
+ - **Plugin/library system**: `registerLibrary`, `registerLibraryFromPath`, built-ins (`humanize`, `cache`, `lorem`).
10
+ - **`load` tag** now activates registered libraries (no longer a stub).
11
+ - **`widthratio`** tag for proportional width calculations.
12
+ - **`debug`** tag for dumping template context during development.
13
+ - **`regroup`** filter for grouping arrays by attribute.
14
+ - **`strftime`** filter with full `date-fns` format support.
15
+ - **Unclosed tag validation**: throws descriptive errors for missing `endif`/`endfor`/`endwith`.
16
+ - **Filter chaining on string literals**: `{{ "Hello"|lower|capfirst }}` now works.
17
+ - New exports: `getFilter`, `stripExpressContext`, `activateLibrary`.
18
+
19
+ ### Fixed
20
+ - `for` loop now supports filter expressions in iterable path (e.g. `items|regroup:"category"`).
21
+ - `Context.reset()` properly clears cycle state and blocks between renders.
22
+ - `__express` strips Express framework keys (`_locals`, `settings`, `cache`) from context.
23
+ - `renderPartial` renders only the named partial, not the full template.
24
+ - `with` tag correctly handles multiple `key=value` assignments and quoted values.
25
+ - `if` tag condition evaluation handles `not`, `and`, `or`, and operator precedence correctly.
26
+ - `forloop.parentloop` correctly propagates for nested loops.
27
+ - `floatformat` default behavior matches Django (1 decimal place).
28
+ - `timesince`/`timeuntil` handle seconds, minutes, hours, days correctly.
29
+ - `pluralize` correctly handles singular vs plural forms.
30
+ - `default` filter treats empty string `''` as falsy like Django.
31
+ - `striptags` correctly removes HTML tags and handles edge cases.
32
+ - Spaceless tag preserves whitespace inside tag attributes correctly.
33
+
34
+ ### Changed
35
+ - Condition evaluation rewritten to use shunting-yard algorithm for correct operator precedence.
36
+ - Simplified `for` loop to use `[key, value]` tuples internally for cleaner unpacking.
37
+ - Improved `partialdef` to support both quoted and unquoted partial names.
38
+ - `floatformat` with `-1` argument now removes all decimals.
39
+
40
+ ## [1.1.0] - 2026-08-31
41
+ ### Added
42
+ - Missing `comment`/`endcomment` tag support for block comments.
43
+ - `firstof` tag to return the first non-falsy value from arguments.
44
+ - `length_is` filter for comparing length to a value.
45
+ - `urlencode`, `escapeuri`, `stringformat`, `cut`, `addslashes`, `removetags` filters for Django parity.
46
+ - `Context.reset()` method for clearing state between renders.
47
+ - `firstof` filter variant for inline first-truthy selection.
48
+
49
+ ### Fixed
50
+ - Malformed code in `inheritance.js` with extra closing braces.
51
+ - `__express` was not exported from the module.
52
+ - `__express` now strips Express framework keys (`_locals`, `settings`, etc.) from context.
53
+ - `renderPartial` now properly extracts and renders only the named partial definition.
54
+ - `with` tag now correctly handles multiple `key=value` assignments and quoted values.
55
+ - `if` tag condition evaluation now properly handles `not`, `and`, `or`, and operator precedence.
56
+ - `for` loop now correctly unpacks `key, value` from objects and `item, index` from arrays.
57
+ - `forloop.parentloop` now correctly chains for nested loops.
58
+ - Cycle state (`cycleStates`) now resets per render via `Context.reset()`.
59
+ - Async helpers now work correctly with `asyncRender`.
60
+ - Partial definitions now collected at compile time for proper `renderPartial` support.
61
+ - Path traversal protection added to `extends` tag (previously only on `include`).
62
+ - `floatformat` default behavior now matches Django (1 decimal place).
63
+ - `timesince`/`timeuntil` now handles seconds, minutes, hours, days correctly.
64
+ - `pluralize` now correctly handles singular vs plural forms.
65
+ - `default` filter now treats empty string `''` as falsy like Django.
66
+ - `striptags` now correctly removes HTML tags and handles edge cases.
67
+ - Spaceless tag now preserves whitespace inside tag attributes correctly.
68
+
69
+ ### Changed
70
+ - Rewrote condition evaluation to use shunting-yard algorithm for correct operator precedence.
71
+ - Simplified `for` loop to use `[key, value]` tuples internally for cleaner unpacking.
72
+ - Improved `partialdef` to support both quoted and unquoted partial names.
73
+ - `floatformat` with `-1` argument now removes all decimals.
74
+
75
+ ## [1.0.0] - 2026-08-29
76
+ ### Added
77
+ - Full Django‑style template engine with variables, filters, tags, inheritance, partials, and context processors.
78
+ - Security features: auto‑escaping, `markSafe`, `csrf_token` and `csp_nonce_attr` tags.
79
+ - Extensible API: `registerTag`, `registerFilter`, `markSafe`.
80
+ - Express integration via `__express` adapter.
81
+ - Comprehensive test suite (40 passing tests) and benchmark suite.
82
+ - Detailed documentation hierarchy (`docs/`): overview, installation, usage, tags, filters, partialdef, security, API reference, contribution guide.
83
+ - CI workflow using GitHub Actions.
84
+ - Project scaffolding, linting, and contribution guardrails (`AGENT.md`).
85
+
86
+ ### Fixed
87
+ - Auto‑escaping now respects `SafeString` values.
88
+ - Context processors correctly propagate returned context.
89
+ - Fixed tag parsing edge cases in `IfNode` and `PartialDefNode`.
90
+
91
+ ### Changed
92
+ - Updated README with npm version and CI badges.
93
+ - Added `CHANGELOG.md` for release notes.
94
+
95
+ [1.2.0]: https://github.com/your-repo/miki-template/releases/tag/v1.2.0
96
+ [1.1.0]: https://github.com/your-repo/miki-template/releases/tag/v1.1.0
97
+ [1.0.0]: https://github.com/your-repo/miki-template/releases/tag/v1.0.0
@@ -0,0 +1,14 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+ We pledge to make participation in our project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity, experience level, nationality, personal appearance, race, religion, or sexual orientation.
5
+
6
+ ## Our Standards
7
+ Examples of behavior that contributes to creating a positive environment include:
8
+ - Using welcoming and inclusive language.
9
+ - Being respectful of differing viewpoints and experiences.
10
+ - Gracefully accepting constructive criticism.
11
+ - Focusing on what is best for the community.
12
+ - Showing empathy towards other community members.
13
+
14
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct.
@@ -0,0 +1,27 @@
1
+ # Contributing to Django-Style Template Engine
2
+
3
+ Thank you for your interest in contributing! Please follow these guidelines:
4
+
5
+ ---
6
+
7
+ ## 🛠️ Development Setup
8
+
9
+ 1. **Clone the Repository**
10
+ 2. **Install Dependencies**:
11
+ ```bash
12
+ npm install
13
+ ```
14
+ 3. **Run the Tests**:
15
+ ```bash
16
+ npm test
17
+ ```
18
+
19
+ ---
20
+
21
+ ## 🛑 Guardrails & Standards
22
+
23
+ Before submitting a PR, make sure your code aligns with the strict requirements outlined in [AGENT.md](file:///c:/Users/Coder%20Miki/Desktop/miki-template/AGENT.md):
24
+ - Ensure full parity with Django syntax behavior.
25
+ - Do not use `eval()` for safety.
26
+ - Write corresponding unit tests in `tests/` for all new features or bug fixes.
27
+ - Verify that auto-escaping remains secure by default.