driftjs-compiler 0.0.4 → 0.0.6

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.
@@ -1,302 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { DriftLexer } from '../src/lexer.js';
3
- import { DriftParser } from '../src/parser.js';
4
- import {
5
- ASTNodeType,
6
- ElementNode,
7
- DriftParserError,
8
- DriftLexerError,
9
- TokenType,
10
- Token,
11
- TextNode,
12
- } from '../types/index.js';
13
-
14
- describe('DriftParser', () => {
15
- it('parses empty templates', () => {
16
- const parser = new DriftParser(new DriftLexer(''));
17
- const ast = parser.parse();
18
-
19
- expect(ast.type).toBe(ASTNodeType.Program);
20
- expect(ast.body).toEqual([]);
21
- });
22
-
23
- it('lets the parser drive token consumption lazily', () => {
24
- const lexer = new DriftLexer('<div><span>Hello</span></div>');
25
- const parser = new DriftParser(lexer);
26
-
27
- expect(lexer.getEmittedTokenCount()).toBe(0);
28
-
29
- const ast = parser.parse();
30
-
31
- expect(ast.body).toHaveLength(1);
32
- expect(lexer.getEmittedTokenCount()).toBeGreaterThan(0);
33
- });
34
-
35
- it('parses deeply nested element structures', () => {
36
- const parser = new DriftParser(
37
- new DriftLexer('<div><main><article><section><p>{text}</p></section></article></main></div>')
38
- );
39
- const ast = parser.parse();
40
-
41
- expect(ast.body.length).toBe(1);
42
- let current = ast.body[0] as ElementNode;
43
- expect(current.tagName).toBe('div');
44
-
45
- current = current.children[0] as ElementNode;
46
- expect(current.tagName).toBe('main');
47
-
48
- current = current.children[0] as ElementNode;
49
- expect(current.tagName).toBe('article');
50
-
51
- current = current.children[0] as ElementNode;
52
- expect(current.tagName).toBe('section');
53
-
54
- current = current.children[0] as ElementNode;
55
- expect(current.tagName).toBe('p');
56
- expect(current.children[0]?.type).toBe(ASTNodeType.Interpolation);
57
- });
58
-
59
- it('parses multiple root-level elements, text, and comments', () => {
60
- const parser = new DriftParser(
61
- new DriftLexer('<!-- Root 1 --><div>One</div><!-- Root 2 --><span>Two</span>')
62
- );
63
- const ast = parser.parse();
64
-
65
- expect(ast.body.length).toBe(4);
66
- expect(ast.body[0]?.type).toBe(ASTNodeType.Comment);
67
- expect(ast.body[1]?.type).toBe(ASTNodeType.Element);
68
- expect(ast.body[2]?.type).toBe(ASTNodeType.Comment);
69
- expect(ast.body[3]?.type).toBe(ASTNodeType.Element);
70
- });
71
-
72
- it('parses attributes of mixed kinds', () => {
73
- const parser = new DriftParser(
74
- new DriftLexer('<input type="checkbox" checked id="terms" data-bind={isBound} />')
75
- );
76
- const ast = parser.parse();
77
-
78
- const inputNode = ast.body[0] as ElementNode;
79
- expect(inputNode.isSelfClosing).toBe(true);
80
- expect(inputNode.attributes.length).toBe(4);
81
- expect(inputNode.attributes[0]).toMatchObject({ name: 'type', value: 'checkbox' });
82
- expect(inputNode.attributes[1]).toMatchObject({ name: 'checked', value: null });
83
- expect(inputNode.attributes[2]).toMatchObject({ name: 'id', value: 'terms' });
84
- expect(inputNode.attributes[3]?.name).toBe('data-bind');
85
- expect(typeof inputNode.attributes[3]?.value).toBe('object');
86
- });
87
-
88
- it('parses script tag content as a raw text child', () => {
89
- const parser = new DriftParser(
90
- new DriftLexer('<script>if (a < b) { console.log("ok"); }</script>')
91
- );
92
- const ast = parser.parse();
93
-
94
- const scriptElement = ast.body[0] as ElementNode;
95
- expect(scriptElement.tagName).toBe('script');
96
- expect(scriptElement.children.length).toBe(1);
97
- const scriptText = scriptElement.children[0] as TextNode;
98
- expect(scriptText.type).toBe(ASTNodeType.Text);
99
- expect(scriptText.content).toBe('if (a < b) { console.log("ok"); }');
100
- });
101
-
102
- it('parses complex JS interpolations into raw expression strings', () => {
103
- const parser = new DriftParser(
104
- new DriftLexer('<div>{ isTrue ? "yes" : "no" }</div><span>{ (x) => x * 2 }</span><p>{ user?.name ?? "Guest" }</p>')
105
- );
106
- const ast = parser.parse();
107
-
108
- const div = ast.body[0] as ElementNode;
109
- const divInterpolation = div.children[0] as any;
110
- expect(divInterpolation.type).toBe(ASTNodeType.Interpolation);
111
- expect(divInterpolation.expression).toBe(' isTrue ? "yes" : "no" ');
112
-
113
- const span = ast.body[1] as ElementNode;
114
- const spanInterpolation = span.children[0] as any;
115
- expect(spanInterpolation.expression).toBe(' (x) => x * 2 ');
116
- });
117
-
118
- it('parses interpolated attribute values as raw expression strings', () => {
119
- const parser = new DriftParser(
120
- new DriftLexer('<button onclick={ (e) => handleClick(e) } />')
121
- );
122
- const ast = parser.parse();
123
-
124
- const button = ast.body[0] as ElementNode;
125
- const attr = button.attributes[0];
126
- expect(attr?.name).toBe('onclick');
127
-
128
- const interpolationValue = attr?.value as any;
129
- expect(interpolationValue.type).toBe(ASTNodeType.Interpolation);
130
- expect(interpolationValue.expression).toBe(' (e) => handleClick(e) ');
131
- });
132
-
133
- it('throws when an unexpected closing tag appears at the top level', () => {
134
- const parser = new DriftParser(new DriftLexer('</div>'));
135
- expect(() => parser.parse()).toThrow(DriftParserError);
136
- });
137
-
138
- it('throws when an attribute value is missing after =', () => {
139
- const parser = new DriftParser(new DriftLexer('<div class=></div>'));
140
- expect(() => parser.parse()).toThrow(DriftLexerError);
141
- });
142
-
143
- it('throws on mismatched nested closing tags', () => {
144
- const parser = new DriftParser(new DriftLexer('<div><span>Content</div></span>'));
145
- expect(() => parser.parse()).toThrow(DriftParserError);
146
- });
147
-
148
- it('throws when the closing bracket is missing in a manual token stream', () => {
149
- const dummyLoc = { line: 1, column: 1, offset: 0 };
150
- const tokens: Token[] = [
151
- { type: TokenType.TagOpen, value: '<', loc: { start: dummyLoc, end: dummyLoc } },
152
- { type: TokenType.Identifier, value: 'div', loc: { start: dummyLoc, end: dummyLoc } },
153
- { type: TokenType.Text, value: 'Hello', loc: { start: dummyLoc, end: dummyLoc } },
154
- { type: TokenType.EOF, value: '', loc: { start: dummyLoc, end: dummyLoc } },
155
- ];
156
-
157
- const parser = new DriftParser(tokens);
158
- expect(() => parser.parse()).toThrow(DriftParserError);
159
- });
160
-
161
- it('parses @if, @else if, and @else directives cleanly', () => {
162
- const parser = new DriftParser(
163
- new DriftLexer('@if isLoggedIn { <span>Welcome back</span> } @else if isGuest { <span>Welcome guest</span> } @else { <span>Please log in</span> }')
164
- );
165
- const ast = parser.parse();
166
-
167
- expect(ast.body).toHaveLength(1);
168
- const ifNode = ast.body[0] as any;
169
- expect(ifNode.type).toBe(ASTNodeType.If);
170
- expect(ifNode.test).toBe('isLoggedIn');
171
- const firstSpan = ifNode.consequent.find((n: any) => n.type === ASTNodeType.Element);
172
- expect(firstSpan.tagName).toBe('span');
173
-
174
- // Alternate is nested IfNode for @else if
175
- const elseIfNode = ifNode.alternate as any;
176
- expect(elseIfNode.type).toBe(ASTNodeType.If);
177
- expect(elseIfNode.test).toBe('isGuest');
178
-
179
- // Else branch is array of nodes in alternate of nested IfNode
180
- expect(Array.isArray(elseIfNode.alternate)).toBe(true);
181
- const elseSpan = elseIfNode.alternate.find((n: any) => n.type === ASTNodeType.Element);
182
- expect(elseSpan.tagName).toBe('span');
183
- });
184
-
185
- it('parses @for directives cleanly', () => {
186
- const parser = new DriftParser(
187
- new DriftLexer('@for (item, index) in items { <li>{item}</li> }')
188
- );
189
- const ast = parser.parse();
190
-
191
- expect(ast.body).toHaveLength(1);
192
- const forNode = ast.body[0] as any;
193
- expect(forNode.type).toBe(ASTNodeType.For);
194
- expect(forNode.item).toBe('item');
195
- expect(forNode.index).toBe('index');
196
- expect(forNode.iterable).toBe('items');
197
- const forLi = forNode.body.find((n: any) => n.type === ASTNodeType.Element);
198
- expect(forLi.tagName).toBe('li');
199
- });
200
-
201
- it('parses @for directive without index with index set to null', () => {
202
- const parser = new DriftParser(
203
- new DriftLexer('@for item in items { <li>{item}</li> }')
204
- );
205
- const ast = parser.parse();
206
-
207
- const forNode = ast.body[0] as any;
208
- expect(forNode.item).toBe('item');
209
- expect(forNode.index).toBeNull();
210
- });
211
-
212
- it('parses @switch, @case, and @default directives cleanly', () => {
213
- const parser = new DriftParser(
214
- new DriftLexer('@switch userRole { @case "admin" { <p>Admin</p> } @case "user" { <p>User</p> } @default { <p>Unknown</p> } }')
215
- );
216
- const ast = parser.parse();
217
-
218
- expect(ast.body).toHaveLength(1);
219
- const switchNode = ast.body[0] as any;
220
- expect(switchNode.type).toBe(ASTNodeType.Switch);
221
- expect(switchNode.discriminant).toBe('userRole');
222
- expect(switchNode.cases).toHaveLength(3);
223
- expect(switchNode.cases[0].expression).toBe('"admin"');
224
- expect(switchNode.cases[1].expression).toBe('"user"');
225
- expect(switchNode.cases[2].expression).toBeNull();
226
- });
227
-
228
- it('parses deeply nested mixed control flows (nested @if inside @for inside @switch)', () => {
229
- const input = `
230
- @switch status {
231
- @case "active" {
232
- @for (item, idx) in list {
233
- @if item.isVisible {
234
- <div>{idx}: {item.title}</div>
235
- }
236
- }
237
- }
238
- @default {
239
- <p>No active items</p>
240
- }
241
- }
242
- `;
243
- const parser = new DriftParser(new DriftLexer(input));
244
- const ast = parser.parse();
245
-
246
- const switchNode = ast.body.find((n: any) => n.type === ASTNodeType.Switch) as any;
247
- expect(switchNode).toBeDefined();
248
- expect(switchNode.type).toBe(ASTNodeType.Switch);
249
- expect(switchNode.discriminant).toBe('status');
250
-
251
- const activeCase = switchNode.cases[0];
252
- const forNode = activeCase.body.find((n: any) => n.type === ASTNodeType.For);
253
- expect(forNode.item).toBe('item');
254
- expect(forNode.index).toBe('idx');
255
-
256
- const ifNode = forNode.body.find((n: any) => n.type === ASTNodeType.If);
257
- expect(ifNode.test).toBe('item.isVisible');
258
- });
259
-
260
- it('throws on unclosed directive blocks', () => {
261
- const parser = new DriftParser(new DriftLexer('@if isLoggedIn { <div>Hello</div>'));
262
- expect(() => parser.parse()).toThrow(DriftParserError);
263
- });
264
-
265
- it('throws on invalid @for header syntax missing in keyword', () => {
266
- const parser = new DriftParser(new DriftLexer('@for item items { <li>{item}</li> }'));
267
- expect(() => parser.parse()).toThrow(DriftParserError);
268
- });
269
-
270
- it('throws on invalid content inside @switch blocks that is not @case or @default', () => {
271
- const parser = new DriftParser(new DriftLexer('@switch mode { <div>invalid direct child</div> }'));
272
- expect(() => parser.parse()).toThrow(DriftParserError);
273
- });
274
-
275
- it('automatically parses HTML void elements as self-closing without requiring explicit closing tags', () => {
276
- const parser = new DriftParser(
277
- new DriftLexer('<div><input type="text"><img src="test.jpg"><br><hr></div>')
278
- );
279
- const ast = parser.parse();
280
-
281
- expect(ast.body).toHaveLength(1);
282
- const divNode = ast.body[0] as any;
283
- expect(divNode.tagName).toBe('div');
284
- expect(divNode.children).toHaveLength(4);
285
-
286
- const inputNode = divNode.children[0];
287
- expect(inputNode.tagName).toBe('input');
288
- expect(inputNode.isSelfClosing).toBe(true);
289
-
290
- const imgNode = divNode.children[1];
291
- expect(imgNode.tagName).toBe('img');
292
- expect(imgNode.isSelfClosing).toBe(true);
293
-
294
- const brNode = divNode.children[2];
295
- expect(brNode.tagName).toBe('br');
296
- expect(brNode.isSelfClosing).toBe(true);
297
-
298
- const hrNode = divNode.children[3];
299
- expect(hrNode.tagName).toBe('hr');
300
- expect(hrNode.isSelfClosing).toBe(true);
301
- });
302
- });
@@ -1,110 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { DriftLexer } from '../src/lexer.js';
3
- import { DriftParser } from '../src/parser.js';
4
- import { DriftTransformer } from '../src/transformer.js';
5
- import { ASTNodeType, ElementNode, TextNode, InterpolationNode } from '../types/index.js';
6
-
7
- describe('DriftTransformer', () => {
8
- it('should strip redundant whitespace and newline text nodes between elements', () => {
9
- const input = `
10
- <div>
11
- <span>Hello</span>
12
- </div>
13
- `;
14
- const lexer = new DriftLexer(input);
15
- const parser = new DriftParser(lexer);
16
- const rawAst = parser.parse();
17
-
18
- const transformer = new DriftTransformer(rawAst);
19
- const transformedAst = transformer.transform();
20
-
21
- // Top-level body should contain only 1 ElementNode (div), whitespace TextNodes removed
22
- expect(transformedAst.body.length).toBe(1);
23
- const div = transformedAst.body[0] as ElementNode;
24
- expect(div.tagName).toBe('div');
25
-
26
- // div.children should contain only 1 ElementNode (span), whitespace TextNodes removed
27
- expect(div.children.length).toBe(1);
28
- const span = div.children[0] as ElementNode;
29
- expect(span.tagName).toBe('span');
30
- expect((span.children[0] as TextNode).content).toBe('Hello');
31
- });
32
-
33
- it('should transform raw interpolation strings into Acorn JS AST nodes', () => {
34
- const input = '<h1>{ user.name }</h1>';
35
- const lexer = new DriftLexer(input);
36
- const parser = new DriftParser(lexer);
37
- const rawAst = parser.parse();
38
-
39
- const transformer = new DriftTransformer(rawAst);
40
- const transformedAst = transformer.transform();
41
-
42
- const h1 = transformedAst.body[0] as ElementNode;
43
- const interp = h1.children[0] as InterpolationNode;
44
- expect(interp.type).toBe(ASTNodeType.Interpolation);
45
-
46
- const expr = interp.expression as any;
47
- expect(expr.type).toBe('MemberExpression');
48
- expect(expr.object.name).toBe('user');
49
- expect(expr.property.name).toBe('name');
50
- });
51
-
52
- it('should transform script tag text child into stripped Acorn JS statement AST', () => {
53
- const input = '<script>let count = 0;</script>';
54
- const lexer = new DriftLexer(input);
55
- const parser = new DriftParser(lexer);
56
- const rawAst = parser.parse();
57
-
58
- const transformer = new DriftTransformer(rawAst);
59
- const transformedAst = transformer.transform();
60
-
61
- const script = transformedAst.body[0] as ElementNode;
62
- expect(script.tagName).toBe('script');
63
-
64
- const scriptTextNode = script.children[0] as TextNode;
65
- const jsContent = scriptTextNode.content as any;
66
- expect(jsContent.type).toBe('VariableDeclaration');
67
- expect(jsContent.kind).toBe('let');
68
- expect(jsContent.declarations[0].id.name).toBe('count');
69
- });
70
-
71
- it('should enrich nested directive expressions (If, For, Switch) into Acorn JS AST nodes', () => {
72
- const input = `
73
- @switch user.getRole() {
74
- @case "admin" {
75
- @for item in store.getItems(10) {
76
- @if item.price > 100 {
77
- <span>{ item.name }</span>
78
- }
79
- }
80
- }
81
- }
82
- `;
83
- const lexer = new DriftLexer(input);
84
- const parser = new DriftParser(lexer);
85
- const rawAst = parser.parse();
86
-
87
- const transformer = new DriftTransformer(rawAst);
88
- const transformedAst = transformer.transform();
89
-
90
- const switchIfNode = transformedAst.body[0] as any;
91
- expect(switchIfNode.type).toBe(ASTNodeType.If);
92
- expect(switchIfNode.test.type).toBe('BinaryExpression');
93
-
94
- const forNode = switchIfNode.consequent.find((n: any) => n.type === ASTNodeType.For);
95
- expect(forNode.iterable.type).toBe('CallExpression');
96
-
97
- const ifNode = forNode.body.find((n: any) => n.type === ASTNodeType.If);
98
- expect(ifNode.test.type).toBe('BinaryExpression');
99
- });
100
-
101
- it('should throw DriftParserError when an invalid JS syntax expression is encountered in an interpolation', () => {
102
- const input = '<div>{ 1 + * 2 }</div>';
103
- const lexer = new DriftLexer(input);
104
- const parser = new DriftParser(lexer);
105
- const rawAst = parser.parse();
106
-
107
- const transformer = new DriftTransformer(rawAst);
108
- expect(() => transformer.transform()).toThrow();
109
- });
110
- });
package/tsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "."
6
- },
7
- "include": ["src/**/*", "types/**/*"]
8
- }
package/types/ast.ts DELETED
@@ -1,88 +0,0 @@
1
- import type { Node as AcornNode } from 'acorn';
2
- import type { SourceRange } from './token.js';
3
-
4
- /**
5
- * AST Node Types supported by DriftParser.
6
- */
7
- export const ASTNodeType = {
8
- Program: 'Program',
9
- Element: 'Element',
10
- Text: 'Text',
11
- Interpolation: 'Interpolation',
12
- Attribute: 'Attribute',
13
- Comment: 'Comment',
14
- If: 'If',
15
- For: 'For',
16
- Switch: 'Switch',
17
- } as const;
18
-
19
- export type ASTNodeType = typeof ASTNodeType[keyof typeof ASTNodeType];
20
-
21
- export interface BaseASTNode {
22
- readonly type: ASTNodeType;
23
- readonly loc: SourceRange;
24
- }
25
-
26
- export interface AttributeNode extends BaseASTNode {
27
- readonly type: typeof ASTNodeType.Attribute;
28
- readonly name: string;
29
- readonly value: string | InterpolationNode | null;
30
- }
31
-
32
- export interface InterpolationNode extends BaseASTNode {
33
- readonly type: typeof ASTNodeType.Interpolation;
34
- readonly expression: string | AcornNode;
35
- }
36
-
37
- export interface TextNode extends BaseASTNode {
38
- readonly type: typeof ASTNodeType.Text;
39
- readonly content: string | AcornNode | readonly AcornNode[];
40
- }
41
-
42
- export interface CommentNode extends BaseASTNode {
43
- readonly type: typeof ASTNodeType.Comment;
44
- readonly content: string;
45
- }
46
-
47
- export interface IfNode extends BaseASTNode {
48
- readonly type: typeof ASTNodeType.If;
49
- readonly test: string | AcornNode;
50
- readonly consequent: readonly TemplateChildNode[];
51
- readonly alternate: readonly TemplateChildNode[] | IfNode | null;
52
- }
53
-
54
- export interface ForNode extends BaseASTNode {
55
- readonly type: typeof ASTNodeType.For;
56
- readonly item: string;
57
- readonly index: string | null;
58
- readonly iterable: string | AcornNode;
59
- readonly key?: string | AcornNode | null;
60
- readonly body: readonly TemplateChildNode[];
61
- }
62
-
63
- export interface CaseBranch {
64
- readonly expression: string | AcornNode | null; // null for default
65
- readonly body: readonly TemplateChildNode[];
66
- readonly loc: SourceRange;
67
- }
68
-
69
- export interface SwitchNode extends BaseASTNode {
70
- readonly type: typeof ASTNodeType.Switch;
71
- readonly discriminant: string | AcornNode;
72
- readonly cases: readonly CaseBranch[];
73
- }
74
-
75
- export interface ElementNode extends BaseASTNode {
76
- readonly type: typeof ASTNodeType.Element;
77
- readonly tagName: string;
78
- readonly attributes: readonly AttributeNode[];
79
- readonly children: readonly TemplateChildNode[];
80
- readonly isSelfClosing: boolean;
81
- }
82
-
83
- export type TemplateChildNode = ElementNode | TextNode | InterpolationNode | CommentNode | IfNode | ForNode | SwitchNode;
84
-
85
- export interface ProgramNode extends BaseASTNode {
86
- readonly type: typeof ASTNodeType.Program;
87
- readonly body: readonly TemplateChildNode[];
88
- }
package/types/error.ts DELETED
@@ -1,29 +0,0 @@
1
- /**
2
- * Custom error class for lexing errors.
3
- */
4
- export class DriftLexerError extends Error {
5
- constructor(
6
- message: string,
7
- public readonly line: number,
8
- public readonly column: number,
9
- public readonly offset: number,
10
- ) {
11
- super(`LexerError [${line}:${column}]: ${message}`);
12
- this.name = 'DriftLexerError';
13
- }
14
- }
15
-
16
- /**
17
- * Custom error class for parsing errors.
18
- */
19
- export class DriftParserError extends Error {
20
- constructor(
21
- message: string,
22
- public readonly line: number,
23
- public readonly column: number,
24
- public readonly offset: number,
25
- ) {
26
- super(`ParserError [${line}:${column}]: ${message}`);
27
- this.name = 'DriftParserError';
28
- }
29
- }
package/types/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export * from './token.js';
2
- export * from './lexer-state.js';
3
- export * from './ast.js';
4
- export * from './error.js';
5
- export * from './opcodes.js';
6
-
@@ -1,118 +0,0 @@
1
- export const LexerStateKind = {
2
- Data: 'Data',
3
- TagOpen: 'TagOpen',
4
- EndTagOpen: 'EndTagOpen',
5
- BeforeAttributeName: 'BeforeAttributeName',
6
- AttributeName: 'AttributeName',
7
- AfterAttributeName: 'AfterAttributeName',
8
- BeforeAttributeValue: 'BeforeAttributeValue',
9
- AttributeValueQuoted: 'AttributeValueQuoted',
10
- AttributeValueInterpolation: 'AttributeValueInterpolation',
11
- Comment: 'Comment',
12
- Interpolation: 'Interpolation',
13
- RawText: 'RawText',
14
- EOF: 'EOF',
15
- } as const;
16
-
17
- export type LexerStateKind = typeof LexerStateKind[keyof typeof LexerStateKind];
18
-
19
- export type RawTextTagName = 'script' | 'style';
20
- export type LexerInterpolationContext = 'content' | 'attribute';
21
- export type AttributeQuote = '"' | "'";
22
-
23
- interface BaseLexerState {
24
- readonly kind: LexerStateKind;
25
- }
26
-
27
- export interface DataLexerState extends BaseLexerState {
28
- readonly kind: typeof LexerStateKind.Data;
29
- }
30
-
31
- export interface TagOpenLexerState extends BaseLexerState {
32
- readonly kind: typeof LexerStateKind.TagOpen;
33
- }
34
-
35
- export interface EndTagOpenLexerState extends BaseLexerState {
36
- readonly kind: typeof LexerStateKind.EndTagOpen;
37
- }
38
-
39
- export interface BeforeAttributeNameLexerState extends BaseLexerState {
40
- readonly kind: typeof LexerStateKind.BeforeAttributeName;
41
- readonly tagName: string;
42
- readonly isClosingTag: boolean;
43
- readonly entersRawText: boolean;
44
- }
45
-
46
- export interface AttributeNameLexerState extends BaseLexerState {
47
- readonly kind: typeof LexerStateKind.AttributeName;
48
- readonly tagName: string;
49
- readonly attributeName: string | null;
50
- }
51
-
52
- export interface AfterAttributeNameLexerState extends BaseLexerState {
53
- readonly kind: typeof LexerStateKind.AfterAttributeName;
54
- readonly tagName: string;
55
- readonly attributeName: string;
56
- }
57
-
58
- export interface BeforeAttributeValueLexerState extends BaseLexerState {
59
- readonly kind: typeof LexerStateKind.BeforeAttributeValue;
60
- readonly tagName: string;
61
- readonly attributeName: string;
62
- }
63
-
64
- export interface AttributeValueQuotedLexerState extends BaseLexerState {
65
- readonly kind: typeof LexerStateKind.AttributeValueQuoted;
66
- readonly tagName: string;
67
- readonly attributeName: string;
68
- readonly quote: AttributeQuote;
69
- }
70
-
71
- export interface AttributeValueInterpolationLexerState extends BaseLexerState {
72
- readonly kind: typeof LexerStateKind.AttributeValueInterpolation;
73
- readonly tagName: string;
74
- readonly attributeName: string;
75
- }
76
-
77
- export interface CommentLexerState extends BaseLexerState {
78
- readonly kind: typeof LexerStateKind.Comment;
79
- }
80
-
81
- export interface InterpolationLexerState extends BaseLexerState {
82
- readonly kind: typeof LexerStateKind.Interpolation;
83
- readonly context: LexerInterpolationContext;
84
- readonly tagName: string | null;
85
- }
86
-
87
- export interface RawTextLexerState extends BaseLexerState {
88
- readonly kind: typeof LexerStateKind.RawText;
89
- readonly tagName: RawTextTagName;
90
- }
91
-
92
- export interface EOFLexerState extends BaseLexerState {
93
- readonly kind: typeof LexerStateKind.EOF;
94
- }
95
-
96
- export type DriftLexerState =
97
- | DataLexerState
98
- | TagOpenLexerState
99
- | EndTagOpenLexerState
100
- | BeforeAttributeNameLexerState
101
- | AttributeNameLexerState
102
- | AfterAttributeNameLexerState
103
- | BeforeAttributeValueLexerState
104
- | AttributeValueQuotedLexerState
105
- | AttributeValueInterpolationLexerState
106
- | CommentLexerState
107
- | InterpolationLexerState
108
- | RawTextLexerState
109
- | EOFLexerState;
110
-
111
- export interface LexerStateTransition {
112
- readonly to: LexerStateKind;
113
- readonly when: string;
114
- readonly emits: string;
115
- }
116
-
117
-
118
-