dalila 1.3.2 → 1.4.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.
@@ -1,442 +0,0 @@
1
- // dalila Template Language Compiler v0.1
2
- // SPEC Implementation
3
- export function isElementNode(node) {
4
- return node.type === 'element';
5
- }
6
- export function isTextNode(node) {
7
- return node.type === 'text';
8
- }
9
- export function isInterpolationNode(node) {
10
- return node.type === 'interpolation';
11
- }
12
- // Parser
13
- export class DalilaParser {
14
- constructor(input) {
15
- this.position = 0;
16
- this.line = 1;
17
- this.column = 1;
18
- this.rawTextTags = new Set(['pre', 'code']);
19
- this.input = input;
20
- }
21
- parse() {
22
- const diagnostics = [];
23
- const ast = [];
24
- try {
25
- while (!this.isAtEnd()) {
26
- this.skipWhitespace();
27
- if (this.isAtEnd())
28
- break;
29
- if (this.peek() === '<') {
30
- const element = this.parseElement();
31
- if (element) {
32
- ast.push(element);
33
- }
34
- }
35
- else {
36
- // Parse text content
37
- const text = this.parseText(true);
38
- if (text) {
39
- if (isTextNode(text)) {
40
- ast.push(text);
41
- }
42
- else if (isInterpolationNode(text)) {
43
- ast.push(text);
44
- }
45
- }
46
- }
47
- }
48
- }
49
- catch (error) {
50
- const errorMessage = error instanceof Error ? error.message : String(error);
51
- diagnostics.push({
52
- message: `Parse error: ${errorMessage}`,
53
- line: this.line,
54
- column: this.column,
55
- severity: 'error'
56
- });
57
- }
58
- return { ast, diagnostics };
59
- }
60
- parseElement() {
61
- if (!this.match('<'))
62
- return null;
63
- const startLocation = this.currentLocation();
64
- // Parse tag name
65
- const tagName = this.parseTagName();
66
- if (!tagName) {
67
- throw new Error('Expected tag name after <');
68
- }
69
- // Parse attributes
70
- const attributes = this.parseAttributes();
71
- // Check if self-closing
72
- const selfClosing = this.match('/>');
73
- let children = [];
74
- if (!selfClosing) {
75
- if (!this.match('>')) {
76
- throw new Error('Expected > after tag attributes');
77
- }
78
- // Parse children until closing tag
79
- children = [];
80
- const allowInterpolation = !this.rawTextTags.has(tagName);
81
- while (!this.isAtEnd() && !this.lookingAt(`</${tagName}`)) {
82
- this.skipWhitespace();
83
- if (this.lookingAt(`</${tagName}`))
84
- break;
85
- if (this.peek() === '<') {
86
- const childElement = this.parseElement();
87
- if (childElement) {
88
- children.push(childElement);
89
- }
90
- }
91
- else {
92
- const text = this.parseText(allowInterpolation);
93
- if (text) {
94
- children.push(text);
95
- }
96
- }
97
- }
98
- // Parse closing tag
99
- if (!this.match(`</${tagName}>`)) {
100
- throw new Error(`Expected closing tag </${tagName}>`);
101
- }
102
- }
103
- return {
104
- type: 'element',
105
- tagName,
106
- attributes,
107
- children,
108
- selfClosing,
109
- location: startLocation
110
- };
111
- }
112
- parseTagName() {
113
- let name = '';
114
- while (!this.isAtEnd() && /[a-zA-Z0-9\-_]/.test(this.peek())) {
115
- name += this.advance();
116
- }
117
- return name || null;
118
- }
119
- parseAttributes() {
120
- const attributes = [];
121
- while (!this.isAtEnd() && !['>', '/>'].includes(this.peek(2))) {
122
- this.skipWhitespace();
123
- if (['>', '/>'].includes(this.peek(2)))
124
- break;
125
- const attr = this.parseAttribute();
126
- if (attr) {
127
- attributes.push(attr);
128
- }
129
- }
130
- return attributes;
131
- }
132
- parseAttribute() {
133
- const startLocation = this.currentLocation();
134
- // Parse attribute name
135
- let name = '';
136
- while (!this.isAtEnd() && /[a-zA-Z0-9\-_:]/.test(this.peek())) {
137
- name += this.advance();
138
- }
139
- if (!name)
140
- return null;
141
- this.skipWhitespace();
142
- let value = null;
143
- if (this.match('=')) {
144
- this.skipWhitespace();
145
- if (this.match('"')) {
146
- // Parse string literal or interpolation
147
- if (this.peek() === '{') {
148
- const interpolation = this.parseInterpolation();
149
- if (interpolation) {
150
- value = interpolation;
151
- }
152
- }
153
- else {
154
- // Parse string literal
155
- let str = '';
156
- while (!this.isAtEnd() && this.peek() !== '"') {
157
- str += this.advance();
158
- }
159
- value = str;
160
- }
161
- this.match('"'); // consume closing quote
162
- }
163
- else if (this.peek() === '{') {
164
- value = this.parseInterpolation();
165
- }
166
- }
167
- return {
168
- name,
169
- value,
170
- location: startLocation
171
- };
172
- }
173
- parseInterpolation() {
174
- if (!this.match('{'))
175
- return null;
176
- const startLocation = this.currentLocation();
177
- this.skipWhitespace();
178
- // Parse identifier
179
- let identifier = '';
180
- while (!this.isAtEnd() && /[a-zA-Z_$][a-zA-Z0-9_$]*/.test(this.peek())) {
181
- identifier += this.advance();
182
- }
183
- this.skipWhitespace();
184
- if (!this.match('}')) {
185
- throw new Error('Expected } to close interpolation');
186
- }
187
- if (!identifier) {
188
- throw new Error('Expected identifier in interpolation');
189
- }
190
- return {
191
- type: 'interpolation',
192
- identifier,
193
- location: startLocation
194
- };
195
- }
196
- parseText(allowInterpolation) {
197
- const startLocation = this.currentLocation();
198
- let content = '';
199
- while (!this.isAtEnd() && this.peek() !== '<') {
200
- if (allowInterpolation && this.peek() === '{') {
201
- // Found interpolation in text
202
- if (content) {
203
- // Return text node up to interpolation
204
- this.backup(content.length);
205
- return {
206
- type: 'text',
207
- content,
208
- location: startLocation
209
- };
210
- }
211
- // Parse interpolation
212
- const interpolation = this.parseInterpolation();
213
- if (interpolation) {
214
- return interpolation;
215
- }
216
- }
217
- else {
218
- content += this.advance();
219
- }
220
- }
221
- if (!content.trim())
222
- return null;
223
- return {
224
- type: 'text',
225
- content,
226
- location: startLocation
227
- };
228
- }
229
- skipWhitespace() {
230
- while (!this.isAtEnd() && /\s/.test(this.peek())) {
231
- if (this.peek() === '\n') {
232
- this.line++;
233
- this.column = 1;
234
- }
235
- else {
236
- this.column++;
237
- }
238
- this.position++;
239
- }
240
- }
241
- match(expected) {
242
- if (this.lookingAt(expected)) {
243
- for (let i = 0; i < expected.length; i++) {
244
- if (expected[i] === '\n') {
245
- this.line++;
246
- this.column = 1;
247
- }
248
- else {
249
- this.column++;
250
- }
251
- this.position++;
252
- }
253
- return true;
254
- }
255
- return false;
256
- }
257
- lookingAt(expected) {
258
- for (let i = 0; i < expected.length; i++) {
259
- if (this.position + i >= this.input.length ||
260
- this.input[this.position + i] !== expected[i]) {
261
- return false;
262
- }
263
- }
264
- return true;
265
- }
266
- peek(n = 1) {
267
- const start = this.position;
268
- return this.input.slice(start, start + n);
269
- }
270
- advance() {
271
- if (this.isAtEnd())
272
- return '\0';
273
- const char = this.input[this.position++];
274
- if (char === '\n') {
275
- this.line++;
276
- this.column = 1;
277
- }
278
- else {
279
- this.column++;
280
- }
281
- return char;
282
- }
283
- backup(count) {
284
- for (let i = 0; i < count; i++) {
285
- this.position--;
286
- const char = this.input[this.position];
287
- if (char === '\n') {
288
- this.line--;
289
- // Note: column would need more complex tracking
290
- }
291
- else {
292
- this.column--;
293
- }
294
- }
295
- }
296
- isAtEnd() {
297
- return this.position >= this.input.length;
298
- }
299
- currentLocation() {
300
- return {
301
- line: this.line,
302
- column: this.column,
303
- offset: this.position
304
- };
305
- }
306
- }
307
- // Code Generator
308
- export class DalilaCodeGenerator {
309
- constructor() {
310
- this.indentLevel = 0;
311
- this.output = [];
312
- }
313
- generate(ast, componentName) {
314
- this.output = [];
315
- const diagnostics = [];
316
- this.emit(`import { watch } from '../core/watch.js';`);
317
- this.emit('');
318
- this.emit(`export function render${componentName}(ctx: any): Node {`);
319
- this.indentLevel++;
320
- // Generate root fragment or single element
321
- if (ast.length === 1 && ast[0].type === 'element') {
322
- this.generateElement(ast[0], 'root');
323
- }
324
- else {
325
- this.emit('const root = document.createDocumentFragment();');
326
- for (let i = 0; i < ast.length; i++) {
327
- const node = ast[i];
328
- const varName = `node_${i}`;
329
- if (node.type === 'element') {
330
- this.generateElement(node, varName);
331
- }
332
- else if (node.type === 'text') {
333
- this.emit(`const ${varName} = document.createTextNode(${JSON.stringify(node.content)});`);
334
- }
335
- else if (node.type === 'interpolation') {
336
- this.generateTextInterpolation(node, varName);
337
- }
338
- this.emit(`root.append(${varName});`);
339
- }
340
- }
341
- this.emit('return root;');
342
- this.indentLevel--;
343
- this.emit('}');
344
- // Generate type definition (simplified)
345
- const typeDef = this.generateTypeDefinition(componentName, ast);
346
- return {
347
- source: this.output.join('\n'),
348
- typeDefinition: typeDef,
349
- diagnostics
350
- };
351
- }
352
- generateElement(node, varName) {
353
- this.emit(`const ${varName} = document.createElement(${JSON.stringify(node.tagName)});`);
354
- // Generate attributes
355
- for (const attr of node.attributes) {
356
- this.generateAttribute(varName, attr);
357
- }
358
- // Generate children
359
- for (let i = 0; i < node.children.length; i++) {
360
- const child = node.children[i];
361
- const childVar = `${varName}_child_${i}`;
362
- if (child.type === 'element') {
363
- this.generateElement(child, childVar);
364
- this.emit(`${varName}.append(${childVar});`);
365
- }
366
- else if (child.type === 'text') {
367
- this.emit(`${varName}.append(${JSON.stringify(child.content)});`);
368
- }
369
- else if (child.type === 'interpolation') {
370
- this.generateTextInterpolation(child, childVar);
371
- this.emit(`${varName}.append(${childVar});`);
372
- }
373
- }
374
- }
375
- generateAttribute(elementVar, attr) {
376
- const attrName = attr.name;
377
- if (attr.value === null) {
378
- // Boolean attribute
379
- this.emit(`${elementVar}.setAttribute(${JSON.stringify(attrName)}, '');`);
380
- }
381
- else if (typeof attr.value === 'string') {
382
- // String literal
383
- this.emit(`${elementVar}.setAttribute(${JSON.stringify(attrName)}, ${JSON.stringify(attr.value)});`);
384
- }
385
- else if (attr.value.type === 'interpolation') {
386
- // Dynamic attribute
387
- if (attrName.startsWith('on:')) {
388
- // Event handler
389
- const eventName = attrName.slice(3); // remove 'on:'
390
- this.emit(`${elementVar}.addEventListener(${JSON.stringify(eventName)}, ctx.${attr.value.identifier});`);
391
- }
392
- else {
393
- // Attribute binding
394
- const watchFn = `() => {
395
- const value = ctx.${attr.value.identifier}();
396
- if (value == null || value === false) {
397
- ${elementVar}.removeAttribute(${JSON.stringify(attrName)});
398
- } else {
399
- ${elementVar}.setAttribute(${JSON.stringify(attrName)}, String(value));
400
- }
401
- }`;
402
- this.emit(`watch(${elementVar}, ${watchFn});`);
403
- }
404
- }
405
- }
406
- generateTextInterpolation(node, varName) {
407
- this.emit(`const ${varName} = document.createTextNode('');`);
408
- const watchFn = `() => {
409
- ${varName}.data = String(ctx.${node.identifier}() ?? '');
410
- }`;
411
- this.emit(`watch(${varName}, ${watchFn});`);
412
- }
413
- generateTypeDefinition(componentName, ast) {
414
- // Simplified type generation - in a real implementation this would analyze all usages
415
- return `export interface ${componentName}Ctx {
416
- // Type definition would be inferred from template usage
417
- [key: string]: any;
418
- }`;
419
- }
420
- emit(line) {
421
- const indent = ' '.repeat(this.indentLevel);
422
- this.output.push(indent + line);
423
- }
424
- }
425
- // Main Compiler
426
- export function compileDalilaTemplate(template) {
427
- const parser = new DalilaParser(template.content);
428
- const { ast, diagnostics } = parser.parse();
429
- if (diagnostics.some(d => d.severity === 'error')) {
430
- return {
431
- source: '',
432
- typeDefinition: '',
433
- diagnostics
434
- };
435
- }
436
- const generator = new DalilaCodeGenerator();
437
- const result = generator.generate(ast, template.componentName);
438
- return {
439
- ...result,
440
- diagnostics: [...diagnostics, ...result.diagnostics]
441
- };
442
- }
@@ -1,15 +0,0 @@
1
- export declare function div(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLDivElement;
2
- export declare function span(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLSpanElement;
3
- export declare function p(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLParagraphElement;
4
- export declare function h1(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLHeadingElement;
5
- export declare function h2(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLHeadingElement;
6
- export declare function h3(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLHeadingElement;
7
- export declare function button(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLButtonElement;
8
- export declare function input(attrs?: Record<string, any>): HTMLInputElement;
9
- export declare function a(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLAnchorElement;
10
- export declare function ul(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLUListElement;
11
- export declare function li(children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLLIElement;
12
- export declare function createElement<K extends keyof HTMLElementTagNameMap>(tag: K, children?: Node | Node[] | string, attrs?: Record<string, any>): HTMLElementTagNameMap[K];
13
- export declare function fragment(...children: Node[]): DocumentFragment;
14
- import { Signal } from '../core/signal.js';
15
- export declare function text(content: string | Signal<any>): Text;
@@ -1,100 +0,0 @@
1
- // DOM element creation utilities
2
- export function div(children, attrs) {
3
- return createElement('div', children, attrs);
4
- }
5
- export function span(children, attrs) {
6
- return createElement('span', children, attrs);
7
- }
8
- export function p(children, attrs) {
9
- return createElement('p', children, attrs);
10
- }
11
- export function h1(children, attrs) {
12
- return createElement('h1', children, attrs);
13
- }
14
- export function h2(children, attrs) {
15
- return createElement('h2', children, attrs);
16
- }
17
- export function h3(children, attrs) {
18
- return createElement('h3', children, attrs);
19
- }
20
- export function button(children, attrs) {
21
- return createElement('button', children, attrs);
22
- }
23
- export function input(attrs) {
24
- return createElement('input', undefined, attrs);
25
- }
26
- export function a(children, attrs) {
27
- return createElement('a', children, attrs);
28
- }
29
- export function ul(children, attrs) {
30
- return createElement('ul', children, attrs);
31
- }
32
- export function li(children, attrs) {
33
- return createElement('li', children, attrs);
34
- }
35
- export function createElement(tag, children, attrs) {
36
- const element = document.createElement(tag);
37
- let resolvedChildren = children;
38
- let resolvedAttrs = attrs;
39
- const isAttrsObject = (value) => value !== null &&
40
- typeof value === 'object' &&
41
- !Array.isArray(value) &&
42
- !(value instanceof Node);
43
- const isChildValue = (value) => value === undefined ||
44
- typeof value === 'string' ||
45
- value instanceof Node ||
46
- Array.isArray(value);
47
- if (isAttrsObject(children) && (attrs === undefined || isChildValue(attrs))) {
48
- resolvedAttrs = children;
49
- resolvedChildren = attrs;
50
- }
51
- // Set attributes
52
- if (resolvedAttrs) {
53
- Object.entries(resolvedAttrs).forEach(([key, value]) => {
54
- if (key === 'class') {
55
- element.className = value;
56
- }
57
- else if (key === 'style' && typeof value === 'object') {
58
- Object.assign(element.style, value);
59
- }
60
- else if (key.startsWith('on') && typeof value === 'function') {
61
- const eventName = key.slice(2).toLowerCase();
62
- element.addEventListener(eventName, value);
63
- }
64
- else {
65
- element.setAttribute(key, String(value));
66
- }
67
- });
68
- }
69
- // Add children
70
- if (resolvedChildren !== undefined) {
71
- if (typeof resolvedChildren === 'string') {
72
- element.textContent = resolvedChildren;
73
- }
74
- else {
75
- const nodes = Array.isArray(resolvedChildren) ? resolvedChildren : [resolvedChildren];
76
- element.append(...nodes);
77
- }
78
- }
79
- return element;
80
- }
81
- // Fragment utility
82
- export function fragment(...children) {
83
- const frag = document.createDocumentFragment();
84
- frag.append(...children);
85
- return frag;
86
- }
87
- // Reactive text binding
88
- import { effect } from '../core/signal.js';
89
- export function text(content) {
90
- const textNode = document.createTextNode('');
91
- if (typeof content === 'string') {
92
- textNode.textContent = content;
93
- }
94
- else {
95
- effect(() => {
96
- textNode.textContent = String(content());
97
- });
98
- }
99
- return textNode;
100
- }
@@ -1,7 +0,0 @@
1
- export type EventHandler<T extends Event = Event> = (event: T) => void;
2
- export declare function on<K extends keyof HTMLElementEventMap>(element: Element, event: K, handler: EventHandler<HTMLElementEventMap[K]>): () => void;
3
- export declare function once<K extends keyof HTMLElementEventMap>(element: Element, event: K, handler: EventHandler<HTMLElementEventMap[K]>): () => void;
4
- export declare function preventDefault<T extends Event>(handler: EventHandler<T>): EventHandler<T>;
5
- export declare function stopPropagation<T extends Event>(handler: EventHandler<T>): EventHandler<T>;
6
- export declare function debounce<T extends Event>(handler: EventHandler<T>, delay: number): EventHandler<T>;
7
- export declare function throttle<T extends Event>(handler: EventHandler<T>, delay: number): EventHandler<T>;
@@ -1,47 +0,0 @@
1
- export function on(element, event, handler) {
2
- element.addEventListener(event, handler);
3
- return () => element.removeEventListener(event, handler);
4
- }
5
- export function once(element, event, handler) {
6
- const onceHandler = (e) => {
7
- handler(e);
8
- element.removeEventListener(event, onceHandler);
9
- };
10
- element.addEventListener(event, onceHandler);
11
- return () => element.removeEventListener(event, onceHandler);
12
- }
13
- // Prevent default utility
14
- export function preventDefault(handler) {
15
- return (event) => {
16
- event.preventDefault();
17
- handler(event);
18
- };
19
- }
20
- // Stop propagation utility
21
- export function stopPropagation(handler) {
22
- return (event) => {
23
- event.stopPropagation();
24
- handler(event);
25
- };
26
- }
27
- // Debounce utility for events
28
- export function debounce(handler, delay) {
29
- let timeoutId = null;
30
- return (event) => {
31
- if (timeoutId !== null) {
32
- clearTimeout(timeoutId);
33
- }
34
- timeoutId = window.setTimeout(() => handler(event), delay);
35
- };
36
- }
37
- // Throttle utility for events
38
- export function throttle(handler, delay) {
39
- let lastCall = 0;
40
- return (event) => {
41
- const now = Date.now();
42
- if (now - lastCall >= delay) {
43
- lastCall = now;
44
- handler(event);
45
- }
46
- };
47
- }
@@ -1,2 +0,0 @@
1
- export * from './elements.js';
2
- export * from './events.js';
package/dist/dom/index.js DELETED
@@ -1,2 +0,0 @@
1
- export * from './elements.js';
2
- export * from './events.js';