dalila 1.1.0 → 1.1.1

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 (49) hide show
  1. package/dist/compiler/dalila-lang.d.ts +85 -0
  2. package/dist/compiler/dalila-lang.js +442 -0
  3. package/dist/context/context.d.ts +7 -0
  4. package/dist/context/context.js +61 -0
  5. package/dist/context/index.d.ts +1 -0
  6. package/dist/context/index.js +1 -0
  7. package/dist/core/dev.d.ts +23 -0
  8. package/dist/core/dev.js +157 -0
  9. package/dist/core/for.d.ts +3 -0
  10. package/dist/core/for.js +157 -0
  11. package/dist/core/index.d.ts +13 -0
  12. package/dist/core/index.js +13 -0
  13. package/dist/core/key.d.ts +33 -0
  14. package/dist/core/key.js +83 -0
  15. package/dist/core/match.d.ts +22 -0
  16. package/dist/core/match.js +169 -0
  17. package/dist/core/mutation.d.ts +55 -0
  18. package/dist/core/mutation.js +128 -0
  19. package/dist/core/query.d.ts +72 -0
  20. package/dist/core/query.js +151 -0
  21. package/dist/core/resource.d.ts +189 -0
  22. package/dist/core/resource.js +678 -0
  23. package/dist/core/scheduler.d.ts +80 -0
  24. package/dist/core/scheduler.js +227 -0
  25. package/dist/core/scope.d.ts +38 -0
  26. package/dist/core/scope.js +54 -0
  27. package/dist/core/signal.d.ts +66 -0
  28. package/dist/core/signal.js +365 -0
  29. package/dist/core/virtual.d.ts +26 -0
  30. package/dist/core/virtual.js +277 -0
  31. package/dist/core/watch.d.ts +19 -0
  32. package/dist/core/watch.js +277 -0
  33. package/dist/core/when.d.ts +23 -0
  34. package/dist/core/when.js +118 -0
  35. package/dist/dom/elements.d.ts +15 -0
  36. package/dist/dom/elements.js +100 -0
  37. package/dist/dom/events.d.ts +7 -0
  38. package/dist/dom/events.js +47 -0
  39. package/dist/dom/index.d.ts +2 -0
  40. package/dist/dom/index.js +2 -0
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.js +8 -0
  43. package/dist/router/index.d.ts +2 -0
  44. package/dist/router/index.js +2 -0
  45. package/dist/router/route.d.ts +23 -0
  46. package/dist/router/route.js +48 -0
  47. package/dist/router/router.d.ts +23 -0
  48. package/dist/router/router.js +169 -0
  49. package/package.json +1 -1
@@ -0,0 +1,85 @@
1
+ export interface DalilaTemplate {
2
+ path: string;
3
+ content: string;
4
+ componentName: string;
5
+ }
6
+ export interface CompilationResult {
7
+ source: string;
8
+ typeDefinition: string;
9
+ diagnostics: Diagnostic[];
10
+ }
11
+ export interface Diagnostic {
12
+ message: string;
13
+ line: number;
14
+ column: number;
15
+ severity: 'error' | 'warning' | 'info';
16
+ }
17
+ export interface ElementNode {
18
+ type: 'element';
19
+ tagName: string;
20
+ attributes: Attribute[];
21
+ children: Node[];
22
+ selfClosing: boolean;
23
+ location: Location;
24
+ }
25
+ export interface TextNode {
26
+ type: 'text';
27
+ content: string;
28
+ location: Location;
29
+ }
30
+ export interface InterpolationNode {
31
+ type: 'interpolation';
32
+ identifier: string;
33
+ location: Location;
34
+ }
35
+ export interface Attribute {
36
+ name: string;
37
+ value: string | InterpolationNode | null;
38
+ location: Location;
39
+ }
40
+ export interface Location {
41
+ line: number;
42
+ column: number;
43
+ offset: number;
44
+ }
45
+ export type Node = ElementNode | TextNode | InterpolationNode;
46
+ export declare function isElementNode(node: Node): node is ElementNode;
47
+ export declare function isTextNode(node: Node): node is TextNode;
48
+ export declare function isInterpolationNode(node: Node): node is InterpolationNode;
49
+ export declare class DalilaParser {
50
+ private input;
51
+ private position;
52
+ private line;
53
+ private column;
54
+ private rawTextTags;
55
+ constructor(input: string);
56
+ parse(): {
57
+ ast: Node[];
58
+ diagnostics: Diagnostic[];
59
+ };
60
+ private parseElement;
61
+ private parseTagName;
62
+ private parseAttributes;
63
+ private parseAttribute;
64
+ private parseInterpolation;
65
+ private parseText;
66
+ private skipWhitespace;
67
+ private match;
68
+ private lookingAt;
69
+ private peek;
70
+ private advance;
71
+ private backup;
72
+ private isAtEnd;
73
+ private currentLocation;
74
+ }
75
+ export declare class DalilaCodeGenerator {
76
+ private indentLevel;
77
+ private output;
78
+ generate(ast: Node[], componentName: string): CompilationResult;
79
+ private generateElement;
80
+ private generateAttribute;
81
+ private generateTextInterpolation;
82
+ private generateTypeDefinition;
83
+ private emit;
84
+ }
85
+ export declare function compileDalilaTemplate(template: DalilaTemplate): CompilationResult;
@@ -0,0 +1,442 @@
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
+ }
@@ -0,0 +1,7 @@
1
+ export interface ContextToken<T> {
2
+ readonly name?: string;
3
+ readonly _brand: T;
4
+ }
5
+ export declare function createContext<T>(name?: string): ContextToken<T>;
6
+ export declare function provide<T>(token: ContextToken<T>, value: T): void;
7
+ export declare function inject<T>(token: ContextToken<T>): T;
@@ -0,0 +1,61 @@
1
+ import { getCurrentScope } from '../core/scope.js';
2
+ export function createContext(name) {
3
+ return { name };
4
+ }
5
+ class ContextRegistry {
6
+ constructor(parent) {
7
+ this.contexts = new Map();
8
+ this.parent = parent;
9
+ }
10
+ set(token, value) {
11
+ this.contexts.set(token, { token, value });
12
+ }
13
+ get(token) {
14
+ const value = this.contexts.get(token);
15
+ if (value !== undefined) {
16
+ return value.value;
17
+ }
18
+ if (this.parent) {
19
+ return this.parent.get(token);
20
+ }
21
+ return undefined;
22
+ }
23
+ }
24
+ const scopeRegistries = new WeakMap();
25
+ function getScopeRegistry(scope) {
26
+ let registry = scopeRegistries.get(scope);
27
+ if (!registry) {
28
+ const parentScope = getCurrentScope();
29
+ const parentRegistry = parentScope ? scopeRegistries.get(parentScope) : undefined;
30
+ registry = new ContextRegistry(parentRegistry);
31
+ scopeRegistries.set(scope, registry);
32
+ }
33
+ return registry;
34
+ }
35
+ export function provide(token, value) {
36
+ const scope = getCurrentScope();
37
+ if (!scope) {
38
+ throw new Error('provide() must be called within a scope');
39
+ }
40
+ const registry = getScopeRegistry(scope);
41
+ registry.set(token, value);
42
+ }
43
+ export function inject(token) {
44
+ const scope = getCurrentScope();
45
+ if (!scope) {
46
+ throw new Error('inject() must be called within a scope');
47
+ }
48
+ let currentScope = scope;
49
+ while (currentScope) {
50
+ const registry = scopeRegistries.get(currentScope);
51
+ if (registry) {
52
+ const value = registry.get(token);
53
+ if (value !== undefined) {
54
+ return value;
55
+ }
56
+ }
57
+ // For now, we don't have parent scope tracking, so we break
58
+ break;
59
+ }
60
+ throw new Error(`Context ${token.name || 'unnamed'} not found`);
61
+ }
@@ -0,0 +1 @@
1
+ export * from './context.js';
@@ -0,0 +1 @@
1
+ export * from './context.js';
@@ -0,0 +1,23 @@
1
+ export declare function setDevMode(enabled: boolean): void;
2
+ export declare function isInDevMode(): boolean;
3
+ export declare function warnIfAsyncEffectWithoutAbort(): void;
4
+ export declare function warnIfLayoutReadInMutate(): void;
5
+ export declare function warnIfSignalUsedOutsideScope(): void;
6
+ export declare function checkLayoutThrashing(): Promise<void>;
7
+ export declare function monitorPerformance(): void;
8
+ export declare function detectMemoryLeaks(): void;
9
+ export declare function initDevTools(): Promise<void>;
10
+ export declare function inspectSignal<T>(signal: () => T): {
11
+ value: T;
12
+ subscribers: number;
13
+ };
14
+ export declare function trackEffect(fn: Function): () => void;
15
+ export declare function getActiveEffects(): Array<{
16
+ id: number;
17
+ fn: Function;
18
+ stack: string;
19
+ }>;
20
+ export declare function getCurrentScopeInfo(): {
21
+ cleanupCount: number;
22
+ effectCount: number;
23
+ };