rip-lang 3.7.3 → 3.8.8
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/CHANGELOG.md +111 -0
- package/README.md +42 -34
- package/docs/RIP-INTERNALS.md +2 -4
- package/docs/RIP-LANG.md +150 -3
- package/docs/RIP-TYPES.md +1 -2
- package/docs/demo.html +342 -0
- package/docs/dist/rip-ui.min.js +516 -0
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.js +379 -461
- package/docs/dist/rip.browser.min.js +204 -220
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/dist/ui.js +956 -0
- package/docs/dist/ui.min.js +2 -0
- package/docs/dist/ui.min.js.br +0 -0
- package/docs/dist/ui.rip +957 -0
- package/docs/dist/ui.rip.br +0 -0
- package/docs/examples.rip +180 -0
- package/docs/index.html +3 -1599
- package/docs/playground-app.html +1022 -0
- package/docs/playground-js.html +1645 -0
- package/docs/playground-rip-ui.html +1419 -0
- package/docs/playground-rip.html +1450 -0
- package/docs/rip-fav.svg +5 -0
- package/package.json +3 -3
- package/scripts/serve.js +3 -2
- package/src/browser.js +38 -16
- package/src/compiler.js +165 -226
- package/src/components.js +153 -140
- package/src/grammar/README.md +234 -0
- package/src/grammar/lunar.rip +2412 -0
- package/src/grammar/solar.rip +18 -4
- package/src/lexer.js +82 -30
- package/src/parser-rd.js +3242 -0
- package/src/parser.js +6 -5
- package/src/repl.js +24 -5
- package/docs/NOTES.md +0 -93
- package/docs/RIP-GUIDE.md +0 -698
- package/docs/RIP-REACTIVITY.md +0 -311
package/src/components.js
CHANGED
|
@@ -101,8 +101,11 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
101
101
|
}
|
|
102
102
|
current = current[1];
|
|
103
103
|
}
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
let raw = typeof current === 'string' ? current : (current instanceof String ? current.valueOf() : 'div');
|
|
105
|
+
// Split tag#id — e.g. "div#content" → tag: "div", id: "content"
|
|
106
|
+
let [tag, id] = raw.split('#');
|
|
107
|
+
if (!tag) tag = 'div'; // bare #id → div
|
|
108
|
+
return { tag, classes, id };
|
|
106
109
|
};
|
|
107
110
|
|
|
108
111
|
// ==========================================================================
|
|
@@ -118,6 +121,9 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
118
121
|
if (typeof sexpr === 'string' && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
|
|
119
122
|
return ['.', ['.', 'this', sexpr], 'value'];
|
|
120
123
|
}
|
|
124
|
+
if (typeof sexpr === 'string' && this.componentMembers && this.componentMembers.has(sexpr)) {
|
|
125
|
+
return ['.', 'this', sexpr];
|
|
126
|
+
}
|
|
121
127
|
return sexpr;
|
|
122
128
|
}
|
|
123
129
|
|
|
@@ -130,6 +136,11 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
130
136
|
return sexpr;
|
|
131
137
|
}
|
|
132
138
|
|
|
139
|
+
// Dot access: transform the object but not the property name
|
|
140
|
+
if (sexpr[0] === '.') {
|
|
141
|
+
return ['.', this.transformComponentMembers(sexpr[1]), sexpr[2]];
|
|
142
|
+
}
|
|
143
|
+
|
|
133
144
|
// Force thin arrows to fat arrows inside components to preserve this binding
|
|
134
145
|
if (sexpr[0] === '->') {
|
|
135
146
|
return ['=>', ...sexpr.slice(1).map(item => this.transformComponentMembers(item))];
|
|
@@ -236,12 +247,10 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
236
247
|
const lines = [];
|
|
237
248
|
let blockFactoriesCode = '';
|
|
238
249
|
|
|
239
|
-
lines.push('class {');
|
|
250
|
+
lines.push('class extends __Component {');
|
|
240
251
|
|
|
241
|
-
// ---
|
|
242
|
-
lines.push('
|
|
243
|
-
lines.push(' const __prevComponent = __pushComponent(this);');
|
|
244
|
-
lines.push('');
|
|
252
|
+
// --- Init (called by __Component constructor) ---
|
|
253
|
+
lines.push(' _init(props) {');
|
|
245
254
|
|
|
246
255
|
// Constants (readonly)
|
|
247
256
|
for (const { name, value } of readonlyVars) {
|
|
@@ -249,10 +258,10 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
249
258
|
lines.push(` this.${name} = props.${name} ?? ${val};`);
|
|
250
259
|
}
|
|
251
260
|
|
|
252
|
-
// State variables (
|
|
261
|
+
// State variables (__state handles signal passthrough)
|
|
253
262
|
for (const { name, value } of stateVars) {
|
|
254
263
|
const val = this.generateInComponent(value, 'value');
|
|
255
|
-
lines.push(` this.${name} =
|
|
264
|
+
lines.push(` this.${name} = __state(props.${name} ?? ${val});`);
|
|
256
265
|
}
|
|
257
266
|
|
|
258
267
|
// Computed (derived)
|
|
@@ -263,13 +272,11 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
263
272
|
|
|
264
273
|
// Effects
|
|
265
274
|
for (const effect of effects) {
|
|
266
|
-
const effectBody = effect[
|
|
275
|
+
const effectBody = effect[2];
|
|
267
276
|
const effectCode = this.generateInComponent(effectBody, 'value');
|
|
268
|
-
lines.push(` __effect(${effectCode});`);
|
|
277
|
+
lines.push(` __effect(() => { ${effectCode}; });`);
|
|
269
278
|
}
|
|
270
279
|
|
|
271
|
-
lines.push('');
|
|
272
|
-
lines.push(' __popComponent(__prevComponent);');
|
|
273
280
|
lines.push(' }');
|
|
274
281
|
|
|
275
282
|
// --- Methods ---
|
|
@@ -277,8 +284,10 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
277
284
|
if (Array.isArray(func) && (func[0] === '->' || func[0] === '=>')) {
|
|
278
285
|
const [, params, methodBody] = func;
|
|
279
286
|
const paramStr = Array.isArray(params) ? params.map(p => this.formatParam(p)).join(', ') : '';
|
|
280
|
-
const
|
|
281
|
-
|
|
287
|
+
const transformed = this.reactiveMembers ? this.transformComponentMembers(methodBody) : methodBody;
|
|
288
|
+
const isAsync = this.containsAwait(methodBody);
|
|
289
|
+
const bodyCode = this.generateFunctionBody(transformed, params || []);
|
|
290
|
+
lines.push(` ${isAsync ? 'async ' : ''}${name}(${paramStr}) ${bodyCode}`);
|
|
282
291
|
}
|
|
283
292
|
}
|
|
284
293
|
|
|
@@ -286,8 +295,10 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
286
295
|
for (const { name, value } of lifecycleHooks) {
|
|
287
296
|
if (Array.isArray(value) && (value[0] === '->' || value[0] === '=>')) {
|
|
288
297
|
const [, , hookBody] = value;
|
|
289
|
-
const
|
|
290
|
-
|
|
298
|
+
const transformed = this.reactiveMembers ? this.transformComponentMembers(hookBody) : hookBody;
|
|
299
|
+
const isAsync = this.containsAwait(hookBody);
|
|
300
|
+
const bodyCode = this.generateFunctionBody(transformed, []);
|
|
301
|
+
lines.push(` ${isAsync ? 'async ' : ''}${name}() ${bodyCode}`);
|
|
291
302
|
}
|
|
292
303
|
}
|
|
293
304
|
|
|
@@ -316,25 +327,6 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
316
327
|
}
|
|
317
328
|
}
|
|
318
329
|
|
|
319
|
-
// --- Mount ---
|
|
320
|
-
lines.push(' mount(target) {');
|
|
321
|
-
lines.push(' if (typeof target === "string") target = document.querySelector(target);');
|
|
322
|
-
lines.push(' this._target = target;');
|
|
323
|
-
lines.push(' this._root = this._create();');
|
|
324
|
-
lines.push(' target.appendChild(this._root);');
|
|
325
|
-
lines.push(' if (this._setup) this._setup();');
|
|
326
|
-
lines.push(' if (this.mounted) this.mounted();');
|
|
327
|
-
lines.push(' return this;');
|
|
328
|
-
lines.push(' }');
|
|
329
|
-
|
|
330
|
-
// --- Unmount ---
|
|
331
|
-
lines.push(' unmount() {');
|
|
332
|
-
lines.push(' if (this.unmounted) this.unmounted();');
|
|
333
|
-
lines.push(' if (this._root && this._root.parentNode) {');
|
|
334
|
-
lines.push(' this._root.parentNode.removeChild(this._root);');
|
|
335
|
-
lines.push(' }');
|
|
336
|
-
lines.push(' }');
|
|
337
|
-
|
|
338
330
|
lines.push('}');
|
|
339
331
|
|
|
340
332
|
// Restore context
|
|
@@ -356,6 +348,9 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
356
348
|
if (typeof sexpr === 'string' && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
|
|
357
349
|
return `this.${sexpr}.value`;
|
|
358
350
|
}
|
|
351
|
+
if (typeof sexpr === 'string' && this.componentMembers && this.componentMembers.has(sexpr)) {
|
|
352
|
+
return `this.${sexpr}`;
|
|
353
|
+
}
|
|
359
354
|
if (Array.isArray(sexpr) && this.reactiveMembers) {
|
|
360
355
|
const transformed = this.transformComponentMembers(sexpr);
|
|
361
356
|
return this.generate(transformed, context);
|
|
@@ -445,9 +440,11 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
445
440
|
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${str}.value; });`);
|
|
446
441
|
return textVar;
|
|
447
442
|
}
|
|
448
|
-
// Static tag without content
|
|
443
|
+
// Static tag without content (possibly with #id)
|
|
444
|
+
const [tagStr, idStr] = str.split('#');
|
|
449
445
|
const elVar = this.newElementVar();
|
|
450
|
-
this._createLines.push(`${elVar} = document.createElement('${
|
|
446
|
+
this._createLines.push(`${elVar} = document.createElement('${tagStr || 'div'}');`);
|
|
447
|
+
if (idStr) this._createLines.push(`${elVar}.id = '${idStr}';`);
|
|
451
448
|
return elVar;
|
|
452
449
|
}
|
|
453
450
|
|
|
@@ -465,9 +462,10 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
465
462
|
return this.generateChildComponent(headStr, rest);
|
|
466
463
|
}
|
|
467
464
|
|
|
468
|
-
// HTML tag
|
|
465
|
+
// HTML tag (possibly with #id, e.g. div#content)
|
|
469
466
|
if (headStr && this.isHtmlTag(headStr)) {
|
|
470
|
-
|
|
467
|
+
let [tagName, id] = headStr.split('#');
|
|
468
|
+
return this.generateTag(tagName || 'div', [], rest, id);
|
|
471
469
|
}
|
|
472
470
|
|
|
473
471
|
// Property chain (div.class or item.name)
|
|
@@ -482,17 +480,16 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
482
480
|
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${prop}.value; });`);
|
|
483
481
|
return textVar;
|
|
484
482
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
483
|
+
// Slot/prop — handle DOM nodes (children) and plain values
|
|
484
|
+
const slotVar = this.newElementVar('slot');
|
|
485
|
+
this._createLines.push(`${slotVar} = this.${prop} instanceof Node ? this.${prop} : (this.${prop} != null ? document.createTextNode(String(this.${prop})) : document.createComment(''));`);
|
|
486
|
+
return slotVar;
|
|
490
487
|
}
|
|
491
488
|
|
|
492
|
-
// HTML tag with classes (div.class)
|
|
493
|
-
const { tag, classes } = this.collectTemplateClasses(sexpr);
|
|
489
|
+
// HTML tag with classes (div.class) and optional #id
|
|
490
|
+
const { tag, classes, id } = this.collectTemplateClasses(sexpr);
|
|
494
491
|
if (tag && this.isHtmlTag(tag)) {
|
|
495
|
-
return this.generateTag(tag, classes, []);
|
|
492
|
+
return this.generateTag(tag, classes, [], id);
|
|
496
493
|
}
|
|
497
494
|
|
|
498
495
|
// General property access (e.g., item.name in a loop)
|
|
@@ -504,21 +501,21 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
504
501
|
|
|
505
502
|
// Call expression: (tag.class args...) or ((tag.class) args...)
|
|
506
503
|
if (Array.isArray(head)) {
|
|
507
|
-
// Nested dynamic class call: (((. div
|
|
504
|
+
// Nested dynamic class call: (((. div __clsx) "classes") children)
|
|
508
505
|
if (Array.isArray(head[0]) && head[0][0] === '.' &&
|
|
509
|
-
(head[0][2] === '
|
|
506
|
+
(head[0][2] === '__clsx' || (head[0][2] instanceof String && head[0][2].valueOf() === '__clsx'))) {
|
|
510
507
|
const tag = typeof head[0][1] === 'string' ? head[0][1] : head[0][1].valueOf();
|
|
511
508
|
const classExprs = head.slice(1);
|
|
512
509
|
return this.generateDynamicTag(tag, classExprs, rest);
|
|
513
510
|
}
|
|
514
511
|
|
|
515
|
-
const { tag, classes } = this.collectTemplateClasses(head);
|
|
512
|
+
const { tag, classes, id } = this.collectTemplateClasses(head);
|
|
516
513
|
if (tag && this.isHtmlTag(tag)) {
|
|
517
|
-
// Dynamic class syntax: div.("classes") → (. div
|
|
518
|
-
if (classes.length === 1 && classes[0] === '
|
|
514
|
+
// Dynamic class syntax: div.("classes") → (. div __clsx) "classes"
|
|
515
|
+
if (classes.length === 1 && classes[0] === '__clsx') {
|
|
519
516
|
return this.generateDynamicTag(tag, rest, []);
|
|
520
517
|
}
|
|
521
|
-
return this.generateTag(tag, classes, rest);
|
|
518
|
+
return this.generateTag(tag, classes, rest, id);
|
|
522
519
|
}
|
|
523
520
|
}
|
|
524
521
|
|
|
@@ -550,20 +547,12 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
550
547
|
};
|
|
551
548
|
|
|
552
549
|
// --------------------------------------------------------------------------
|
|
553
|
-
//
|
|
550
|
+
// appendChildren — shared child-processing loop for generateTag/generateDynamicTag
|
|
554
551
|
// --------------------------------------------------------------------------
|
|
555
552
|
|
|
556
|
-
proto.
|
|
557
|
-
const elVar = this.newElementVar();
|
|
558
|
-
this._createLines.push(`${elVar} = document.createElement('${tag}');`);
|
|
559
|
-
|
|
560
|
-
if (classes.length > 0) {
|
|
561
|
-
this._createLines.push(`${elVar}.className = '${classes.join(' ')}';`);
|
|
562
|
-
}
|
|
563
|
-
|
|
553
|
+
proto.appendChildren = function(elVar, args) {
|
|
564
554
|
for (const arg of args) {
|
|
565
|
-
|
|
566
|
-
if (Array.isArray(arg) && (arg[0] === '->' || arg[0] === '=>')) {
|
|
555
|
+
if (this.is(arg, '->') || this.is(arg, '=>')) {
|
|
567
556
|
const block = arg[2];
|
|
568
557
|
if (this.is(block, 'block')) {
|
|
569
558
|
for (const child of block.slice(1)) {
|
|
@@ -575,46 +564,47 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
575
564
|
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
576
565
|
}
|
|
577
566
|
}
|
|
578
|
-
// Object = attributes/events
|
|
579
567
|
else if (this.is(arg, 'object')) {
|
|
580
568
|
this.generateAttributes(elVar, arg);
|
|
581
569
|
}
|
|
582
|
-
|
|
583
|
-
else if (typeof arg === 'string') {
|
|
570
|
+
else if (typeof arg === 'string' || arg instanceof String) {
|
|
584
571
|
const textVar = this.newTextVar();
|
|
585
|
-
if (arg.startsWith('"') || arg.startsWith("'") || arg.startsWith('`')) {
|
|
586
|
-
this._createLines.push(`${textVar} = document.createTextNode(${arg});`);
|
|
587
|
-
} else if (this.reactiveMembers && this.reactiveMembers.has(arg)) {
|
|
588
|
-
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
589
|
-
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${arg}.value; });`);
|
|
590
|
-
} else if (this.componentMembers && this.componentMembers.has(arg)) {
|
|
591
|
-
this._createLines.push(`${textVar} = document.createTextNode(String(this.${arg}));`);
|
|
592
|
-
} else {
|
|
593
|
-
this._createLines.push(`${textVar} = document.createTextNode(String(${arg}));`);
|
|
594
|
-
}
|
|
595
|
-
this._createLines.push(`${elVar}.appendChild(${textVar});`);
|
|
596
|
-
}
|
|
597
|
-
// String object (from parser)
|
|
598
|
-
else if (arg instanceof String) {
|
|
599
572
|
const val = arg.valueOf();
|
|
600
|
-
const textVar = this.newTextVar();
|
|
601
573
|
if (val.startsWith('"') || val.startsWith("'") || val.startsWith('`')) {
|
|
602
574
|
this._createLines.push(`${textVar} = document.createTextNode(${val});`);
|
|
603
575
|
} else if (this.reactiveMembers && this.reactiveMembers.has(val)) {
|
|
604
576
|
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
605
577
|
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${val}.value; });`);
|
|
578
|
+
} else if (this.componentMembers && this.componentMembers.has(val)) {
|
|
579
|
+
this._createLines.push(`${textVar} = document.createTextNode(String(this.${val}));`);
|
|
606
580
|
} else {
|
|
607
|
-
this._createLines.push(`${textVar} = document.createTextNode(
|
|
581
|
+
this._createLines.push(`${textVar} = document.createTextNode(${this.generateInComponent(arg, 'value')});`);
|
|
608
582
|
}
|
|
609
583
|
this._createLines.push(`${elVar}.appendChild(${textVar});`);
|
|
610
584
|
}
|
|
611
|
-
// Other = nested element
|
|
612
585
|
else if (arg) {
|
|
613
586
|
const childVar = this.generateNode(arg);
|
|
614
587
|
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
615
588
|
}
|
|
616
589
|
}
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// --------------------------------------------------------------------------
|
|
593
|
+
// generateTag — HTML element with static classes and children
|
|
594
|
+
// --------------------------------------------------------------------------
|
|
595
|
+
|
|
596
|
+
proto.generateTag = function(tag, classes, args, id) {
|
|
597
|
+
const elVar = this.newElementVar();
|
|
598
|
+
this._createLines.push(`${elVar} = document.createElement('${tag}');`);
|
|
599
|
+
|
|
600
|
+
if (id) {
|
|
601
|
+
this._createLines.push(`${elVar}.id = '${id}';`);
|
|
602
|
+
}
|
|
603
|
+
if (classes.length > 0) {
|
|
604
|
+
this._createLines.push(`${elVar}.className = '${classes.join(' ')}';`);
|
|
605
|
+
}
|
|
617
606
|
|
|
607
|
+
this.appendChildren(elVar, args);
|
|
618
608
|
return elVar;
|
|
619
609
|
};
|
|
620
610
|
|
|
@@ -628,51 +618,13 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
628
618
|
|
|
629
619
|
if (classExprs.length > 0) {
|
|
630
620
|
const classArgs = classExprs.map(e => this.generateInComponent(e, 'value')).join(', ');
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
635
|
-
this._createLines.push(`${elVar}.className = __cx__(${classArgs});`);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
for (const arg of children) {
|
|
640
|
-
const argHead = Array.isArray(arg) ? (arg[0] instanceof String ? arg[0].valueOf() : arg[0]) : null;
|
|
641
|
-
if (argHead === '->' || argHead === '=>') {
|
|
642
|
-
const block = arg[2];
|
|
643
|
-
const blockHead = Array.isArray(block) ? (block[0] instanceof String ? block[0].valueOf() : block[0]) : null;
|
|
644
|
-
if (blockHead === 'block') {
|
|
645
|
-
for (const child of block.slice(1)) {
|
|
646
|
-
const childVar = this.generateNode(child);
|
|
647
|
-
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
648
|
-
}
|
|
649
|
-
} else if (block) {
|
|
650
|
-
const childVar = this.generateNode(block);
|
|
651
|
-
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
else if (this.is(arg, 'object')) {
|
|
655
|
-
this.generateAttributes(elVar, arg);
|
|
656
|
-
}
|
|
657
|
-
else if (typeof arg === 'string' || arg instanceof String) {
|
|
658
|
-
const textVar = this.newTextVar();
|
|
659
|
-
const argStr = arg.valueOf();
|
|
660
|
-
if (argStr.startsWith('"') || argStr.startsWith("'") || argStr.startsWith('`')) {
|
|
661
|
-
this._createLines.push(`${textVar} = document.createTextNode(${argStr});`);
|
|
662
|
-
} else if (this.reactiveMembers && this.reactiveMembers.has(argStr)) {
|
|
663
|
-
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
664
|
-
this._setupLines.push(`__effect(() => { ${textVar}.data = this.${argStr}.value; });`);
|
|
665
|
-
} else {
|
|
666
|
-
this._createLines.push(`${textVar} = document.createTextNode(${this.generateInComponent(arg, 'value')});`);
|
|
667
|
-
}
|
|
668
|
-
this._createLines.push(`${elVar}.appendChild(${textVar});`);
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
const childVar = this.generateNode(arg);
|
|
672
|
-
this._createLines.push(`${elVar}.appendChild(${childVar});`);
|
|
673
|
-
}
|
|
621
|
+
// Dynamic classes are always wrapped in __effect — the .() syntax exists
|
|
622
|
+
// precisely for reactive class expressions. If a class were static, you'd
|
|
623
|
+
// just write div.foo.bar instead. The effect tracks signal reads at runtime.
|
|
624
|
+
this._setupLines.push(`__effect(() => { ${elVar}.className = __clsx(${classArgs}); });`);
|
|
674
625
|
}
|
|
675
626
|
|
|
627
|
+
this.appendChildren(elVar, children);
|
|
676
628
|
return elVar;
|
|
677
629
|
};
|
|
678
630
|
|
|
@@ -689,8 +641,13 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
689
641
|
// Event handler: @click or (. this eventName)
|
|
690
642
|
if (this.is(key, '.') && key[1] === 'this') {
|
|
691
643
|
const eventName = key[2];
|
|
692
|
-
|
|
693
|
-
|
|
644
|
+
// Bind method references to this
|
|
645
|
+
if (typeof value === 'string' && this.componentMembers?.has(value)) {
|
|
646
|
+
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => this.${value}(e));`);
|
|
647
|
+
} else {
|
|
648
|
+
const handlerCode = this.generateInComponent(value, 'value');
|
|
649
|
+
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => (${handlerCode})(e));`);
|
|
650
|
+
}
|
|
694
651
|
continue;
|
|
695
652
|
}
|
|
696
653
|
|
|
@@ -701,6 +658,13 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
701
658
|
key = key.slice(1, -1);
|
|
702
659
|
}
|
|
703
660
|
|
|
661
|
+
// Element ref: ref: "name" → this.name = element
|
|
662
|
+
if (key === 'ref') {
|
|
663
|
+
const refName = String(value).replace(/^["']|["']$/g, '');
|
|
664
|
+
this._createLines.push(`this.${refName} = ${elVar};`);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
|
|
704
668
|
// Two-way binding: __bind_value__ pattern
|
|
705
669
|
if (key.startsWith(BIND_PREFIX) && key.endsWith(BIND_SUFFIX)) {
|
|
706
670
|
const prop = key.slice(BIND_PREFIX.length, -BIND_SUFFIX.length);
|
|
@@ -1087,6 +1051,7 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
1087
1051
|
|
|
1088
1052
|
this._createLines.push(`${instVar} = new ${componentName}(${propsCode});`);
|
|
1089
1053
|
this._createLines.push(`${elVar} = ${instVar}._create();`);
|
|
1054
|
+
this._createLines.push(`(this._children || (this._children = [])).push(${instVar});`);
|
|
1090
1055
|
|
|
1091
1056
|
this._setupLines.push(`if (${instVar}._setup) ${instVar}._setup();`);
|
|
1092
1057
|
|
|
@@ -1111,7 +1076,12 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
1111
1076
|
for (let i = 1; i < arg.length; i++) {
|
|
1112
1077
|
const [key, value] = arg[i];
|
|
1113
1078
|
if (typeof key === 'string') {
|
|
1079
|
+
// Pass reactive members as signals (not values) for reactive prop binding.
|
|
1080
|
+
// Child's __state passthrough returns the signal as-is — shared reactivity.
|
|
1081
|
+
const prevReactive = this.reactiveMembers;
|
|
1082
|
+
this.reactiveMembers = new Set();
|
|
1114
1083
|
const valueCode = this.generateInComponent(value, 'value');
|
|
1084
|
+
this.reactiveMembers = prevReactive;
|
|
1115
1085
|
props.push(`${key}: ${valueCode}`);
|
|
1116
1086
|
}
|
|
1117
1087
|
}
|
|
@@ -1149,16 +1119,24 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
1149
1119
|
// --------------------------------------------------------------------------
|
|
1150
1120
|
|
|
1151
1121
|
proto.hasReactiveDeps = function(sexpr) {
|
|
1152
|
-
if (!this.reactiveMembers || this.reactiveMembers.size === 0) return false;
|
|
1153
|
-
|
|
1154
1122
|
if (typeof sexpr === 'string') {
|
|
1155
|
-
return this.reactiveMembers.has(sexpr);
|
|
1123
|
+
return !!(this.reactiveMembers && this.reactiveMembers.has(sexpr));
|
|
1156
1124
|
}
|
|
1157
1125
|
|
|
1158
1126
|
if (!Array.isArray(sexpr)) return false;
|
|
1159
1127
|
|
|
1128
|
+
// Direct this.X — check reactive members
|
|
1160
1129
|
if (sexpr[0] === '.' && sexpr[1] === 'this' && typeof sexpr[2] === 'string') {
|
|
1161
|
-
return this.reactiveMembers.has(sexpr[2]);
|
|
1130
|
+
return !!(this.reactiveMembers && this.reactiveMembers.has(sexpr[2]));
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// Property chain through this (e.g., this.router.path, this.app.data.count)
|
|
1134
|
+
// Props and members may hold reactive objects with signal-backed getters,
|
|
1135
|
+
// so treat deeper this.X.Y chains as potentially reactive. The effect
|
|
1136
|
+
// system handles actual tracking at runtime — wrapping a non-reactive
|
|
1137
|
+
// chain in __effect just means it runs once with no overhead.
|
|
1138
|
+
if (sexpr[0] === '.' && this._rootsAtThis(sexpr[1])) {
|
|
1139
|
+
return true;
|
|
1162
1140
|
}
|
|
1163
1141
|
|
|
1164
1142
|
for (const child of sexpr) {
|
|
@@ -1168,6 +1146,15 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
1168
1146
|
return false;
|
|
1169
1147
|
};
|
|
1170
1148
|
|
|
1149
|
+
// _rootsAtThis — check if a property-access chain is rooted at 'this'
|
|
1150
|
+
// --------------------------------------------------------------------------
|
|
1151
|
+
|
|
1152
|
+
proto._rootsAtThis = function(sexpr) {
|
|
1153
|
+
if (typeof sexpr === 'string') return sexpr === 'this';
|
|
1154
|
+
if (!Array.isArray(sexpr) || sexpr[0] !== '.') return false;
|
|
1155
|
+
return this._rootsAtThis(sexpr[1]);
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1171
1158
|
// ==========================================================================
|
|
1172
1159
|
// Component Runtime
|
|
1173
1160
|
// ==========================================================================
|
|
@@ -1182,10 +1169,6 @@ export function installComponentSupport(CodeGenerator) {
|
|
|
1182
1169
|
// Rip Component Runtime
|
|
1183
1170
|
// ============================================================================
|
|
1184
1171
|
|
|
1185
|
-
function isSignal(v) {
|
|
1186
|
-
return v != null && typeof v === 'object' && typeof v.read === 'function';
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
1172
|
let __currentComponent = null;
|
|
1190
1173
|
|
|
1191
1174
|
function __pushComponent(component) {
|
|
@@ -1223,13 +1206,43 @@ function hasContext(key) {
|
|
|
1223
1206
|
return false;
|
|
1224
1207
|
}
|
|
1225
1208
|
|
|
1226
|
-
function
|
|
1209
|
+
function __clsx(...args) {
|
|
1227
1210
|
return args.filter(Boolean).join(' ');
|
|
1228
1211
|
}
|
|
1229
1212
|
|
|
1213
|
+
class __Component {
|
|
1214
|
+
constructor(props = {}) {
|
|
1215
|
+
Object.assign(this, props);
|
|
1216
|
+
const prev = __pushComponent(this);
|
|
1217
|
+
this._init(props);
|
|
1218
|
+
__popComponent(prev);
|
|
1219
|
+
}
|
|
1220
|
+
_init() {}
|
|
1221
|
+
mount(target) {
|
|
1222
|
+
if (typeof target === "string") target = document.querySelector(target);
|
|
1223
|
+
this._target = target;
|
|
1224
|
+
this._root = this._create();
|
|
1225
|
+
target.appendChild(this._root);
|
|
1226
|
+
if (this._setup) this._setup();
|
|
1227
|
+
if (this.mounted) this.mounted();
|
|
1228
|
+
return this;
|
|
1229
|
+
}
|
|
1230
|
+
unmount() {
|
|
1231
|
+
if (this._children) {
|
|
1232
|
+
for (const child of this._children) {
|
|
1233
|
+
child.unmount();
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (this.unmounted) this.unmounted();
|
|
1237
|
+
if (this._root && this._root.parentNode) {
|
|
1238
|
+
this._root.parentNode.removeChild(this._root);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1230
1243
|
// Register on globalThis for runtime deduplication
|
|
1231
1244
|
if (typeof globalThis !== 'undefined') {
|
|
1232
|
-
globalThis.__ripComponent = {
|
|
1245
|
+
globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component };
|
|
1233
1246
|
}
|
|
1234
1247
|
|
|
1235
1248
|
`;
|