@xaendar/compiler 0.0.4 → 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.
- package/package.json +19 -11
- package/xaendar-compiler.es.d.ts +199 -0
- package/xaendar-compiler.es.js +505 -0
- package/xaendar-compiler.es.js.map +1 -0
- package/src/costants/chars.constants.ts +0 -188
- package/src/costants/tags/base-tags.constants.ts +0 -106
- package/src/costants/tags/not-alllowed-tags.constants.ts +0 -13
- package/src/costants/tags/not-allowed-chars.constants.ts +0 -24
- package/src/lexer/lexer.ts +0 -91
- package/src/lexer/models/current-char.type.ts +0 -17
- package/src/lexer/models/current-position.type.ts +0 -13
- package/src/lexer/models/lexer-cursor.model.ts +0 -192
- package/src/lexer/models/lexer-state.enum.ts +0 -13
- package/src/lexer/models/token-type.enum.ts +0 -17
- package/src/lexer/models/token.type.ts +0 -62
- package/src/lexer/models/transition-function/transition-function-context.type.ts +0 -27
- package/src/lexer/models/transition-function/transition-function-return-type.type.ts +0 -48
- package/src/lexer/models/transition-function/transition-function.type.ts +0 -36
- package/src/lexer/states/attribute.state.ts +0 -47
- package/src/lexer/states/event.state.ts +0 -38
- package/src/lexer/states/interpolation-expression.state.ts +0 -68
- package/src/lexer/states/interpolation-literal.state.ts +0 -71
- package/src/lexer/states/interpolation.state.ts +0 -31
- package/src/lexer/states/tag-body.state.ts +0 -43
- package/src/lexer/states/tag-close.state.ts +0 -46
- package/src/lexer/states/tag-open-end.state.ts +0 -40
- package/src/lexer/states/tag-open-name.state.ts +0 -50
- package/src/lexer/states/text.state.ts +0 -61
- package/src/parser/models/ast.type.ts +0 -34
- package/src/parser/models/current-token.type.ts +0 -6
- package/src/parser/models/node.enum.ts +0 -5
- package/src/parser/models/parser-cursor.model.ts +0 -133
- package/src/parser/parser.ts +0 -225
- package/src/public-api.ts +0 -3
- package/src/render-generator/render-generator.model.ts +0 -73
- package/src/utils/chars.utils.ts +0 -70
- package/src/utils/tags.utils.ts +0 -36
- package/tsconfig.json +0 -3
- package/vite.config.ts +0 -4
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { ASTNodeType } from './node.enum';
|
|
2
|
-
|
|
3
|
-
export type ASTNode =
|
|
4
|
-
| ElementNode
|
|
5
|
-
| TextNode
|
|
6
|
-
| InterpolationNode;
|
|
7
|
-
|
|
8
|
-
export type ElementNode = {
|
|
9
|
-
type: ASTNodeType.Element
|
|
10
|
-
tagName: string;
|
|
11
|
-
attributes: AttributeNode[];
|
|
12
|
-
events: EventNode[];
|
|
13
|
-
children: ASTNode[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type AttributeNode = {
|
|
17
|
-
name: string;
|
|
18
|
-
value: string | InterpolationNode;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type EventNode = {
|
|
22
|
-
name: string;
|
|
23
|
-
handler: string | InterpolationNode;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type TextNode = {
|
|
27
|
-
type: ASTNodeType.Text
|
|
28
|
-
value: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type InterpolationNode = {
|
|
32
|
-
type: ASTNodeType.Interpolation
|
|
33
|
-
expression: string;
|
|
34
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { PositiveInteger, TupleOfLength } from '@xaendar/common';
|
|
2
|
-
import { EOF } from '../../costants/chars.constants';
|
|
3
|
-
import { TokenType } from '../../lexer/models/token-type.enum';
|
|
4
|
-
import { Token } from '../../lexer/models/token.type';
|
|
5
|
-
import { CurrentToken } from './current-token.type';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Cursor abstraction used by the Parser to navigate
|
|
9
|
-
* through a sequence of tokens produced by the Lexer.
|
|
10
|
-
*
|
|
11
|
-
* Responsibilities:
|
|
12
|
-
* - Sequential token consumption
|
|
13
|
-
* - Lookahead (peek) operations without mutating state
|
|
14
|
-
* - Handling end-of-file conditions
|
|
15
|
-
*
|
|
16
|
-
* This class does not perform parsing itself: it only
|
|
17
|
-
* manages position and access to the token stream.
|
|
18
|
-
*/
|
|
19
|
-
export class ParserCursor {
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Representation of the current token.
|
|
23
|
-
*
|
|
24
|
-
* - `index`: absolute index within the token array
|
|
25
|
-
* - `value`: current token object (or EOF token)
|
|
26
|
-
*
|
|
27
|
-
* An index of `-1` indicates that the cursor has not
|
|
28
|
-
* yet consumed any token or has reached EOF.
|
|
29
|
-
*/
|
|
30
|
-
private readonly _currentToken: CurrentToken = {
|
|
31
|
-
value: { type: TokenType.EOF },
|
|
32
|
-
index: -1
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Returns a read-only snapshot of the current token.
|
|
37
|
-
*/
|
|
38
|
-
public get currentToken(): Readonly<CurrentToken> {
|
|
39
|
-
return this._currentToken;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Creates a new ParserCursor for the given token array.
|
|
44
|
-
*
|
|
45
|
-
* @param _tokens Array of tokens to navigate.
|
|
46
|
-
*/
|
|
47
|
-
constructor(private readonly _tokens: Token[]) { }
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Advances the cursor by the specified number of tokens.
|
|
51
|
-
*
|
|
52
|
-
* Updates the current token and index.
|
|
53
|
-
*
|
|
54
|
-
* @param chars Number of tokens to advance (must be >= 1)
|
|
55
|
-
*
|
|
56
|
-
* @throws Error with cause `EOF` when advancing past the end
|
|
57
|
-
*/
|
|
58
|
-
public advance(chars = 1): void {
|
|
59
|
-
if (chars < 1) {
|
|
60
|
-
throw new Error(`${chars} is not a valid value. Please enter a number equal or greater than 1`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const newIndex = this._currentToken.index + chars;
|
|
64
|
-
|
|
65
|
-
if (newIndex >= this._tokens.length) {
|
|
66
|
-
this._currentToken.value = { type: TokenType.EOF };
|
|
67
|
-
this._currentToken.index = -1;
|
|
68
|
-
this.throwEOFError();
|
|
69
|
-
} else {
|
|
70
|
-
this._currentToken.index = newIndex;
|
|
71
|
-
this._currentToken.value = this._tokens[newIndex]!;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Peeks ahead in the token stream without advancing the cursor.
|
|
77
|
-
*
|
|
78
|
-
* Supports:
|
|
79
|
-
* - Single-token lookahead
|
|
80
|
-
* - Multi-token lookahead
|
|
81
|
-
* - Optional offset from the current token
|
|
82
|
-
*
|
|
83
|
-
* @returns
|
|
84
|
-
* - A single Token when peeking one token
|
|
85
|
-
* - An array of Tokens when peeking multiple tokens
|
|
86
|
-
*
|
|
87
|
-
* @throws Error with cause `EOF` if the peek exceeds the token array
|
|
88
|
-
*/
|
|
89
|
-
public peek(): Token;
|
|
90
|
-
public peek<OffSet extends number>(options?: { offset?: PositiveInteger<OffSet> }): Token;
|
|
91
|
-
public peek(chars: 1): Token;
|
|
92
|
-
public peek<OffSet extends number>(chars: 1, options?: { offset?: PositiveInteger<OffSet> }): Token;
|
|
93
|
-
public peek<ReadChars extends number>(chars: PositiveInteger<ReadChars>): TupleOfLength<ReadChars, Token>;
|
|
94
|
-
public peek<ReadChars extends number, OffSet extends number>(chars: PositiveInteger<ReadChars>, options?: { offset?: PositiveInteger<OffSet> }): TupleOfLength<ReadChars, Token>;
|
|
95
|
-
public peek(charsOrOptions?: number | { offset?: number }, options?: { offset?: number }): Token | Token[] {
|
|
96
|
-
const tokens = typeof charsOrOptions === 'number' ? charsOrOptions : 1;
|
|
97
|
-
const offset = (typeof charsOrOptions === 'object' ? charsOrOptions : options)?.offset ?? 0;
|
|
98
|
-
return tokens === 1 ? this.peekOneToken(this._currentToken.index + offset + 1) : this.peekMany(tokens + offset);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Peeks multiple tokens ahead.
|
|
103
|
-
*/
|
|
104
|
-
private peekMany(chars: number): Token[] {
|
|
105
|
-
const peekedTokens: Token[] = [];
|
|
106
|
-
const nextTokenIndex = this._currentToken.index + 1;
|
|
107
|
-
|
|
108
|
-
for (let i = nextTokenIndex; i < nextTokenIndex + chars; i++) {
|
|
109
|
-
peekedTokens.push(this.peekOneToken(i));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return peekedTokens;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Peeks a single token at the given absolute index.
|
|
117
|
-
*/
|
|
118
|
-
private peekOneToken(index: number): Token {
|
|
119
|
-
if (index >= this._tokens.length) {
|
|
120
|
-
this.throwEOFError();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return this._tokens[index]!;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Throws a standardized EOF error used by the parser
|
|
128
|
-
* to terminate token consumption.
|
|
129
|
-
*/
|
|
130
|
-
private throwEOFError(): never {
|
|
131
|
-
throw new Error('', { cause: EOF });
|
|
132
|
-
}
|
|
133
|
-
}
|
package/src/parser/parser.ts
DELETED
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { EOF } from '../costants/chars.constants';
|
|
2
|
-
import { TokenType } from '../lexer/models/token-type.enum';
|
|
3
|
-
import { AttributeToken, EventToken, InterpolationExpressionToken, InterpolationLiteralToken, TagOpenNameToken, TextToken, Token } from '../lexer/models/token.type';
|
|
4
|
-
import { ASTNode, AttributeNode, ElementNode, EventNode, InterpolationNode, TextNode } from './models/ast.type';
|
|
5
|
-
import { ASTNodeType } from './models/node.enum';
|
|
6
|
-
import { ParserCursor } from './models/parser-cursor.model';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Parser class that transforms a stream of tokens (from the Lexer)
|
|
10
|
-
* into an Abstract Syntax Tree (AST) representing the template structure.
|
|
11
|
-
*
|
|
12
|
-
* Responsibilities:
|
|
13
|
-
* - Parse text nodes, elements, attributes, events, and interpolations
|
|
14
|
-
* - Maintain cursor state for sequential token consumption
|
|
15
|
-
* - Detect tag boundaries and nested structures
|
|
16
|
-
*
|
|
17
|
-
* The parser assumes that the token stream is syntactically valid according
|
|
18
|
-
* to the Lexer rules. Parsing errors are thrown as exceptions.
|
|
19
|
-
*/
|
|
20
|
-
export class Parser {
|
|
21
|
-
|
|
22
|
-
/** Internal cursor for navigating tokens */
|
|
23
|
-
private readonly _cursor: ParserCursor;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Creates a new Parser instance.
|
|
27
|
-
*
|
|
28
|
-
* @param tokens Array of tokens produced by the Lexer
|
|
29
|
-
*/
|
|
30
|
-
constructor(private readonly tokens: Token[]) {
|
|
31
|
-
this._cursor = new ParserCursor(this.tokens);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Entry point for parsing the token stream into AST nodes.
|
|
36
|
-
*
|
|
37
|
-
* @returns Array of top-level AST nodes
|
|
38
|
-
*/
|
|
39
|
-
public parse(): ASTNode[] {
|
|
40
|
-
let eof = false;
|
|
41
|
-
const nodes: ASTNode[] = [];
|
|
42
|
-
|
|
43
|
-
while (!eof) {
|
|
44
|
-
try {
|
|
45
|
-
nodes.push(this.parseNode());
|
|
46
|
-
} catch (err) {
|
|
47
|
-
const error = err as Error;
|
|
48
|
-
if (error.cause === EOF) {
|
|
49
|
-
eof = true;
|
|
50
|
-
} else {
|
|
51
|
-
throw err;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return nodes;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Parses the next AST node based on the current token.
|
|
61
|
-
*
|
|
62
|
-
* @returns Parsed AST node
|
|
63
|
-
* @throws Error if an unexpected token is encountered
|
|
64
|
-
*/
|
|
65
|
-
private parseNode(): ASTNode {
|
|
66
|
-
const token = this._cursor.peek();
|
|
67
|
-
|
|
68
|
-
switch (token.type) {
|
|
69
|
-
case TokenType.TEXT:
|
|
70
|
-
return this.parseText(token);
|
|
71
|
-
|
|
72
|
-
case TokenType.INTERPOLATION_EXPRESSION:
|
|
73
|
-
case TokenType.INTERPOLATION_LITERAL:
|
|
74
|
-
return this.parseInterpolation(token);
|
|
75
|
-
|
|
76
|
-
case TokenType.TAG_OPEN_NAME:
|
|
77
|
-
return this.parseElement(token);
|
|
78
|
-
|
|
79
|
-
default:
|
|
80
|
-
throw this.error(`Unexpected token ${TokenType[token.type]}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Parses a text token into a TextNode.
|
|
86
|
-
*/
|
|
87
|
-
private parseText(token: TextToken): TextNode {
|
|
88
|
-
this._cursor.advance();
|
|
89
|
-
return {
|
|
90
|
-
type: ASTNodeType.Text,
|
|
91
|
-
value: token.parts[0]
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Parses an interpolation token into an InterpolationNode.
|
|
97
|
-
*/
|
|
98
|
-
private parseInterpolation(token: InterpolationExpressionToken | InterpolationLiteralToken): InterpolationNode {
|
|
99
|
-
this._cursor.advance();
|
|
100
|
-
return {
|
|
101
|
-
type: ASTNodeType.Interpolation,
|
|
102
|
-
expression: token.parts[0]
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Parses an element starting from a TAG_OPEN_NAME token.
|
|
108
|
-
* Handles attributes, events, children, and tag closure.
|
|
109
|
-
*/
|
|
110
|
-
private parseElement(token: TagOpenNameToken): ElementNode {
|
|
111
|
-
this._cursor.advance();
|
|
112
|
-
const tagName = token.parts[0];
|
|
113
|
-
|
|
114
|
-
const attributes: AttributeNode[] = [];
|
|
115
|
-
const events: EventNode[] = [];
|
|
116
|
-
|
|
117
|
-
let read = true;
|
|
118
|
-
while (read) {
|
|
119
|
-
const token = this._cursor.peek();
|
|
120
|
-
switch (token.type) {
|
|
121
|
-
case TokenType.ATTRIBUTE:
|
|
122
|
-
attributes.push(this.parseAttribute(token));
|
|
123
|
-
break;
|
|
124
|
-
|
|
125
|
-
case TokenType.EVENT:
|
|
126
|
-
events.push(this.parseEvent(token));
|
|
127
|
-
break;
|
|
128
|
-
|
|
129
|
-
default:
|
|
130
|
-
read = false;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Consume TAG_OPEN_END if present: <div>
|
|
135
|
-
if (this._cursor.peek().type === TokenType.TAG_OPEN_END) {
|
|
136
|
-
this._cursor.advance();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Handle self-closing tags: <div />
|
|
140
|
-
if (this._cursor.peek().type === TokenType.TAG_SELF_CLOSE) {
|
|
141
|
-
this._cursor.advance();
|
|
142
|
-
return {
|
|
143
|
-
type: ASTNodeType.Element,
|
|
144
|
-
tagName,
|
|
145
|
-
attributes,
|
|
146
|
-
events,
|
|
147
|
-
children: []
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Parse children recursively until closing tag
|
|
152
|
-
const children: ASTNode[] = [];
|
|
153
|
-
while (!this.isTagClose(tagName)) {
|
|
154
|
-
children.push(this.parseNode());
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Consume closing tag </div>
|
|
158
|
-
this._cursor.advance();
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
type: ASTNodeType.Element,
|
|
162
|
-
tagName,
|
|
163
|
-
attributes,
|
|
164
|
-
events,
|
|
165
|
-
children
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Parses an attribute token into an AttributeNode.
|
|
171
|
-
* Supports literal values and interpolations as attribute values.
|
|
172
|
-
*/
|
|
173
|
-
private parseAttribute(token: AttributeToken): AttributeNode {
|
|
174
|
-
this._cursor.advance();
|
|
175
|
-
const raw = token.parts[0];
|
|
176
|
-
|
|
177
|
-
if (!raw.includes('=')) {
|
|
178
|
-
return { name: raw, value: 'true' };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const [name, value] = raw.split('=');
|
|
182
|
-
|
|
183
|
-
const nextToken = this._cursor.peek();
|
|
184
|
-
if (nextToken.type === TokenType.INTERPOLATION_EXPRESSION || nextToken.type === TokenType.INTERPOLATION_LITERAL) {
|
|
185
|
-
return {
|
|
186
|
-
name: name!,
|
|
187
|
-
value: this.parseInterpolation(nextToken)
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
name: name!,
|
|
193
|
-
value: value!.replace(/^['']|['']$/g, '')
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Parses an event token into an EventNode.
|
|
199
|
-
*/
|
|
200
|
-
private parseEvent(token: EventToken): EventNode {
|
|
201
|
-
this._cursor.advance();
|
|
202
|
-
const raw = token.parts[0];
|
|
203
|
-
const [name, value] = raw.split('=');
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
name: name!,
|
|
207
|
-
handler: value!.replace(/^['']|['']$/g, '')
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Checks whether the next token is a closing tag matching the given name.
|
|
213
|
-
*/
|
|
214
|
-
private isTagClose(tagName: string): boolean {
|
|
215
|
-
const nextToken = this._cursor.peek();
|
|
216
|
-
return nextToken.type === TokenType.TAG_CLOSE_NAME && nextToken.parts[0] === tagName;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Creates a parser error with a consistent prefix.
|
|
221
|
-
*/
|
|
222
|
-
private error(message: string): Error {
|
|
223
|
-
return new Error(`[Parser] ${message}`);
|
|
224
|
-
}
|
|
225
|
-
}
|
package/src/public-api.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { ASTNode, TextNode, InterpolationNode, ElementNode, AttributeNode, EventNode } from '../parser/models/ast.type';
|
|
2
|
-
import { ASTNodeType } from '../parser/models/node.enum';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Genera il codice TypeScript della funzione render
|
|
6
|
-
* @param ast L'albero AST del template
|
|
7
|
-
* @param componentVar Nome della variabile 'this' del componente (di solito `this`)
|
|
8
|
-
* @returns Stringa contenente il corpo della funzione render
|
|
9
|
-
*/
|
|
10
|
-
export function generateRenderFunction(ast: ASTNode[], componentVar = 'this'): string {
|
|
11
|
-
const lines: string[] = [];
|
|
12
|
-
|
|
13
|
-
// Apertura funzione
|
|
14
|
-
lines.push(`const shadow = ${componentVar}.shadowRoot!;`);
|
|
15
|
-
|
|
16
|
-
ast.forEach((node, i) => {
|
|
17
|
-
lines.push(...processNode(node, `node${i}`, componentVar));
|
|
18
|
-
lines.push(`shadow.appendChild(node${i});`);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
return lines.join('\n');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Genera il codice per creare un singolo nodo
|
|
26
|
-
*/
|
|
27
|
-
function processNode(node: ASTNode, varName: string, componentVar: string): string[] {
|
|
28
|
-
const code = new Array<string>;
|
|
29
|
-
|
|
30
|
-
switch (node.type) {
|
|
31
|
-
case ASTNodeType.Text:
|
|
32
|
-
code.push(`const ${varName} = document.createTextNode(${JSON.stringify((node as TextNode).value)});`);
|
|
33
|
-
break;
|
|
34
|
-
|
|
35
|
-
case ASTNodeType.Interpolation:
|
|
36
|
-
code.push(`const ${varName} = document.createTextNode(${componentVar}.${(node as InterpolationNode).expression});`);
|
|
37
|
-
break;
|
|
38
|
-
|
|
39
|
-
case ASTNodeType.Element:
|
|
40
|
-
const elNode = node as ElementNode;
|
|
41
|
-
code.push(`const ${varName} = document.createElement(${JSON.stringify(elNode.tagName)});`);
|
|
42
|
-
|
|
43
|
-
// Attributi
|
|
44
|
-
(elNode.attributes || []).forEach((attr: AttributeNode) => {
|
|
45
|
-
if (typeof attr.value === 'string') {
|
|
46
|
-
code.push(`${varName}.setAttribute(${JSON.stringify(attr.name)}, ${JSON.stringify(attr.value)});`);
|
|
47
|
-
} else {
|
|
48
|
-
// Interpolazione come value
|
|
49
|
-
const interp = attr.value as InterpolationNode;
|
|
50
|
-
code.push(`${varName}.setAttribute(${JSON.stringify(attr.name)}, ${componentVar}.${interp.expression});`);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Eventi
|
|
55
|
-
(elNode.events || []).forEach((event: EventNode) => {
|
|
56
|
-
if (event.name.startsWith('@')) {
|
|
57
|
-
event.name = event.name.slice(1);
|
|
58
|
-
}
|
|
59
|
-
code.push(`${varName}.addEventListener(${JSON.stringify(event.name)}, ${componentVar}.${event.handler}.bind(${componentVar}));`);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Children
|
|
63
|
-
(elNode.children || []).forEach((child, idx) => {
|
|
64
|
-
const childVar = `${varName}_child${idx}`;
|
|
65
|
-
code.push(...processNode(child, childVar, componentVar));
|
|
66
|
-
code.push(`${varName}.appendChild(${childVar});`);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return code;
|
|
73
|
-
}
|
package/src/utils/chars.utils.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { A, a, CR, LF, Z, z } from '../costants/chars.constants';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Check if char code is a Line Feed (\n) or Carriage Return (\r)
|
|
5
|
-
* @param char Character to control
|
|
6
|
-
* @returns True if character is LF or CR, false otherwise
|
|
7
|
-
*/
|
|
8
|
-
export function isNewLine(char: number): boolean {
|
|
9
|
-
return char === LF || char === CR;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Check if char code is is a lower case letter
|
|
14
|
-
* @param char Character to control
|
|
15
|
-
* @returns True if character is a lowercase letter, false otherwise
|
|
16
|
-
*/
|
|
17
|
-
export function isLowerCase(char: number): boolean {
|
|
18
|
-
return char >= a && char <= z;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Check if char code is is a upper case letter
|
|
23
|
-
* @param char Character to control
|
|
24
|
-
* @returns True if character is a upper case letter, false otherwise
|
|
25
|
-
*/
|
|
26
|
-
export function isUpperCase(char: number): boolean {
|
|
27
|
-
return char >= A && char <= Z;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Check if the string contains at least one character different from
|
|
32
|
-
* ' '
|
|
33
|
-
* \n
|
|
34
|
-
* \r
|
|
35
|
-
* \t
|
|
36
|
-
* \f
|
|
37
|
-
* \v
|
|
38
|
-
* @param str String to check
|
|
39
|
-
* @returns True if string is not blank, false otherwise
|
|
40
|
-
*/
|
|
41
|
-
export function isNotBlank(str: string): boolean {
|
|
42
|
-
/*
|
|
43
|
-
Differently from the approach of the other functions
|
|
44
|
-
here we are working with string and not numbers.
|
|
45
|
-
|
|
46
|
-
Number checks are usually faster when checking a character is
|
|
47
|
-
included in a specific range.
|
|
48
|
-
For this case we are checking if the string contains at least one char
|
|
49
|
-
different from a list of non adiacent characters in the ASCII code, resulting
|
|
50
|
-
in a very long condition with multiple OR
|
|
51
|
-
|
|
52
|
-
This has been proven slower than using a regex
|
|
53
|
-
*/
|
|
54
|
-
return /\S/.test(str)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Check if given ascii code is a valid First Character
|
|
59
|
-
* for Javasript Identifiers
|
|
60
|
-
* @param code The ascii code to valuate
|
|
61
|
-
* @returns True if is valid, false otherwise
|
|
62
|
-
*/
|
|
63
|
-
export function isJSIdentifierStart(code: number): boolean {
|
|
64
|
-
return (
|
|
65
|
-
(code >= 65 && code <= 90) || // A-Z
|
|
66
|
-
(code >= 97 && code <= 122) || // a-z
|
|
67
|
-
code === 36 || // $
|
|
68
|
-
code === 95 // _
|
|
69
|
-
);
|
|
70
|
-
}
|
package/src/utils/tags.utils.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { HTML_TAGS } from '../costants/tags/base-tags.constants';
|
|
2
|
-
import { NOT_ALLOWED_TAGS } from '../costants/tags/not-alllowed-tags.constants';
|
|
3
|
-
import { NOT_ALLOWED_CHARS_FOR_TAGS } from '../costants/tags/not-allowed-chars.constants';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Checks if a character is allowed in a custom element tag name.
|
|
7
|
-
*
|
|
8
|
-
* @param char - The character to check.
|
|
9
|
-
* @returns `true` if the character is allowed, `false` if it is forbidden.
|
|
10
|
-
*/
|
|
11
|
-
export function isAllowedCharForTag(char: string): boolean {
|
|
12
|
-
return !NOT_ALLOWED_CHARS_FOR_TAGS.includes(char);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Checks if a tag name is a native HTML tag.
|
|
17
|
-
*
|
|
18
|
-
* @param tagName - The tag name to check.
|
|
19
|
-
* @returns `true` if the tag name is a standard HTML tag, `false` otherwise.
|
|
20
|
-
*/
|
|
21
|
-
export function isNativeHTMLTag(tagName: string): boolean {
|
|
22
|
-
return HTML_TAGS.includes(tagName);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Checks if a tag name is reserved and cannot be used for a custom element.
|
|
27
|
-
*
|
|
28
|
-
* Reserved names include certain HTML, SVG, or other names that are forbidden by the specification.
|
|
29
|
-
* https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
|
|
30
|
-
*
|
|
31
|
-
* @param tagName - The tag name to check.
|
|
32
|
-
* @returns `true` if the tag name is reserved, `false` otherwise.
|
|
33
|
-
*/
|
|
34
|
-
export function isReservedTagName(tagName: string): boolean {
|
|
35
|
-
return NOT_ALLOWED_TAGS.includes(tagName);
|
|
36
|
-
}
|
package/tsconfig.json
DELETED
package/vite.config.ts
DELETED