rip-lang 3.13.23 → 3.13.25
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/README.md +2 -2
- package/package.json +1 -1
- package/src/compiler.js +2 -2
- package/src/components.js +295 -291
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.
|
|
12
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.25-blue.svg" alt="Version"></a>
|
|
13
13
|
<a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
|
|
14
|
-
<a href="#"><img src="https://img.shields.io/badge/tests-1%
|
|
14
|
+
<a href="#"><img src="https://img.shields.io/badge/tests-1%2C300%2F1%2C300-brightgreen.svg" alt="Tests"></a>
|
|
15
15
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
package/package.json
CHANGED
package/src/compiler.js
CHANGED
|
@@ -688,9 +688,9 @@ export class CodeGenerator {
|
|
|
688
688
|
|
|
689
689
|
if (this.usesTemplates && !skip) {
|
|
690
690
|
if (skipRT) {
|
|
691
|
-
code += 'var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component } = globalThis.__ripComponent;\n';
|
|
691
|
+
code += 'var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component } = globalThis.__ripComponent;\n';
|
|
692
692
|
} else if (typeof globalThis !== 'undefined' && globalThis.__ripComponent) {
|
|
693
|
-
code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component } = globalThis.__ripComponent;\n';
|
|
693
|
+
code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component } = globalThis.__ripComponent;\n';
|
|
694
694
|
} else {
|
|
695
695
|
code += this.getComponentRuntime();
|
|
696
696
|
}
|
package/src/components.js
CHANGED
|
@@ -106,23 +106,6 @@ function isPublicProp(target) {
|
|
|
106
106
|
return Array.isArray(target) && target[0] === '.' && target[1] === 'this';
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* Detect fragment root and collect direct child variables for proper removal.
|
|
111
|
-
* After insertBefore, a DocumentFragment is empty — .remove() is a no-op.
|
|
112
|
-
* Callers must remove each child element individually.
|
|
113
|
-
*/
|
|
114
|
-
function getFragChildren(rootVar, createLines, localizeVar) {
|
|
115
|
-
const root = localizeVar(rootVar);
|
|
116
|
-
if (!/_frag\d+$/.test(root)) return null;
|
|
117
|
-
const children = [];
|
|
118
|
-
const re = new RegExp(`^${root.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.appendChild\\(([^)]+)\\);`);
|
|
119
|
-
for (const line of createLines) {
|
|
120
|
-
const m = localizeVar(line).match(re);
|
|
121
|
-
if (m) children.push(m[1]);
|
|
122
|
-
}
|
|
123
|
-
return children.length > 0 ? children : null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
109
|
// ============================================================================
|
|
127
110
|
// Prototype Installation
|
|
128
111
|
// ============================================================================
|
|
@@ -398,16 +381,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
398
381
|
// Implicit nesting (inject -> before INDENT)
|
|
399
382
|
// ─────────────────────────────────────────────────────────────────────
|
|
400
383
|
if (nextToken && nextToken[0] === 'INDENT') {
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
for (let j = i; j >= 0; j--) {
|
|
405
|
-
let jt = tokens[j][0];
|
|
406
|
-
if (jt === 'INTERPOLATION_END' || jt === 'STRING_END') depth++;
|
|
407
|
-
if (jt === 'INTERPOLATION_START' || jt === 'STRING_START') depth--;
|
|
408
|
-
if (depth < 0) { return 1; }
|
|
409
|
-
}
|
|
410
|
-
}
|
|
384
|
+
// fromThen INDENTs are inline conditional values (if x then y else z),
|
|
385
|
+
// never template nesting — normalizeLines only creates them for single-line then clauses
|
|
386
|
+
if (nextToken.fromThen) return 1;
|
|
411
387
|
if (tag === '->' || tag === '=>' || tag === 'CALL_START' || tag === '(') {
|
|
412
388
|
return 1;
|
|
413
389
|
}
|
|
@@ -437,31 +413,17 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
437
413
|
isTemplateElement = true;
|
|
438
414
|
} else if (tag === 'IDENTIFIER' && isTemplateTag(token[1]) && !isAfterControlFlow) {
|
|
439
415
|
isTemplateElement = true;
|
|
440
|
-
} else if (tag === '
|
|
416
|
+
} else if (tag === 'IDENTIFIER' && !isAfterControlFlow) {
|
|
417
|
+
isTemplateElement = startsWithTag(tokens, i);
|
|
418
|
+
} else if (tag === 'PROPERTY' || tag === 'STRING' || tag === 'STRING_END' || tag === 'NUMBER' || tag === 'BOOL' || tag === 'CALL_END' || tag === ')') {
|
|
441
419
|
isTemplateElement = startsWithTag(tokens, i);
|
|
442
|
-
}
|
|
443
|
-
else if (tag === 'IDENTIFIER' && i > 1 && tokens[i - 1][0] === '...') {
|
|
444
|
-
if (startsWithTag(tokens, i)) {
|
|
445
|
-
let commaToken = gen(',', ',', token);
|
|
446
|
-
let arrowToken = gen('->', '->', token);
|
|
447
|
-
arrowToken.newLine = true;
|
|
448
|
-
tokens.splice(i + 1, 0, commaToken, arrowToken);
|
|
449
|
-
return 3;
|
|
450
|
-
}
|
|
451
420
|
}
|
|
452
421
|
|
|
453
422
|
if (isTemplateElement) {
|
|
454
423
|
let isClassOrIdTail = tag === 'PROPERTY' && i > 0 && (tokens[i - 1][0] === '.' || tokens[i - 1][0] === '#');
|
|
424
|
+
let isBareTag = isClsxCallEnd || (tag === 'IDENTIFIER' && isTemplateTag(token[1])) || isClassOrIdTail;
|
|
455
425
|
|
|
456
|
-
if (
|
|
457
|
-
let callStartToken = gen('CALL_START', '(', token);
|
|
458
|
-
let arrowToken = gen('->', '->', token);
|
|
459
|
-
arrowToken.newLine = true;
|
|
460
|
-
tokens.splice(i + 1, 0, callStartToken, arrowToken);
|
|
461
|
-
pendingCallEnds.push(currentIndent + 1);
|
|
462
|
-
return 3;
|
|
463
|
-
} else if ((tag === 'IDENTIFIER' && isTemplateTag(token[1])) || isClassOrIdTail) {
|
|
464
|
-
// Bare tag or tag.class/tag#id (no other args): inject CALL_START -> and manage CALL_END
|
|
426
|
+
if (isBareTag) {
|
|
465
427
|
let callStartToken = gen('CALL_START', '(', token);
|
|
466
428
|
let arrowToken = gen('->', '->', token);
|
|
467
429
|
arrowToken.newLine = true;
|
|
@@ -469,7 +431,6 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
469
431
|
pendingCallEnds.push(currentIndent + 1);
|
|
470
432
|
return 3;
|
|
471
433
|
} else {
|
|
472
|
-
// Tag with args: inject , -> (call wrapping handled by addImplicitBracesAndParens)
|
|
473
434
|
let commaToken = gen(',', ',', token);
|
|
474
435
|
let arrowToken = gen('->', '->', token);
|
|
475
436
|
arrowToken.newLine = true;
|
|
@@ -503,16 +464,6 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
503
464
|
// Utilities
|
|
504
465
|
// ==========================================================================
|
|
505
466
|
|
|
506
|
-
/**
|
|
507
|
-
* Localize variable references for block factories.
|
|
508
|
-
* Converts this._elN to _elN and this.x to ctx.x.
|
|
509
|
-
*/
|
|
510
|
-
proto.localizeVar = function(line) {
|
|
511
|
-
let result = line.replace(/this\.(_el\d+|_t\d+|_anchor\d+|_frag\d+|_slot\d+|_c\d+|_inst\d+|_empty\d+)/g, '$1');
|
|
512
|
-
result = result.replace(/\bthis\b/g, 'ctx');
|
|
513
|
-
return result;
|
|
514
|
-
};
|
|
515
|
-
|
|
516
467
|
/**
|
|
517
468
|
* Check if name is an HTML/SVG tag
|
|
518
469
|
*/
|
|
@@ -560,12 +511,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
560
511
|
* For component context where state variables are signals.
|
|
561
512
|
*/
|
|
562
513
|
proto.transformComponentMembers = function(sexpr) {
|
|
514
|
+
const self = this._self;
|
|
563
515
|
if (!Array.isArray(sexpr)) {
|
|
564
516
|
if (typeof sexpr === 'string' && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
|
|
565
|
-
return ['.', ['.',
|
|
517
|
+
return ['.', ['.', self, sexpr], 'value'];
|
|
566
518
|
}
|
|
567
519
|
if (typeof sexpr === 'string' && this.componentMembers && this.componentMembers.has(sexpr)) {
|
|
568
|
-
return ['.',
|
|
520
|
+
return ['.', self, sexpr];
|
|
569
521
|
}
|
|
570
522
|
return sexpr;
|
|
571
523
|
}
|
|
@@ -574,9 +526,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
574
526
|
if (sexpr[0] === '.' && sexpr[1] === 'this' && typeof sexpr[2] === 'string') {
|
|
575
527
|
const memberName = sexpr[2];
|
|
576
528
|
if (this.reactiveMembers && this.reactiveMembers.has(memberName)) {
|
|
577
|
-
return ['.',
|
|
529
|
+
return ['.', ['.', self, memberName], 'value'];
|
|
578
530
|
}
|
|
579
|
-
return sexpr;
|
|
531
|
+
return this._factoryMode ? ['.', self, sexpr[2]] : sexpr;
|
|
580
532
|
}
|
|
581
533
|
|
|
582
534
|
// Dot access: transform the object but not the property name
|
|
@@ -819,10 +771,10 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
819
771
|
*/
|
|
820
772
|
proto.generateInComponent = function(sexpr, context) {
|
|
821
773
|
if (typeof sexpr === 'string' && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
|
|
822
|
-
return
|
|
774
|
+
return `${this._self}.${sexpr}.value`;
|
|
823
775
|
}
|
|
824
776
|
if (typeof sexpr === 'string' && this.componentMembers && this.componentMembers.has(sexpr)) {
|
|
825
|
-
return
|
|
777
|
+
return `${this._self}.${sexpr}`;
|
|
826
778
|
}
|
|
827
779
|
if (Array.isArray(sexpr) && this.reactiveMembers) {
|
|
828
780
|
const transformed = this.transformComponentMembers(sexpr);
|
|
@@ -854,6 +806,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
854
806
|
this._setupLines = [];
|
|
855
807
|
this._blockFactories = [];
|
|
856
808
|
this._loopVarStack = [];
|
|
809
|
+
this._factoryMode = false;
|
|
810
|
+
this._factoryVars = null;
|
|
811
|
+
this._fragChildren = new Map();
|
|
857
812
|
|
|
858
813
|
const statements = this.is(body, 'block') ? body.slice(1) : [body];
|
|
859
814
|
|
|
@@ -865,10 +820,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
865
820
|
} else {
|
|
866
821
|
rootVar = this.newElementVar('frag');
|
|
867
822
|
this._createLines.push(`${rootVar} = document.createDocumentFragment();`);
|
|
823
|
+
const children = [];
|
|
868
824
|
for (const stmt of statements) {
|
|
869
825
|
const childVar = this.generateNode(stmt);
|
|
870
826
|
this._createLines.push(`${rootVar}.appendChild(${childVar});`);
|
|
827
|
+
children.push(childVar);
|
|
871
828
|
}
|
|
829
|
+
this._fragChildren.set(rootVar, children);
|
|
872
830
|
}
|
|
873
831
|
|
|
874
832
|
return {
|
|
@@ -886,12 +844,30 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
886
844
|
|
|
887
845
|
/** Generate a unique element variable name */
|
|
888
846
|
proto.newElementVar = function(hint = 'el') {
|
|
889
|
-
|
|
847
|
+
const name = `_${hint}${this._elementCount++}`;
|
|
848
|
+
if (this._factoryVars) this._factoryVars.add(name);
|
|
849
|
+
return this._factoryMode ? name : `this.${name}`;
|
|
890
850
|
};
|
|
891
851
|
|
|
892
852
|
/** Generate a unique text node variable name */
|
|
893
853
|
proto.newTextVar = function() {
|
|
894
|
-
|
|
854
|
+
const name = `_t${this._textCount++}`;
|
|
855
|
+
if (this._factoryVars) this._factoryVars.add(name);
|
|
856
|
+
return this._factoryMode ? name : `this.${name}`;
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
/** Context reference — 'this' in component body, 'ctx' in block factories */
|
|
860
|
+
Object.defineProperty(proto, '_self', {
|
|
861
|
+
get() { return this._factoryMode ? 'ctx' : 'this'; }
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
/** Push an effect line, wrapping with disposer tracking in factory mode */
|
|
865
|
+
proto._pushEffect = function(body) {
|
|
866
|
+
if (this._factoryMode) {
|
|
867
|
+
this._setupLines.push(`disposers.push(__effect(() => { ${body} }));`);
|
|
868
|
+
} else {
|
|
869
|
+
this._setupLines.push(`__effect(() => { ${body} });`);
|
|
870
|
+
}
|
|
895
871
|
};
|
|
896
872
|
|
|
897
873
|
// --------------------------------------------------------------------------
|
|
@@ -911,7 +887,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
911
887
|
if (this.reactiveMembers && this.reactiveMembers.has(str)) {
|
|
912
888
|
const textVar = this.newTextVar();
|
|
913
889
|
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
914
|
-
this.
|
|
890
|
+
this._pushEffect(`${textVar}.data = ${this._self}.${str}.value;`);
|
|
915
891
|
return textVar;
|
|
916
892
|
}
|
|
917
893
|
// Static tag without content (possibly with #id)
|
|
@@ -953,15 +929,15 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
953
929
|
|
|
954
930
|
// Property access on this (e.g., @prop, @children)
|
|
955
931
|
if (obj === 'this' && typeof prop === 'string') {
|
|
932
|
+
const s = this._self;
|
|
956
933
|
if (this.reactiveMembers && this.reactiveMembers.has(prop)) {
|
|
957
934
|
const textVar = this.newTextVar();
|
|
958
935
|
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
959
|
-
this.
|
|
936
|
+
this._pushEffect(`${textVar}.data = ${s}.${prop}.value;`);
|
|
960
937
|
return textVar;
|
|
961
938
|
}
|
|
962
|
-
// Slot/prop — handle DOM nodes (children) and plain values
|
|
963
939
|
const slotVar = this.newElementVar('slot');
|
|
964
|
-
this._createLines.push(`${slotVar} =
|
|
940
|
+
this._createLines.push(`${slotVar} = ${s}.${prop} instanceof Node ? ${s}.${prop} : (${s}.${prop} != null ? document.createTextNode(String(${s}.${prop})) : document.createComment(''));`);
|
|
965
941
|
return slotVar;
|
|
966
942
|
}
|
|
967
943
|
|
|
@@ -1018,7 +994,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1018
994
|
const exprCode = this.generateInComponent(sexpr, 'value');
|
|
1019
995
|
if (this.hasReactiveDeps(sexpr)) {
|
|
1020
996
|
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
1021
|
-
this.
|
|
997
|
+
this._pushEffect(`${textVar}.data = ${exprCode};`);
|
|
1022
998
|
} else {
|
|
1023
999
|
this._createLines.push(`${textVar} = document.createTextNode(String(${exprCode}));`);
|
|
1024
1000
|
}
|
|
@@ -1057,9 +1033,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1057
1033
|
this._createLines.push(`${textVar} = document.createTextNode(${val});`);
|
|
1058
1034
|
} else if (this.reactiveMembers && this.reactiveMembers.has(val)) {
|
|
1059
1035
|
this._createLines.push(`${textVar} = document.createTextNode('');`);
|
|
1060
|
-
this.
|
|
1036
|
+
this._pushEffect(`${textVar}.data = ${this._self}.${val}.value;`);
|
|
1061
1037
|
} else if (this.componentMembers && this.componentMembers.has(val)) {
|
|
1062
|
-
this._createLines.push(`${textVar} = document.createTextNode(String(this.${val}));`);
|
|
1038
|
+
this._createLines.push(`${textVar} = document.createTextNode(String(${this._self}.${val}));`);
|
|
1063
1039
|
} else {
|
|
1064
1040
|
this._createLines.push(`${textVar} = document.createTextNode(${this.generateInComponent(arg, 'value')});`);
|
|
1065
1041
|
}
|
|
@@ -1112,9 +1088,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1112
1088
|
} else {
|
|
1113
1089
|
const combined = this._pendingClassArgs.join(', ');
|
|
1114
1090
|
if (isSvg) {
|
|
1115
|
-
this.
|
|
1091
|
+
this._pushEffect(`${elVar}.setAttribute('class', __clsx(${combined}));`);
|
|
1116
1092
|
} else {
|
|
1117
|
-
this.
|
|
1093
|
+
this._pushEffect(`${elVar}.className = __clsx(${combined});`);
|
|
1118
1094
|
}
|
|
1119
1095
|
}
|
|
1120
1096
|
this._pendingClassArgs = prevClassArgs;
|
|
@@ -1151,9 +1127,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1151
1127
|
const combined = this._pendingClassArgs.join(', ');
|
|
1152
1128
|
const isSvg = SVG_TAGS.has(tag) || this._svgDepth > 0;
|
|
1153
1129
|
if (isSvg) {
|
|
1154
|
-
this.
|
|
1130
|
+
this._pushEffect(`${elVar}.setAttribute('class', __clsx(${combined}));`);
|
|
1155
1131
|
} else {
|
|
1156
|
-
this.
|
|
1132
|
+
this._pushEffect(`${elVar}.className = __clsx(${combined});`);
|
|
1157
1133
|
}
|
|
1158
1134
|
}
|
|
1159
1135
|
this._pendingClassArgs = prevClassArgs;
|
|
@@ -1175,9 +1151,8 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1175
1151
|
// Event handler: @click or (. this eventName)
|
|
1176
1152
|
if (this.is(key, '.') && key[1] === 'this') {
|
|
1177
1153
|
const eventName = key[2];
|
|
1178
|
-
// Bind method references to this
|
|
1179
1154
|
if (typeof value === 'string' && this.componentMembers?.has(value)) {
|
|
1180
|
-
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => this.${value}(e)));`);
|
|
1155
|
+
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => ${this._self}.${value}(e)));`);
|
|
1181
1156
|
} else {
|
|
1182
1157
|
const handlerCode = this.generateInComponent(value, 'value');
|
|
1183
1158
|
this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => (${handlerCode})(e)));`);
|
|
@@ -1199,9 +1174,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1199
1174
|
this._pendingClassArgs.push(valueCode);
|
|
1200
1175
|
} else if (this.hasReactiveDeps(value)) {
|
|
1201
1176
|
if (this._svgDepth > 0) {
|
|
1202
|
-
this.
|
|
1177
|
+
this._pushEffect(`${elVar}.setAttribute('class', __clsx(${valueCode}));`);
|
|
1203
1178
|
} else {
|
|
1204
|
-
this.
|
|
1179
|
+
this._pushEffect(`${elVar}.className = __clsx(${valueCode});`);
|
|
1205
1180
|
}
|
|
1206
1181
|
} else {
|
|
1207
1182
|
if (this._svgDepth > 0) {
|
|
@@ -1216,7 +1191,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1216
1191
|
// Element ref: ref: "name" → this.name = element
|
|
1217
1192
|
if (key === 'ref') {
|
|
1218
1193
|
const refName = String(value).replace(/^["']|["']$/g, '');
|
|
1219
|
-
this._createLines.push(
|
|
1194
|
+
this._createLines.push(`${this._self}.${refName} = ${elVar};`);
|
|
1220
1195
|
continue;
|
|
1221
1196
|
}
|
|
1222
1197
|
|
|
@@ -1235,11 +1210,11 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1235
1210
|
? 'e.target.valueAsNumber' : 'e.target.value';
|
|
1236
1211
|
}
|
|
1237
1212
|
|
|
1238
|
-
this.
|
|
1213
|
+
this._pushEffect(`${elVar}.${prop} = ${valueCode};`);
|
|
1239
1214
|
let assignCode = `${valueCode} = ${valueAccessor}`;
|
|
1240
1215
|
const rootMember = !this.isSimpleAssignable(value) && this.findRootReactiveMember(value);
|
|
1241
1216
|
if (rootMember) {
|
|
1242
|
-
assignCode += `; this.${rootMember}.touch?.()`;
|
|
1217
|
+
assignCode += `; ${this._self}.${rootMember}.touch?.()`;
|
|
1243
1218
|
}
|
|
1244
1219
|
this._createLines.push(`${elVar}.addEventListener('${event}', (e) => { ${assignCode}; });`);
|
|
1245
1220
|
continue;
|
|
@@ -1249,9 +1224,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1249
1224
|
|
|
1250
1225
|
// Smart two-way binding for value/checked when bound to reactive state
|
|
1251
1226
|
if ((key === 'value' || key === 'checked') && this.hasReactiveDeps(value)) {
|
|
1252
|
-
this.
|
|
1253
|
-
// Generate reverse binding for simple assignable targets or nested
|
|
1254
|
-
// reactive paths (with touch() for Svelte-style invalidation)
|
|
1227
|
+
this._pushEffect(`${elVar}.${key} = ${valueCode};`);
|
|
1255
1228
|
const rootMemberImplicit = !this.isSimpleAssignable(value) && this.findRootReactiveMember(value);
|
|
1256
1229
|
if (this.isSimpleAssignable(value) || rootMemberImplicit) {
|
|
1257
1230
|
const event = key === 'checked' ? 'change' : 'input';
|
|
@@ -1260,7 +1233,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1260
1233
|
: 'e.target.value';
|
|
1261
1234
|
let assignCode = `${valueCode} = ${accessor}`;
|
|
1262
1235
|
if (rootMemberImplicit) {
|
|
1263
|
-
assignCode += `; this.${rootMemberImplicit}.touch?.()`;
|
|
1236
|
+
assignCode += `; ${this._self}.${rootMemberImplicit}.touch?.()`;
|
|
1264
1237
|
}
|
|
1265
1238
|
this._createLines.push(`${elVar}.addEventListener('${event}', (e) => { ${assignCode}; });`);
|
|
1266
1239
|
}
|
|
@@ -1269,18 +1242,18 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1269
1242
|
|
|
1270
1243
|
if (key === 'innerHTML' || key === 'textContent' || key === 'innerText') {
|
|
1271
1244
|
if (this.hasReactiveDeps(value)) {
|
|
1272
|
-
this.
|
|
1245
|
+
this._pushEffect(`${elVar}.${key} = ${valueCode};`);
|
|
1273
1246
|
} else {
|
|
1274
1247
|
this._createLines.push(`${elVar}.${key} = ${valueCode};`);
|
|
1275
1248
|
}
|
|
1276
1249
|
} else if (BOOLEAN_ATTRS.has(key)) {
|
|
1277
1250
|
if (this.hasReactiveDeps(value)) {
|
|
1278
|
-
this.
|
|
1251
|
+
this._pushEffect(`${elVar}.toggleAttribute('${key}', !!${valueCode});`);
|
|
1279
1252
|
} else {
|
|
1280
1253
|
this._createLines.push(`if (${valueCode}) ${elVar}.setAttribute('${key}', '');`);
|
|
1281
1254
|
}
|
|
1282
1255
|
} else if (this.hasReactiveDeps(value)) {
|
|
1283
|
-
this.
|
|
1256
|
+
this._pushEffect(`${elVar}.setAttribute('${key}', ${valueCode});`);
|
|
1284
1257
|
} else {
|
|
1285
1258
|
this._createLines.push(`${elVar}.setAttribute('${key}', ${valueCode});`);
|
|
1286
1259
|
}
|
|
@@ -1309,10 +1282,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1309
1282
|
|
|
1310
1283
|
const fragVar = this.newElementVar('frag');
|
|
1311
1284
|
this._createLines.push(`${fragVar} = document.createDocumentFragment();`);
|
|
1285
|
+
const children = [];
|
|
1312
1286
|
for (const stmt of statements) {
|
|
1313
1287
|
const childVar = this.generateNode(stmt);
|
|
1314
1288
|
this._createLines.push(`${fragVar}.appendChild(${childVar});`);
|
|
1289
|
+
children.push(childVar);
|
|
1315
1290
|
}
|
|
1291
|
+
this._fragChildren.set(fragVar, children);
|
|
1316
1292
|
return fragVar;
|
|
1317
1293
|
};
|
|
1318
1294
|
|
|
@@ -1328,9 +1304,8 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1328
1304
|
|
|
1329
1305
|
const condCode = this.generateInComponent(condition, 'value');
|
|
1330
1306
|
|
|
1331
|
-
|
|
1332
|
-
const
|
|
1333
|
-
const extraArgs = loopParams ? `, ${loopParams}` : '';
|
|
1307
|
+
const outerParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
|
|
1308
|
+
const outerExtra = outerParams ? `, ${outerParams}` : '';
|
|
1334
1309
|
|
|
1335
1310
|
const thenBlockName = this.newBlockVar();
|
|
1336
1311
|
this.generateConditionBranch(thenBlockName, thenBlock);
|
|
@@ -1347,7 +1322,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1347
1322
|
setupLines.push(` const anchor = ${anchorVar};`);
|
|
1348
1323
|
setupLines.push(` let currentBlock = null;`);
|
|
1349
1324
|
setupLines.push(` let showing = null;`);
|
|
1350
|
-
|
|
1325
|
+
const effOpen = this._factoryMode ? 'disposers.push(__effect(() => {' : '__effect(() => {';
|
|
1326
|
+
const effClose = this._factoryMode ? '}));' : '});';
|
|
1327
|
+
setupLines.push(` ${effOpen}`);
|
|
1351
1328
|
setupLines.push(` const show = !!(${condCode});`);
|
|
1352
1329
|
setupLines.push(` const want = show ? 'then' : ${elseBlock ? "'else'" : 'null'};`);
|
|
1353
1330
|
setupLines.push(` if (want === showing) return;`);
|
|
@@ -1359,20 +1336,20 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1359
1336
|
setupLines.push(` showing = want;`);
|
|
1360
1337
|
setupLines.push(``);
|
|
1361
1338
|
setupLines.push(` if (want === 'then') {`);
|
|
1362
|
-
setupLines.push(` currentBlock = ${thenBlockName}(this${
|
|
1339
|
+
setupLines.push(` currentBlock = ${thenBlockName}(${this._self}${outerExtra});`);
|
|
1363
1340
|
setupLines.push(` currentBlock.c();`);
|
|
1364
1341
|
setupLines.push(` if (anchor.parentNode) currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
|
|
1365
|
-
setupLines.push(` currentBlock.p(this${
|
|
1342
|
+
setupLines.push(` currentBlock.p(${this._self}${outerExtra});`);
|
|
1366
1343
|
setupLines.push(` }`);
|
|
1367
1344
|
if (elseBlock) {
|
|
1368
1345
|
setupLines.push(` if (want === 'else') {`);
|
|
1369
|
-
setupLines.push(` currentBlock = ${elseBlockName}(this${
|
|
1346
|
+
setupLines.push(` currentBlock = ${elseBlockName}(${this._self}${outerExtra});`);
|
|
1370
1347
|
setupLines.push(` currentBlock.c();`);
|
|
1371
1348
|
setupLines.push(` if (anchor.parentNode) currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
|
|
1372
|
-
setupLines.push(` currentBlock.p(this${
|
|
1349
|
+
setupLines.push(` currentBlock.p(${this._self}${outerExtra});`);
|
|
1373
1350
|
setupLines.push(` }`);
|
|
1374
1351
|
}
|
|
1375
|
-
setupLines.push(` })
|
|
1352
|
+
setupLines.push(` ${effClose}`);
|
|
1376
1353
|
setupLines.push(`}`);
|
|
1377
1354
|
|
|
1378
1355
|
this._setupLines.push(setupLines.join('\n '));
|
|
@@ -1385,36 +1362,36 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1385
1362
|
// --------------------------------------------------------------------------
|
|
1386
1363
|
|
|
1387
1364
|
proto.generateConditionBranch = function(blockName, block) {
|
|
1388
|
-
const
|
|
1389
|
-
const savedSetupLines = this._setupLines;
|
|
1365
|
+
const saved = [this._createLines, this._setupLines, this._factoryMode, this._factoryVars];
|
|
1390
1366
|
|
|
1391
1367
|
this._createLines = [];
|
|
1392
1368
|
this._setupLines = [];
|
|
1369
|
+
this._factoryMode = true;
|
|
1370
|
+
this._factoryVars = new Set();
|
|
1393
1371
|
|
|
1394
1372
|
const rootVar = this.generateTemplateBlock(block);
|
|
1395
1373
|
const createLines = this._createLines;
|
|
1396
1374
|
const setupLines = this._setupLines;
|
|
1375
|
+
const factoryVars = this._factoryVars;
|
|
1376
|
+
|
|
1377
|
+
[this._createLines, this._setupLines, this._factoryMode, this._factoryVars] = saved;
|
|
1397
1378
|
|
|
1398
|
-
this.
|
|
1399
|
-
|
|
1379
|
+
const outerParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
|
|
1380
|
+
const extraParams = outerParams ? `, ${outerParams}` : '';
|
|
1400
1381
|
|
|
1401
|
-
|
|
1382
|
+
this.emitBlockFactory(blockName, `ctx${extraParams}`, rootVar, createLines, setupLines, factoryVars);
|
|
1383
|
+
};
|
|
1402
1384
|
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1385
|
+
// --------------------------------------------------------------------------
|
|
1386
|
+
// emitBlockFactory — shared factory generation for conditionals and loops
|
|
1387
|
+
// --------------------------------------------------------------------------
|
|
1406
1388
|
|
|
1389
|
+
proto.emitBlockFactory = function(blockName, params, rootVar, createLines, setupLines, factoryVars, isStatic) {
|
|
1407
1390
|
const factoryLines = [];
|
|
1408
|
-
factoryLines.push(`function ${blockName}(
|
|
1391
|
+
factoryLines.push(`function ${blockName}(${params}) {`);
|
|
1409
1392
|
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
for (const line of createLines) {
|
|
1413
|
-
const match = line.match(/^this\.(_(?:el|t|anchor|frag|slot|c|inst|empty)\d+)\s*=/);
|
|
1414
|
-
if (match) localVars.add(match[1]);
|
|
1415
|
-
}
|
|
1416
|
-
if (localVars.size > 0) {
|
|
1417
|
-
factoryLines.push(` let ${[...localVars].join(', ')};`);
|
|
1393
|
+
if (factoryVars.size > 0) {
|
|
1394
|
+
factoryLines.push(` let ${[...factoryVars].join(', ')};`);
|
|
1418
1395
|
}
|
|
1419
1396
|
|
|
1420
1397
|
const hasEffects = setupLines.length > 0;
|
|
@@ -1424,49 +1401,50 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1424
1401
|
|
|
1425
1402
|
factoryLines.push(` return {`);
|
|
1426
1403
|
|
|
1427
|
-
|
|
1404
|
+
if (isStatic) {
|
|
1405
|
+
factoryLines.push(` _s: true,`);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
const fragChildren = this._fragChildren.get(rootVar);
|
|
1409
|
+
const firstNode = fragChildren ? fragChildren[0] : rootVar;
|
|
1410
|
+
|
|
1428
1411
|
factoryLines.push(` c() {`);
|
|
1429
1412
|
for (const line of createLines) {
|
|
1430
|
-
factoryLines.push(` ${
|
|
1413
|
+
factoryLines.push(` ${line}`);
|
|
1431
1414
|
}
|
|
1415
|
+
factoryLines.push(` this._first = ${firstNode};`);
|
|
1432
1416
|
factoryLines.push(` },`);
|
|
1433
1417
|
|
|
1434
|
-
// m() - mount
|
|
1435
1418
|
factoryLines.push(` m(target, anchor) {`);
|
|
1436
|
-
|
|
1419
|
+
if (fragChildren) {
|
|
1420
|
+
for (const child of fragChildren) {
|
|
1421
|
+
factoryLines.push(` if (target) target.insertBefore(${child}, anchor);`);
|
|
1422
|
+
}
|
|
1423
|
+
} else {
|
|
1424
|
+
factoryLines.push(` if (target) target.insertBefore(${rootVar}, anchor);`);
|
|
1425
|
+
}
|
|
1437
1426
|
factoryLines.push(` },`);
|
|
1438
1427
|
|
|
1439
|
-
|
|
1440
|
-
factoryLines.push(` p(ctx${extraParams}) {`);
|
|
1428
|
+
factoryLines.push(` p(${params}) {`);
|
|
1441
1429
|
if (hasEffects) {
|
|
1442
1430
|
factoryLines.push(` disposers.forEach(d => d());`);
|
|
1443
1431
|
factoryLines.push(` disposers = [];`);
|
|
1444
1432
|
for (const line of setupLines) {
|
|
1445
|
-
|
|
1446
|
-
const wrappedLine = localizedLine.replace(
|
|
1447
|
-
/__effect\(\(\) => \{/g,
|
|
1448
|
-
'disposers.push(__effect(() => {'
|
|
1449
|
-
).replace(
|
|
1450
|
-
/\}\);$/gm,
|
|
1451
|
-
'}));'
|
|
1452
|
-
);
|
|
1453
|
-
factoryLines.push(` ${wrappedLine}`);
|
|
1433
|
+
factoryLines.push(` ${line}`);
|
|
1454
1434
|
}
|
|
1455
1435
|
}
|
|
1456
1436
|
factoryLines.push(` },`);
|
|
1457
1437
|
|
|
1458
|
-
// d() - destroy
|
|
1459
1438
|
factoryLines.push(` d(detaching) {`);
|
|
1460
1439
|
if (hasEffects) {
|
|
1461
1440
|
factoryLines.push(` disposers.forEach(d => d());`);
|
|
1462
1441
|
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
for (const child of condFragChildren) {
|
|
1442
|
+
if (fragChildren) {
|
|
1443
|
+
for (const child of fragChildren) {
|
|
1466
1444
|
factoryLines.push(` if (detaching && ${child}) ${child}.remove();`);
|
|
1467
1445
|
}
|
|
1468
1446
|
} else {
|
|
1469
|
-
factoryLines.push(` if (detaching && ${
|
|
1447
|
+
factoryLines.push(` if (detaching && ${rootVar}) ${rootVar}.remove();`);
|
|
1470
1448
|
}
|
|
1471
1449
|
factoryLines.push(` }`);
|
|
1472
1450
|
|
|
@@ -1514,136 +1492,46 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1514
1492
|
}
|
|
1515
1493
|
}
|
|
1516
1494
|
|
|
1517
|
-
|
|
1518
|
-
const savedCreateLines = this._createLines;
|
|
1519
|
-
const savedSetupLines = this._setupLines;
|
|
1495
|
+
const saved = [this._createLines, this._setupLines, this._factoryMode, this._factoryVars];
|
|
1520
1496
|
|
|
1521
1497
|
this._createLines = [];
|
|
1522
1498
|
this._setupLines = [];
|
|
1499
|
+
this._factoryMode = true;
|
|
1500
|
+
this._factoryVars = new Set();
|
|
1523
1501
|
|
|
1524
|
-
|
|
1525
|
-
const
|
|
1526
|
-
const outerExtra = outerLoopParams ? `, ${outerLoopParams}` : '';
|
|
1502
|
+
const outerParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
|
|
1503
|
+
const outerExtra = outerParams ? `, ${outerParams}` : '';
|
|
1527
1504
|
|
|
1528
1505
|
this._loopVarStack.push({ itemVar, indexVar });
|
|
1529
1506
|
const itemNode = this.generateTemplateBlock(body);
|
|
1530
1507
|
this._loopVarStack.pop();
|
|
1531
1508
|
const itemCreateLines = this._createLines;
|
|
1532
1509
|
const itemSetupLines = this._setupLines;
|
|
1510
|
+
const itemFactoryVars = this._factoryVars;
|
|
1533
1511
|
|
|
1534
|
-
this._createLines =
|
|
1535
|
-
this._setupLines = savedSetupLines;
|
|
1536
|
-
|
|
1537
|
-
const localizeVar = (line) => this.localizeVar(line);
|
|
1538
|
-
|
|
1539
|
-
// Generate block factory
|
|
1540
|
-
const factoryLines = [];
|
|
1541
|
-
factoryLines.push(`function ${blockName}(ctx, ${itemVar}, ${indexVar}${outerExtra}) {`);
|
|
1542
|
-
|
|
1543
|
-
const localVars = new Set();
|
|
1544
|
-
for (const line of itemCreateLines) {
|
|
1545
|
-
const match = line.match(/^this\.(_(?:el|t|anchor|frag|slot|c|inst|empty)\d+)\s*=/);
|
|
1546
|
-
if (match) localVars.add(match[1]);
|
|
1547
|
-
}
|
|
1548
|
-
if (localVars.size > 0) {
|
|
1549
|
-
factoryLines.push(` let ${[...localVars].join(', ')};`);
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
const hasEffects = itemSetupLines.length > 0;
|
|
1553
|
-
if (hasEffects) {
|
|
1554
|
-
factoryLines.push(` let disposers = [];`);
|
|
1555
|
-
}
|
|
1512
|
+
[this._createLines, this._setupLines, this._factoryMode, this._factoryVars] = saved;
|
|
1556
1513
|
|
|
1557
|
-
|
|
1514
|
+
const isStatic = itemSetupLines.length === 0;
|
|
1515
|
+
const loopParams = `ctx, ${itemVar}, ${indexVar}${outerExtra}`;
|
|
1516
|
+
this.emitBlockFactory(blockName, loopParams, itemNode, itemCreateLines, itemSetupLines, itemFactoryVars, isStatic);
|
|
1558
1517
|
|
|
1559
|
-
//
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
factoryLines.push(` ${localizeVar(line)}`);
|
|
1563
|
-
}
|
|
1564
|
-
factoryLines.push(` },`);
|
|
1518
|
+
// Build key function argument (null = use item as key)
|
|
1519
|
+
const hasCustomKey = keyExpr !== itemVar;
|
|
1520
|
+
const keyFnCode = hasCustomKey ? `(${itemVar}, ${indexVar}) => ${keyExpr}` : 'null';
|
|
1565
1521
|
|
|
1566
|
-
//
|
|
1567
|
-
const
|
|
1568
|
-
factoryLines.push(` m(target, anchor) {`);
|
|
1569
|
-
if (loopFragChildren) {
|
|
1570
|
-
for (const child of loopFragChildren) {
|
|
1571
|
-
factoryLines.push(` if (target) target.insertBefore(${child}, anchor);`);
|
|
1572
|
-
}
|
|
1573
|
-
} else {
|
|
1574
|
-
factoryLines.push(` if (target) target.insertBefore(${localizeVar(itemNode)}, anchor);`);
|
|
1575
|
-
}
|
|
1576
|
-
factoryLines.push(` },`);
|
|
1577
|
-
|
|
1578
|
-
// p() - update
|
|
1579
|
-
factoryLines.push(` p(ctx, ${itemVar}, ${indexVar}${outerExtra}) {`);
|
|
1580
|
-
if (hasEffects) {
|
|
1581
|
-
factoryLines.push(` disposers.forEach(d => d());`);
|
|
1582
|
-
factoryLines.push(` disposers = [];`);
|
|
1583
|
-
for (const line of itemSetupLines) {
|
|
1584
|
-
const localizedLine = localizeVar(line);
|
|
1585
|
-
const wrappedLine = localizedLine.replace(
|
|
1586
|
-
/__effect\(\(\) => \{/g,
|
|
1587
|
-
'disposers.push(__effect(() => {'
|
|
1588
|
-
).replace(
|
|
1589
|
-
/\}\);$/gm,
|
|
1590
|
-
'}));'
|
|
1591
|
-
);
|
|
1592
|
-
factoryLines.push(` ${wrappedLine}`);
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
factoryLines.push(` },`);
|
|
1596
|
-
|
|
1597
|
-
// d() - destroy
|
|
1598
|
-
factoryLines.push(` d(detaching) {`);
|
|
1599
|
-
if (hasEffects) {
|
|
1600
|
-
factoryLines.push(` disposers.forEach(d => d());`);
|
|
1601
|
-
}
|
|
1602
|
-
if (loopFragChildren) {
|
|
1603
|
-
for (const child of loopFragChildren) {
|
|
1604
|
-
factoryLines.push(` if (detaching && ${child}) ${child}.remove();`);
|
|
1605
|
-
}
|
|
1606
|
-
} else {
|
|
1607
|
-
factoryLines.push(` if (detaching && ${localizeVar(itemNode)}) ${localizeVar(itemNode)}.remove();`);
|
|
1608
|
-
}
|
|
1609
|
-
factoryLines.push(` }`);
|
|
1610
|
-
|
|
1611
|
-
factoryLines.push(` };`);
|
|
1612
|
-
factoryLines.push(`}`);
|
|
1613
|
-
|
|
1614
|
-
this._blockFactories.push(factoryLines.join('\n'));
|
|
1522
|
+
// Build outer vars argument list for nested loops
|
|
1523
|
+
const outerArgs = outerParams ? `, ${outerParams}` : '';
|
|
1615
1524
|
|
|
1616
1525
|
// Generate reconciliation code in _setup()
|
|
1617
1526
|
const setupLines = [];
|
|
1618
1527
|
setupLines.push(`// Loop: ${blockName}`);
|
|
1619
1528
|
setupLines.push(`{`);
|
|
1620
|
-
setupLines.push(` const
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
setupLines.push(`
|
|
1624
|
-
setupLines.push(`
|
|
1625
|
-
setupLines.push(`
|
|
1626
|
-
setupLines.push(``);
|
|
1627
|
-
setupLines.push(` for (let ${indexVar} = 0; ${indexVar} < __items.length; ${indexVar}++) {`);
|
|
1628
|
-
setupLines.push(` const ${itemVar} = __items[${indexVar}];`);
|
|
1629
|
-
setupLines.push(` const __key = ${keyExpr};`);
|
|
1630
|
-
setupLines.push(` let __block = __map.get(__key);`);
|
|
1631
|
-
setupLines.push(` if (!__block) {`);
|
|
1632
|
-
setupLines.push(` __block = ${blockName}(this, ${itemVar}, ${indexVar}${outerExtra});`);
|
|
1633
|
-
setupLines.push(` __block.c();`);
|
|
1634
|
-
setupLines.push(` }`);
|
|
1635
|
-
setupLines.push(` __block.m(__parent, __anchor);`);
|
|
1636
|
-
setupLines.push(` __block.p(this, ${itemVar}, ${indexVar}${outerExtra});`);
|
|
1637
|
-
setupLines.push(` __newMap.set(__key, __block);`);
|
|
1638
|
-
setupLines.push(` }`);
|
|
1639
|
-
setupLines.push(``);
|
|
1640
|
-
setupLines.push(` for (const [__k, __b] of __map) {`);
|
|
1641
|
-
setupLines.push(` if (!__newMap.has(__k)) __b.d(true);`);
|
|
1642
|
-
setupLines.push(` }`);
|
|
1643
|
-
setupLines.push(``);
|
|
1644
|
-
setupLines.push(` __map.clear();`);
|
|
1645
|
-
setupLines.push(` for (const [__k, __v] of __newMap) __map.set(__k, __v);`);
|
|
1646
|
-
setupLines.push(` });`);
|
|
1529
|
+
setupLines.push(` const __s = { blocks: [], keys: [] };`);
|
|
1530
|
+
const effOpen = this._factoryMode ? 'disposers.push(__effect(() => {' : '__effect(() => {';
|
|
1531
|
+
const effClose = this._factoryMode ? '}));' : '});';
|
|
1532
|
+
setupLines.push(` ${effOpen}`);
|
|
1533
|
+
setupLines.push(` __reconcile(${anchorVar}, __s, ${collectionCode}, ${this._self}, ${blockName}, ${keyFnCode}${outerArgs});`);
|
|
1534
|
+
setupLines.push(` ${effClose}`);
|
|
1647
1535
|
setupLines.push(`}`);
|
|
1648
1536
|
|
|
1649
1537
|
this._setupLines.push(setupLines.join('\n '));
|
|
@@ -1660,15 +1548,16 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1660
1548
|
const elVar = this.newElementVar('el');
|
|
1661
1549
|
const { propsCode, reactiveProps, childrenSetupLines } = this.buildComponentProps(args);
|
|
1662
1550
|
|
|
1551
|
+
const s = this._self;
|
|
1663
1552
|
this._createLines.push(`${instVar} = new ${componentName}(${propsCode});`);
|
|
1664
1553
|
this._createLines.push(`${elVar} = ${instVar}._create();`);
|
|
1665
|
-
this._createLines.push(`(
|
|
1554
|
+
this._createLines.push(`(${s}._children || (${s}._children = [])).push(${instVar});`);
|
|
1666
1555
|
|
|
1667
1556
|
this._setupLines.push(`if (${instVar}._setup) ${instVar}._setup();`);
|
|
1668
1557
|
this._setupLines.push(`if (${instVar}.mounted) ${instVar}.mounted();`);
|
|
1669
1558
|
|
|
1670
1559
|
for (const { key, valueCode } of reactiveProps) {
|
|
1671
|
-
this.
|
|
1560
|
+
this._pushEffect(`if (${instVar}.${key}) ${instVar}.${key}.value = ${valueCode};`);
|
|
1672
1561
|
}
|
|
1673
1562
|
|
|
1674
1563
|
for (const line of childrenSetupLines) {
|
|
@@ -1688,56 +1577,43 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1688
1577
|
let childrenVar = null;
|
|
1689
1578
|
const childrenSetupLines = [];
|
|
1690
1579
|
|
|
1580
|
+
// Simple reactive values pass the signal directly for shared reactivity;
|
|
1581
|
+
// complex expressions use normal .value unwrapping to compute the value.
|
|
1582
|
+
const addProp = (key, value) => {
|
|
1583
|
+
const isDirectSignal = this.reactiveMembers && (
|
|
1584
|
+
(typeof value === 'string' && this.reactiveMembers.has(value)) ||
|
|
1585
|
+
(Array.isArray(value) && value[0] === '.' && value[1] === 'this' && typeof value[2] === 'string' && this.reactiveMembers.has(value[2]))
|
|
1586
|
+
);
|
|
1587
|
+
if (isDirectSignal) {
|
|
1588
|
+
const member = typeof value === 'string' ? value : value[2];
|
|
1589
|
+
props.push(`${key}: ${this._self}.${member}`);
|
|
1590
|
+
} else {
|
|
1591
|
+
const valueCode = this.generateInComponent(value, 'value');
|
|
1592
|
+
props.push(`${key}: ${valueCode}`);
|
|
1593
|
+
if (this.hasReactiveDeps(value)) {
|
|
1594
|
+
reactiveProps.push({ key, valueCode });
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
};
|
|
1598
|
+
|
|
1599
|
+
const addObjectProps = (objExpr) => {
|
|
1600
|
+
for (let i = 1; i < objExpr.length; i++) {
|
|
1601
|
+
const [key, value] = objExpr[i];
|
|
1602
|
+
if (typeof key === 'string') addProp(key, value);
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
|
|
1691
1606
|
for (const arg of args) {
|
|
1692
1607
|
if (this.is(arg, 'object')) {
|
|
1693
|
-
|
|
1694
|
-
const [key, value] = arg[i];
|
|
1695
|
-
if (typeof key === 'string') {
|
|
1696
|
-
// Simple reactive identifier — pass signal directly for shared reactivity.
|
|
1697
|
-
// Complex expressions — use normal .value unwrapping to compute the value.
|
|
1698
|
-
const isSimpleReactive = this.reactiveMembers && (
|
|
1699
|
-
(typeof value === 'string' && this.reactiveMembers.has(value)) ||
|
|
1700
|
-
(Array.isArray(value) && value[0] === '.' && value[1] === 'this' && typeof value[2] === 'string' && this.reactiveMembers.has(value[2]))
|
|
1701
|
-
);
|
|
1702
|
-
if (isSimpleReactive) {
|
|
1703
|
-
const member = typeof value === 'string' ? value : value[2];
|
|
1704
|
-
props.push(`${key}: this.${member}`);
|
|
1705
|
-
} else {
|
|
1706
|
-
const valueCode = this.generateInComponent(value, 'value');
|
|
1707
|
-
props.push(`${key}: ${valueCode}`);
|
|
1708
|
-
if (this.hasReactiveDeps(value)) {
|
|
1709
|
-
reactiveProps.push({ key, valueCode });
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1608
|
+
addObjectProps(arg);
|
|
1714
1609
|
} else if (Array.isArray(arg) && (arg[0] === '->' || arg[0] === '=>')) {
|
|
1715
1610
|
let block = arg[2];
|
|
1716
1611
|
if (block) {
|
|
1717
|
-
// Indented attributes: extract object nodes from block as props
|
|
1718
1612
|
if (this.is(block, 'block')) {
|
|
1719
1613
|
const domChildren = [];
|
|
1720
1614
|
for (const child of block.slice(1)) {
|
|
1721
1615
|
if (this.is(child, 'object')) {
|
|
1722
|
-
|
|
1723
|
-
const [key, value] = child[i];
|
|
1724
|
-
if (typeof key === 'string') {
|
|
1725
|
-
const isSimpleReactive = this.reactiveMembers && (
|
|
1726
|
-
(typeof value === 'string' && this.reactiveMembers.has(value)) ||
|
|
1727
|
-
(Array.isArray(value) && value[0] === '.' && value[1] === 'this' && typeof value[2] === 'string' && this.reactiveMembers.has(value[2]))
|
|
1728
|
-
);
|
|
1729
|
-
if (isSimpleReactive) {
|
|
1730
|
-
const member = typeof value === 'string' ? value : value[2];
|
|
1731
|
-
props.push(`${key}: this.${member}`);
|
|
1732
|
-
} else {
|
|
1733
|
-
const valueCode = this.generateInComponent(value, 'value');
|
|
1734
|
-
props.push(`${key}: ${valueCode}`);
|
|
1735
|
-
if (this.hasReactiveDeps(value)) {
|
|
1736
|
-
reactiveProps.push({ key, valueCode });
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1616
|
+
addObjectProps(child);
|
|
1741
1617
|
} else {
|
|
1742
1618
|
domChildren.push(child);
|
|
1743
1619
|
}
|
|
@@ -1900,6 +1776,134 @@ function __clsx(...args) {
|
|
|
1900
1776
|
return args.filter(Boolean).join(' ');
|
|
1901
1777
|
}
|
|
1902
1778
|
|
|
1779
|
+
function __lis(arr) {
|
|
1780
|
+
const n = arr.length;
|
|
1781
|
+
if (n === 0) return [];
|
|
1782
|
+
const tails = [], indices = [], prev = new Array(n).fill(-1);
|
|
1783
|
+
for (let i = 0; i < n; i++) {
|
|
1784
|
+
if (arr[i] === -1) continue;
|
|
1785
|
+
let lo = 0, hi = tails.length;
|
|
1786
|
+
while (lo < hi) {
|
|
1787
|
+
const mid = (lo + hi) >> 1;
|
|
1788
|
+
if (tails[mid] < arr[i]) lo = mid + 1; else hi = mid;
|
|
1789
|
+
}
|
|
1790
|
+
tails[lo] = arr[i];
|
|
1791
|
+
indices[lo] = i;
|
|
1792
|
+
if (lo > 0) prev[i] = indices[lo - 1];
|
|
1793
|
+
}
|
|
1794
|
+
const result = [];
|
|
1795
|
+
let k = indices[tails.length - 1];
|
|
1796
|
+
for (let i = tails.length - 1; i >= 0; i--) { result.push(k); k = prev[k]; }
|
|
1797
|
+
result.reverse();
|
|
1798
|
+
return result;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
function __reconcile(anchor, state, items, ctx, factory, keyFn, ...outer) {
|
|
1802
|
+
const parent = anchor.parentNode;
|
|
1803
|
+
if (!parent) return;
|
|
1804
|
+
|
|
1805
|
+
const oldKeys = state.keys;
|
|
1806
|
+
const oldBlocks = state.blocks;
|
|
1807
|
+
const oldLen = oldKeys.length;
|
|
1808
|
+
const newLen = items.length;
|
|
1809
|
+
const newBlocks = new Array(newLen);
|
|
1810
|
+
const hasKeyFn = keyFn != null;
|
|
1811
|
+
const newKeys = hasKeyFn ? items.map((item, i) => keyFn(item, i)) : items;
|
|
1812
|
+
|
|
1813
|
+
// Phase 0: first render — batch create via DocumentFragment
|
|
1814
|
+
if (oldLen === 0) {
|
|
1815
|
+
if (newLen > 0) {
|
|
1816
|
+
const frag = document.createDocumentFragment();
|
|
1817
|
+
for (let i = 0; i < newLen; i++) {
|
|
1818
|
+
const block = factory(ctx, items[i], i, ...outer);
|
|
1819
|
+
block.c();
|
|
1820
|
+
block.m(frag, null);
|
|
1821
|
+
if (!block._s) block.p(ctx, items[i], i, ...outer);
|
|
1822
|
+
newBlocks[i] = block;
|
|
1823
|
+
}
|
|
1824
|
+
parent.insertBefore(frag, anchor);
|
|
1825
|
+
}
|
|
1826
|
+
state.keys = hasKeyFn ? newKeys : items.slice();
|
|
1827
|
+
state.blocks = newBlocks;
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// Phase 1: prefix scan — skip p() (item+index identical, effects already live)
|
|
1832
|
+
let start = 0;
|
|
1833
|
+
const minLen = oldLen < newLen ? oldLen : newLen;
|
|
1834
|
+
while (start < minLen && oldKeys[start] === newKeys[start]) {
|
|
1835
|
+
newBlocks[start] = oldBlocks[start];
|
|
1836
|
+
start++;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// Phase 2: suffix scan — call p() (index may differ)
|
|
1840
|
+
let oldEnd = oldLen - 1;
|
|
1841
|
+
let newEnd = newLen - 1;
|
|
1842
|
+
while (oldEnd >= start && newEnd >= start && oldKeys[oldEnd] === newKeys[newEnd]) {
|
|
1843
|
+
const block = oldBlocks[oldEnd];
|
|
1844
|
+
if (!block._s) block.p(ctx, items[newEnd], newEnd, ...outer);
|
|
1845
|
+
newBlocks[newEnd] = block;
|
|
1846
|
+
oldEnd--;
|
|
1847
|
+
newEnd--;
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
// Remove old blocks in the middle that aren't in the new set
|
|
1851
|
+
if (start > newEnd) {
|
|
1852
|
+
for (let i = start; i <= oldEnd; i++) oldBlocks[i].d(true);
|
|
1853
|
+
} else if (start > oldEnd) {
|
|
1854
|
+
// Phase 3a: pure insertion — batch via DocumentFragment
|
|
1855
|
+
const next = newEnd + 1 < newLen ? newBlocks[newEnd + 1]._first : anchor;
|
|
1856
|
+
const frag = document.createDocumentFragment();
|
|
1857
|
+
for (let i = start; i <= newEnd; i++) {
|
|
1858
|
+
const block = factory(ctx, items[i], i, ...outer);
|
|
1859
|
+
block.c();
|
|
1860
|
+
block.m(frag, null);
|
|
1861
|
+
if (!block._s) block.p(ctx, items[i], i, ...outer);
|
|
1862
|
+
newBlocks[i] = block;
|
|
1863
|
+
}
|
|
1864
|
+
parent.insertBefore(frag, next);
|
|
1865
|
+
} else {
|
|
1866
|
+
// Phase 4: general case — temp Map + LIS
|
|
1867
|
+
const oldKeyIdx = new Map();
|
|
1868
|
+
for (let i = start; i <= oldEnd; i++) oldKeyIdx.set(oldKeys[i], i);
|
|
1869
|
+
|
|
1870
|
+
const seq = new Array(newEnd - start + 1);
|
|
1871
|
+
for (let i = start; i <= newEnd; i++) {
|
|
1872
|
+
const key = newKeys[i];
|
|
1873
|
+
const oldIdx = oldKeyIdx.get(key);
|
|
1874
|
+
if (oldIdx !== undefined) {
|
|
1875
|
+
seq[i - start] = oldIdx - start;
|
|
1876
|
+
const block = oldBlocks[oldIdx];
|
|
1877
|
+
if (!block._s) block.p(ctx, items[i], i, ...outer);
|
|
1878
|
+
newBlocks[i] = block;
|
|
1879
|
+
oldKeyIdx.delete(key);
|
|
1880
|
+
} else {
|
|
1881
|
+
seq[i - start] = -1;
|
|
1882
|
+
const block = factory(ctx, items[i], i, ...outer);
|
|
1883
|
+
block.c();
|
|
1884
|
+
if (!block._s) block.p(ctx, items[i], i, ...outer);
|
|
1885
|
+
newBlocks[i] = block;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
for (const idx of oldKeyIdx.values()) oldBlocks[idx].d(true);
|
|
1890
|
+
|
|
1891
|
+
const lis = __lis(seq);
|
|
1892
|
+
const lisSet = new Set(lis);
|
|
1893
|
+
let next = newEnd + 1 < newLen ? newBlocks[newEnd + 1]._first : anchor;
|
|
1894
|
+
for (let i = newEnd; i >= start; i--) {
|
|
1895
|
+
const block = newBlocks[i];
|
|
1896
|
+
if (!lisSet.has(i - start)) {
|
|
1897
|
+
block.m(parent, next);
|
|
1898
|
+
}
|
|
1899
|
+
next = block._first;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
state.keys = hasKeyFn ? newKeys : items.slice();
|
|
1904
|
+
state.blocks = newBlocks;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1903
1907
|
class __Component {
|
|
1904
1908
|
constructor(props = {}) {
|
|
1905
1909
|
Object.assign(this, props);
|
|
@@ -1935,7 +1939,7 @@ class __Component {
|
|
|
1935
1939
|
|
|
1936
1940
|
// Register on globalThis for runtime deduplication
|
|
1937
1941
|
if (typeof globalThis !== 'undefined') {
|
|
1938
|
-
globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component };
|
|
1942
|
+
globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component };
|
|
1939
1943
|
}
|
|
1940
1944
|
|
|
1941
1945
|
`;
|