miki-template 2.3.1 → 2.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +1 -1
  2. package/package.json +24 -2
  3. package/src/codegen.js +140 -9
  4. package/src/parser.js +69 -1
  5. package/src/tags/control.js +112 -51
  6. package/src/tags/extra.js +4 -17
  7. package/src/tags/i18n.js +6 -17
  8. package/src/tags/inheritance.js +6 -20
  9. package/src/tags/util.js +5 -20
  10. package/.eslintrc.json +0 -16
  11. package/.github/release-notes/v1.3.1.md +0 -55
  12. package/.github/release-notes/v1.3.3.md +0 -77
  13. package/.github/release-notes/v1.3.7.md +0 -20
  14. package/.github/workflows/ci.yml +0 -42
  15. package/.github/workflows/docs.yml +0 -107
  16. package/.github/workflows/release.yml +0 -107
  17. package/AGENT.md +0 -71
  18. package/API_REFERENCE.md +0 -341
  19. package/CHANGELOG.md +0 -173
  20. package/CODE_OF_CONDUCT.md +0 -14
  21. package/CONTRIBUTING.md +0 -27
  22. package/ROADMAP.md +0 -40
  23. package/assets/banner.png +0 -0
  24. package/assets/logo.png +0 -0
  25. package/benchmarks/ejs-results.json +0 -17
  26. package/benchmarks/ejs.js +0 -38
  27. package/benchmarks/handlebars-results.json +0 -17
  28. package/benchmarks/handlebars.js +0 -45
  29. package/benchmarks/miki-results.json +0 -17
  30. package/benchmarks/miki.js +0 -39
  31. package/benchmarks/pug-results.json +0 -17
  32. package/benchmarks/pug.js +0 -38
  33. package/benchmarks/report.json +0 -17
  34. package/benchmarks/run.js +0 -81
  35. package/benchmarks/stress.mjs +0 -647
  36. package/benchmarks/templates/large.dtpl +0 -7
  37. package/benchmarks/templates/medium.dtpl +0 -3
  38. package/benchmarks/templates/small.dtpl +0 -7
  39. package/context/component.md +0 -109
  40. package/context/prd.md +0 -131
  41. package/context/project-structure.md +0 -33
  42. package/dir/base.html +0 -23
  43. package/dir/cmpnt.html +0 -11
  44. package/dir/footer.html +0 -3
  45. package/dir/home.html +0 -80
  46. package/dir/index.html +0 -80
  47. package/dir/navbar.html +0 -9
  48. package/docs/api/async-render.md +0 -170
  49. package/docs/api/cache.md +0 -174
  50. package/docs/api/compile.md +0 -256
  51. package/docs/api/context-processors.md +0 -154
  52. package/docs/api/filters.md +0 -437
  53. package/docs/api/finder.md +0 -188
  54. package/docs/api/helpers.md +0 -106
  55. package/docs/api/i18n.md +0 -314
  56. package/docs/api/index.md +0 -108
  57. package/docs/api/libraries.md +0 -414
  58. package/docs/api/render-partial.md +0 -162
  59. package/docs/api/render.md +0 -184
  60. package/docs/api/security.md +0 -290
  61. package/docs/api/setup-express.md +0 -152
  62. package/docs/api/tags.md +0 -268
  63. package/docs/assets/banner.png +0 -0
  64. package/docs/assets/logo.png +0 -0
  65. package/docs/filter.md +0 -0
  66. package/docs/guide/advanced-usage.md +0 -794
  67. package/docs/guide/async-rendering.md +0 -616
  68. package/docs/guide/context-processors.md +0 -514
  69. package/docs/guide/custom-filters.md +0 -622
  70. package/docs/guide/custom-tags.md +0 -542
  71. package/docs/guide/filters.md +0 -1314
  72. package/docs/guide/getting-started.md +0 -204
  73. package/docs/guide/installation.md +0 -190
  74. package/docs/guide/partial-templates.md +0 -734
  75. package/docs/guide/quick-start.md +0 -444
  76. package/docs/guide/security.md +0 -690
  77. package/docs/guide/tags.md +0 -1566
  78. package/docs/guide/template-discovery.md +0 -340
  79. package/docs/guide/template-inheritance.md +0 -546
  80. package/docs/guide/what-is-miki-template.md +0 -28
  81. package/docs/guide/why-miki-template.md +0 -75
  82. package/docs/index.md +0 -86
  83. package/docs/integrations/elysia.md +0 -80
  84. package/docs/integrations/express.md +0 -219
  85. package/docs/integrations/fastify.md +0 -79
  86. package/docs/integrations/hono.md +0 -80
  87. package/docs/integrations/index.md +0 -68
  88. package/docs/integrations/koa.md +0 -90
  89. package/docs/integrations/nestjs.md +0 -80
  90. package/docs/integrations/tsed.md +0 -83
  91. package/docs/performance.md +0 -74
  92. package/eslint.config.mjs +0 -42
  93. package/ex.mjs +0 -36
  94. package/live-test/integrations/elysia-example.js +0 -16
  95. package/live-test/integrations/express-example.js +0 -24
  96. package/live-test/integrations/fastify-example.js +0 -20
  97. package/live-test/integrations/hono-example.js +0 -16
  98. package/live-test/integrations/koa-example.js +0 -30
  99. package/live-test/integrations/nestjs-example.js +0 -25
  100. package/live-test/integrations/smoke-test.js +0 -166
  101. package/live-test/integrations/tsed-example.js +0 -23
  102. package/live-test/package-lock.json +0 -1150
  103. package/live-test/package.json +0 -13
  104. package/live-test/packages/product/templates/product/detail.html +0 -7
  105. package/live-test/server.js +0 -38
  106. package/live-test/templates/app_templates/detail.html +0 -6
  107. package/live-test/views/base.html +0 -8
  108. package/live-test/views/child.html +0 -7
  109. package/live-test/views/home.html +0 -17
  110. package/live-test/views/index.html +0 -1
  111. package/miki-template-extension/.github/workflows/ci.yml +0 -116
  112. package/miki-template-extension/.vscodeignore +0 -7
  113. package/miki-template-extension/CHANGELOG.md +0 -99
  114. package/miki-template-extension/LICENSE +0 -21
  115. package/miki-template-extension/README.md +0 -273
  116. package/miki-template-extension/extension.js +0 -1013
  117. package/miki-template-extension/icon.png +0 -0
  118. package/miki-template-extension/icon.svg +0 -10
  119. package/miki-template-extension/miki-template-1.7.1.vsix +0 -0
  120. package/miki-template-extension/package.json +0 -280
  121. package/miki-template-extension/snippets/miki-template.json +0 -717
  122. package/miki-template-extension/syntaxes/language-configuration.json +0 -114
  123. package/miki-template-extension/syntaxes/miki-template.tmLanguage.json +0 -355
  124. package/miki-template-extension/tests/grammar-tests.json +0 -162
  125. package/miki-template-extension/tests/run-grammar-tests.js +0 -82
  126. package/mkdocs.yml +0 -195
  127. package/overrides/main.html +0 -26
  128. package/requirements-docs.txt +0 -2
  129. package/sample-app/package-lock.json +0 -901
  130. package/sample-app/package.json +0 -9
  131. package/sample-app/server.js +0 -14
  132. package/sample-app/views/index.html +0 -1
  133. package/scripts/build-vsix.js +0 -129
  134. package/scripts/build-vsix.ps1 +0 -15
  135. package/snippets/miki-template.json +0 -177
  136. package/src/asyncRender.js +0 -21
  137. package/src/types.d.ts +0 -107
  138. package/syntaxes/language-configuration.json +0 -26
  139. package/syntaxes/miki-template.tmLanguage.json +0 -146
  140. package/tests/asyncRender.test.js +0 -17
  141. package/tests/base.html +0 -6
  142. package/tests/child.html +0 -3
  143. package/tests/context_processors.test.js +0 -13
  144. package/tests/esm.test.mjs +0 -61
  145. package/tests/filters.test.js +0 -263
  146. package/tests/finder-appdirs.test.js +0 -19
  147. package/tests/finder.test.js +0 -17
  148. package/tests/fixtures/views/nested/index.html +0 -1
  149. package/tests/fixtures/views/partial.html +0 -1
  150. package/tests/fixtures/views/sub/deepfile.html +0 -1
  151. package/tests/fixtures/views-appdirs/product/site/detail.html +0 -1
  152. package/tests/include_security.test.js +0 -9
  153. package/tests/integration/README.md +0 -32
  154. package/tests/integration/features.test.cjs +0 -1681
  155. package/tests/integration/features.test.mjs +0 -1697
  156. package/tests/integration/finder.esm.test.mjs +0 -13
  157. package/tests/integration/partial-render.test.cjs +0 -13
  158. package/tests/integration/templates/base.miki +0 -6
  159. package/tests/integration/templates/child.miki +0 -6
  160. package/tests/integration/templates/index.html +0 -17
  161. package/tests/lexer.test.js +0 -45
  162. package/tests/parser.test.js +0 -57
  163. package/tests/partial.html +0 -1
  164. package/tests/partialdef.test.js +0 -79
  165. package/tests/production_checks.js +0 -57
  166. package/tests/security.test.js +0 -28
  167. package/tests/tags.test.js +0 -233
@@ -4,21 +4,7 @@
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const { Context } = require('../context');
7
-
8
- // Helper to resolve expression values (literals, numbers, booleans, or context lookups)
9
- function resolveValue(token, context) {
10
- if (token === undefined || token === null) return '';
11
- if (typeof token !== 'string') return token;
12
- if (token === '') return '';
13
- if ((token.startsWith('"') && token.endsWith('"')) || (token.startsWith('\'') && token.endsWith('\''))) {
14
- return token.slice(1, -1);
15
- }
16
- if (token === 'true' || token === 'True') return true;
17
- if (token === 'false' || token === 'False') return false;
18
- if (token === 'none' || token === 'None' || token === 'null') return null;
19
- if (/^-?\d+(\.\d+)?$/.test(token)) return Number(token);
20
- return context.get(token);
21
- }
7
+ const { evaluateExpression } = require('../parser');
22
8
 
23
9
  class ExtendsNode {
24
10
  constructor(parentTemplateExpr) {
@@ -26,7 +12,7 @@ class ExtendsNode {
26
12
  }
27
13
 
28
14
  render(context) {
29
- const parentName = resolveValue(this.parentTemplateExpr, context);
15
+ const parentName = evaluateExpression(this.parentTemplateExpr, context);
30
16
  if (!parentName) return '';
31
17
  context.parentTemplate = parentName;
32
18
  return '';
@@ -98,7 +84,7 @@ class IncludeNode {
98
84
  }
99
85
 
100
86
  render(context) {
101
- const rawName = resolveValue(this.templateNameExpr, context);
87
+ const rawName = evaluateExpression(this.templateNameExpr, context);
102
88
  if (!rawName) return '';
103
89
 
104
90
  // Django-style partial selector: "home.html#card" means render
@@ -183,7 +169,7 @@ class IncludeNode {
183
169
  if (this.extraMappings && this.extraMappings.length > 0) {
184
170
  const extraScope = {};
185
171
  for (const m of this.extraMappings) {
186
- extraScope[m.name] = resolveValue(m.valPath, context);
172
+ extraScope[m.name] = evaluateExpression(m.valPath, context);
187
173
  }
188
174
  renderCtx.push(extraScope);
189
175
  const out = partial.body.map(n => n.render(renderCtx)).join('');
@@ -198,7 +184,7 @@ class IncludeNode {
198
184
  if (this.extraMappings && this.extraMappings.length > 0) {
199
185
  const extraScope = {};
200
186
  for (const mapping of this.extraMappings) {
201
- extraScope[mapping.name] = resolveValue(mapping.valPath, context);
187
+ extraScope[mapping.name] = evaluateExpression(mapping.valPath, context);
202
188
  }
203
189
  context.push(extraScope);
204
190
  const res = nodes.map(n => n.render(context)).join('');
@@ -308,7 +294,7 @@ function parseKeyValuePairs(str) {
308
294
  val += str[i++];
309
295
  }
310
296
  if (str[i] === quote) i++;
311
- // Preserve quotes so resolveValue can recognize the literal
297
+ // Preserve quotes so evaluateExpression can recognize the literal
312
298
  val = quote + val + quote;
313
299
  } else {
314
300
  // Unquoted value: read until comma or whitespace
package/src/tags/util.js CHANGED
@@ -2,22 +2,7 @@
2
2
  * Utility template tags: static, url, regroup, spaceless.
3
3
  */
4
4
 
5
- // Helper to resolve expression values (literals, numbers, booleans, or context lookups)
6
- function resolveValue(token, context) {
7
- if (token === undefined || token === null) return '';
8
- if (typeof token !== 'string') return token;
9
- if (token === '') return '';
10
- if ((token.startsWith('"') && token.endsWith('"')) || (token.startsWith('\'') && token.endsWith('\''))) {
11
- return token.slice(1, -1);
12
- }
13
- if (token === 'true' || token === 'True') return true;
14
- if (token === 'false' || token === 'False') return false;
15
- if (token === 'none' || token === 'None' || token === 'null') return null;
16
- // Numeric literal (integer or float)
17
- if (/^-?\d+(\.\d+)?$/.test(token)) return Number(token);
18
- // Otherwise treat as a context variable lookup
19
- return context.get(token);
20
- }
5
+ const { evaluateExpression } = require('../parser');
21
6
 
22
7
  class StaticNode {
23
8
  constructor(pathExpr) {
@@ -25,7 +10,7 @@ class StaticNode {
25
10
  }
26
11
 
27
12
  render(context) {
28
- const resolvedPath = resolveValue(this.pathExpr, context);
13
+ const resolvedPath = evaluateExpression(this.pathExpr, context);
29
14
 
30
15
  // Resolve static prefix
31
16
  let prefix = '/static/';
@@ -55,11 +40,11 @@ class UrlNode {
55
40
  }
56
41
 
57
42
  render(context) {
58
- const routeName = resolveValue(this.routeNameExpr, context);
59
- const resolvedPositional = this.positionalArgs.map(arg => resolveValue(arg, context));
43
+ const routeName = evaluateExpression(this.routeNameExpr, context);
44
+ const resolvedPositional = this.positionalArgs.map(arg => evaluateExpression(arg, context));
60
45
  const resolvedKwargs = {};
61
46
  for (const kw of this.kwargs) {
62
- resolvedKwargs[kw.name] = resolveValue(kw.valueExpr, context);
47
+ resolvedKwargs[kw.name] = evaluateExpression(kw.valueExpr, context);
63
48
  }
64
49
  const hasKwargs = Object.keys(resolvedKwargs).length > 0;
65
50
 
package/.eslintrc.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "env": {
3
- "node": true,
4
- "es2024": true
5
- },
6
- "extends": "eslint:recommended",
7
- "parserOptions": {
8
- "ecmaVersion": "latest",
9
- "sourceType": "module"
10
- },
11
- "rules": {
12
- "no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
13
- "no-console": "off",
14
- "semi": ["error", "always"]
15
- }
16
- }
@@ -1,55 +0,0 @@
1
- # miki-template v1.3.1
2
-
3
- ## Highlights
4
-
5
- - **One-line Express integration**: `miki.setupExpress(app, { extension: 'html', views: dir })` wires the view engine, the views directory, and a `res.render` shim that makes HTMX-style partial responses Just Work.
6
-
7
- ```js
8
- const express = require('express');
9
- const miki = require('miki-template');
10
-
11
- const app = express();
12
- miki.setupExpress(app, { extension: 'html', views: './views' });
13
-
14
- // Full page
15
- app.get('/', (req, res) => res.render('home', { user: req.user }));
16
-
17
- // Partial — just append `#partialName` to the view name
18
- app.get('/partials/:name', (req, res) =>
19
- res.render(`home#${req.params.name}`, { user: req.user })
20
- );
21
- ```
22
-
23
- - **`miki.expressPartialRenderer()` middleware** for drop-in `res.renderPartial(view, locals)` without changing your existing `app.engine()` setup.
24
- - **`miki.express()`** factory for `app.engine('html', miki.express())` — honors `view#partial` selectors.
25
- - **`renderPartialFromSource(source, partialName, ctx, opts)`** and **`renderPartialFromFile(...)`** — render a single named `{% partialdef %}` from a string or file. Walks the AST (and the `extends` chain) to find partials nested inside blocks.
26
- - **`{% include "file.html#partial" with ... %}`** — partial-selector syntax works with `include`, not just `res.render`.
27
- - **Built-in libraries** (`humanize`, `cache`, `lorem`) are now auto-activated on module load. `{% lorem 5 p %}` works without `{% load lorem %}`.
28
-
29
- ## Tag fixes
30
-
31
- - `{% with x=1, y=2 as pair %}` (multi-pair + alias) parses correctly.
32
- - `{% with a=x b=y %}` (multi-pair, no alias) now binds each pair instead of being treated as one expression.
33
- - `{% cycle 'a' 'b' as name %}` now emits nothing but stores the value (Django semantics).
34
- - `{{ block.super }}` works inside partials that override blocks.
35
- - `extends` resolves `views` from `options.settings.views` (Express path), `options.views`, or default.
36
- - Path-traversal protection applies to `extends` *and* `include`, including `include "file#partial"`.
37
- - `default` vs `default_if_none`: `default` falls back on `''`, `null`, and `undefined`; `default_if_none` only on `null`/`undefined` (Django parity).
38
- - `removetags` accepts multiple tag names.
39
- - `Context.get('a.b.c')` returns `undefined` for missing keys (was `''`), so `{% if x %}` and `default_if_none` work correctly.
40
-
41
- ## Test coverage
42
-
43
- **382/382 tests pass.** Added a 190-test integration suite (`tests/integration/`) covering every tag, filter, and feature under both CommonJS and ESM, exercised against a real Express HTTP server on ephemeral ports. The test runner recursively picks up `.test.{cjs,mjs,js}` files.
44
-
45
- ## Docs
46
-
47
- - `README.md` — promoted `miki.setupExpress` to the first listed feature; the Express section leads with the one-liner.
48
- - `docs/usage.md` — full Express section rewritten with the new setup, options table, and middleware variant.
49
- - `docs/api.md` — added rows for the new APIs; updated CJS/ESM import snippets.
50
- - `docs/partialdef.md` — documented `include "file#partial"`, `renderPartialFromSource/File`, and the HTMX-over-HTTP flow.
51
- - `CHANGELOG.md` — full entry for 1.3.1.
52
-
53
- ## Compatibility
54
-
55
- No breaking changes. All existing public APIs continue to work; new APIs are opt-in.
@@ -1,77 +0,0 @@
1
- # miki-template v1.3.3
2
-
3
- ## Highlights
4
-
5
- - **Date/time filter fixed** — `{{ d|date:"yyyy-MM-dd" }}` now returns `"2024-06-15"` instead of `"24242424-JunJun-1515"`. Multi-character tokens (`yyyy`, `MM`, `dd`, `HH`, `mm`, `ii`, `ss`) are matched longest-first; backward-compatible single-character aliases (`m`/`d`/`H`/`i`/`s`) still work for Django-style unpadded values.
6
- - **Inheritance 4× faster** — `{% extends %}` no longer re-reads the parent file from disk on every render. A 64-entry LRU cache in `src/cache.js` memoises the parent source. Inheritance rendering improved from ~481 rps to **~2,056 rps** in the strict benchmark.
7
-
8
- ## Installation
9
-
10
- ```bash
11
- npm install miki-template@1.3.3
12
- ```
13
-
14
- ## Quick start
15
-
16
- ```js
17
- const miki = require('miki-template');
18
- const express = require('express');
19
-
20
- const app = express();
21
- miki.setupExpress(app, { extension: 'html', views: './views' });
22
-
23
- // Full page
24
- app.get('/', (req, res) => res.render('home', { user: req.user }));
25
-
26
- // HTMX partial — just append #partialName
27
- app.get('/partials/:name', (req, res) =>
28
- res.render(`home#${req.params.name}`, { user: req.user })
29
- );
30
-
31
- app.listen(3000);
32
- ```
33
-
34
- ## What's changed
35
-
36
- ### Fixed
37
- - `date` and `time` filters — multi-character tokens render correctly.
38
-
39
- ### Performance
40
- - Inheritance parent-source LRU cache in `src/cache.js` (`getParentSource` / `hasParentSource`). `clearCache()` also clears it.
41
-
42
- ### Tooling
43
- - New `benchmarks/stress.mjs` — 37-check strict benchmark (correctness, compile speed, render speed, cache, scale, endurance, partial rendering, async, concurrency). Run with `node benchmarks/stress.mjs`. Exits with code 1 on any failure.
44
- - `eslint.config.mjs` — added `URL`, `URLSearchParams`, `TextEncoder`, `TextDecoder`, `fetch`, `crypto`, `performance` to the Node 18+ globals list.
45
- - Removed dead code: unused `elifBranches` variable in `parseIfChanged`, unused `extractPluralMappings` function in `i18n.js`.
46
- - Renamed unused tag-parser `parser` parameters to `_parser` in `lorem` library and `parseLoad`.
47
-
48
- ## Verification
49
-
50
- | Check | Result |
51
- |---|---|
52
- | `npx eslint src/**/*.js` | 0 errors, 0 warnings |
53
- | `npm test` | 397/397 passing |
54
- | `node benchmarks/stress.mjs` | 37/37 passing |
55
-
56
- ## Performance (from the stress benchmark)
57
-
58
- | Template | Speed |
59
- |---|---|
60
- | Small (76 B) | **175,439 rps** (0.006 ms) |
61
- | Medium (595 B) | 13,947 rps (0.072 ms) |
62
- | Large (1.6 KB, 6 sections × 12 items) | 2,277 rps (0.439 ms) |
63
- | Inheritance child | 2,056 rps (0.486 ms) |
64
- | Partials (50 entries) | 2,235 rps (0.447 ms) |
65
- | Scale: 50,000 items | 107 ms median |
66
- | Endurance: 100,000 renders | 7.5s total, RSS +0.9 MB |
67
- | Async: 200 concurrent LARGE | 237 ms total |
68
-
69
- ## Compatibility
70
-
71
- No breaking changes. All existing public APIs continue to work; new APIs are opt-in.
72
-
73
- ## Links
74
-
75
- - **Repository**: https://github.com/alainmiki/miki-template
76
- - **Tag**: https://github.com/alainmiki/miki-template/releases/tag/v1.3.3
77
- - **Previous**: [v1.3.1](https://github.com/alainmiki/miki-template/releases/tag/v1.3.1)
@@ -1,20 +0,0 @@
1
- ## v1.3.7 — Template discovery improvements
2
-
3
- - Add recursive/app-style template discovery so projects can place
4
- templates in nested `templates/` folders (Django-style) and have
5
- them discovered automatically.
6
- - `setupExpress` now expands `app.get('views')` to include nested
7
- directories that contain template files so `res.render('name')`
8
- works for templates located in project-level or package-level
9
- `templates/` directories.
10
- - Expose `findTemplateInViews(name, roots)` helper and
11
- `setAppTemplateDirNames()/getAppTemplateDirNames()` to configure
12
- app-style template folder names.
13
- - Improve `res.render` fallback to use the recursive finder before
14
- throwing Express's "Failed to lookup view" error.
15
- - Update docs and API reference with usage examples and migration
16
- notes.
17
-
18
- CI: runs lint + tests (all passing locally). If you'd like a more
19
- comprehensive changelog, I can expand this with links to issues and
20
- code snippets.
@@ -1,42 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [ main ]
6
- pull_request:
7
- branches: [ main ]
8
- workflow_dispatch:
9
-
10
- jobs:
11
- lint:
12
- runs-on: ubuntu-latest
13
- steps:
14
- - uses: actions/checkout@v4
15
- - name: Setup Node.js
16
- uses: actions/setup-node@v4
17
- with:
18
- node-version: '20'
19
- cache: 'npm'
20
- - run: npm ci
21
- - name: Lint code
22
- run: npx eslint src/**/*.js
23
-
24
- test:
25
- runs-on: ubuntu-latest
26
- needs: lint
27
- strategy:
28
- matrix:
29
- node-version: [18.x, 20.x]
30
- steps:
31
- - uses: actions/checkout@v4
32
- - name: Use Node.js
33
- uses: actions/setup-node@v4
34
- with:
35
- node-version: ${{ matrix.node-version }}
36
- cache: 'npm'
37
- - run: npm ci
38
- - name: Run unit tests
39
- run: npm test
40
- - name: Run integration smoke-test
41
- if: github.event_name == 'workflow_dispatch'
42
- run: node live-test/integrations/smoke-test.js
@@ -1,107 +0,0 @@
1
- name: Deploy Documentation
2
-
3
- on:
4
- push:
5
- branches: [ main ]
6
- pull_request:
7
- branches: [ main ]
8
- workflow_dispatch:
9
-
10
- permissions:
11
- contents: write
12
- pages: write
13
- id-token: write
14
-
15
- concurrency:
16
- group: "pages"
17
- cancel-in-progress: false
18
-
19
- jobs:
20
- deploy-docs:
21
- runs-on: ubuntu-latest
22
- steps:
23
- - name: Checkout repository
24
- uses: actions/checkout@v4
25
-
26
- - name: Setup Python
27
- uses: actions/setup-python@v5
28
- with:
29
- python-version: '3.12'
30
- cache: 'pip'
31
- cache-dependency-path: 'requirements-docs.txt'
32
-
33
- - name: Install MkDocs Material with imaging support
34
- run: |
35
- python -m pip install --upgrade pip
36
- pip install -r requirements-docs.txt
37
-
38
- - name: Verify docs build
39
- run: mkdocs build --strict
40
- env:
41
- NO_MKDOCS_2_WARNING: 1
42
-
43
- - name: Deploy docs to GitHub Pages
44
- run: 'mkdocs gh-deploy --force --message "chore: deploy docs to GitHub Pages"'
45
-
46
- - name: Output Pages URL
47
- run: echo "Docs deployed to https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/"
48
-
49
- sync-wiki:
50
- runs-on: ubuntu-latest
51
- needs: deploy-docs
52
- steps:
53
- - name: Checkout repository
54
- uses: actions/checkout@v4
55
- with:
56
- fetch-depth: 0
57
-
58
- - name: Setup Git config
59
- run: |
60
- git config --global user.name "github-actions[bot]"
61
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
62
-
63
- - name: Create wiki clone directory
64
- run: mkdir -p wiki-repo
65
-
66
- - name: Clone wiki repository
67
- run: |
68
- git clone https://github.com/${{ github.repository }}.wiki.git wiki-repo || true
69
-
70
- - name: Sync docs to wiki
71
- if: hashFiles('wiki-repo/.git/config') != ''
72
- run: |
73
- cd wiki-repo
74
-
75
- # Copy all markdown docs preserving directory structure
76
- cp -r ../docs/*.md .
77
- cp -r ../docs/guide/*.md ./guide/ 2>/dev/null || true
78
- cp -r ../docs/api/*.md ./api/ 2>/dev/null || true
79
- cp -r ../docs/integrations/*.md ./integrations/ 2>/dev/null || true
80
-
81
- # Remove index.md if it exists (GitHub wiki uses README.md as home page)
82
- if [ -f index.md ]; then
83
- if [ -f README.md ]; then
84
- rm index.md
85
- else
86
- mv index.md README.md
87
- fi
88
- fi
89
-
90
- # Create Home.md from index.md if README.md doesn't exist
91
- if [ ! -f README.md ] && [ ! -f Home.md ] && [ -f docs/index.md ]; then
92
- cp docs/index.md Home.md
93
- fi
94
-
95
- # Commit and push if there are changes
96
- git add -A
97
- git diff --cached --quiet || {
98
- git commit -m "docs: sync documentation to wiki"
99
- git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.wiki.git master
100
- }
101
-
102
- - name: Wiki initialization hint
103
- if: hashFiles('wiki-repo/.git/config') == ''
104
- run: |
105
- echo "::warning::Wiki repository not found. Enable the wiki in repository settings first."
106
- echo "Go to https://github.com/${{ github.repository }}/settings/wiki and click 'Create the first page'"
107
- continue-on-error: true
@@ -1,107 +0,0 @@
1
- # Release workflow
2
- #
3
- # Fully automatic release pipeline. Triggered when a commit to main matches
4
- # the `chore(release): v*` pattern produced by `npm run release:patch|minor|major`.
5
- #
6
- # Flow:
7
- # 1. Detect the new version in package.json.
8
- # 2. Lint + test + stress benchmark (must pass before continuing).
9
- # 3. Create an annotated git tag v<version> and push it.
10
- # 4. Create a GitHub Release with notes from .github/release-notes/v<version>.md
11
- # (or a generic message if the file is missing).
12
- # 5. Publish the package to npm using the NPM_TOKEN secret.
13
- #
14
- # Local usage:
15
- # npm run release:patch # 1.3.3 → 1.3.4
16
- # npm run release:minor # 1.3.3 → 1.4.0
17
- # npm run release:major # 1.3.3 → 2.0.0
18
- # That bumps package.json, creates a commit, pushes to main, and the rest
19
- # happens here.
20
-
21
- name: Release
22
-
23
- on:
24
- push:
25
- branches: [main]
26
-
27
- permissions:
28
- contents: write
29
- packages: write
30
-
31
- jobs:
32
- release:
33
- # Only run for the explicit "chore(release):" commits. Any other
34
- # push to main is ignored so contributors can edit package.json
35
- # without accidentally publishing.
36
- if: startsWith(github.event.head_commit.message, 'chore(release):')
37
- runs-on: ubuntu-latest
38
- steps:
39
- - uses: actions/checkout@v4
40
- with:
41
- fetch-depth: 0
42
- token: ${{ secrets.GITHUB_TOKEN }}
43
-
44
- - uses: actions/setup-node@v4
45
- with:
46
- node-version: '20'
47
- registry-url: 'https://registry.npmjs.org'
48
-
49
- - name: Install dependencies
50
- run: npm ci
51
-
52
- - name: Lint
53
- run: npx eslint src/**/*.js
54
-
55
- - name: Test
56
- run: npm test
57
-
58
- - name: Stress benchmark
59
- run: node benchmarks/stress.mjs
60
-
61
- - name: Read package version
62
- id: ver
63
- run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
64
-
65
- - name: Check tag does not already exist
66
- run: |
67
- if git rev-parse "v${{ steps.ver.outputs.version }}" >/dev/null 2>&1; then
68
- echo "::error::Tag v${{ steps.ver.outputs.version }} already exists."
69
- exit 1
70
- fi
71
-
72
- - name: Create and push git tag
73
- run: |
74
- git config user.name "github-actions[bot]"
75
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
76
- git tag -a "v${{ steps.ver.outputs.version }}" -m "v${{ steps.ver.outputs.version }}"
77
- git push origin "v${{ steps.ver.outputs.version }}"
78
-
79
- - name: Build release body
80
- id: notes
81
- env:
82
- NOTES_FILE: .github/release-notes/v${{ steps.ver.outputs.version }}.md
83
- VERSION: ${{ steps.ver.outputs.version }}
84
- run: |
85
- {
86
- echo "body<<EOF_RELEASE"
87
- if [ -f "$NOTES_FILE" ]; then
88
- cat "$NOTES_FILE"
89
- else
90
- echo "## v${VERSION}"
91
- echo ""
92
- echo "See [CHANGELOG.md](../../blob/main/CHANGELOG.md) for details."
93
- fi
94
- echo "EOF_RELEASE"
95
- } >> "$GITHUB_OUTPUT"
96
-
97
- - name: Create GitHub Release
98
- uses: softprops/action-gh-release@v2
99
- with:
100
- tag_name: v${{ steps.ver.outputs.version }}
101
- name: v${{ steps.ver.outputs.version }}
102
- body: ${{ steps.notes.outputs.body }}
103
-
104
- - name: Publish to npm
105
- run: npm publish --access public
106
- env:
107
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/AGENT.md DELETED
@@ -1,71 +0,0 @@
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.