@lithent/lithent-template-parser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +159 -0
  3. package/dist/codegen/createElement.d.ts +10 -0
  4. package/dist/codegen/index.d.ts +1 -0
  5. package/dist/compiler.d.ts +26 -0
  6. package/dist/docPipe/index.d.ts +40 -0
  7. package/dist/docPipe/scan.d.ts +6 -0
  8. package/dist/index.d.ts +13 -0
  9. package/dist/index.mjs +2302 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/dist/index.umd.cjs +13 -0
  12. package/dist/index.umd.cjs.map +1 -0
  13. package/dist/parser/ast.d.ts +207 -0
  14. package/dist/parser/lexer.d.ts +109 -0
  15. package/dist/parser/parser.d.ts +79 -0
  16. package/dist/parser/tokens.d.ts +51 -0
  17. package/dist/transform/directives.d.ts +30 -0
  18. package/dist/transform/index.d.ts +14 -0
  19. package/dist/transform/optimize.d.ts +21 -0
  20. package/package.json +53 -0
  21. package/src/codegen/createElement.ts +661 -0
  22. package/src/codegen/index.ts +1 -0
  23. package/src/compiler.ts +70 -0
  24. package/src/docPipe/index.ts +104 -0
  25. package/src/docPipe/scan.ts +318 -0
  26. package/src/index.ts +16 -0
  27. package/src/parser/ast.ts +478 -0
  28. package/src/parser/lexer.ts +561 -0
  29. package/src/parser/parser.ts +514 -0
  30. package/src/parser/tokens.ts +84 -0
  31. package/src/playground/main.ts +206 -0
  32. package/src/playground/style.css +135 -0
  33. package/src/shims/babel-env.ts +17 -0
  34. package/src/tests/compiler.test.ts +434 -0
  35. package/src/tests/docPipe.test.ts +41 -0
  36. package/src/tests/helpers.ts +96 -0
  37. package/src/tests/lexer.test.ts +282 -0
  38. package/src/tests/parser.test.ts +691 -0
  39. package/src/transform/directives.ts +160 -0
  40. package/src/transform/index.ts +29 -0
  41. package/src/transform/optimize.ts +203 -0
@@ -0,0 +1,207 @@
1
+ import { Position } from './tokens';
2
+ /**
3
+ * Base AST Node interface
4
+ */
5
+ export interface BaseNode {
6
+ type: string;
7
+ start: Position;
8
+ end: Position;
9
+ }
10
+ /**
11
+ * Node types
12
+ */
13
+ export declare enum NodeType {
14
+ ROOT = "Root",
15
+ ELEMENT = "Element",
16
+ FRAGMENT = "Fragment",
17
+ TEXT = "Text",
18
+ INTERPOLATION = "Interpolation",
19
+ COMMENT = "Comment",
20
+ ATTRIBUTE = "Attribute",
21
+ DIRECTIVE_IF = "DirectiveIf",
22
+ DIRECTIVE_ELSE_IF = "DirectiveElseIf",
23
+ DIRECTIVE_ELSE = "DirectiveElse",
24
+ DIRECTIVE_FOR = "DirectiveFor"
25
+ }
26
+ /**
27
+ * Root node - represents the entire template
28
+ */
29
+ export interface RootNode extends BaseNode {
30
+ type: NodeType.ROOT;
31
+ children: TemplateNode[];
32
+ }
33
+ /**
34
+ * Element node - represents HTML element or component
35
+ */
36
+ export interface ElementNode extends BaseNode {
37
+ type: NodeType.ELEMENT;
38
+ tag: string;
39
+ isComponent: boolean;
40
+ isSelfClosing: boolean;
41
+ attributes: AttributeNode[];
42
+ directives: DirectiveNode[];
43
+ children: TemplateNode[];
44
+ ref?: string;
45
+ }
46
+ /**
47
+ * Fragment node - represents <></>
48
+ */
49
+ export interface FragmentNode extends BaseNode {
50
+ type: NodeType.FRAGMENT;
51
+ children: TemplateNode[];
52
+ }
53
+ /**
54
+ * Text node - represents plain text content
55
+ */
56
+ export interface TextNode extends BaseNode {
57
+ type: NodeType.TEXT;
58
+ content: string;
59
+ }
60
+ /**
61
+ * Interpolation node - represents {expression}
62
+ */
63
+ export interface InterpolationNode extends BaseNode {
64
+ type: NodeType.INTERPOLATION;
65
+ expression: string;
66
+ }
67
+ /**
68
+ * Comment node - represents HTML comments
69
+ */
70
+ export interface CommentNode extends BaseNode {
71
+ type: NodeType.COMMENT;
72
+ content: string;
73
+ }
74
+ /**
75
+ * Attribute node - represents element attributes
76
+ */
77
+ export interface AttributeNode extends BaseNode {
78
+ type: NodeType.ATTRIBUTE;
79
+ name: string;
80
+ value: AttributeValue | null;
81
+ isDynamic: boolean;
82
+ }
83
+ /**
84
+ * Attribute value can be static string or expression
85
+ */
86
+ export type AttributeValue = string | {
87
+ expression: string;
88
+ };
89
+ /**
90
+ * Base directive node
91
+ */
92
+ export interface BaseDirectiveNode extends BaseNode {
93
+ type: NodeType.DIRECTIVE_IF | NodeType.DIRECTIVE_ELSE_IF | NodeType.DIRECTIVE_ELSE | NodeType.DIRECTIVE_FOR;
94
+ }
95
+ /**
96
+ * l-if directive
97
+ */
98
+ export interface DirectiveIfNode extends BaseDirectiveNode {
99
+ type: NodeType.DIRECTIVE_IF;
100
+ condition: string;
101
+ }
102
+ /**
103
+ * l-else-if directive
104
+ */
105
+ export interface DirectiveElseIfNode extends BaseDirectiveNode {
106
+ type: NodeType.DIRECTIVE_ELSE_IF;
107
+ condition: string;
108
+ }
109
+ /**
110
+ * l-else directive
111
+ */
112
+ export interface DirectiveElseNode extends BaseDirectiveNode {
113
+ type: NodeType.DIRECTIVE_ELSE;
114
+ }
115
+ /**
116
+ * l-for directive
117
+ */
118
+ export interface DirectiveForNode extends BaseDirectiveNode {
119
+ type: NodeType.DIRECTIVE_FOR;
120
+ item: string;
121
+ index?: string;
122
+ list: string;
123
+ }
124
+ /**
125
+ * Union type for all directive nodes
126
+ */
127
+ export type DirectiveNode = DirectiveIfNode | DirectiveElseIfNode | DirectiveElseNode | DirectiveForNode;
128
+ /**
129
+ * Union type for all template nodes (excluding Root)
130
+ */
131
+ export type TemplateNode = ElementNode | FragmentNode | TextNode | InterpolationNode | CommentNode;
132
+ /**
133
+ * Type guard for Element nodes
134
+ */
135
+ export declare function isElementNode(node: TemplateNode): node is ElementNode;
136
+ /**
137
+ * Type guard for Fragment nodes
138
+ */
139
+ export declare function isFragmentNode(node: TemplateNode): node is FragmentNode;
140
+ /**
141
+ * Type guard for Text nodes
142
+ */
143
+ export declare function isTextNode(node: TemplateNode): node is TextNode;
144
+ /**
145
+ * Type guard for Interpolation nodes
146
+ */
147
+ export declare function isInterpolationNode(node: TemplateNode): node is InterpolationNode;
148
+ /**
149
+ * Type guard for Comment nodes
150
+ */
151
+ export declare function isCommentNode(node: TemplateNode): node is CommentNode;
152
+ /**
153
+ * Type guard for directive nodes
154
+ */
155
+ export declare function isDirective(node: AttributeNode | DirectiveNode): node is DirectiveNode;
156
+ /**
157
+ * Create a root node
158
+ */
159
+ export declare function createRootNode(children: TemplateNode[], start: Position, end: Position): RootNode;
160
+ /**
161
+ * Create an element node
162
+ */
163
+ export declare function createElementNode(tag: string, attributes: AttributeNode[], directives: DirectiveNode[], children: TemplateNode[], isSelfClosing: boolean, start: Position, end: Position): ElementNode;
164
+ /**
165
+ * Create a fragment node
166
+ */
167
+ export declare function createFragmentNode(children: TemplateNode[], start: Position, end: Position): FragmentNode;
168
+ /**
169
+ * Create a text node
170
+ */
171
+ export declare function createTextNode(content: string, start: Position, end: Position): TextNode;
172
+ /**
173
+ * Create an interpolation node
174
+ */
175
+ export declare function createInterpolationNode(expression: string, start: Position, end: Position): InterpolationNode;
176
+ /**
177
+ * Create a comment node
178
+ */
179
+ export declare function createCommentNode(content: string, start: Position, end: Position): CommentNode;
180
+ /**
181
+ * Create an attribute node
182
+ */
183
+ export declare function createAttributeNode(name: string, value: AttributeValue | null, isDynamic: boolean, start: Position, end: Position): AttributeNode;
184
+ /**
185
+ * Create a l-if directive node
186
+ */
187
+ export declare function createDirectiveIfNode(condition: string, start: Position, end: Position): DirectiveIfNode;
188
+ /**
189
+ * Create a l-else-if directive node
190
+ */
191
+ export declare function createDirectiveElseIfNode(condition: string, start: Position, end: Position): DirectiveElseIfNode;
192
+ /**
193
+ * Create a l-else directive node
194
+ */
195
+ export declare function createDirectiveElseNode(start: Position, end: Position): DirectiveElseNode;
196
+ /**
197
+ * Create a l-for directive node
198
+ */
199
+ export declare function createDirectiveForNode(item: string, list: string, index: string | undefined, start: Position, end: Position): DirectiveForNode;
200
+ /**
201
+ * Walk through AST and call visitor function for each node
202
+ */
203
+ export declare function walk(node: RootNode | TemplateNode, visitor: (node: TemplateNode) => void): void;
204
+ /**
205
+ * Transform AST by applying a transformer function to each node
206
+ */
207
+ export declare function transformTree(node: RootNode | TemplateNode, transformer: (node: TemplateNode) => TemplateNode): RootNode | TemplateNode;
@@ -0,0 +1,109 @@
1
+ import { Token } from './tokens';
2
+ /**
3
+ * Lexer for Lithent templates
4
+ * Converts template string into a stream of tokens
5
+ */
6
+ export declare class Lexer {
7
+ private input;
8
+ private position;
9
+ private line;
10
+ private column;
11
+ private tokens;
12
+ constructor(input: string);
13
+ /**
14
+ * Tokenize the entire input
15
+ */
16
+ tokenize(): Token[];
17
+ /**
18
+ * Get current position
19
+ */
20
+ private getCurrentPosition;
21
+ /**
22
+ * Peek at character without consuming
23
+ */
24
+ private peek;
25
+ /**
26
+ * Consume and return current character
27
+ */
28
+ private advance;
29
+ /**
30
+ * Check if we've reached the end
31
+ */
32
+ private isEOF;
33
+ /**
34
+ * Read next token
35
+ */
36
+ private readToken;
37
+ /**
38
+ * Read HTML comment
39
+ */
40
+ private readComment;
41
+ /**
42
+ * Read opening tag <
43
+ */
44
+ private readOpenTag;
45
+ /**
46
+ * Read closing tag </
47
+ */
48
+ private readCloseTag;
49
+ /**
50
+ * Read tag content (tag name and attributes)
51
+ */
52
+ private readTagContent;
53
+ /**
54
+ * Read an attribute (name and optional value)
55
+ */
56
+ private readAttribute;
57
+ /**
58
+ * Read identifier (tag name or attribute name)
59
+ */
60
+ private readIdentifier;
61
+ /**
62
+ * Determine token type for identifier
63
+ */
64
+ private getIdentifierTokenType;
65
+ /**
66
+ * Read string literal
67
+ */
68
+ private readStringLiteral;
69
+ /**
70
+ * Read fragment open <>
71
+ */
72
+ private readFragmentOpen;
73
+ /**
74
+ * Read fragment close </>
75
+ */
76
+ private readFragmentClose;
77
+ /**
78
+ * Read expression content inside {}
79
+ */
80
+ private readExpression;
81
+ /**
82
+ * Read text content
83
+ */
84
+ private readText;
85
+ /**
86
+ * Read whitespace
87
+ */
88
+ private readWhitespace;
89
+ /**
90
+ * Skip whitespace without creating tokens
91
+ */
92
+ private skipWhitespace;
93
+ /**
94
+ * Check if character is whitespace
95
+ */
96
+ private isWhitespace;
97
+ /**
98
+ * Check if character can start an identifier
99
+ */
100
+ private isIdentifierStart;
101
+ /**
102
+ * Check if character can be part of an identifier
103
+ */
104
+ private isIdentifierPart;
105
+ }
106
+ /**
107
+ * Tokenize a template string
108
+ */
109
+ export declare function tokenize(input: string): Token[];
@@ -0,0 +1,79 @@
1
+ import { Token } from './tokens';
2
+ import { RootNode } from './ast';
3
+ /**
4
+ * Parser for Lithent templates
5
+ * Converts token stream into AST
6
+ */
7
+ export declare class Parser {
8
+ private tokens;
9
+ private position;
10
+ constructor(tokens: Token[]);
11
+ /**
12
+ * Parse tokens into AST
13
+ */
14
+ parse(): RootNode;
15
+ /**
16
+ * Parse a single template node
17
+ */
18
+ private parseTemplateNode;
19
+ /**
20
+ * Parse fragment node
21
+ */
22
+ private parseFragment;
23
+ /**
24
+ * Parse element
25
+ */
26
+ private parseElement;
27
+ /**
28
+ * Parse attributes and directives
29
+ */
30
+ private parseAttributesAndDirectives;
31
+ /**
32
+ * Parse attribute
33
+ */
34
+ private parseAttribute;
35
+ /**
36
+ * Parse directive
37
+ */
38
+ private parseDirective;
39
+ /**
40
+ * Parse l-for expression
41
+ */
42
+ private parseForExpression;
43
+ /**
44
+ * Parse text node
45
+ */
46
+ private parseText;
47
+ /**
48
+ * Parse interpolation node
49
+ */
50
+ private parseInterpolation;
51
+ /**
52
+ * Parse comment node
53
+ */
54
+ private parseComment;
55
+ /**
56
+ * Peek at current token without consuming
57
+ */
58
+ private peek;
59
+ /**
60
+ * Advance to next token and return current
61
+ */
62
+ private advance;
63
+ /**
64
+ * Check if current token matches type
65
+ */
66
+ private check;
67
+ /**
68
+ * Expect a token of specific type, throw error if not found
69
+ */
70
+ private expect;
71
+ /**
72
+ * Check if we've reached EOF
73
+ */
74
+ private isEOF;
75
+ }
76
+ /**
77
+ * Parse template string into AST
78
+ */
79
+ export declare function parse(tokens: Token[]): RootNode;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Token types for Lithent template lexer
3
+ */
4
+ export declare enum TokenType {
5
+ TAG_OPEN_START = "TAG_OPEN_START",
6
+ TAG_OPEN_END = "TAG_OPEN_END",
7
+ TAG_CLOSE_START = "TAG_CLOSE_START",
8
+ TAG_SELF_CLOSE = "TAG_SELF_CLOSE",
9
+ IDENTIFIER = "IDENTIFIER",
10
+ ATTRIBUTE_EQUALS = "ATTRIBUTE_EQUALS",
11
+ STRING_LITERAL = "STRING_LITERAL",
12
+ EXPRESSION_START = "EXPRESSION_START",
13
+ EXPRESSION_END = "EXPRESSION_END",
14
+ EXPRESSION_CONTENT = "EXPRESSION_CONTENT",
15
+ DIRECTIVE_IF = "DIRECTIVE_IF",
16
+ DIRECTIVE_ELSE_IF = "DIRECTIVE_ELSE_IF",
17
+ DIRECTIVE_ELSE = "DIRECTIVE_ELSE",
18
+ DIRECTIVE_FOR = "DIRECTIVE_FOR",
19
+ FRAGMENT_OPEN = "FRAGMENT_OPEN",
20
+ FRAGMENT_CLOSE = "FRAGMENT_CLOSE",
21
+ KEYWORD_REF = "KEYWORD_REF",
22
+ TEXT = "TEXT",
23
+ COMMENT = "COMMENT",
24
+ WHITESPACE = "WHITESPACE",
25
+ EOF = "EOF"
26
+ }
27
+ /**
28
+ * Position information for error reporting
29
+ */
30
+ export interface Position {
31
+ line: number;
32
+ column: number;
33
+ offset: number;
34
+ }
35
+ /**
36
+ * Token interface
37
+ */
38
+ export interface Token {
39
+ type: TokenType;
40
+ value: string;
41
+ start: Position;
42
+ end: Position;
43
+ }
44
+ /**
45
+ * Create a position object
46
+ */
47
+ export declare function createPosition(line: number, column: number, offset: number): Position;
48
+ /**
49
+ * Create a token
50
+ */
51
+ export declare function createToken(type: TokenType, value: string, start: Position, end: Position): Token;
@@ -0,0 +1,30 @@
1
+ import { RootNode, ElementNode, NodeType, DirectiveIfNode, DirectiveElseIfNode, DirectiveElseNode } from '../parser/ast';
2
+ /**
3
+ * Group conditional elements (l-if/l-else-if/l-else chain)
4
+ * This transform marks related conditional elements for easier codegen
5
+ */
6
+ export interface ConditionalGroup {
7
+ if: ElementNode;
8
+ elseIfs: ElementNode[];
9
+ else?: ElementNode;
10
+ }
11
+ /**
12
+ * Transform directives in the AST
13
+ * Currently handles:
14
+ * - Grouping l-if/l-else-if/l-else chains
15
+ */
16
+ export declare function transformDirectives(ast: RootNode): RootNode;
17
+ /**
18
+ * Get conditional group if element is part of one
19
+ */
20
+ export declare function getConditionalGroup(element: ElementNode): ConditionalGroup | undefined;
21
+ /**
22
+ * Check if element has a specific directive type
23
+ */
24
+ export declare function hasDirective(element: ElementNode, directiveType: NodeType): boolean;
25
+ /**
26
+ * Get directive of specific type from element
27
+ */
28
+ export declare function getDirective(element: ElementNode, directiveType: NodeType.DIRECTIVE_IF): DirectiveIfNode | undefined;
29
+ export declare function getDirective(element: ElementNode, directiveType: NodeType.DIRECTIVE_ELSE_IF): DirectiveElseIfNode | undefined;
30
+ export declare function getDirective(element: ElementNode, directiveType: NodeType.DIRECTIVE_ELSE): DirectiveElseNode | undefined;
@@ -0,0 +1,14 @@
1
+ import { RootNode } from '../parser/ast';
2
+ import { transformDirectives } from './directives';
3
+ import { optimize, OptimizeOptions } from './optimize';
4
+ /**
5
+ * Apply all transformations to the AST
6
+ */
7
+ export declare function transform(ast: RootNode, options?: TransformOptions): RootNode;
8
+ export interface TransformOptions {
9
+ optimize?: boolean;
10
+ optimizeOptions?: OptimizeOptions;
11
+ }
12
+ export { transformDirectives, optimize };
13
+ export * from './directives';
14
+ export * from './optimize';
@@ -0,0 +1,21 @@
1
+ import { RootNode, TemplateNode, TextNode } from '../parser/ast';
2
+ /**
3
+ * Optimize the AST
4
+ * - Merge consecutive text nodes
5
+ * - Remove empty text nodes (whitespace-only)
6
+ * - Trim text nodes at boundaries
7
+ */
8
+ export declare function optimize(ast: RootNode, options?: OptimizeOptions): RootNode;
9
+ export interface OptimizeOptions {
10
+ mergeTextNodes?: boolean;
11
+ removeWhitespace?: boolean;
12
+ trimText?: boolean;
13
+ }
14
+ /**
15
+ * Check if a text node is whitespace-only
16
+ */
17
+ export declare function isWhitespaceOnly(node: TextNode): boolean;
18
+ /**
19
+ * Count nodes in the tree
20
+ */
21
+ export declare function countNodes(node: RootNode | TemplateNode): number;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "type": "module",
3
+ "name": "@lithent/lithent-template-parser",
4
+ "version": "0.1.0",
5
+ "description": "Lithent Template Parser",
6
+ "main": "./dist/index.umd.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.umd.cjs",
14
+ "default": "./dist/index.mjs"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "dependencies": {
19
+ "magic-string": "^0.30.11"
20
+ },
21
+ "devDependencies": {
22
+ "lithent": "1.20.1"
23
+ },
24
+ "keywords": [
25
+ "lithent",
26
+ "vite",
27
+ "hmr"
28
+ ],
29
+ "author": "superlucky84",
30
+ "license": "MIT",
31
+ "homepage": "https://github.com/superlucky84/lithent",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/superlucky84/lithent.git"
35
+ },
36
+ "peerDependencies": {},
37
+ "files": [
38
+ "dist",
39
+ "src"
40
+ ],
41
+ "volta": {
42
+ "node": "20.3.0",
43
+ "pnpm": "9.12.3"
44
+ },
45
+ "scripts": {
46
+ "dev": "vite",
47
+ "dev:plugin": "vite --config vite.config.plugin.ts",
48
+ "build": "vite build",
49
+ "watch": "vite build --watch",
50
+ "test": "vitest run --reporter verbose",
51
+ "test:dev": "vitest"
52
+ }
53
+ }