tempile-core 1.0.0 → 1.0.2

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 ADDED
@@ -0,0 +1,913 @@
1
+ # Tempile Core
2
+
3
+ A powerful multi-language template engine core that parses templates with custom syntax and generates an Abstract Syntax Tree (AST). This library is designed for compiler developers who want to build language-specific compilers for the Tempile template system.
4
+
5
+ ## Overview
6
+
7
+ Tempile Core is the foundation of the Tempile template engine ecosystem. It handles the parsing of template files and produces a structured AST that can be transformed into native code for any target language.
8
+
9
+ ### Architecture
10
+
11
+ The Tempile ecosystem consists of three main components:
12
+
13
+ 1. **Core** (this package) - Parses templates and generates AST
14
+ 2. **Compiler** - Transforms AST into target language native code
15
+ 3. **CLI** - Orchestrates core and compilers, handles file and config management
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install tempile-core
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```javascript
26
+ import { parse } from 'tempile-core';
27
+ import fs from 'fs';
28
+
29
+ // Read template file
30
+ const template = fs.readFileSync('template.html', 'utf-8');
31
+
32
+ // Parse template and get AST root
33
+ const root = parse(template, 'template.html');
34
+
35
+ // Resolve includes (if template uses <include> tags)
36
+ root.resolveIncludes('./src');
37
+
38
+ // Match slots with contents (for component composition)
39
+ root.matchSlotsAndContents();
40
+
41
+ // Now you can traverse the AST and generate target code
42
+ ```
43
+
44
+ ## API Reference
45
+
46
+ ### Core Functions
47
+
48
+ #### `parse(source: string, fileName: string): Root`
49
+
50
+ Parses a template string and returns the root AST node.
51
+
52
+ **Parameters:**
53
+ - `source` - Template content as string
54
+ - `fileName` - Name of the template file (used in error messages and position tracking)
55
+
56
+ **Returns:** `Root` object containing the parsed AST
57
+
58
+ **Example:**
59
+ ```javascript
60
+ const root = parse('<div>Hello {{name}}</div>', 'greeting.html');
61
+ ```
62
+
63
+ ### Root Class
64
+
65
+ The `Root` class represents the top-level AST node and provides methods for post-processing.
66
+
67
+ #### Properties
68
+
69
+ - `children: Node[]` - Array of child nodes
70
+ - `fileName: string` - Source file name
71
+
72
+ #### Methods
73
+
74
+ ##### `resolveIncludes(srcPath: string): void`
75
+
76
+ Recursively resolves all `<include>` tags by reading and parsing the referenced files. Must be called before `matchSlotsAndContents()` if your templates use includes.
77
+
78
+ **Parameters:**
79
+ - `srcPath` - Base directory path for resolving relative include paths
80
+
81
+ **Throws:**
82
+ - Error if circular includes are detected
83
+ - Error if included file cannot be read
84
+
85
+ **Example:**
86
+ ```javascript
87
+ root.resolveIncludes('./templates');
88
+ ```
89
+
90
+ ##### `matchSlotsAndContents(): void`
91
+
92
+ Matches `<content>` nodes with their corresponding `<slot>` nodes in included templates. This enables component composition by filling slots with provided content.
93
+
94
+ Must be called after `resolveIncludes()` if using the slot/content system.
95
+
96
+ **Example:**
97
+ ```javascript
98
+ root.resolveIncludes('./templates');
99
+ root.matchSlotsAndContents(); // Slots are now filled with contents
100
+ ```
101
+
102
+ ## AST Node Types
103
+
104
+ All nodes extend the abstract `Node` class and include position information for error reporting.
105
+
106
+ ### Base Node Structure
107
+
108
+ ```typescript
109
+ abstract class Node {
110
+ type: NodeType;
111
+ pos: Pos | null | undefined;
112
+ abstract getChildren(): Node[];
113
+ }
114
+ ```
115
+
116
+ ### Position Information
117
+
118
+ ```typescript
119
+ type Pos = {
120
+ fileName: string;
121
+ startLine?: number;
122
+ startCol?: number;
123
+ startOffset?: number;
124
+ endLine?: number;
125
+ endCol?: number;
126
+ endOffset?: number;
127
+ }
128
+ ```
129
+
130
+ ### Node Types Enum
131
+
132
+ ```typescript
133
+ enum NodeType {
134
+ Import = "import",
135
+ Doctype = "doctype",
136
+ Tempile = "tempile",
137
+ Comment = "comment",
138
+ Text = "text",
139
+ Element = "element",
140
+ If = "if",
141
+ ElseIf = "elseif",
142
+ Else = "else",
143
+ For = "for",
144
+ Include = "include",
145
+ Slot = "slot",
146
+ Content = "content",
147
+ Out = "out",
148
+ Logic = "logic",
149
+ }
150
+ ```
151
+
152
+ ### HTML Nodes
153
+
154
+ #### TextNode
155
+
156
+ Represents plain text content.
157
+
158
+ ```typescript
159
+ class TextNode extends Node {
160
+ type: NodeType.Text;
161
+ data: string;
162
+ getChildren(): [] // No children
163
+ }
164
+ ```
165
+
166
+ #### CommentNode
167
+
168
+ Represents HTML comments.
169
+
170
+ ```typescript
171
+ class CommentNode extends Node {
172
+ type: NodeType.Comment;
173
+ data: string;
174
+ getChildren(): [] // No children
175
+ }
176
+ ```
177
+
178
+ #### ElementNode
179
+
180
+ Represents standard HTML elements.
181
+
182
+ ```typescript
183
+ class ElementNode extends Node {
184
+ type: NodeType.Element;
185
+ tag: string;
186
+ attrs: Attribute[];
187
+ children: Node[];
188
+ getChildren(): Node[]
189
+ }
190
+ ```
191
+
192
+ **Attribute Structure:**
193
+ ```typescript
194
+ type Attribute = {
195
+ name: string;
196
+ value: string;
197
+ valueNodes?: AttrValueNode[]; // Parsed interpolations
198
+ pos: Pos;
199
+ }
200
+
201
+ type AttrValueNode =
202
+ | { type: "text"; value: string }
203
+ | { type: "expr"; value: string }
204
+ ```
205
+
206
+ Attributes can contain expressions using `{{expression}}` syntax. The `valueNodes` array contains parsed segments alternating between text and expressions.
207
+
208
+ **Example:**
209
+ ```html
210
+ <div class="btn {{variant}} {{size}}">
211
+ ```
212
+ Results in:
213
+ ```javascript
214
+ {
215
+ name: "class",
216
+ value: "btn {{variant}} {{size}}",
217
+ valueNodes: [
218
+ { type: "text", value: "btn " },
219
+ { type: "expr", value: "variant" },
220
+ { type: "text", value: " " },
221
+ { type: "expr", value: "size" }
222
+ ]
223
+ }
224
+ ```
225
+
226
+ #### DoctypeNode
227
+
228
+ Represents the DOCTYPE declaration.
229
+
230
+ ```typescript
231
+ class DoctypeNode extends Node {
232
+ type: NodeType.Doctype;
233
+ data: string; // "<!DOCTYPE html>"
234
+ getChildren(): [] // No children
235
+ }
236
+ ```
237
+
238
+ ### Control Flow Nodes
239
+
240
+ #### IfNode
241
+
242
+ Represents conditional rendering with optional elseif and else branches.
243
+
244
+ ```typescript
245
+ class IfNode extends Node {
246
+ type: NodeType.If;
247
+ conditions: Attribute[]; // Multi-language conditions
248
+ ifContent: Node[]; // Content when condition is true
249
+ elseIfNodes: ElseIfNode[]; // Optional elseif branches
250
+ elseNode?: ElseNode; // Optional else branch
251
+ getChildren(): Node[] // Returns ifContent
252
+ }
253
+ ```
254
+
255
+ **Template Syntax:**
256
+ ```html
257
+ <if @js="user.age >= 18" @go="user.Age >= 18">
258
+ <p>Adult content</p>
259
+ <elseif @js="user.age >= 13" @go="user.Age >= 13">
260
+ <p>Teen content</p>
261
+ </elseif>
262
+ <else>
263
+ <p>Child content</p>
264
+ </else>
265
+ </if>
266
+ ```
267
+
268
+ #### ElseIfNode
269
+
270
+ Represents an elseif branch within an if statement.
271
+
272
+ ```typescript
273
+ class ElseIfNode extends Node {
274
+ type: NodeType.ElseIf;
275
+ conditions: Attribute[]; // Multi-language conditions
276
+ children: Node[];
277
+ getChildren(): Node[]
278
+ }
279
+ ```
280
+
281
+ **Validation:**
282
+ - Must be a direct child of `<if>` tag
283
+ - Requires at least one `@[lang]` condition attribute
284
+
285
+ #### ElseNode
286
+
287
+ Represents an else branch within an if statement.
288
+
289
+ ```typescript
290
+ class ElseNode extends Node {
291
+ type: NodeType.Else;
292
+ children: Node[];
293
+ getChildren(): Node[]
294
+ }
295
+ ```
296
+
297
+ **Validation:**
298
+ - Must be a direct child of `<if>` tag
299
+ - Only one `<else>` per `<if>` block allowed
300
+
301
+ #### ForNode
302
+
303
+ Represents loop iteration.
304
+
305
+ ```typescript
306
+ class ForNode extends Node {
307
+ type: NodeType.For;
308
+ loops: Attribute[]; // Multi-language loop expressions
309
+ children: Node[];
310
+ getChildren(): Node[]
311
+ }
312
+ ```
313
+
314
+ **Template Syntax:**
315
+ ```html
316
+ <for @js="const item of items" @go="_, item := range items">
317
+ <p>{{item.name}}</p>
318
+ </for>
319
+ ```
320
+
321
+ ### Feature Nodes
322
+
323
+ #### ImportNode
324
+
325
+ Allows importing dependencies for specific target languages. Only text and comment nodes are allowed as children.
326
+
327
+ ```typescript
328
+ class ImportNode extends Node {
329
+ type: NodeType.Import;
330
+ lang: string; // Target language (e.g., "js", "go")
331
+ data: Node[]; // Text/Comment nodes containing import statements
332
+ getChildren(): Node[]
333
+ }
334
+ ```
335
+
336
+ **Template Syntax:**
337
+ ```html
338
+ <import @js>
339
+ import axios from 'axios';
340
+ import { helper } from './utils';
341
+ </import>
342
+
343
+ <import @go>
344
+ "fmt"
345
+ "github.com/user/package"
346
+ </import>
347
+ ```
348
+
349
+ **Validation:**
350
+ - Must be at document root level (not nested in other elements)
351
+ - Only one `@[lang]` attribute allowed
352
+ - Only text and comment nodes as children
353
+
354
+ #### TempileNode
355
+
356
+ Special node for wrapping HTML structural elements (doctype, html, head, body). This provides easier parsing and allows attributes on these elements.
357
+
358
+ ```typescript
359
+ class TempileNode extends Node {
360
+ type: NodeType.Tempile;
361
+ nodeTypeData: string; // "doctype" | "html" | "head" | "body"
362
+ attrs: Attribute[];
363
+ children: Node[];
364
+ getChildren(): Node[]
365
+ }
366
+ ```
367
+
368
+ **Template Syntax:**
369
+ ```html
370
+ <tempile @doctype></tempile>
371
+ <tempile @html lang="en">
372
+ <tempile @head>
373
+ <meta charset="UTF-8">
374
+ <title>Page Title</title>
375
+ </tempile>
376
+ <tempile @body>
377
+ <!-- Content here -->
378
+ </tempile>
379
+ </tempile>
380
+ ```
381
+
382
+ **Note:** For `@doctype`, a `DoctypeNode` is created instead during parsing.
383
+
384
+ #### IncludeNode
385
+
386
+ Includes external template files and enables component composition.
387
+
388
+ ```typescript
389
+ class IncludeNode extends Node {
390
+ type: NodeType.Include;
391
+ ctxId: string; // Unique context ID for slot matching
392
+ path: Attribute; // Path to included file
393
+ children: Node[]; // Content nodes to fill slots
394
+ getChildren(): Node[]
395
+ }
396
+ ```
397
+
398
+ **Template Syntax:**
399
+ ```html
400
+ <!-- Simple include without slot filling -->
401
+ <include @path="components/header.html"></include>
402
+
403
+ <!-- Include with slot content -->
404
+ <include @path="layouts/main.html">
405
+ <content @name="sidebar">
406
+ <nav>Navigation items</nav>
407
+ </content>
408
+ <content @name="main">
409
+ <h1>Page content</h1>
410
+ </content>
411
+ </include>
412
+ ```
413
+
414
+ **How it works:**
415
+ 1. Parser creates `IncludeNode` with unique `ctxId`
416
+ 2. `resolveIncludes()` reads and parses the referenced file
417
+ 3. Parsed content is added to `IncludeNode.children`
418
+ 4. `matchSlotsAndContents()` matches `ContentNode`s with `SlotNode`s using `ctxId`
419
+
420
+ #### SlotNode
421
+
422
+ Defines a placeholder in a template that can be filled with content from an include.
423
+
424
+ ```typescript
425
+ class SlotNode extends Node {
426
+ type: NodeType.Slot;
427
+ name: string; // Slot identifier
428
+ children: Node[]; // Default content (if not filled)
429
+ getChildren(): Node[]
430
+ }
431
+ ```
432
+
433
+ **Template Syntax:**
434
+ ```html
435
+ <!-- In layout.html -->
436
+ <div class="layout">
437
+ <slot @name="header">
438
+ <h1>Default Header</h1>
439
+ </slot>
440
+ <slot @name="content">
441
+ <p>Default content</p>
442
+ </slot>
443
+ </div>
444
+ ```
445
+
446
+ **Usage in parent template:**
447
+ ```html
448
+ <include @path="layout.html">
449
+ <content @name="header">
450
+ <h1>Custom Header</h1>
451
+ </content>
452
+ </include>
453
+ ```
454
+
455
+ #### ContentNode
456
+
457
+ Provides content to fill a slot in an included template. Must be a direct child of `<include>`.
458
+
459
+ ```typescript
460
+ class ContentNode extends Node {
461
+ type: NodeType.Content;
462
+ name: string; // Target slot name
463
+ children: Node[];
464
+ getChildren(): Node[]
465
+ }
466
+ ```
467
+
468
+ **Validation:**
469
+ - Must be a direct child of `<include>` tag
470
+ - Only one `@[name]` attribute allowed
471
+
472
+ **After processing:**
473
+ - `ContentNode`s are removed from the tree
474
+ - Their children are moved into matching `SlotNode`s
475
+ - Unmatched slots keep their default content
476
+
477
+ #### OutNode
478
+
479
+ Outputs an expression value. By default, content is escaped for security. Use `@raw` to output unescaped HTML.
480
+
481
+ ```typescript
482
+ class OutNode extends Node {
483
+ type: NodeType.Out;
484
+ data: string; // Expression to output
485
+ isRaw: boolean; // Whether to escape output
486
+ getChildren(): [] // No children
487
+ }
488
+ ```
489
+
490
+ **Template Syntax:**
491
+ ```html
492
+ <!-- Escaped output (safe) -->
493
+ <out>user.name</out>
494
+
495
+ <!-- Raw output (dangerous - use with caution) -->
496
+ <out @raw>article.htmlContent</out>
497
+ ```
498
+
499
+ **Validation:**
500
+ - Only text nodes allowed as children
501
+ - Maximum one attribute (`@raw`)
502
+
503
+ #### LogicNode
504
+
505
+ Embeds native code in the target language within the template. Code is not isolated and can access variables from other parts of the template.
506
+
507
+ ```typescript
508
+ class LogicNode extends Node {
509
+ type: NodeType.Logic;
510
+ lang: string; // Target language
511
+ data: string; // Native code
512
+ getChildren(): [] // No children
513
+ }
514
+ ```
515
+
516
+ **Template Syntax:**
517
+ ```html
518
+ <logic @js>
519
+ const user = getUser();
520
+ const isAdmin = user.role === 'admin';
521
+ </logic>
522
+
523
+ <if @js="isAdmin">
524
+ <p>Admin panel</p>
525
+ </if>
526
+
527
+ <logic @go>
528
+ user := getUser()
529
+ isAdmin := user.Role == "admin"
530
+ </logic>
531
+ ```
532
+
533
+ **Validation:**
534
+ - Only one `@[lang]` attribute allowed
535
+ - Only text nodes as children
536
+
537
+ ## Template Syntax Guide
538
+
539
+ ### Multi-Language Support
540
+
541
+ Most control structures and logic nodes support multiple target languages through `@[lang]` attributes. This allows a single template to be compiled to different languages.
542
+
543
+ ```html
544
+ <if @js="count > 0" @go="count > 0" @python="count > 0">
545
+ <p>Has items</p>
546
+ </if>
547
+ ```
548
+
549
+ ### Expression Interpolation
550
+
551
+ Use `{{expression}}` syntax within attribute values:
552
+
553
+ ```html
554
+ <div
555
+ class="card {{variant}}"
556
+ data-id="{{item.id}}"
557
+ style="width: {{width}}px">
558
+ </div>
559
+ ```
560
+
561
+ Expressions are parsed into `AttrValueNode` arrays for easy processing by compilers.
562
+
563
+ ### Component Composition
564
+
565
+ Tempile provides a powerful slot/content system for building reusable components:
566
+
567
+ **1. Create a layout with slots:**
568
+ ```html
569
+ <!-- layouts/base.html -->
570
+ <tempile @html>
571
+ <tempile @head>
572
+ <slot @name="head-extra"></slot>
573
+ </tempile>
574
+ <tempile @body>
575
+ <header>
576
+ <slot @name="header">
577
+ <h1>Default Site Title</h1>
578
+ </slot>
579
+ </header>
580
+ <main>
581
+ <slot @name="content"></slot>
582
+ </main>
583
+ </tempile>
584
+ </tempile>
585
+ ```
586
+
587
+ **2. Use the layout and fill slots:**
588
+ ```html
589
+ <!-- pages/home.html -->
590
+ <include @path="layouts/base.html">
591
+ <content @name="head-extra">
592
+ <link rel="stylesheet" href="home.css">
593
+ </content>
594
+ <content @name="header">
595
+ <h1>Welcome Home</h1>
596
+ <nav>...</nav>
597
+ </content>
598
+ <content @name="content">
599
+ <p>Homepage content</p>
600
+ </content>
601
+ </include>
602
+ ```
603
+
604
+ ## Building a Compiler
605
+
606
+ To build a compiler for Tempile, you need to:
607
+
608
+ 1. Parse templates using `parse()`
609
+ 2. Process includes and slots
610
+ 3. Traverse the AST
611
+ 4. Generate native code for your target language
612
+
613
+ ### Basic Compiler Structure
614
+
615
+ ```javascript
616
+ import { parse, NodeType } from 'tempile-core';
617
+
618
+ class MyCompiler {
619
+ compile(source, fileName) {
620
+ // 1. Parse
621
+ const root = parse(source, fileName);
622
+
623
+ // 2. Resolve includes
624
+ root.resolveIncludes('./templates');
625
+
626
+ // 3. Match slots with contents
627
+ root.matchSlotsAndContents();
628
+
629
+ // 4. Generate code
630
+ return this.generateCode(root.children);
631
+ }
632
+
633
+ generateCode(nodes) {
634
+ let code = '';
635
+
636
+ for (const node of nodes) {
637
+ switch (node.type) {
638
+ case NodeType.Text:
639
+ code += this.generateText(node);
640
+ break;
641
+ case NodeType.Element:
642
+ code += this.generateElement(node);
643
+ break;
644
+ case NodeType.If:
645
+ code += this.generateIf(node);
646
+ break;
647
+ case NodeType.For:
648
+ code += this.generateFor(node);
649
+ break;
650
+ case NodeType.Out:
651
+ code += this.generateOut(node);
652
+ break;
653
+ case NodeType.Logic:
654
+ code += this.generateLogic(node);
655
+ break;
656
+ case NodeType.Import:
657
+ code += this.generateImport(node);
658
+ break;
659
+ // Handle other node types...
660
+ }
661
+ }
662
+
663
+ return code;
664
+ }
665
+
666
+ generateIf(node) {
667
+ // Get condition for your target language
668
+ const condition = node.conditions.find(c => c.name === 'js');
669
+
670
+ let code = `if (${condition.value}) {\n`;
671
+ code += this.generateCode(node.ifContent);
672
+
673
+ for (const elseif of node.elseIfNodes) {
674
+ const elseifCond = elseif.conditions.find(c => c.name === 'js');
675
+ code += `} else if (${elseifCond.value}) {\n`;
676
+ code += this.generateCode(elseif.children);
677
+ }
678
+
679
+ if (node.elseNode) {
680
+ code += `} else {\n`;
681
+ code += this.generateCode(node.elseNode.children);
682
+ }
683
+
684
+ code += `}\n`;
685
+ return code;
686
+ }
687
+
688
+ // Implement other generate methods...
689
+ }
690
+ ```
691
+
692
+ ### Handling Attributes with Expressions
693
+
694
+ ```javascript
695
+ generateElement(node) {
696
+ let code = `<${node.tag}`;
697
+
698
+ for (const attr of node.attrs) {
699
+ if (attr.valueNodes && attr.valueNodes.length > 0) {
700
+ // Attribute has expressions
701
+ code += ` ${attr.name}="`;
702
+ for (const valueNode of attr.valueNodes) {
703
+ if (valueNode.type === 'text') {
704
+ code += valueNode.value;
705
+ } else {
706
+ // Generate expression evaluation code
707
+ code += `\${${valueNode.value}}`;
708
+ }
709
+ }
710
+ code += '"';
711
+ } else {
712
+ // Plain attribute
713
+ code += ` ${attr.name}="${attr.value}"`;
714
+ }
715
+ }
716
+
717
+ code += '>';
718
+ code += this.generateCode(node.children);
719
+ code += `</${node.tag}>`;
720
+
721
+ return code;
722
+ }
723
+ ```
724
+
725
+ ## Error Handling
726
+
727
+ All nodes include position information (`pos`) for detailed error reporting:
728
+
729
+ ```javascript
730
+ function reportError(node, message) {
731
+ const pos = node.pos;
732
+ throw new Error(
733
+ `${message}\n` +
734
+ `File: ${pos.fileName}\n` +
735
+ `Line: ${pos.startLine}, Column: ${pos.startCol}`
736
+ );
737
+ }
738
+ ```
739
+
740
+ ## Best Practices
741
+
742
+ 1. **Always process in order:**
743
+ ```javascript
744
+ const root = parse(source, fileName);
745
+ root.resolveIncludes(srcPath); // First
746
+ root.matchSlotsAndContents(); // Second
747
+ // Then traverse and generate code
748
+ ```
749
+
750
+ 2. **Handle missing language attributes:**
751
+ ```javascript
752
+ const condition = node.conditions.find(c => c.name === targetLang);
753
+ if (!condition) {
754
+ throw new Error(
755
+ `No condition for language '${targetLang}' in if statement at ` +
756
+ `${node.pos.fileName}:${node.pos.startLine}`
757
+ );
758
+ }
759
+ ```
760
+
761
+ 3. **Validate node structures:**
762
+ - Check for required attributes
763
+ - Verify parent-child relationships
764
+ - Ensure proper nesting
765
+
766
+ 4. **Use position information:**
767
+ - Include in error messages
768
+ - Generate source maps
769
+ - Help with debugging
770
+
771
+ ## Complete Example
772
+
773
+ ### Input Templates
774
+
775
+ **layout.html:**
776
+ ```html
777
+ <tempile @doctype></tempile>
778
+ <tempile @html lang="en">
779
+ <tempile @head>
780
+ <meta charset="UTF-8">
781
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
782
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
783
+ <slot @name="header-extra"></slot>
784
+ </tempile>
785
+ <tempile @body>
786
+ <slot @name="body-content"></slot>
787
+ </tempile>
788
+ </tempile>
789
+ ```
790
+
791
+ **page.html:**
792
+ ```html
793
+ <include @path="layout.html">
794
+ <content @name="header-extra">
795
+ <title>Home Page</title>
796
+ </content>
797
+ <content @name="body-content">
798
+ <h1>Page Body</h1>
799
+ <if @js="1 === 1">
800
+ if blok
801
+ <elseif @js="1 > 2">
802
+ elseif blok
803
+ </elseif>
804
+ <else>
805
+ else blok
806
+ </else>
807
+ </if>
808
+ <for @js="const item of items">
809
+ <out>item.name</out>
810
+ </for>
811
+ </content>
812
+ </include>
813
+ ```
814
+
815
+ ### Processing
816
+
817
+ ```javascript
818
+ import { parse } from 'tempile-core';
819
+ import fs from 'fs';
820
+
821
+ // Parse page.html
822
+ const source = fs.readFileSync('page.html', 'utf-8');
823
+ const root = parse(source, 'page.html');
824
+
825
+ // Resolve includes - reads and parses layout.html
826
+ root.resolveIncludes('./');
827
+
828
+ // Match slots with contents - fills header-extra and body-content slots
829
+ root.matchSlotsAndContents();
830
+
831
+ // AST is now ready for compilation
832
+ console.log(JSON.stringify(root, null, 2));
833
+ ```
834
+
835
+ ### Resulting AST Structure
836
+
837
+ After processing, the AST will have this structure:
838
+
839
+ ```json
840
+ {
841
+ "children": [
842
+ {
843
+ "type": "doctype",
844
+ "data": "<!DOCTYPE html>"
845
+ },
846
+ {
847
+ "type": "element",
848
+ "tag": "html",
849
+ "attrs": [{"name": "lang", "value": "en"}],
850
+ "children": [
851
+ {
852
+ "type": "element",
853
+ "tag": "head",
854
+ "children": [
855
+ {"type": "element", "tag": "meta", "attrs": [...]},
856
+ {"type": "element", "tag": "title", "children": [
857
+ {"type": "text", "data": "Home Page"}
858
+ ]}
859
+ ]
860
+ },
861
+ {
862
+ "type": "element",
863
+ "tag": "body",
864
+ "children": [
865
+ {"type": "element", "tag": "h1", "children": [
866
+ {"type": "text", "data": "Page Body"}
867
+ ]},
868
+ {
869
+ "type": "if",
870
+ "conditions": [{"name": "js", "value": "1 === 1"}],
871
+ "ifContent": [
872
+ {"type": "text", "data": "\n\t\t\tif blok\n\t\t\t"}
873
+ ],
874
+ "elseIfNodes": [{
875
+ "type": "elseif",
876
+ "conditions": [{"name": "js", "value": "1 > 2"}],
877
+ "children": [
878
+ {"type": "text", "data": "\n\t\t\t\telseif blok\n\t\t\t"}
879
+ ]
880
+ }],
881
+ "elseNode": {
882
+ "type": "else",
883
+ "children": [
884
+ {"type": "text", "data": "\n\t\t\t\telse blok\n\t\t\t"}
885
+ ]
886
+ }
887
+ },
888
+ {
889
+ "type": "for",
890
+ "loops": [{"name": "js", "value": "const item of items"}],
891
+ "children": [
892
+ {"type": "out", "data": "item.name", "isRaw": false}
893
+ ]
894
+ }
895
+ ]
896
+ }
897
+ ]
898
+ }
899
+ ],
900
+ "fileName": "page.html"
901
+ }
902
+ ```
903
+
904
+ **Key observations:**
905
+
906
+ 1. **Include resolution**: The `<include>` and `<tempile>` tags are unwrapped, their content is merged into the main tree
907
+ 2. **Slot matching**: `<slot>` tags are replaced with their corresponding `<content>` children
908
+ 3. **Structure flattening**: The final AST is a clean tree of standard HTML elements and control flow nodes
909
+ 4. **Position tracking**: Each node includes `pos` with file name and line/column information (omitted above for clarity)
910
+
911
+ ## License
912
+
913
+ MIT
package/dist/index.d.ts CHANGED
@@ -1 +1,4 @@
1
- export {};
1
+ import NodeType from "./ast/base/node-types.js";
2
+ export { parse } from "./parser/parser.js";
3
+ export * from "./ast/nodes.js";
4
+ export { NodeType };
package/dist/index.js CHANGED
@@ -1 +1,4 @@
1
- export {};
1
+ import NodeType from "./ast/base/node-types.js";
2
+ export { parse } from "./parser/parser.js";
3
+ export * from "./ast/nodes.js";
4
+ export { NodeType };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tempile-core",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "tempile-core",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -34,4 +34,4 @@
34
34
  "dependencies": {
35
35
  "parse5": "^8.0.0"
36
36
  }
37
- }
37
+ }