@dogsbay/minja 0.2.0-beta.100

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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +623 -0
  3. package/bin/minja.js +1225 -0
  4. package/dist/browser.d.ts +12 -0
  5. package/dist/browser.d.ts.map +1 -0
  6. package/dist/browser.js +11 -0
  7. package/dist/browser.js.map +1 -0
  8. package/dist/cli.d.ts +5 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +98 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/context.d.ts +47 -0
  13. package/dist/context.d.ts.map +1 -0
  14. package/dist/context.js +112 -0
  15. package/dist/context.js.map +1 -0
  16. package/dist/evaluator.d.ts +20 -0
  17. package/dist/evaluator.d.ts.map +1 -0
  18. package/dist/evaluator.js +213 -0
  19. package/dist/evaluator.js.map +1 -0
  20. package/dist/index.d.ts +20 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +18 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/index.umd.js +1160 -0
  25. package/dist/index.umd.js.map +7 -0
  26. package/dist/index.umd.min.js +12 -0
  27. package/dist/index.umd.min.js.map +7 -0
  28. package/dist/loader-fetch.d.ts +8 -0
  29. package/dist/loader-fetch.d.ts.map +1 -0
  30. package/dist/loader-fetch.js +15 -0
  31. package/dist/loader-fetch.js.map +1 -0
  32. package/dist/loader-memory.d.ts +11 -0
  33. package/dist/loader-memory.d.ts.map +1 -0
  34. package/dist/loader-memory.js +36 -0
  35. package/dist/loader-memory.js.map +1 -0
  36. package/dist/loader.d.ts +73 -0
  37. package/dist/loader.d.ts.map +1 -0
  38. package/dist/loader.js +159 -0
  39. package/dist/loader.js.map +1 -0
  40. package/dist/parser.d.ts +7 -0
  41. package/dist/parser.d.ts.map +1 -0
  42. package/dist/parser.js +609 -0
  43. package/dist/parser.js.map +1 -0
  44. package/dist/renderer.d.ts +13 -0
  45. package/dist/renderer.d.ts.map +1 -0
  46. package/dist/renderer.js +494 -0
  47. package/dist/renderer.js.map +1 -0
  48. package/dist/scan.d.ts +46 -0
  49. package/dist/scan.d.ts.map +1 -0
  50. package/dist/scan.js +46 -0
  51. package/dist/scan.js.map +1 -0
  52. package/dist/types.d.ts +175 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +5 -0
  55. package/dist/types.js.map +1 -0
  56. package/package.json +72 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,623 @@
1
+ # minja
2
+
3
+ **Minimal, secure Jinja2/Nunjucks subset for documentation preprocessing**
4
+
5
+ Minja is a template engine designed specifically for documentation and content preprocessing, using standard Jinja2/Nunjucks syntax with **zero arbitrary code execution risk**.
6
+
7
+ ## Features
8
+
9
+ - **Security First**: No `eval()`, no `new Function()`, no arbitrary code execution
10
+ - **Standard Jinja2 Syntax**: Fully compatible with Jinja2/Nunjucks conditionals
11
+ - **Variable Substitution**: Simple `{{ variable }}` syntax
12
+ - **Conditionals**: `{% if %}`, `{% elif %}`, `{% else %}` with safe expression evaluation
13
+ - **Switch Statements**: `{% switch %}` / `{% case %}` for multiple conditions
14
+ - **Nested Includes**: Recursive `{% include "file.md" %}` support
15
+ - **Leveloffset**: `{% leveloffset +1 %}` for shifting heading levels in included content
16
+ - **Variable Scoping**: Global and local variables with dot notation
17
+ - **Modern**: TypeScript, ES modules, works in browsers and Node.js
18
+ - **Small**: ~10KB minified + gzipped
19
+ - **Zero Dependencies**: No runtime dependencies
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install minja
25
+ ```
26
+
27
+ Or install globally to use the CLI:
28
+
29
+ ```bash
30
+ npm install -g minja
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ```typescript
36
+ import { render } from 'minja'
37
+
38
+ const template = `
39
+ {% set product = "MyApp" %}
40
+ {% set version = "1.0" %}
41
+
42
+ # {{ product }} Documentation
43
+
44
+ Version: {{ version }}
45
+
46
+ {% if version >= "1.0" %}
47
+ This is a stable release.
48
+ {% endif %}
49
+
50
+ {% include "common/footer.md" %}
51
+ `
52
+
53
+ const output = await render(template, {
54
+ context: {
55
+ author: 'John Doe'
56
+ }
57
+ })
58
+
59
+ console.log(output)
60
+ ```
61
+
62
+ ## CLI Usage
63
+
64
+ Minja includes a command-line interface for rendering templates from files or stdin.
65
+
66
+ ### Basic Usage
67
+
68
+ ```bash
69
+ # Render a template file
70
+ minja template.md
71
+
72
+ # Render with context from JSON file
73
+ minja template.md --context vars.json
74
+
75
+ # Render with context from YAML file
76
+ minja template.md --context vars.yaml
77
+
78
+ # Render with inline variables
79
+ minja template.md --vars '{"title":"My Doc","version":"1.0"}'
80
+
81
+ # Save output to file
82
+ minja template.md --context vars.json --output result.html
83
+
84
+ # Read from stdin
85
+ cat template.md | minja --vars '{"name":"World"}'
86
+ echo "# {{ title }}" | minja --vars '{"title":"Test"}'
87
+ ```
88
+
89
+ ### CLI Options
90
+
91
+ ```
92
+ Usage: minja [options] [template]
93
+
94
+ Arguments:
95
+ template Template file to render (or read from stdin)
96
+
97
+ Options:
98
+ -V, --version output the version number
99
+ -c, --context <file> Context file (JSON or YAML)
100
+ -o, --output <file> Output file (default: stdout)
101
+ -d, --max-depth <number> Maximum include depth (default: 10)
102
+ -t, --timeout <ms> Rendering timeout in milliseconds (default: 5000)
103
+ -v, --vars <json> Inline context variables as JSON
104
+ -r, --include-root <dir> Refuse {% include %} targets that resolve
105
+ (after symlinks) outside this directory
106
+ -h, --help display help for command
107
+ ```
108
+
109
+ ### Examples
110
+
111
+ **Simple template rendering:**
112
+ ```bash
113
+ minja hello.md --vars '{"name":"World"}'
114
+ ```
115
+
116
+ **With context file:**
117
+ ```bash
118
+ # vars.json
119
+ {
120
+ "product": "MyApp",
121
+ "version": "2.0"
122
+ }
123
+
124
+ # Render
125
+ minja README.md --context vars.json
126
+ ```
127
+
128
+ **Using includes:**
129
+ ```bash
130
+ # document.md includes common/vars.md
131
+ minja document.md --output docs/index.html
132
+ ```
133
+
134
+ **Untrusted input — contain includes to a directory:**
135
+ ```bash
136
+ # Any {% include %} resolving (after symlink resolution) outside docs/
137
+ # is REFUSED and the render fails — without this, an include like
138
+ # "../secrets.txt" or "/etc/hostname" splices any readable file into
139
+ # the output. `..` and absolute paths that stay inside the root are fine.
140
+ minja docs/page.md --include-root docs/
141
+ ```
142
+
143
+ Programmatic equivalent:
144
+ ```ts
145
+ import { render, FileSystemLoader } from '@dogsbay/minja'
146
+ const out = await render(template, {
147
+ basePath: 'docs/',
148
+ loader: new FileSystemLoader('docs/', { root: 'docs/' }),
149
+ })
150
+ ```
151
+
152
+ **Pipeline usage:**
153
+ ```bash
154
+ # Generate docs from multiple sources
155
+ cat header.md content.md footer.md | minja --context config.yaml > output.html
156
+ ```
157
+
158
+ ## Syntax
159
+
160
+ ### Variable Substitution
161
+
162
+ ```
163
+ {{ variable }}
164
+ {{ object.property }}
165
+ {{ nested.property.value }}
166
+ ```
167
+
168
+ ### Variable Definition
169
+
170
+ ```
171
+ {% set variable = "value" %}
172
+ {% set count = 42 %}
173
+ {% set enabled = true %}
174
+ ```
175
+
176
+ ### Conditionals
177
+
178
+ Basic if statement:
179
+ ```
180
+ {% if condition %}
181
+ Content when true
182
+ {% endif %}
183
+ ```
184
+
185
+ If-else statement:
186
+ ```
187
+ {% if condition %}
188
+ Content when true
189
+ {% else %}
190
+ Content when false
191
+ {% endif %}
192
+ ```
193
+
194
+ If-elif-else statement:
195
+ ```
196
+ {% if platform == "aws" %}
197
+ Amazon Web Services
198
+ {% elif platform == "azure" %}
199
+ Microsoft Azure
200
+ {% elif platform == "gcp" %}
201
+ Google Cloud Platform
202
+ {% else %}
203
+ Unknown platform
204
+ {% endif %}
205
+ ```
206
+
207
+ ### Ifdef/Ifndef (AsciiDoc-style)
208
+
209
+ ```
210
+ {% ifdef variable %}
211
+ Content when variable is defined
212
+ {% endifdef %}
213
+
214
+ {% ifndef variable %}
215
+ Content when variable is not defined
216
+ {% endifndef %}
217
+ ```
218
+
219
+ ### Ifeval (Conditional Expressions)
220
+
221
+ ```
222
+ {% ifeval [version >= "1.0"] %}
223
+ Stable release content
224
+ {% endifeval %}
225
+
226
+ {% ifeval [platform == "linux"] %}
227
+ Linux-specific content
228
+ {% endifeval %}
229
+ ```
230
+
231
+ ### Switch Statements
232
+
233
+ Switch-case for multiple conditions:
234
+ ```
235
+ {% switch platform %}
236
+ {% case "aws" %}
237
+ Amazon Web Services (AWS)
238
+ {% case "azure" %}
239
+ Microsoft Azure
240
+ {% case "gcp" %}
241
+ Google Cloud Platform (GCP)
242
+ {% endswitch %}
243
+ ```
244
+
245
+ Switch statements support:
246
+ - String and numeric case values
247
+ - Expression-based switch values
248
+ - Multiple case blocks
249
+ - Falls through on first match only (no default case)
250
+
251
+ ### Leveloffset (Heading Level Control)
252
+
253
+ Shift markdown heading levels in included content:
254
+
255
+ ```
256
+ {% leveloffset +1 %}
257
+ {% include "chapter.md" %}
258
+ {% endleveloffset %}
259
+ ```
260
+
261
+ **Offset types:**
262
+ - **Absolute**: `{% leveloffset 2 %}` - Sets offset to exact value
263
+ - **Relative**: `{% leveloffset +1 %}` or `{% leveloffset -1 %}` - Adds/subtracts from current
264
+ - **Reset**: `{% leveloffset 0 %}` - No offset
265
+
266
+ **Example:**
267
+
268
+ `chapter.md`:
269
+ ```markdown
270
+ # Chapter Title
271
+ ## Section
272
+ ```
273
+
274
+ With `{% leveloffset +1 %}`:
275
+ ```markdown
276
+ ## Chapter Title
277
+ ### Section
278
+ ```
279
+
280
+ This is perfect for modular documentation where you include the same content at different hierarchical levels. Inspired by AsciiDoc's `leveloffset` attribute.
281
+
282
+ See [Leveloffset documentation](docs/syntax-reference.md#leveloffset-heading-level-control) for detailed usage and examples.
283
+
284
+ ### Includes
285
+
286
+ ```
287
+ {% include "path/to/file.md" %}
288
+ {% include "common/header.md" %}
289
+ ```
290
+
291
+ Includes support:
292
+ - Relative paths
293
+ - Nested includes (with depth limit)
294
+ - URL-based loading (in browsers)
295
+ - Filesystem loading (in Node.js)
296
+
297
+ ### Comments
298
+
299
+ ```
300
+ {# This is a comment and won't appear in output #}
301
+ ```
302
+
303
+ Comments on their own line are automatically stripped along with surrounding whitespace.
304
+
305
+ ### Whitespace Control
306
+
307
+ Minja supports Nunjucks/Jinja2 whitespace control syntax to manage spacing and newlines in your output.
308
+
309
+ #### Basic Syntax
310
+
311
+ - `{%-` - Left strip: removes whitespace before the tag
312
+ - `-%}` - Right strip: removes whitespace after the tag
313
+ - `{%- -%}` - Both: removes whitespace on both sides
314
+
315
+ #### Examples
316
+
317
+ **Without whitespace control:**
318
+ ```markdown
319
+ {% set product = "MyApp" %}
320
+ {% set version = "1.0" %}
321
+ # {{ product }}
322
+ ```
323
+
324
+ Output (note the blank lines from set statements):
325
+ ```
326
+
327
+
328
+ # MyApp
329
+ ```
330
+
331
+ **With whitespace control:**
332
+ ```markdown
333
+ {%- set product = "MyApp" -%}
334
+ {%- set version = "1.0" -%}
335
+ # {{ product }}
336
+ ```
337
+
338
+ Output (clean, no blank lines):
339
+ ```
340
+ # MyApp
341
+ ```
342
+
343
+ **Mixed control:**
344
+ ```markdown
345
+ Line 1
346
+ {%- set var = "value" %}
347
+ Line 2
348
+ ```
349
+
350
+ Output (left strip removes newline before set):
351
+ ```
352
+ Line 1
353
+ Line 2
354
+ ```
355
+
356
+ #### Best Practices
357
+
358
+ For attribute files with many set statements, use `{%- set -%}` to avoid blank lines:
359
+
360
+ `common-attributes.md`:
361
+ ```markdown
362
+ {%- set product_name = "MyProduct" -%}
363
+ {%- set product_version = "1.0" -%}
364
+ {%- set company = "ACME Corp" -%}
365
+ ```
366
+
367
+ This prevents each set statement from adding blank lines to your output.
368
+
369
+ ## API
370
+
371
+ ### `render(template, options)`
372
+
373
+ Render a template string.
374
+
375
+ ```typescript
376
+ async function render(
377
+ template: string,
378
+ options?: RenderOptions
379
+ ): Promise<string>
380
+ ```
381
+
382
+ **Options:**
383
+
384
+ - `loader?: Loader` - File loader for includes (default: `FetchLoader`)
385
+ - `context?: Record<string, unknown>` - Initial variables
386
+ - `maxIncludeDepth?: number` - Maximum include recursion depth (default: 10)
387
+ - `timeout?: number` - Rendering timeout in ms (default: 5000)
388
+
389
+ **Example:**
390
+
391
+ ```typescript
392
+ import { render, FileSystemLoader } from 'minja'
393
+
394
+ const output = await render(template, {
395
+ loader: new FileSystemLoader('/path/to/docs'),
396
+ context: {
397
+ product: 'MyApp',
398
+ version: '2.0'
399
+ },
400
+ maxIncludeDepth: 5,
401
+ timeout: 10000
402
+ })
403
+ ```
404
+
405
+ ### Loaders
406
+
407
+ #### `FetchLoader`
408
+
409
+ For browsers and service workers (uses Fetch API).
410
+
411
+ ```typescript
412
+ import { render, FetchLoader } from 'minja'
413
+
414
+ const output = await render(template, {
415
+ loader: new FetchLoader()
416
+ })
417
+ ```
418
+
419
+ #### `FileSystemLoader`
420
+
421
+ For Node.js (uses `fs.readFile`).
422
+
423
+ ```typescript
424
+ import { render, FileSystemLoader } from 'minja'
425
+
426
+ const loader = new FileSystemLoader('/base/path')
427
+ const output = await render(template, { loader })
428
+ ```
429
+
430
+ #### `MemoryLoader`
431
+
432
+ For testing (stores files in memory).
433
+
434
+ ```typescript
435
+ import { render, MemoryLoader } from 'minja'
436
+
437
+ const loader = new MemoryLoader({
438
+ 'header.md': '# {{ title }}',
439
+ 'footer.md': '© 2026'
440
+ })
441
+
442
+ const output = await render('{% include "header.md" %}', { loader })
443
+ ```
444
+
445
+ ## Security Model
446
+
447
+ Minja is designed to be **secure by default**:
448
+
449
+ ### What's Allowed
450
+
451
+ - Variable substitution
452
+ - Safe comparisons (`==`, `!=`, `<`, `>`, `<=`, `>=`)
453
+ - Logical operators (`and`, `or`, `not`)
454
+ - Simple literals (strings, numbers, booleans, null)
455
+ - Dot notation for object properties
456
+
457
+ ### What's Blocked
458
+
459
+ - ❌ Arbitrary code execution
460
+ - ❌ Function calls (except whitelisted built-ins)
461
+ - ❌ Access to `eval`, `Function`, `require`, `import`
462
+ - ❌ Access to `window`, `process`, `globalThis`
463
+ - ❌ Prototype pollution
464
+ - ❌ Property assignment beyond template variables
465
+
466
+ ### Resource Limits
467
+
468
+ - Maximum include depth (default: 10)
469
+ - Rendering timeout (default: 5000ms)
470
+ - Expression complexity limits
471
+
472
+ ## Use Cases
473
+
474
+ Minja is perfect for:
475
+
476
+ - **Documentation preprocessing**: AsciiDoc-style attribute substitution
477
+ - **Static site generation**: Conditional content based on variables
478
+ - **Multi-variant docs**: Platform-specific or version-specific content
479
+ - **Email templates**: Safe user-data substitution
480
+ - **Configuration files**: Template-driven config generation
481
+
482
+ ## Comparison with Nunjucks/Jinja2
483
+
484
+ | Feature | minja | Nunjucks |
485
+ |---------|-------|----------|
486
+ | Variable substitution | ✅ | ✅ |
487
+ | Conditionals (if/elif/else) | ✅ | ✅ |
488
+ | Switch statements | ✅ | ❌ |
489
+ | Includes | ✅ | ✅ |
490
+ | Security | ✅ No code execution | ❌ Arbitrary code |
491
+ | AsciiDoc-style | ✅ ifdef/ifndef/ifeval | ❌ |
492
+ | Loops | ❌ | ✅ |
493
+ | Filters | ❌ | ✅ |
494
+ | Macros | ❌ | ✅ |
495
+ | Extends/Blocks | ❌ | ✅ |
496
+ | Bundle size | ~10KB | ~50KB+ |
497
+
498
+ ## Examples
499
+
500
+ ### Platform-Specific Documentation
501
+
502
+ Using ifeval:
503
+ ```markdown
504
+ {% set platform = "linux" %}
505
+
506
+ # Installation Guide
507
+
508
+ {% ifeval [platform == "linux"] %}
509
+ Install using your package manager:
510
+ ```bash
511
+ sudo apt install myapp
512
+ ```
513
+ {% endifeval %}
514
+
515
+ {% ifeval [platform == "macos"] %}
516
+ Install using Homebrew:
517
+ ```bash
518
+ brew install myapp
519
+ ```
520
+ {% endifeval %}
521
+ ```
522
+
523
+ Using switch statements:
524
+ ```markdown
525
+ {% set platform = "linux" %}
526
+
527
+ # Installation Guide
528
+
529
+ {% switch platform %}
530
+ {% case "linux" %}
531
+ Install using your package manager:
532
+ ```bash
533
+ sudo apt install myapp
534
+ ```
535
+ {% case "macos" %}
536
+ Install using Homebrew:
537
+ ```bash
538
+ brew install myapp
539
+ ```
540
+ {% case "windows" %}
541
+ Download the installer from our website.
542
+ {% endswitch %}
543
+ ```
544
+
545
+ ### Version-Specific Content
546
+
547
+ ```markdown
548
+ {% set version = "2.0" %}
549
+
550
+ # Features
551
+
552
+ {% ifeval [version >= "2.0"] %}
553
+ ## New in 2.0
554
+ - Feature A
555
+ - Feature B
556
+ {% endifeval %}
557
+
558
+ {% ifeval [version >= "1.0"] %}
559
+ ## Available since 1.0
560
+ - Feature X
561
+ - Feature Y
562
+ {% endifeval %}
563
+ ```
564
+
565
+ ### Common Attributes Pattern (AsciiDoc-style)
566
+
567
+ `common-attributes.md`:
568
+ ```
569
+ {%- set product_name = "MyProduct" -%}
570
+ {%- set product_version = "1.0" -%}
571
+ {%- set company = "ACME Corp" -%}
572
+ ```
573
+
574
+ `document.md`:
575
+ ```
576
+ {% include "common-attributes.md" %}
577
+
578
+ # {{ product_name }} Guide
579
+
580
+ Version {{ product_version }}
581
+
582
+ Welcome to {{ product_name }} by {{ company }}.
583
+ ```
584
+
585
+ **Note:** Use `{%- set -%}` in attribute files to prevent blank lines in the output.
586
+
587
+ ## Documentation
588
+
589
+ For complete documentation, see the [docs/](docs/) directory:
590
+
591
+ - **[Getting Started](docs/getting-started.md)** - Installation and your first template
592
+ - **[Syntax Reference](docs/syntax-reference.md)** - Complete syntax documentation
593
+ - **[AsciiDoc Migration](docs/asciidoc-migration.md)** - Converting from AsciiDoc to Minja
594
+ - **[CLI Usage](docs/cli-usage.md)** - Command-line interface guide
595
+ - **[Examples](docs/examples.md)** - Common patterns and use cases
596
+ - **[API Reference](docs/api-reference.md)** - Programmatic API documentation
597
+ - **[FAQ](docs/faq.md)** - Frequently asked questions
598
+
599
+ ## Development
600
+
601
+ ```bash
602
+ # Install dependencies
603
+ npm install
604
+
605
+ # Build
606
+ npm run build
607
+
608
+ # Test
609
+ npm test
610
+
611
+ # Test with coverage
612
+ npm run test:coverage
613
+
614
+ # Lint
615
+ npm run lint
616
+
617
+ # Format
618
+ npm run format
619
+ ```
620
+
621
+ ## License
622
+
623
+ MIT