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/src/compiler.js CHANGED
@@ -201,9 +201,7 @@ export class CodeGenerator {
201
201
 
202
202
  // Control flow — simple
203
203
  'break': 'generateBreak',
204
- 'break-if': 'generateBreakIf',
205
204
  'continue': 'generateContinue',
206
- 'continue-if': 'generateContinueIf',
207
205
  '?': 'generateExistential',
208
206
  '?:': 'generateTernary',
209
207
  '|>': 'generatePipe',
@@ -276,6 +274,7 @@ export class CodeGenerator {
276
274
  this.programVars = new Set();
277
275
  this.functionVars = new Map();
278
276
  this.helpers = new Set();
277
+ this.scopeStack = []; // Track enclosing function scopes for proper variable hoisting
279
278
  this.collectProgramVariables(sexpr);
280
279
  let code = this.generate(sexpr);
281
280
 
@@ -310,14 +309,13 @@ export class CodeGenerator {
310
309
  collect(sexpr);
311
310
 
312
311
  // Match output lines to collected statement locations in parallel.
313
- // Skip generated lines that have no source correspondence.
312
+ // Skip lines with no source correspondence (preamble, braces, etc.).
314
313
  let lines = code.split('\n');
315
314
  let locIdx = 0;
316
315
  for (let outLine = 0; outLine < lines.length; outLine++) {
317
316
  let line = lines[outLine];
318
317
  let trimmed = line.trim();
319
318
 
320
- // Skip lines with no source correspondence
321
319
  if (!trimmed || trimmed === '}' || trimmed === '});') continue;
322
320
  if (trimmed.startsWith('let ') || trimmed.startsWith('var ')) continue;
323
321
  if (trimmed.startsWith('const slice') || trimmed.startsWith('const modulo') || trimmed.startsWith('const toSearchable')) continue;
@@ -358,6 +356,7 @@ export class CodeGenerator {
358
356
  }
359
357
 
360
358
  if (head === 'readonly') return;
359
+ if (head === 'component') return; // Component body has its own scope
361
360
 
362
361
  if (CodeGenerator.ASSIGNMENT_OPS.has(head)) {
363
362
  let [target, value] = rest;
@@ -715,7 +714,7 @@ export class CodeGenerator {
715
714
 
716
715
  if (this.usesTemplates && !skip) {
717
716
  if (typeof globalThis !== 'undefined' && globalThis.__ripComponent) {
718
- code += 'const { isSignal, __pushComponent, __popComponent, setContext, getContext, hasContext, __cx__ } = globalThis.__ripComponent;\n';
717
+ code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component } = globalThis.__ripComponent;\n';
719
718
  } else {
720
719
  code += this.getComponentRuntime();
721
720
  }
@@ -928,11 +927,17 @@ export class CodeGenerator {
928
927
 
929
928
  generatePropertyAccess(head, rest, context, sexpr) {
930
929
  let [obj, prop] = rest;
930
+ // In subclass constructors, rewrite @param refs (this.x) to _x for super() safety
931
+ if (this._atParamMap && obj === 'this') {
932
+ let mapped = this._atParamMap.get(str(prop));
933
+ if (mapped) return mapped;
934
+ }
931
935
  this.suppressReactiveUnwrap = true;
932
936
  let objCode = this.generate(obj, 'value');
933
937
  this.suppressReactiveUnwrap = false;
934
938
  let needsParens = CodeGenerator.NUMBER_LITERAL_RE.test(objCode) ||
935
- ((this.is(obj, 'object') || this.is(obj, 'await') || this.is(obj, 'yield')));
939
+ objCode.startsWith('await ') ||
940
+ ((this.is(obj, 'object') || this.is(obj, 'yield')));
936
941
  let base = needsParens ? `(${objCode})` : objCode;
937
942
  if (meta(prop, 'await') === true) return `await ${base}.${str(prop)}()`;
938
943
  if (meta(prop, 'predicate')) return `(${base}.${str(prop)} != null)`;
@@ -1133,9 +1138,7 @@ export class CodeGenerator {
1133
1138
  // ---------------------------------------------------------------------------
1134
1139
 
1135
1140
  generateBreak() { return 'break'; }
1136
- generateBreakIf(head, rest) { return `if (${this.generate(rest[0], 'value')}) break`; }
1137
1141
  generateContinue() { return 'continue'; }
1138
- generateContinueIf(head, rest) { return `if (${this.generate(rest[0], 'value')}) continue`; }
1139
1142
 
1140
1143
  generateExistential(head, rest) {
1141
1144
  return `(${this.generate(rest[0], 'value')} != null)`;
@@ -1573,7 +1576,11 @@ export class CodeGenerator {
1573
1576
  let keyCode;
1574
1577
  if (this.is(key, 'dynamicKey')) keyCode = `[${this.generate(key[1], 'value')}]`;
1575
1578
  else if (this.is(key, 'str')) keyCode = `[${this.generate(key, 'value')}]`;
1576
- else keyCode = this.generate(key, 'value');
1579
+ else {
1580
+ this.suppressReactiveUnwrap = true;
1581
+ keyCode = this.generate(key, 'value');
1582
+ this.suppressReactiveUnwrap = false;
1583
+ }
1577
1584
  let valCode = this.generate(value, 'value');
1578
1585
  if (operator === '=') return `${keyCode} = ${valCode}`;
1579
1586
  if (operator === ':') return `${keyCode}: ${valCode}`;
@@ -1728,6 +1735,65 @@ export class CodeGenerator {
1728
1735
  // Comprehensions
1729
1736
  // ---------------------------------------------------------------------------
1730
1737
 
1738
+ // Shared: parse a for-in iterator and return { header, setup }.
1739
+ // header: the for(...) clause (no trailing brace)
1740
+ // setup: any `const x = arr[i]` preamble line, or null
1741
+ _forInHeader(vars, iterable, step) {
1742
+ let va = Array.isArray(vars) ? vars : [vars];
1743
+ let noVar = va.length === 0;
1744
+ let [itemVar, indexVar] = noVar ? ['_i', null] : va;
1745
+ let ivp = (this.is(itemVar, 'array') || this.is(itemVar, 'object'))
1746
+ ? this.generateDestructuringPattern(itemVar) : itemVar;
1747
+
1748
+ if (step && step !== null) {
1749
+ let ih = Array.isArray(iterable) && iterable[0];
1750
+ if (ih instanceof String) ih = str(ih);
1751
+ let isRange = ih === '..' || ih === '...';
1752
+ if (isRange) {
1753
+ let isExcl = ih === '...';
1754
+ let [s, e] = iterable.slice(1);
1755
+ let sc = this.generate(s, 'value'), ec = this.generate(e, 'value'), stc = this.generate(step, 'value');
1756
+ return { header: `for (let ${ivp} = ${sc}; ${ivp} ${isExcl ? '<' : '<='} ${ec}; ${ivp} += ${stc})`, setup: null };
1757
+ }
1758
+ let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
1759
+ let isNeg = this.is(step, '-', 1);
1760
+ let isMinus1 = isNeg && (step[1] === '1' || step[1] === 1 || str(step[1]) === '1');
1761
+ let isPlus1 = !isNeg && (step === '1' || step === 1 || str(step) === '1');
1762
+ let update = isMinus1 ? `${idxN}--` : isPlus1 ? `${idxN}++` : `${idxN} += ${stc}`;
1763
+ let header = isNeg
1764
+ ? `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${update})`
1765
+ : `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${update})`;
1766
+ return { header, setup: noVar ? null : `const ${ivp} = ${ic}[${idxN}];` };
1767
+ }
1768
+ if (indexVar) {
1769
+ let ic = this.generate(iterable, 'value');
1770
+ return {
1771
+ header: `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++)`,
1772
+ setup: `const ${ivp} = ${ic}[${indexVar}];`,
1773
+ };
1774
+ }
1775
+ return { header: `for (const ${ivp} of ${this.generate(iterable, 'value')})`, setup: null };
1776
+ }
1777
+
1778
+ // Shared: parse a for-of (object) iterator and return { header, own, vv, oc, kvp }.
1779
+ _forOfHeader(vars, iterable, own) {
1780
+ let va = Array.isArray(vars) ? vars : [vars];
1781
+ let [kv, vv] = va;
1782
+ let kvp = (this.is(kv, 'array') || this.is(kv, 'object'))
1783
+ ? this.generateDestructuringPattern(kv) : kv;
1784
+ let oc = this.generate(iterable, 'value');
1785
+ return { header: `for (const ${kvp} in ${oc})`, own, vv, oc, kvp };
1786
+ }
1787
+
1788
+ // Shared: parse a for-as (iterator) spec and return { header }.
1789
+ _forAsHeader(vars, iterable, isAwait) {
1790
+ let va = Array.isArray(vars) ? vars : [vars];
1791
+ let [fv] = va;
1792
+ let ivp = (this.is(fv, 'array') || this.is(fv, 'object'))
1793
+ ? this.generateDestructuringPattern(fv) : fv;
1794
+ return { header: `for ${isAwait ? 'await ' : ''}(const ${ivp} of ${this.generate(iterable, 'value')})` };
1795
+ }
1796
+
1731
1797
  generateComprehension(head, rest, context) {
1732
1798
  let [expr, iterators, guards] = rest;
1733
1799
  if (context === 'statement') return this.generateComprehensionAsLoop(expr, iterators, guards);
@@ -1742,59 +1808,19 @@ export class CodeGenerator {
1742
1808
  for (let iter of iterators) {
1743
1809
  let [iterType, vars, iterable, stepOrOwn] = iter;
1744
1810
  if (iterType === 'for-in') {
1745
- let step = stepOrOwn;
1746
- let va = Array.isArray(vars) ? vars : [vars];
1747
- let noVar = va.length === 0;
1748
- let [itemVar, indexVar] = noVar ? ['_i', null] : va;
1749
- let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
1750
- ? this.generateDestructuringPattern(itemVar) : itemVar;
1751
-
1752
- if (step && step !== null) {
1753
- let ih = Array.isArray(iterable) && iterable[0];
1754
- if (ih instanceof String) ih = str(ih);
1755
- let isRange = ih === '..' || ih === '...';
1756
- if (isRange) {
1757
- let isExcl = ih === '...';
1758
- let [s, e] = iterable.slice(1);
1759
- let sc = this.generate(s, 'value'), ec = this.generate(e, 'value'), stc = this.generate(step, 'value');
1760
- code += this.indent() + `for (let ${ivp} = ${sc}; ${ivp} ${isExcl ? '<' : '<='} ${ec}; ${ivp} += ${stc}) {\n`;
1761
- this.indentLevel++;
1762
- } else {
1763
- let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
1764
- let isNeg = this.is(step, '-', 1);
1765
- code += isNeg
1766
- ? this.indent() + `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) {\n`
1767
- : this.indent() + `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) {\n`;
1768
- this.indentLevel++;
1769
- if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
1770
- }
1771
- } else if (indexVar) {
1772
- let ic = this.generate(iterable, 'value');
1773
- code += this.indent() + `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++) {\n`;
1774
- this.indentLevel++;
1775
- code += this.indent() + `const ${ivp} = ${ic}[${indexVar}];\n`;
1776
- } else {
1777
- code += this.indent() + `for (const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
1778
- this.indentLevel++;
1779
- }
1811
+ let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
1812
+ code += this.indent() + header + ' {\n';
1813
+ this.indentLevel++;
1814
+ if (setup) code += this.indent() + setup + '\n';
1780
1815
  } else if (iterType === 'for-of') {
1781
- let own = stepOrOwn;
1782
- let va = Array.isArray(vars) ? vars : [vars];
1783
- let [kv, vv] = va;
1784
- let kvp = ((this.is(kv, 'array') || this.is(kv, 'object')))
1785
- ? this.generateDestructuringPattern(kv) : kv;
1786
- let oc = this.generate(iterable, 'value');
1787
- code += this.indent() + `for (const ${kvp} in ${oc}) {\n`;
1816
+ let { header, own, vv, oc, kvp } = this._forOfHeader(vars, iterable, stepOrOwn);
1817
+ code += this.indent() + header + ' {\n';
1788
1818
  this.indentLevel++;
1789
1819
  if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
1790
1820
  if (vv) code += this.indent() + `const ${vv} = ${oc}[${kvp}];\n`;
1791
1821
  } else if (iterType === 'for-as') {
1792
- let isAwait = iter[3];
1793
- let va = Array.isArray(vars) ? vars : [vars];
1794
- let [fv] = va;
1795
- let ivp = ((this.is(fv, 'array') || this.is(fv, 'object')))
1796
- ? this.generateDestructuringPattern(fv) : fv;
1797
- code += this.indent() + `for ${isAwait ? 'await ' : ''}(const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
1822
+ let { header } = this._forAsHeader(vars, iterable, iter[3]);
1823
+ code += this.indent() + header + ' {\n';
1798
1824
  this.indentLevel++;
1799
1825
  }
1800
1826
  }
@@ -1807,7 +1833,7 @@ export class CodeGenerator {
1807
1833
  let hasCtrl = (node) => {
1808
1834
  if (typeof node === 'string' && (node === 'break' || node === 'continue')) return true;
1809
1835
  if (!Array.isArray(node)) return false;
1810
- if (['break', 'continue', 'break-if', 'continue-if', 'return', 'throw'].includes(node[0])) return true;
1836
+ if (['break', 'continue', 'return', 'throw'].includes(node[0])) return true;
1811
1837
  if (node[0] === 'if' || node[0] === 'unless') return node.slice(1).some(hasCtrl);
1812
1838
  return node.some(hasCtrl);
1813
1839
  };
@@ -1908,17 +1934,36 @@ export class CodeGenerator {
1908
1934
  let hasAwait = this.containsAwait(body), hasYield = this.containsYield(body);
1909
1935
  let cleanParams = params, autoAssign = [];
1910
1936
  if (mName === 'constructor') {
1937
+ let isSubclass = !!parentClass;
1938
+ let atParamMap = isSubclass ? new Map() : null;
1911
1939
  cleanParams = params.map(p => {
1912
- if (this.is(p, '.') && p[1] === 'this') { autoAssign.push(`this.${p[2]} = ${p[2]}`); return p[2]; }
1940
+ // Handle @param: ['.', 'this', 'name']
1941
+ if (this.is(p, '.') && p[1] === 'this') {
1942
+ let name = p[2];
1943
+ let param = isSubclass ? `_${name}` : name;
1944
+ autoAssign.push(`this.${name} = ${param}`);
1945
+ if (isSubclass) atParamMap.set(name, param);
1946
+ return param;
1947
+ }
1948
+ // Handle @param with default: ['default', ['.', 'this', 'name'], value]
1949
+ if (this.is(p, 'default') && this.is(p[1], '.') && p[1][1] === 'this') {
1950
+ let name = p[1][2];
1951
+ let param = isSubclass ? `_${name}` : name;
1952
+ autoAssign.push(`this.${name} = ${param}`);
1953
+ if (isSubclass) atParamMap.set(name, param);
1954
+ return ['default', param, p[2]];
1955
+ }
1913
1956
  return p;
1914
1957
  });
1915
1958
  for (let bm of boundMethods) autoAssign.unshift(`this.${bm} = this.${bm}.bind(this)`);
1959
+ if (atParamMap?.size > 0) this._atParamMap = atParamMap;
1916
1960
  }
1917
1961
  let pList = this.generateParamList(cleanParams);
1918
1962
  let prefix = (isStatic ? 'static ' : '') + (hasAwait ? 'async ' : '') + (hasYield ? '*' : '');
1919
1963
  code += this.indent() + `${prefix}${mName}(${pList}) `;
1920
1964
  if (!isComputed) this.currentMethodName = mName;
1921
1965
  code += this.generateMethodBody(body, autoAssign, mName === 'constructor', cleanParams);
1966
+ this._atParamMap = null;
1922
1967
  this.currentMethodName = null;
1923
1968
  code += '\n';
1924
1969
  } else if (isStatic) {
@@ -2186,10 +2231,16 @@ export class CodeGenerator {
2186
2231
  if (Array.isArray(params)) params.forEach(extractPN);
2187
2232
 
2188
2233
  let bodyVars = this.collectFunctionVariables(body);
2189
- let newVars = new Set([...bodyVars].filter(v => !this.programVars.has(v) && !this.reactiveVars?.has(v) && !paramNames.has(v)));
2234
+ let newVars = new Set([...bodyVars].filter(v =>
2235
+ !this.programVars.has(v) && !this.reactiveVars?.has(v) && !paramNames.has(v) &&
2236
+ !this.scopeStack.some(s => s.has(v)) // don't re-declare variables from enclosing scopes
2237
+ ));
2190
2238
  let noRetStmts = ['return', 'throw', 'break', 'continue'];
2191
2239
  let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'until', 'loop'];
2192
2240
 
2241
+ // Track this function's scope so nested functions don't re-declare its variables
2242
+ this.scopeStack.push(new Set([...newVars, ...paramNames]));
2243
+
2193
2244
  if (this.is(body, 'block')) {
2194
2245
  let statements = this.unwrapBlock(body);
2195
2246
 
@@ -2284,16 +2335,26 @@ export class CodeGenerator {
2284
2335
 
2285
2336
  this.indentLevel--;
2286
2337
  code += this.indent() + '}';
2338
+ this.scopeStack.pop();
2287
2339
  this.sideEffectOnly = prevSEO;
2288
2340
  return code;
2289
2341
  }
2290
2342
 
2291
2343
  // Single expression
2292
2344
  this.sideEffectOnly = prevSEO;
2293
- if (isConstructor || this.hasExplicitControlFlow(body)) return `{ ${this.generate(body, 'statement')}; }`;
2294
- if (Array.isArray(body) && (noRetStmts.includes(body[0]) || loopStmts.includes(body[0]))) return `{ ${this.generate(body, 'statement')}; }`;
2295
- if (sideEffectOnly) return `{ ${this.generate(body, 'statement')}; return; }`;
2296
- return `{ return ${this.generate(body, 'value')}; }`;
2345
+ let result;
2346
+ if (isConstructor && autoAssignments.length > 0) {
2347
+ // Constructor with @params as a single expression — need to emit autoAssignments
2348
+ let isSuper = Array.isArray(body) && body[0] === 'super';
2349
+ let bodyCode = this.generate(body, 'statement');
2350
+ let assigns = autoAssignments.map(a => `${a};`).join(' ');
2351
+ result = isSuper ? `{ ${bodyCode}; ${assigns} }` : `{ ${assigns} ${bodyCode}; }`;
2352
+ } else if (isConstructor || this.hasExplicitControlFlow(body)) result = `{ ${this.generate(body, 'statement')}; }`;
2353
+ else if (Array.isArray(body) && (noRetStmts.includes(body[0]) || loopStmts.includes(body[0]))) result = `{ ${this.generate(body, 'statement')}; }`;
2354
+ else if (sideEffectOnly) result = `{ ${this.generate(body, 'statement')}; return; }`;
2355
+ else result = `{ return ${this.generate(body, 'value')}; }`;
2356
+ this.scopeStack.pop();
2357
+ return result;
2297
2358
  }
2298
2359
 
2299
2360
  generateFunctionBody(body, params = [], sideEffectOnly = false) {
@@ -2368,34 +2429,10 @@ export class CodeGenerator {
2368
2429
  if (iterators.length === 1) {
2369
2430
  let [iterType, vars, iterable, stepOrOwn] = iterators[0];
2370
2431
  if (iterType === 'for-in') {
2371
- let step = stepOrOwn;
2372
- let va = Array.isArray(vars) ? vars : [vars];
2373
- let noVar = va.length === 0;
2374
- let [itemVar, indexVar] = noVar ? ['_i', null] : va;
2375
- let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
2376
- ? this.generateDestructuringPattern(itemVar) : itemVar;
2377
-
2378
- if (step && step !== null) {
2379
- let ih = Array.isArray(iterable) && iterable[0];
2380
- if (ih instanceof String) ih = str(ih);
2381
- let isRange = ih === '..' || ih === '...';
2382
- if (isRange) {
2383
- let isExcl = ih === '...';
2384
- let [s, e] = iterable.slice(1);
2385
- code += this.indent() + `for (let ${ivp} = ${this.generate(s, 'value')}; ${ivp} ${isExcl ? '<' : '<='} ${this.generate(e, 'value')}; ${ivp} += ${this.generate(step, 'value')}) {\n`;
2386
- } else {
2387
- let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
2388
- let isNeg = this.is(step, '-', 1);
2389
- code += isNeg
2390
- ? this.indent() + `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) {\n`
2391
- : this.indent() + `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) {\n`;
2392
- this.indentLevel++;
2393
- if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
2394
- }
2395
- } else {
2396
- code += this.indent() + `for (const ${ivp} of ${this.generate(iterable, 'value')}) {\n`;
2397
- }
2432
+ let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
2433
+ code += this.indent() + header + ' {\n';
2398
2434
  this.indentLevel++;
2435
+ if (setup) code += this.indent() + setup + '\n';
2399
2436
  if (guards && guards.length > 0) {
2400
2437
  code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
2401
2438
  this.indentLevel++;
@@ -2412,155 +2449,51 @@ export class CodeGenerator {
2412
2449
 
2413
2450
  generateComprehensionAsLoop(expr, iterators, guards) {
2414
2451
  let code = '';
2452
+ let guardCond = guards?.length ? guards.map(g => this.generate(g, 'value')).join(' && ') : null;
2453
+
2454
+ // Helper: emit the loop body with optional guard wrapping
2455
+ let emitBody = () => {
2456
+ if (guardCond) {
2457
+ code += this.indent() + `if (${guardCond}) {\n`;
2458
+ this.indentLevel++;
2459
+ code += this.indent() + this.generate(expr, 'statement') + ';\n';
2460
+ this.indentLevel--; code += this.indent() + '}\n';
2461
+ } else {
2462
+ code += this.indent() + this.generate(expr, 'statement') + ';\n';
2463
+ }
2464
+ };
2465
+
2415
2466
  if (iterators.length === 1) {
2416
2467
  let [iterType, vars, iterable, stepOrOwn] = iterators[0];
2417
2468
 
2418
2469
  if (iterType === 'for-in') {
2419
- let step = stepOrOwn;
2420
- let va = Array.isArray(vars) ? vars : [vars];
2421
- let noVar = va.length === 0;
2422
- let [itemVar, indexVar] = noVar ? ['_i', null] : va;
2423
- let ivp = ((this.is(itemVar, 'array') || this.is(itemVar, 'object')))
2424
- ? this.generateDestructuringPattern(itemVar) : itemVar;
2425
-
2426
- if (step && step !== null) {
2427
- let ih = Array.isArray(iterable) && iterable[0];
2428
- if (ih instanceof String) ih = str(ih);
2429
- let isRange = ih === '..' || ih === '...';
2430
- if (isRange) {
2431
- let isExcl = ih === '...';
2432
- let [s, e] = iterable.slice(1);
2433
- code += `for (let ${ivp} = ${this.generate(s, 'value')}; ${ivp} ${isExcl ? '<' : '<='} ${this.generate(e, 'value')}; ${ivp} += ${this.generate(step, 'value')}) `;
2434
- } else {
2435
- let ic = this.generate(iterable, 'value'), idxN = indexVar || '_i', stc = this.generate(step, 'value');
2436
- let isNeg = this.is(step, '-', 1);
2437
- let isMinus1 = isNeg && (step[1] === '1' || step[1] === 1 || str(step[1]) === '1');
2438
- let isPlus1 = !isNeg && (step === '1' || step === 1 || str(step) === '1');
2439
- if (isMinus1) code += `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN}--) `;
2440
- else if (isPlus1) code += `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN}++) `;
2441
- else if (isNeg) code += `for (let ${idxN} = ${ic}.length - 1; ${idxN} >= 0; ${idxN} += ${stc}) `;
2442
- else code += `for (let ${idxN} = 0; ${idxN} < ${ic}.length; ${idxN} += ${stc}) `;
2443
- code += '{\n';
2444
- this.indentLevel++;
2445
- if (!noVar) code += this.indent() + `const ${ivp} = ${ic}[${idxN}];\n`;
2446
- }
2447
- if (guards?.length) {
2448
- if (!isRange) code += this.indent();
2449
- code += '{\n'; this.indentLevel++;
2450
- code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
2451
- this.indentLevel++;
2452
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2453
- this.indentLevel--; code += this.indent() + '}\n';
2454
- this.indentLevel--; code += this.indent() + '}';
2455
- } else {
2456
- if (!isRange) code += this.indent();
2457
- code += '{\n'; this.indentLevel++;
2458
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2459
- this.indentLevel--; code += this.indent() + '}';
2460
- }
2461
- if (!isRange) { this.indentLevel--; code += '\n' + this.indent() + '}'; }
2462
- return code;
2463
- }
2464
-
2465
- if (indexVar) {
2466
- let ic = this.generate(iterable, 'value');
2467
- code += `for (let ${indexVar} = 0; ${indexVar} < ${ic}.length; ${indexVar}++) `;
2468
- code += '{\n'; this.indentLevel++;
2469
- code += this.indent() + `const ${ivp} = ${ic}[${indexVar}];\n`;
2470
- } else {
2471
- code += `for (const ${ivp} of ${this.generate(iterable, 'value')}) `;
2472
- if (guards?.length) {
2473
- code += '{\n'; this.indentLevel++;
2474
- code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
2475
- this.indentLevel++;
2476
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2477
- this.indentLevel--; code += this.indent() + '}\n';
2478
- this.indentLevel--; code += this.indent() + '}';
2479
- } else {
2480
- code += '{\n'; this.indentLevel++;
2481
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2482
- this.indentLevel--; code += this.indent() + '}';
2483
- }
2484
- return code;
2485
- }
2486
-
2487
- // Fall through for indexVar case
2488
- if (guards?.length) {
2489
- code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
2490
- this.indentLevel++;
2491
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2492
- this.indentLevel--; code += this.indent() + '}\n';
2493
- } else {
2494
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2495
- }
2470
+ let { header, setup } = this._forInHeader(vars, iterable, stepOrOwn);
2471
+ code += header + ' {\n';
2472
+ this.indentLevel++;
2473
+ if (setup) code += this.indent() + setup + '\n';
2474
+ emitBody();
2496
2475
  this.indentLevel--;
2497
2476
  code += this.indent() + '}';
2498
2477
  return code;
2499
2478
  }
2500
2479
 
2501
2480
  if (iterType === 'for-as') {
2502
- let va = Array.isArray(vars) ? vars : [vars];
2503
- let [fv] = va;
2504
- let ivp = ((this.is(fv, 'array') || this.is(fv, 'object')))
2505
- ? this.generateDestructuringPattern(fv) : fv;
2506
- code += `for (const ${ivp} of ${this.generate(iterable, 'value')}) `;
2507
- if (guards?.length) {
2508
- code += '{\n'; this.indentLevel++;
2509
- code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
2510
- this.indentLevel++;
2511
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2512
- this.indentLevel--; code += this.indent() + '}\n';
2513
- this.indentLevel--; code += this.indent() + '}';
2514
- } else {
2515
- code += '{\n'; this.indentLevel++;
2516
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2517
- this.indentLevel--; code += this.indent() + '}';
2518
- }
2481
+ let { header } = this._forAsHeader(vars, iterable, stepOrOwn);
2482
+ code += header + ' {\n';
2483
+ this.indentLevel++;
2484
+ emitBody();
2485
+ this.indentLevel--;
2486
+ code += this.indent() + '}';
2519
2487
  return code;
2520
2488
  }
2521
2489
 
2522
2490
  if (iterType === 'for-of') {
2523
- let va = Array.isArray(vars) ? vars : [vars];
2524
- let [kv, vv] = va;
2525
- let own = stepOrOwn;
2526
- let oc = this.generate(iterable, 'value');
2527
- code += `for (const ${kv} in ${oc}) {\n`;
2491
+ let { header, own, vv, oc, kvp } = this._forOfHeader(vars, iterable, stepOrOwn);
2492
+ code += header + ' {\n';
2528
2493
  this.indentLevel++;
2529
- if (own && !vv && !guards?.length) {
2530
- code += this.indent() + `if (!Object.hasOwn(${oc}, ${kv})) continue;\n`;
2531
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2532
- } else if (own && vv && guards?.length) {
2533
- code += this.indent() + `if (Object.hasOwn(${oc}, ${kv})) {\n`;
2534
- this.indentLevel++;
2535
- code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
2536
- code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
2537
- this.indentLevel++;
2538
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2539
- this.indentLevel--; code += this.indent() + '}\n';
2540
- this.indentLevel--; code += this.indent() + '}\n';
2541
- } else if (own && vv) {
2542
- code += this.indent() + `if (Object.hasOwn(${oc}, ${kv})) {\n`;
2543
- this.indentLevel++;
2544
- code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
2545
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2546
- this.indentLevel--; code += this.indent() + '}\n';
2547
- } else if (vv && guards?.length) {
2548
- code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
2549
- code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
2550
- this.indentLevel++;
2551
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2552
- this.indentLevel--; code += this.indent() + '}\n';
2553
- } else if (vv) {
2554
- code += this.indent() + `const ${vv} = ${oc}[${kv}];\n`;
2555
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2556
- } else if (guards?.length) {
2557
- code += this.indent() + `if (${guards.map(g => this.generate(g, 'value')).join(' && ')}) {\n`;
2558
- this.indentLevel++;
2559
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2560
- this.indentLevel--; code += this.indent() + '}\n';
2561
- } else {
2562
- code += this.indent() + this.generate(expr, 'statement') + ';\n';
2563
- }
2494
+ if (own) code += this.indent() + `if (!Object.hasOwn(${oc}, ${kvp})) continue;\n`;
2495
+ if (vv) code += this.indent() + `const ${vv} = ${oc}[${kvp}];\n`;
2496
+ emitBody();
2564
2497
  this.indentLevel--;
2565
2498
  code += this.indent() + '}';
2566
2499
  return code;
@@ -3043,6 +2976,7 @@ const __primitiveCoercion = {
3043
2976
  };
3044
2977
 
3045
2978
  function __state(initialValue) {
2979
+ if (initialValue != null && typeof initialValue === 'object' && typeof initialValue.read === 'function') return initialValue;
3046
2980
  let value = initialValue;
3047
2981
  const subscribers = new Set();
3048
2982
  let notifying = false;
@@ -3142,14 +3076,19 @@ function __effect(fn) {
3142
3076
  dependencies: new Set(),
3143
3077
 
3144
3078
  run() {
3079
+ if (effect._cleanup) { effect._cleanup(); effect._cleanup = null; }
3145
3080
  for (const dep of effect.dependencies) dep.delete(effect);
3146
3081
  effect.dependencies.clear();
3147
3082
  const prev = __currentEffect;
3148
3083
  __currentEffect = effect;
3149
- try { fn(); } finally { __currentEffect = prev; }
3084
+ try {
3085
+ const result = fn();
3086
+ if (typeof result === 'function') effect._cleanup = result;
3087
+ } finally { __currentEffect = prev; }
3150
3088
  },
3151
3089
 
3152
3090
  dispose() {
3091
+ if (effect._cleanup) { effect._cleanup(); effect._cleanup = null; }
3153
3092
  for (const dep of effect.dependencies) dep.delete(effect);
3154
3093
  effect.dependencies.clear();
3155
3094
  }