binja 0.7.2 → 0.8.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.
package/README.md CHANGED
@@ -27,10 +27,11 @@
27
27
  |---------|-----------|------------------|
28
28
  | **Runtime Performance** | ✅ 2-4x faster | ❌ |
29
29
  | **AOT Compilation** | ✅ 160x faster | ❌ |
30
+ | **Multi-Engine** | ✅ Jinja2, Handlebars, Liquid | ❌ |
30
31
  | Django DTL Compatible | ✅ 100% | ❌ Partial |
31
32
  | Jinja2 Compatible | ✅ Full | ⚠️ Limited |
32
33
  | Template Inheritance | ✅ | ⚠️ |
33
- | 80+ Built-in Filters | ✅ | ❌ |
34
+ | 84 Built-in Filters | ✅ | ❌ |
34
35
  | 28 Built-in Tests | ✅ | ❌ |
35
36
  | Debug Panel | ✅ | ❌ |
36
37
  | CLI Tool | ✅ | ⚠️ |
@@ -418,6 +419,69 @@ console.log(Object.keys(builtinTests))
418
419
 
419
420
  ---
420
421
 
422
+ ## Multi-Engine Support
423
+
424
+ Binja supports multiple template engines through a unified API. All engines parse to a common AST and share the same runtime, filters, and optimizations.
425
+
426
+ ### Supported Engines
427
+
428
+ | Engine | Syntax | Use Case |
429
+ |--------|--------|----------|
430
+ | **Jinja2/DTL** | `{{ var }}` `{% if %}` | Default, Python/Django compatibility |
431
+ | **Handlebars** | `{{var}}` `{{#if}}` | JavaScript ecosystem, Ember.js |
432
+ | **Liquid** | `{{ var }}` `{% if %}` | Shopify, Jekyll, static sites |
433
+
434
+ ### Usage
435
+
436
+ ```typescript
437
+ // Direct engine imports
438
+ import * as handlebars from 'binja/engines/handlebars'
439
+ import * as liquid from 'binja/engines/liquid'
440
+
441
+ // Handlebars
442
+ await handlebars.render('Hello {{name}}!', { name: 'World' })
443
+ await handlebars.render('{{#each items}}{{this}}{{/each}}', { items: ['a', 'b'] })
444
+ await handlebars.render('{{{html}}}', { html: '<b>unescaped</b>' })
445
+
446
+ // Liquid (Shopify)
447
+ await liquid.render('Hello {{ name }}!', { name: 'World' })
448
+ await liquid.render('{% for item in items %}{{ item }}{% endfor %}', { items: ['a', 'b'] })
449
+ await liquid.render('{% assign x = "value" %}{{ x }}', {})
450
+ ```
451
+
452
+ ### MultiEngine API
453
+
454
+ ```typescript
455
+ import { MultiEngine } from 'binja/engines'
456
+
457
+ const engine = new MultiEngine()
458
+
459
+ // Render with any engine
460
+ await engine.render('Hello {{name}}!', { name: 'World' }, 'handlebars')
461
+ await engine.render('Hello {{ name }}!', { name: 'World' }, 'liquid')
462
+ await engine.render('Hello {{ name }}!', { name: 'World' }, 'jinja2')
463
+
464
+ // Auto-detect from file extension
465
+ import { detectEngine } from 'binja/engines'
466
+ const eng = detectEngine('template.hbs') // Returns Handlebars engine
467
+ const eng2 = detectEngine('page.liquid') // Returns Liquid engine
468
+ ```
469
+
470
+ ### Engine Feature Matrix
471
+
472
+ | Feature | Jinja2 | Handlebars | Liquid |
473
+ |---------|--------|------------|--------|
474
+ | Variables | `{{ x }}` | `{{x}}` | `{{ x }}` |
475
+ | Conditionals | `{% if %}` | `{{#if}}` | `{% if %}` |
476
+ | Loops | `{% for %}` | `{{#each}}` | `{% for %}` |
477
+ | Filters | `{{ x\|filter }}` | `{{ x }}` | `{{ x \| filter }}` |
478
+ | Raw output | `{% raw %}` | - | `{% raw %}` |
479
+ | Comments | `{# #}` | `{{! }}` | `{% comment %}` |
480
+ | Assignment | `{% set %}` | - | `{% assign %}` |
481
+ | Unescaped | `{{ x\|safe }}` | `{{{x}}}` | - |
482
+
483
+ ---
484
+
421
485
  ## Django Compatibility
422
486
 
423
487
  binja is designed to be a drop-in replacement for Django templates:
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Handlebars Engine
3
+ * Converts Handlebars templates to binja's common AST format
4
+ */
5
+ import type { TemplateNode } from '../../parser/nodes';
6
+ export { HandlebarsLexer, HbsTokenType, type HbsToken } from './lexer';
7
+ export { HandlebarsParser } from './parser';
8
+ /**
9
+ * Parse a Handlebars template string into an AST
10
+ */
11
+ export declare function parse(source: string): TemplateNode;
12
+ /**
13
+ * Compile a Handlebars template to a render function
14
+ */
15
+ export declare function compile(source: string): (context: Record<string, any>) => Promise<string>;
16
+ /**
17
+ * Render a Handlebars template with context
18
+ */
19
+ export declare function render(source: string, context?: Record<string, any>): Promise<string>;
20
+ /**
21
+ * Engine interface for multi-engine support
22
+ */
23
+ export declare const engine: {
24
+ name: string;
25
+ extensions: string[];
26
+ parse: typeof parse;
27
+ compile: typeof compile;
28
+ render: typeof render;
29
+ };
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Handlebars Lexer
3
+ * Tokenizes Handlebars template syntax: {{expr}}, {{#if}}, {{/if}}, {{>partial}}
4
+ */
5
+ export declare enum HbsTokenType {
6
+ TEXT = "TEXT",
7
+ OPEN = "OPEN",// {{
8
+ OPEN_BLOCK = "OPEN_BLOCK",// {{#
9
+ OPEN_END = "OPEN_END",// {{/
10
+ OPEN_PARTIAL = "OPEN_PARTIAL",// {{>
11
+ OPEN_UNESCAPED = "OPEN_UNESCAPED",// {{{
12
+ OPEN_COMMENT = "OPEN_COMMENT",// {{!
13
+ CLOSE = "CLOSE",// }}
14
+ CLOSE_UNESCAPED = "CLOSE_UNESCAPED",// }}}
15
+ ID = "ID",// identifier
16
+ STRING = "STRING",
17
+ NUMBER = "NUMBER",
18
+ BOOLEAN = "BOOLEAN",
19
+ DOT = "DOT",
20
+ DOTDOT = "DOTDOT",// ..
21
+ SLASH = "SLASH",
22
+ EQUALS = "EQUALS",
23
+ PIPE = "PIPE",
24
+ EOF = "EOF"
25
+ }
26
+ export interface HbsToken {
27
+ type: HbsTokenType;
28
+ value: string;
29
+ line: number;
30
+ column: number;
31
+ }
32
+ export declare class HandlebarsLexer {
33
+ private source;
34
+ private pos;
35
+ private line;
36
+ private column;
37
+ private tokens;
38
+ constructor(source: string);
39
+ tokenize(): HbsToken[];
40
+ private scanToken;
41
+ private scanText;
42
+ private scanExpression;
43
+ private scanExpressionToken;
44
+ private scanString;
45
+ private scanNumber;
46
+ private scanIdentifier;
47
+ private scanComment;
48
+ private scanBlockComment;
49
+ private isAtEnd;
50
+ private peek;
51
+ private peekNext;
52
+ private advance;
53
+ private check;
54
+ private match;
55
+ private skipWhitespace;
56
+ private isDigit;
57
+ private isAlpha;
58
+ private isAlphaNumeric;
59
+ private addToken;
60
+ }
61
+ //# sourceMappingURL=lexer.d.ts.map
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Handlebars Parser
3
+ * Converts Handlebars tokens to a common AST format
4
+ */
5
+ import { HbsToken } from './lexer';
6
+ import type { TemplateNode } from '../../parser/nodes';
7
+ export declare class HandlebarsParser {
8
+ private tokens;
9
+ private current;
10
+ private source;
11
+ constructor(tokens: HbsToken[], source?: string);
12
+ parse(): TemplateNode;
13
+ private parseNodes;
14
+ private parseNode;
15
+ private parseText;
16
+ private parseOutput;
17
+ private parseBlock;
18
+ private parseIfBlock;
19
+ private parseUnlessBlock;
20
+ private parseEachBlock;
21
+ private parseWithBlock;
22
+ private parseCustomBlock;
23
+ private parsePartial;
24
+ private parseExpression;
25
+ private parseExpressionAtom;
26
+ private parsePath;
27
+ private checkOpenBlock;
28
+ private consumeElse;
29
+ private consumeEndBlock;
30
+ private skipComment;
31
+ private isAtEnd;
32
+ private peek;
33
+ private advance;
34
+ private check;
35
+ private expect;
36
+ }
37
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Multi-Engine Support
3
+ * Unified interface for multiple template engines
4
+ */
5
+ import * as handlebars from './handlebars';
6
+ import * as liquid from './liquid';
7
+ export { handlebars, liquid };
8
+ /**
9
+ * Engine interface
10
+ */
11
+ export interface TemplateEngine {
12
+ name: string;
13
+ extensions: string[];
14
+ parse: (source: string) => any;
15
+ compile: (source: string) => (context: Record<string, any>) => Promise<string>;
16
+ render: (source: string, context?: Record<string, any>) => Promise<string>;
17
+ }
18
+ /**
19
+ * Registry of all available engines
20
+ */
21
+ export declare const engines: Record<string, TemplateEngine>;
22
+ /**
23
+ * Get engine by name or file extension
24
+ */
25
+ export declare function getEngine(nameOrExt: string): TemplateEngine | undefined;
26
+ /**
27
+ * Detect engine from file path
28
+ */
29
+ export declare function detectEngine(filePath: string): TemplateEngine | undefined;
30
+ /**
31
+ * Render a template with auto-detected engine
32
+ */
33
+ export declare function render(source: string, context?: Record<string, any>, engineName?: string): Promise<string>;
34
+ /**
35
+ * Multi-engine environment for API service
36
+ */
37
+ export declare class MultiEngine {
38
+ private defaultEngine;
39
+ constructor(defaultEngine?: string);
40
+ /**
41
+ * Render with specified engine
42
+ */
43
+ render(source: string, context?: Record<string, any>, engineName?: string): Promise<string>;
44
+ /**
45
+ * Compile template with specified engine
46
+ */
47
+ compile(source: string, engineName?: string): (context: Record<string, any>) => Promise<string>;
48
+ /**
49
+ * List all available engines
50
+ */
51
+ listEngines(): string[];
52
+ }
53
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Liquid Engine
3
+ * Converts Liquid (Shopify) templates to binja's common AST format
4
+ */
5
+ import type { TemplateNode } from '../../parser/nodes';
6
+ export { LiquidLexer, LiquidTokenType, type LiquidToken } from './lexer';
7
+ export { LiquidParser } from './parser';
8
+ /**
9
+ * Parse a Liquid template string into an AST
10
+ */
11
+ export declare function parse(source: string): TemplateNode;
12
+ /**
13
+ * Compile a Liquid template to a render function
14
+ */
15
+ export declare function compile(source: string): (context: Record<string, any>) => Promise<string>;
16
+ /**
17
+ * Render a Liquid template with context
18
+ */
19
+ export declare function render(source: string, context?: Record<string, any>): Promise<string>;
20
+ /**
21
+ * Engine interface for multi-engine support
22
+ */
23
+ export declare const engine: {
24
+ name: string;
25
+ extensions: string[];
26
+ parse: typeof parse;
27
+ compile: typeof compile;
28
+ render: typeof render;
29
+ };
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Liquid Lexer
3
+ * Tokenizes Liquid template syntax: {{ output }}, {% tags %}
4
+ * Shopify-compatible implementation
5
+ */
6
+ export declare enum LiquidTokenType {
7
+ TEXT = "TEXT",
8
+ VAR_START = "VAR_START",// {{
9
+ VAR_END = "VAR_END",// }}
10
+ TAG_START = "TAG_START",// {%
11
+ TAG_END = "TAG_END",// %}
12
+ ID = "ID",
13
+ STRING = "STRING",
14
+ NUMBER = "NUMBER",
15
+ DOT = "DOT",
16
+ PIPE = "PIPE",
17
+ COLON = "COLON",
18
+ COMMA = "COMMA",
19
+ LBRACKET = "LBRACKET",
20
+ RBRACKET = "RBRACKET",
21
+ RANGE = "RANGE",// ..
22
+ EQUALS = "EQUALS",// =
23
+ EQ = "EQ",// ==
24
+ NE = "NE",// != or <>
25
+ LT = "LT",
26
+ LE = "LE",
27
+ GT = "GT",
28
+ GE = "GE",
29
+ CONTAINS = "CONTAINS",
30
+ AND = "AND",
31
+ OR = "OR",
32
+ EOF = "EOF"
33
+ }
34
+ export interface LiquidToken {
35
+ type: LiquidTokenType;
36
+ value: string;
37
+ line: number;
38
+ column: number;
39
+ }
40
+ export declare class LiquidLexer {
41
+ private source;
42
+ private pos;
43
+ private line;
44
+ private column;
45
+ private tokens;
46
+ constructor(source: string);
47
+ tokenize(): LiquidToken[];
48
+ private scanToken;
49
+ private checkRawTag;
50
+ private scanRawBlock;
51
+ private scanText;
52
+ private scanExpression;
53
+ private scanExpressionToken;
54
+ private scanString;
55
+ private scanNumber;
56
+ private scanIdentifier;
57
+ private isAtEnd;
58
+ private peek;
59
+ private peekNext;
60
+ private advance;
61
+ private check;
62
+ private match;
63
+ private skipWhitespace;
64
+ private isDigit;
65
+ private isAlpha;
66
+ private isAlphaNumeric;
67
+ private addToken;
68
+ }
69
+ //# sourceMappingURL=lexer.d.ts.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Liquid Parser
3
+ * Converts Liquid tokens to a common AST format
4
+ * Shopify-compatible implementation
5
+ */
6
+ import { LiquidToken } from './lexer';
7
+ import type { TemplateNode } from '../../parser/nodes';
8
+ export declare class LiquidParser {
9
+ private tokens;
10
+ private current;
11
+ private source;
12
+ constructor(tokens: LiquidToken[], source?: string);
13
+ parse(): TemplateNode;
14
+ private parseNodes;
15
+ private parseNode;
16
+ private parseText;
17
+ private parseOutput;
18
+ private parseTag;
19
+ private parseIfTag;
20
+ private parseUnlessTag;
21
+ private parseCaseTag;
22
+ private parseForTag;
23
+ private parseForIterable;
24
+ private parseAssignTag;
25
+ private parseCaptureTag;
26
+ private parseIncrementTag;
27
+ private parseIncludeTag;
28
+ private parseCommentTag;
29
+ private parseRawTag;
30
+ private parseCondition;
31
+ private parseOr;
32
+ private parseAnd;
33
+ private parseComparison;
34
+ private parseExpression;
35
+ private parseExpressionAtom;
36
+ private parsePath;
37
+ private checkTag;
38
+ private consumeTag;
39
+ private checkKeyword;
40
+ private expectKeyword;
41
+ private isAtEnd;
42
+ private peek;
43
+ private advance;
44
+ private check;
45
+ private expect;
46
+ }
47
+ //# sourceMappingURL=parser.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "binja",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "High-performance Jinja2/Django template engine for Bun",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",