rip-lang 3.6.1 → 3.6.2
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/docs/RIP-TYPES.md +98 -0
- package/docs/dist/rip.browser.js +611 -272
- package/docs/dist/rip.browser.min.js +170 -170
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/index.html +35 -27
- package/package.json +1 -1
- package/src/compiler.js +55 -43
- package/src/components.js +157 -159
- package/src/grammar/grammar.rip +4 -0
- package/src/parser.js +41 -41
- package/src/repl.js +4 -128
- package/src/types.js +416 -35
package/src/components.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// CodeGenerator prototype, enabling component compilation. A separate
|
|
5
5
|
// getComponentRuntime() emits runtime helpers only when components are used.
|
|
6
6
|
//
|
|
7
|
-
// Naming: All render-tree
|
|
7
|
+
// Naming: All render-tree generators use generate* (consistent with compiler).
|
|
8
8
|
|
|
9
9
|
import { TEMPLATE_TAGS } from './tags.js';
|
|
10
10
|
|
|
@@ -15,7 +15,7 @@ import { TEMPLATE_TAGS } from './tags.js';
|
|
|
15
15
|
const BIND_PREFIX = '__bind_';
|
|
16
16
|
const BIND_SUFFIX = '__';
|
|
17
17
|
|
|
18
|
-
const LIFECYCLE_HOOKS = new Set(['
|
|
18
|
+
const LIFECYCLE_HOOKS = new Set(['beforeMount', 'mounted', 'updated', 'beforeUnmount', 'unmounted']);
|
|
19
19
|
|
|
20
20
|
// ============================================================================
|
|
21
21
|
// Standalone Utilities
|
|
@@ -61,6 +61,16 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
61
61
|
// Utilities
|
|
62
62
|
// ==========================================================================
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Localize variable references for block factories.
|
|
66
|
+
* Converts this._elN to _elN and this.x to ctx.x.
|
|
67
|
+
*/
|
|
68
|
+
proto.localizeVar = function(line) {
|
|
69
|
+
let result = line.replace(/this\.(_el\d+|_t\d+|_anchor\d+|_frag\d+|_slot\d+|_c\d+|_inst\d+|_empty\d+)/g, '$1');
|
|
70
|
+
result = result.replace(/\bthis\./g, 'ctx.');
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
|
|
64
74
|
/**
|
|
65
75
|
* Check if name is an HTML/SVG tag
|
|
66
76
|
*/
|
|
@@ -369,12 +379,12 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
369
379
|
* Entry point for processing an entire render block.
|
|
370
380
|
*/
|
|
371
381
|
proto.buildRender = function(body) {
|
|
372
|
-
this.
|
|
373
|
-
this.
|
|
374
|
-
this.
|
|
375
|
-
this.
|
|
376
|
-
this.
|
|
377
|
-
this.
|
|
382
|
+
this._elementCount = 0;
|
|
383
|
+
this._textCount = 0;
|
|
384
|
+
this._blockCount = 0;
|
|
385
|
+
this._createLines = [];
|
|
386
|
+
this._setupLines = [];
|
|
387
|
+
this._blockFactories = [];
|
|
378
388
|
|
|
379
389
|
const statements = Array.isArray(body) && body[0] === 'block' ? body.slice(1) : [body];
|
|
380
390
|
|
|
@@ -382,68 +392,68 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
382
392
|
if (statements.length === 0) {
|
|
383
393
|
rootVar = 'null';
|
|
384
394
|
} else if (statements.length === 1) {
|
|
385
|
-
rootVar = this.
|
|
395
|
+
rootVar = this.generateNode(statements[0]);
|
|
386
396
|
} else {
|
|
387
397
|
rootVar = this.newElementVar('frag');
|
|
388
|
-
this.
|
|
398
|
+
this._createLines.push(`${rootVar} = document.createDocumentFragment();`);
|
|
389
399
|
for (const stmt of statements) {
|
|
390
|
-
const childVar = this.
|
|
391
|
-
this.
|
|
400
|
+
const childVar = this.generateNode(stmt);
|
|
401
|
+
this._createLines.push(`${rootVar}.appendChild(${childVar});`);
|
|
392
402
|
}
|
|
393
403
|
}
|
|
394
404
|
|
|
395
405
|
return {
|
|
396
|
-
createLines: this.
|
|
397
|
-
setupLines: this.
|
|
398
|
-
blockFactories: this.
|
|
406
|
+
createLines: this._createLines,
|
|
407
|
+
setupLines: this._setupLines,
|
|
408
|
+
blockFactories: this._blockFactories,
|
|
399
409
|
rootVar
|
|
400
410
|
};
|
|
401
411
|
};
|
|
402
412
|
|
|
403
413
|
/** Generate a unique block factory name */
|
|
404
414
|
proto.newBlockVar = function() {
|
|
405
|
-
return `create_block_${this.
|
|
415
|
+
return `create_block_${this._blockCount++}`;
|
|
406
416
|
};
|
|
407
417
|
|
|
408
418
|
/** Generate a unique element variable name */
|
|
409
419
|
proto.newElementVar = function(hint = 'el') {
|
|
410
|
-
return `this._${hint}${this.
|
|
420
|
+
return `this._${hint}${this._elementCount++}`;
|
|
411
421
|
};
|
|
412
422
|
|
|
413
423
|
/** Generate a unique text node variable name */
|
|
414
424
|
proto.newTextVar = function() {
|
|
415
|
-
return `this._t${this.
|
|
425
|
+
return `this._t${this._textCount++}`;
|
|
416
426
|
};
|
|
417
427
|
|
|
418
428
|
// --------------------------------------------------------------------------
|
|
419
|
-
//
|
|
429
|
+
// generateNode — main dispatch for all render tree nodes
|
|
420
430
|
// --------------------------------------------------------------------------
|
|
421
431
|
|
|
422
|
-
proto.
|
|
432
|
+
proto.generateNode = function(sexpr) {
|
|
423
433
|
// String literal → text node (handle both primitive and String objects)
|
|
424
434
|
if (typeof sexpr === 'string' || sexpr instanceof String) {
|
|
425
435
|
const str = sexpr.valueOf();
|
|
426
436
|
if (str.startsWith('"') || str.startsWith("'") || str.startsWith('`')) {
|
|
427
437
|
const textVar = this.newTextVar();
|
|
428
|
-
this.
|
|
438
|
+
this._createLines.push(`${textVar} = document.createTextNode(${str});`);
|
|
429
439
|
return textVar;
|
|
430
440
|
}
|
|
431
441
|
// Dynamic text binding (reactive member)
|
|
432
442
|
if (this.reactiveMembers && this.reactiveMembers.has(str)) {
|
|
433
443
|
const textVar = this.newTextVar();
|
|
434
|
-
this.
|
|
435
|
-
this.
|
|
444
|
+
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
445
|
+
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${str}.value; });`);
|
|
436
446
|
return textVar;
|
|
437
447
|
}
|
|
438
448
|
// Static tag without content
|
|
439
449
|
const elVar = this.newElementVar();
|
|
440
|
-
this.
|
|
450
|
+
this._createLines.push(`${elVar} = document.createElement('${str}');`);
|
|
441
451
|
return elVar;
|
|
442
452
|
}
|
|
443
453
|
|
|
444
454
|
if (!Array.isArray(sexpr)) {
|
|
445
455
|
const commentVar = this.newElementVar('c');
|
|
446
|
-
this.
|
|
456
|
+
this._createLines.push(`${commentVar} = document.createComment('unknown');`);
|
|
447
457
|
return commentVar;
|
|
448
458
|
}
|
|
449
459
|
|
|
@@ -452,12 +462,12 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
452
462
|
|
|
453
463
|
// Component instantiation (PascalCase)
|
|
454
464
|
if (headStr && this.isComponent(headStr)) {
|
|
455
|
-
return this.
|
|
465
|
+
return this.generateChildComponent(headStr, rest);
|
|
456
466
|
}
|
|
457
467
|
|
|
458
468
|
// HTML tag
|
|
459
469
|
if (headStr && this.isHtmlTag(headStr)) {
|
|
460
|
-
return this.
|
|
470
|
+
return this.generateTag(headStr, [], rest);
|
|
461
471
|
}
|
|
462
472
|
|
|
463
473
|
// Property chain (div.class or item.name)
|
|
@@ -468,13 +478,13 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
468
478
|
if (obj === 'this' && typeof prop === 'string') {
|
|
469
479
|
if (this.reactiveMembers && this.reactiveMembers.has(prop)) {
|
|
470
480
|
const textVar = this.newTextVar();
|
|
471
|
-
this.
|
|
472
|
-
this.
|
|
481
|
+
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
482
|
+
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${prop}.value; });`);
|
|
473
483
|
return textVar;
|
|
474
484
|
}
|
|
475
485
|
if (this.componentMembers && this.componentMembers.has(prop)) {
|
|
476
486
|
const slotVar = this.newElementVar('slot');
|
|
477
|
-
this.
|
|
487
|
+
this._createLines.push(`${slotVar} = this.${prop} instanceof Node ? this.${prop} : (this.${prop} != null ? document.createTextNode(String(this.${prop})) : document.createComment(''));`);
|
|
478
488
|
return slotVar;
|
|
479
489
|
}
|
|
480
490
|
}
|
|
@@ -482,13 +492,13 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
482
492
|
// HTML tag with classes (div.class)
|
|
483
493
|
const { tag, classes } = this.collectTemplateClasses(sexpr);
|
|
484
494
|
if (tag && this.isHtmlTag(tag)) {
|
|
485
|
-
return this.
|
|
495
|
+
return this.generateTag(tag, classes, []);
|
|
486
496
|
}
|
|
487
497
|
|
|
488
498
|
// General property access (e.g., item.name in a loop)
|
|
489
499
|
const textVar = this.newTextVar();
|
|
490
500
|
const exprCode = this.generateInComponent(sexpr, 'value');
|
|
491
|
-
this.
|
|
501
|
+
this._createLines.push(`${textVar} = document.createTextNode(String(${exprCode}));`);
|
|
492
502
|
return textVar;
|
|
493
503
|
}
|
|
494
504
|
|
|
@@ -499,56 +509,56 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
499
509
|
(head[0][2] === '__cx__' || (head[0][2] instanceof String && head[0][2].valueOf() === '__cx__'))) {
|
|
500
510
|
const tag = typeof head[0][1] === 'string' ? head[0][1] : head[0][1].valueOf();
|
|
501
511
|
const classExprs = head.slice(1);
|
|
502
|
-
return this.
|
|
512
|
+
return this.generateDynamicTag(tag, classExprs, rest);
|
|
503
513
|
}
|
|
504
514
|
|
|
505
515
|
const { tag, classes } = this.collectTemplateClasses(head);
|
|
506
516
|
if (tag && this.isHtmlTag(tag)) {
|
|
507
517
|
// Dynamic class syntax: div.("classes") → (. div __cx__) "classes"
|
|
508
518
|
if (classes.length === 1 && classes[0] === '__cx__') {
|
|
509
|
-
return this.
|
|
519
|
+
return this.generateDynamicTag(tag, rest, []);
|
|
510
520
|
}
|
|
511
|
-
return this.
|
|
521
|
+
return this.generateTag(tag, classes, rest);
|
|
512
522
|
}
|
|
513
523
|
}
|
|
514
524
|
|
|
515
525
|
// Arrow function (children block)
|
|
516
526
|
if (headStr === '->' || headStr === '=>') {
|
|
517
|
-
return this.
|
|
527
|
+
return this.generateTemplateBlock(rest[1]);
|
|
518
528
|
}
|
|
519
529
|
|
|
520
530
|
// Conditional: if/else
|
|
521
531
|
if (headStr === 'if') {
|
|
522
|
-
return this.
|
|
532
|
+
return this.generateConditional(sexpr);
|
|
523
533
|
}
|
|
524
534
|
|
|
525
535
|
// For loop
|
|
526
536
|
if (headStr === 'for' || headStr === 'for-in' || headStr === 'for-of' || headStr === 'for-as') {
|
|
527
|
-
return this.
|
|
537
|
+
return this.generateTemplateLoop(sexpr);
|
|
528
538
|
}
|
|
529
539
|
|
|
530
540
|
// General expression (computed value, function call, binary op, etc.)
|
|
531
541
|
const textVar = this.newTextVar();
|
|
532
542
|
const exprCode = this.generateInComponent(sexpr, 'value');
|
|
533
543
|
if (this.hasReactiveDeps(sexpr)) {
|
|
534
|
-
this.
|
|
535
|
-
this.
|
|
544
|
+
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
545
|
+
this._setupLines.push(`__effect(() => { ${textVar}.data = ${exprCode}; });`);
|
|
536
546
|
} else {
|
|
537
|
-
this.
|
|
547
|
+
this._createLines.push(`${textVar} = document.createTextNode(String(${exprCode}));`);
|
|
538
548
|
}
|
|
539
549
|
return textVar;
|
|
540
550
|
};
|
|
541
551
|
|
|
542
552
|
// --------------------------------------------------------------------------
|
|
543
|
-
//
|
|
553
|
+
// generateTag — HTML element with static classes and children
|
|
544
554
|
// --------------------------------------------------------------------------
|
|
545
555
|
|
|
546
|
-
proto.
|
|
556
|
+
proto.generateTag = function(tag, classes, args) {
|
|
547
557
|
const elVar = this.newElementVar();
|
|
548
|
-
this.
|
|
558
|
+
this._createLines.push(`${elVar} = document.createElement('${tag}');`);
|
|
549
559
|
|
|
550
560
|
if (classes.length > 0) {
|
|
551
|
-
this.
|
|
561
|
+
this._createLines.push(`${elVar}.className = '${classes.join(' ')}';`);
|
|
552
562
|
}
|
|
553
563
|
|
|
554
564
|
for (const arg of args) {
|
|
@@ -557,51 +567,51 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
557
567
|
const block = arg[2];
|
|
558
568
|
if (Array.isArray(block) && block[0] === 'block') {
|
|
559
569
|
for (const child of block.slice(1)) {
|
|
560
|
-
const childVar = this.
|
|
561
|
-
this.
|
|
570
|
+
const childVar = this.generateNode(child);
|
|
571
|
+
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
562
572
|
}
|
|
563
573
|
} else if (block) {
|
|
564
|
-
const childVar = this.
|
|
565
|
-
this.
|
|
574
|
+
const childVar = this.generateNode(block);
|
|
575
|
+
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
566
576
|
}
|
|
567
577
|
}
|
|
568
578
|
// Object = attributes/events
|
|
569
579
|
else if (Array.isArray(arg) && arg[0] === 'object') {
|
|
570
|
-
this.
|
|
580
|
+
this.generateAttributes(elVar, arg);
|
|
571
581
|
}
|
|
572
582
|
// String = text child
|
|
573
583
|
else if (typeof arg === 'string') {
|
|
574
584
|
const textVar = this.newTextVar();
|
|
575
585
|
if (arg.startsWith('"') || arg.startsWith("'") || arg.startsWith('`')) {
|
|
576
|
-
this.
|
|
586
|
+
this._createLines.push(`${textVar} = document.createTextNode(${arg});`);
|
|
577
587
|
} else if (this.reactiveMembers && this.reactiveMembers.has(arg)) {
|
|
578
|
-
this.
|
|
579
|
-
this.
|
|
588
|
+
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
589
|
+
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${arg}.value; });`);
|
|
580
590
|
} else if (this.componentMembers && this.componentMembers.has(arg)) {
|
|
581
|
-
this.
|
|
591
|
+
this._createLines.push(`${textVar} = document.createTextNode(String(this.${arg}));`);
|
|
582
592
|
} else {
|
|
583
|
-
this.
|
|
593
|
+
this._createLines.push(`${textVar} = document.createTextNode(String(${arg}));`);
|
|
584
594
|
}
|
|
585
|
-
this.
|
|
595
|
+
this._createLines.push(`${elVar}.appendChild(${textVar});`);
|
|
586
596
|
}
|
|
587
597
|
// String object (from parser)
|
|
588
598
|
else if (arg instanceof String) {
|
|
589
599
|
const val = arg.valueOf();
|
|
590
600
|
const textVar = this.newTextVar();
|
|
591
601
|
if (val.startsWith('"') || val.startsWith("'") || val.startsWith('`')) {
|
|
592
|
-
this.
|
|
602
|
+
this._createLines.push(`${textVar} = document.createTextNode(${val});`);
|
|
593
603
|
} else if (this.reactiveMembers && this.reactiveMembers.has(val)) {
|
|
594
|
-
this.
|
|
595
|
-
this.
|
|
604
|
+
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
605
|
+
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${val}.value; });`);
|
|
596
606
|
} else {
|
|
597
|
-
this.
|
|
607
|
+
this._createLines.push(`${textVar} = document.createTextNode(String(${val}));`);
|
|
598
608
|
}
|
|
599
|
-
this.
|
|
609
|
+
this._createLines.push(`${elVar}.appendChild(${textVar});`);
|
|
600
610
|
}
|
|
601
611
|
// Other = nested element
|
|
602
612
|
else if (arg) {
|
|
603
|
-
const childVar = this.
|
|
604
|
-
this.
|
|
613
|
+
const childVar = this.generateNode(arg);
|
|
614
|
+
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
605
615
|
}
|
|
606
616
|
}
|
|
607
617
|
|
|
@@ -609,20 +619,20 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
609
619
|
};
|
|
610
620
|
|
|
611
621
|
// --------------------------------------------------------------------------
|
|
612
|
-
//
|
|
622
|
+
// generateDynamicTag — tag with .() CLSX dynamic classes
|
|
613
623
|
// --------------------------------------------------------------------------
|
|
614
624
|
|
|
615
|
-
proto.
|
|
625
|
+
proto.generateDynamicTag = function(tag, classExprs, children) {
|
|
616
626
|
const elVar = this.newElementVar();
|
|
617
|
-
this.
|
|
627
|
+
this._createLines.push(`${elVar} = document.createElement('${tag}');`);
|
|
618
628
|
|
|
619
629
|
if (classExprs.length > 0) {
|
|
620
630
|
const classArgs = classExprs.map(e => this.generateInComponent(e, 'value')).join(', ');
|
|
621
631
|
const hasReactive = classExprs.some(e => this.hasReactiveDeps(e));
|
|
622
632
|
if (hasReactive) {
|
|
623
|
-
this.
|
|
633
|
+
this._setupLines.push(`__effect(() => { ${elVar}.className = __cx__(${classArgs}); });`);
|
|
624
634
|
} else {
|
|
625
|
-
this.
|
|
635
|
+
this._createLines.push(`${elVar}.className = __cx__(${classArgs});`);
|
|
626
636
|
}
|
|
627
637
|
}
|
|
628
638
|
|
|
@@ -633,33 +643,33 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
633
643
|
const blockHead = Array.isArray(block) ? (block[0] instanceof String ? block[0].valueOf() : block[0]) : null;
|
|
634
644
|
if (blockHead === 'block') {
|
|
635
645
|
for (const child of block.slice(1)) {
|
|
636
|
-
const childVar = this.
|
|
637
|
-
this.
|
|
646
|
+
const childVar = this.generateNode(child);
|
|
647
|
+
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
638
648
|
}
|
|
639
649
|
} else if (block) {
|
|
640
|
-
const childVar = this.
|
|
641
|
-
this.
|
|
650
|
+
const childVar = this.generateNode(block);
|
|
651
|
+
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
642
652
|
}
|
|
643
653
|
}
|
|
644
654
|
else if (Array.isArray(arg) && arg[0] === 'object') {
|
|
645
|
-
this.
|
|
655
|
+
this.generateAttributes(elVar, arg);
|
|
646
656
|
}
|
|
647
657
|
else if (typeof arg === 'string' || arg instanceof String) {
|
|
648
658
|
const textVar = this.newTextVar();
|
|
649
659
|
const argStr = arg.valueOf();
|
|
650
660
|
if (argStr.startsWith('"') || argStr.startsWith("'") || argStr.startsWith('`')) {
|
|
651
|
-
this.
|
|
661
|
+
this._createLines.push(`${textVar} = document.createTextNode(${argStr});`);
|
|
652
662
|
} else if (this.reactiveMembers && this.reactiveMembers.has(argStr)) {
|
|
653
|
-
this.
|
|
654
|
-
this.
|
|
663
|
+
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
664
|
+
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${argStr}.value; });`);
|
|
655
665
|
} else {
|
|
656
|
-
this.
|
|
666
|
+
this._createLines.push(`${textVar} = document.createTextNode(${this.generateInComponent(arg, 'value')});`);
|
|
657
667
|
}
|
|
658
|
-
this.
|
|
668
|
+
this._createLines.push(`${elVar}.appendChild(${textVar});`);
|
|
659
669
|
}
|
|
660
670
|
else {
|
|
661
|
-
const childVar = this.
|
|
662
|
-
this.
|
|
671
|
+
const childVar = this.generateNode(arg);
|
|
672
|
+
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
663
673
|
}
|
|
664
674
|
}
|
|
665
675
|
|
|
@@ -667,10 +677,10 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
667
677
|
};
|
|
668
678
|
|
|
669
679
|
// --------------------------------------------------------------------------
|
|
670
|
-
//
|
|
680
|
+
// generateAttributes — attributes, events, and bindings on an element
|
|
671
681
|
// --------------------------------------------------------------------------
|
|
672
682
|
|
|
673
|
-
proto.
|
|
683
|
+
proto.generateAttributes = function(elVar, objExpr) {
|
|
674
684
|
const inputType = extractInputType(objExpr.slice(1));
|
|
675
685
|
|
|
676
686
|
for (let i = 1; i < objExpr.length; i++) {
|
|
@@ -680,7 +690,7 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
680
690
|
if (Array.isArray(key) && key[0] === '.' && key[1] === 'this') {
|
|
681
691
|
const eventName = key[2];
|
|
682
692
|
const handlerCode = this.generateInComponent(value, 'value');
|
|
683
|
-
this.
|
|
693
|
+
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => (${handlerCode})(e));`);
|
|
684
694
|
continue;
|
|
685
695
|
}
|
|
686
696
|
|
|
@@ -706,8 +716,8 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
706
716
|
? 'e.target.valueAsNumber' : 'e.target.value';
|
|
707
717
|
}
|
|
708
718
|
|
|
709
|
-
this.
|
|
710
|
-
this.
|
|
719
|
+
this._setupLines.push(`__effect(() => { ${elVar}.${prop} = ${valueCode}; });`);
|
|
720
|
+
this._createLines.push(`${elVar}.addEventListener('${event}', (e) => ${valueCode} = ${valueAccessor});`);
|
|
711
721
|
continue;
|
|
712
722
|
}
|
|
713
723
|
|
|
@@ -716,72 +726,72 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
716
726
|
// Smart two-way binding for value/checked when bound to reactive state
|
|
717
727
|
if ((key === 'value' || key === 'checked') && this.hasReactiveDeps(value)) {
|
|
718
728
|
// Reactive effect: signal → DOM property
|
|
719
|
-
this.
|
|
729
|
+
this._setupLines.push(`__effect(() => { ${elVar}.${key} = ${valueCode}; });`);
|
|
720
730
|
// Event listener: DOM → signal (two-way)
|
|
721
731
|
const event = key === 'checked' ? 'change' : 'input';
|
|
722
732
|
const accessor = key === 'checked' ? 'e.target.checked'
|
|
723
733
|
: (inputType === 'number' || inputType === 'range') ? 'e.target.valueAsNumber'
|
|
724
734
|
: 'e.target.value';
|
|
725
|
-
this.
|
|
735
|
+
this._createLines.push(`${elVar}.addEventListener('${event}', (e) => { ${valueCode} = ${accessor}; });`);
|
|
726
736
|
continue;
|
|
727
737
|
}
|
|
728
738
|
|
|
729
739
|
if (this.hasReactiveDeps(value)) {
|
|
730
|
-
this.
|
|
740
|
+
this._setupLines.push(`__effect(() => { ${elVar}.setAttribute('${key}', ${valueCode}); });`);
|
|
731
741
|
} else {
|
|
732
|
-
this.
|
|
742
|
+
this._createLines.push(`${elVar}.setAttribute('${key}', ${valueCode});`);
|
|
733
743
|
}
|
|
734
744
|
}
|
|
735
745
|
}
|
|
736
746
|
};
|
|
737
747
|
|
|
738
748
|
// --------------------------------------------------------------------------
|
|
739
|
-
//
|
|
749
|
+
// generateTemplateBlock — a block of template children
|
|
740
750
|
// --------------------------------------------------------------------------
|
|
741
751
|
|
|
742
|
-
proto.
|
|
752
|
+
proto.generateTemplateBlock = function(body) {
|
|
743
753
|
if (!Array.isArray(body) || body[0] !== 'block') {
|
|
744
|
-
return this.
|
|
754
|
+
return this.generateNode(body);
|
|
745
755
|
}
|
|
746
756
|
|
|
747
757
|
const statements = body.slice(1);
|
|
748
758
|
if (statements.length === 0) {
|
|
749
759
|
const commentVar = this.newElementVar('empty');
|
|
750
|
-
this.
|
|
760
|
+
this._createLines.push(`${commentVar} = document.createComment('');`);
|
|
751
761
|
return commentVar;
|
|
752
762
|
}
|
|
753
763
|
if (statements.length === 1) {
|
|
754
|
-
return this.
|
|
764
|
+
return this.generateNode(statements[0]);
|
|
755
765
|
}
|
|
756
766
|
|
|
757
767
|
const fragVar = this.newElementVar('frag');
|
|
758
|
-
this.
|
|
768
|
+
this._createLines.push(`${fragVar} = document.createDocumentFragment();`);
|
|
759
769
|
for (const stmt of statements) {
|
|
760
|
-
const childVar = this.
|
|
761
|
-
this.
|
|
770
|
+
const childVar = this.generateNode(stmt);
|
|
771
|
+
this._createLines.push(`${fragVar}.appendChild(${childVar});`);
|
|
762
772
|
}
|
|
763
773
|
return fragVar;
|
|
764
774
|
};
|
|
765
775
|
|
|
766
776
|
// --------------------------------------------------------------------------
|
|
767
|
-
//
|
|
777
|
+
// generateConditional — reactive if/else using block factories
|
|
768
778
|
// --------------------------------------------------------------------------
|
|
769
779
|
|
|
770
|
-
proto.
|
|
780
|
+
proto.generateConditional = function(sexpr) {
|
|
771
781
|
const [, condition, thenBlock, elseBlock] = sexpr;
|
|
772
782
|
|
|
773
783
|
const anchorVar = this.newElementVar('anchor');
|
|
774
|
-
this.
|
|
784
|
+
this._createLines.push(`${anchorVar} = document.createComment('if');`);
|
|
775
785
|
|
|
776
786
|
const condCode = this.generateInComponent(condition, 'value');
|
|
777
787
|
|
|
778
788
|
const thenBlockName = this.newBlockVar();
|
|
779
|
-
this.
|
|
789
|
+
this.generateConditionBranch(thenBlockName, thenBlock);
|
|
780
790
|
|
|
781
791
|
let elseBlockName = null;
|
|
782
792
|
if (elseBlock) {
|
|
783
793
|
elseBlockName = this.newBlockVar();
|
|
784
|
-
this.
|
|
794
|
+
this.generateConditionBranch(elseBlockName, elseBlock);
|
|
785
795
|
}
|
|
786
796
|
|
|
787
797
|
const setupLines = [];
|
|
@@ -818,36 +828,30 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
818
828
|
setupLines.push(` });`);
|
|
819
829
|
setupLines.push(`}`);
|
|
820
830
|
|
|
821
|
-
this.
|
|
831
|
+
this._setupLines.push(setupLines.join('\n '));
|
|
822
832
|
|
|
823
833
|
return anchorVar;
|
|
824
834
|
};
|
|
825
835
|
|
|
826
836
|
// --------------------------------------------------------------------------
|
|
827
|
-
//
|
|
837
|
+
// generateConditionBranch — block factory for a conditional branch
|
|
828
838
|
// --------------------------------------------------------------------------
|
|
829
839
|
|
|
830
|
-
proto.
|
|
831
|
-
const savedCreateLines = this.
|
|
832
|
-
const savedSetupLines = this.
|
|
840
|
+
proto.generateConditionBranch = function(blockName, block) {
|
|
841
|
+
const savedCreateLines = this._createLines;
|
|
842
|
+
const savedSetupLines = this._setupLines;
|
|
833
843
|
|
|
834
|
-
this.
|
|
835
|
-
this.
|
|
844
|
+
this._createLines = [];
|
|
845
|
+
this._setupLines = [];
|
|
836
846
|
|
|
837
|
-
const rootVar = this.
|
|
838
|
-
const createLines = this.
|
|
839
|
-
const setupLines = this.
|
|
847
|
+
const rootVar = this.generateTemplateBlock(block);
|
|
848
|
+
const createLines = this._createLines;
|
|
849
|
+
const setupLines = this._setupLines;
|
|
840
850
|
|
|
841
|
-
this.
|
|
842
|
-
this.
|
|
851
|
+
this._createLines = savedCreateLines;
|
|
852
|
+
this._setupLines = savedSetupLines;
|
|
843
853
|
|
|
844
|
-
const localizeVar = (line) =>
|
|
845
|
-
// First localize template element refs (this._elN → _elN)
|
|
846
|
-
let result = line.replace(/this\.(_el\d+|_t\d+|_anchor\d+|_frag\d+|_slot\d+|_c\d+|_inst\d+|_empty\d+)/g, '$1');
|
|
847
|
-
// Then replace remaining this. with ctx. (component instance in block context)
|
|
848
|
-
result = result.replace(/\bthis\./g, 'ctx.');
|
|
849
|
-
return result;
|
|
850
|
-
};
|
|
854
|
+
const localizeVar = (line) => this.localizeVar(line);
|
|
851
855
|
|
|
852
856
|
const factoryLines = [];
|
|
853
857
|
factoryLines.push(`function ${blockName}(ctx) {`);
|
|
@@ -911,20 +915,20 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
911
915
|
factoryLines.push(` };`);
|
|
912
916
|
factoryLines.push(`}`);
|
|
913
917
|
|
|
914
|
-
this.
|
|
918
|
+
this._blockFactories.push(factoryLines.join('\n'));
|
|
915
919
|
};
|
|
916
920
|
|
|
917
921
|
// --------------------------------------------------------------------------
|
|
918
|
-
//
|
|
922
|
+
// generateTemplateLoop — reactive for-loop with keyed reconciliation
|
|
919
923
|
// --------------------------------------------------------------------------
|
|
920
924
|
|
|
921
|
-
proto.
|
|
925
|
+
proto.generateTemplateLoop = function(sexpr) {
|
|
922
926
|
const [head, vars, collection, guard, step, body] = sexpr;
|
|
923
927
|
|
|
924
928
|
const blockName = this.newBlockVar();
|
|
925
929
|
|
|
926
930
|
const anchorVar = this.newElementVar('anchor');
|
|
927
|
-
this.
|
|
931
|
+
this._createLines.push(`${anchorVar} = document.createComment('for');`);
|
|
928
932
|
|
|
929
933
|
const varNames = Array.isArray(vars) ? vars : [vars];
|
|
930
934
|
const itemVar = varNames[0];
|
|
@@ -953,26 +957,20 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
953
957
|
}
|
|
954
958
|
|
|
955
959
|
// Save state and generate item template in isolation
|
|
956
|
-
const savedCreateLines = this.
|
|
957
|
-
const savedSetupLines = this.
|
|
960
|
+
const savedCreateLines = this._createLines;
|
|
961
|
+
const savedSetupLines = this._setupLines;
|
|
958
962
|
|
|
959
|
-
this.
|
|
960
|
-
this.
|
|
963
|
+
this._createLines = [];
|
|
964
|
+
this._setupLines = [];
|
|
961
965
|
|
|
962
|
-
const itemNode = this.
|
|
963
|
-
const itemCreateLines = this.
|
|
964
|
-
const itemSetupLines = this.
|
|
966
|
+
const itemNode = this.generateTemplateBlock(body);
|
|
967
|
+
const itemCreateLines = this._createLines;
|
|
968
|
+
const itemSetupLines = this._setupLines;
|
|
965
969
|
|
|
966
|
-
this.
|
|
967
|
-
this.
|
|
970
|
+
this._createLines = savedCreateLines;
|
|
971
|
+
this._setupLines = savedSetupLines;
|
|
968
972
|
|
|
969
|
-
const localizeVar = (line) =>
|
|
970
|
-
// First localize template element refs (this._elN → _elN)
|
|
971
|
-
let result = line.replace(/this\.(_el\d+|_t\d+|_anchor\d+|_frag\d+|_slot\d+|_c\d+|_inst\d+|_empty\d+)/g, '$1');
|
|
972
|
-
// Then replace remaining this. with ctx. (component instance in block context)
|
|
973
|
-
result = result.replace(/\bthis\./g, 'ctx.');
|
|
974
|
-
return result;
|
|
975
|
-
};
|
|
973
|
+
const localizeVar = (line) => this.localizeVar(line);
|
|
976
974
|
|
|
977
975
|
// Generate block factory
|
|
978
976
|
const factoryLines = [];
|
|
@@ -1036,7 +1034,7 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
1036
1034
|
factoryLines.push(` };`);
|
|
1037
1035
|
factoryLines.push(`}`);
|
|
1038
1036
|
|
|
1039
|
-
this.
|
|
1037
|
+
this._blockFactories.push(factoryLines.join('\n'));
|
|
1040
1038
|
|
|
1041
1039
|
// Generate reconciliation code in _setup()
|
|
1042
1040
|
const setupLines = [];
|
|
@@ -1073,27 +1071,27 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
1073
1071
|
setupLines.push(` });`);
|
|
1074
1072
|
setupLines.push(`}`);
|
|
1075
1073
|
|
|
1076
|
-
this.
|
|
1074
|
+
this._setupLines.push(setupLines.join('\n '));
|
|
1077
1075
|
|
|
1078
1076
|
return anchorVar;
|
|
1079
1077
|
};
|
|
1080
1078
|
|
|
1081
1079
|
// --------------------------------------------------------------------------
|
|
1082
|
-
//
|
|
1080
|
+
// generateChildComponent — instantiate a child component
|
|
1083
1081
|
// --------------------------------------------------------------------------
|
|
1084
1082
|
|
|
1085
|
-
proto.
|
|
1083
|
+
proto.generateChildComponent = function(componentName, args) {
|
|
1086
1084
|
const instVar = this.newElementVar('inst');
|
|
1087
1085
|
const elVar = this.newElementVar('el');
|
|
1088
1086
|
const { propsCode, childrenSetupLines } = this.buildComponentProps(args);
|
|
1089
1087
|
|
|
1090
|
-
this.
|
|
1091
|
-
this.
|
|
1088
|
+
this._createLines.push(`${instVar} = new ${componentName}(${propsCode});`);
|
|
1089
|
+
this._createLines.push(`${elVar} = ${instVar}._create();`);
|
|
1092
1090
|
|
|
1093
|
-
this.
|
|
1091
|
+
this._setupLines.push(`if (${instVar}._setup) ${instVar}._setup();`);
|
|
1094
1092
|
|
|
1095
1093
|
for (const line of childrenSetupLines) {
|
|
1096
|
-
this.
|
|
1094
|
+
this._setupLines.push(line);
|
|
1097
1095
|
}
|
|
1098
1096
|
|
|
1099
1097
|
return elVar;
|
|
@@ -1120,21 +1118,21 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
1120
1118
|
} else if (Array.isArray(arg) && (arg[0] === '->' || arg[0] === '=>')) {
|
|
1121
1119
|
const block = arg[2];
|
|
1122
1120
|
if (block) {
|
|
1123
|
-
const savedCreateLines = this.
|
|
1124
|
-
const savedSetupLines = this.
|
|
1125
|
-
this.
|
|
1126
|
-
this.
|
|
1121
|
+
const savedCreateLines = this._createLines;
|
|
1122
|
+
const savedSetupLines = this._setupLines;
|
|
1123
|
+
this._createLines = [];
|
|
1124
|
+
this._setupLines = [];
|
|
1127
1125
|
|
|
1128
|
-
childrenVar = this.
|
|
1126
|
+
childrenVar = this.generateTemplateBlock(block);
|
|
1129
1127
|
|
|
1130
|
-
const childCreateLines = this.
|
|
1131
|
-
const childSetupLinesCopy = this.
|
|
1128
|
+
const childCreateLines = this._createLines;
|
|
1129
|
+
const childSetupLinesCopy = this._setupLines;
|
|
1132
1130
|
|
|
1133
|
-
this.
|
|
1134
|
-
this.
|
|
1131
|
+
this._createLines = savedCreateLines;
|
|
1132
|
+
this._setupLines = savedSetupLines;
|
|
1135
1133
|
|
|
1136
1134
|
for (const line of childCreateLines) {
|
|
1137
|
-
this.
|
|
1135
|
+
this._createLines.push(line);
|
|
1138
1136
|
}
|
|
1139
1137
|
childrenSetupLines.push(...childSetupLinesCopy);
|
|
1140
1138
|
props.push(`children: ${childrenVar}`);
|