rip-lang 3.13.24 → 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 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.24-blue.svg" alt="Version"></a>
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%2C265%2F1%2C265-brightgreen.svg" alt="Tests"></a>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.24",
3
+ "version": "3.13.25",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
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
@@ -1386,7 +1386,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1386
1386
  // emitBlockFactory — shared factory generation for conditionals and loops
1387
1387
  // --------------------------------------------------------------------------
1388
1388
 
1389
- proto.emitBlockFactory = function(blockName, params, rootVar, createLines, setupLines, factoryVars) {
1389
+ proto.emitBlockFactory = function(blockName, params, rootVar, createLines, setupLines, factoryVars, isStatic) {
1390
1390
  const factoryLines = [];
1391
1391
  factoryLines.push(`function ${blockName}(${params}) {`);
1392
1392
 
@@ -1401,13 +1401,20 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1401
1401
 
1402
1402
  factoryLines.push(` return {`);
1403
1403
 
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
+
1404
1411
  factoryLines.push(` c() {`);
1405
1412
  for (const line of createLines) {
1406
1413
  factoryLines.push(` ${line}`);
1407
1414
  }
1415
+ factoryLines.push(` this._first = ${firstNode};`);
1408
1416
  factoryLines.push(` },`);
1409
1417
 
1410
- const fragChildren = this._fragChildren.get(rootVar);
1411
1418
  factoryLines.push(` m(target, anchor) {`);
1412
1419
  if (fragChildren) {
1413
1420
  for (const child of fragChildren) {
@@ -1504,41 +1511,26 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1504
1511
 
1505
1512
  [this._createLines, this._setupLines, this._factoryMode, this._factoryVars] = saved;
1506
1513
 
1514
+ const isStatic = itemSetupLines.length === 0;
1507
1515
  const loopParams = `ctx, ${itemVar}, ${indexVar}${outerExtra}`;
1508
- this.emitBlockFactory(blockName, loopParams, itemNode, itemCreateLines, itemSetupLines, itemFactoryVars);
1516
+ this.emitBlockFactory(blockName, loopParams, itemNode, itemCreateLines, itemSetupLines, itemFactoryVars, isStatic);
1517
+
1518
+ // Build key function argument (null = use item as key)
1519
+ const hasCustomKey = keyExpr !== itemVar;
1520
+ const keyFnCode = hasCustomKey ? `(${itemVar}, ${indexVar}) => ${keyExpr}` : 'null';
1521
+
1522
+ // Build outer vars argument list for nested loops
1523
+ const outerArgs = outerParams ? `, ${outerParams}` : '';
1509
1524
 
1510
1525
  // Generate reconciliation code in _setup()
1511
1526
  const setupLines = [];
1512
1527
  setupLines.push(`// Loop: ${blockName}`);
1513
1528
  setupLines.push(`{`);
1514
- setupLines.push(` const __anchor = ${anchorVar};`);
1515
- setupLines.push(` const __map = new Map();`);
1529
+ setupLines.push(` const __s = { blocks: [], keys: [] };`);
1516
1530
  const effOpen = this._factoryMode ? 'disposers.push(__effect(() => {' : '__effect(() => {';
1517
1531
  const effClose = this._factoryMode ? '}));' : '});';
1518
1532
  setupLines.push(` ${effOpen}`);
1519
- setupLines.push(` const __items = ${collectionCode};`);
1520
- setupLines.push(` const __parent = __anchor.parentNode;`);
1521
- setupLines.push(` const __newMap = new Map();`);
1522
- setupLines.push(``);
1523
- setupLines.push(` for (let ${indexVar} = 0; ${indexVar} < __items.length; ${indexVar}++) {`);
1524
- setupLines.push(` const ${itemVar} = __items[${indexVar}];`);
1525
- setupLines.push(` const __key = ${keyExpr};`);
1526
- setupLines.push(` let __block = __map.get(__key);`);
1527
- setupLines.push(` if (!__block) {`);
1528
- setupLines.push(` __block = ${blockName}(${this._self}, ${itemVar}, ${indexVar}${outerExtra});`);
1529
- setupLines.push(` __block.c();`);
1530
- setupLines.push(` }`);
1531
- setupLines.push(` __block.m(__parent, __anchor);`);
1532
- setupLines.push(` __block.p(${this._self}, ${itemVar}, ${indexVar}${outerExtra});`);
1533
- setupLines.push(` __newMap.set(__key, __block);`);
1534
- setupLines.push(` }`);
1535
- setupLines.push(``);
1536
- setupLines.push(` for (const [__k, __b] of __map) {`);
1537
- setupLines.push(` if (!__newMap.has(__k)) __b.d(true);`);
1538
- setupLines.push(` }`);
1539
- setupLines.push(``);
1540
- setupLines.push(` __map.clear();`);
1541
- setupLines.push(` for (const [__k, __v] of __newMap) __map.set(__k, __v);`);
1533
+ setupLines.push(` __reconcile(${anchorVar}, __s, ${collectionCode}, ${this._self}, ${blockName}, ${keyFnCode}${outerArgs});`);
1542
1534
  setupLines.push(` ${effClose}`);
1543
1535
  setupLines.push(`}`);
1544
1536
 
@@ -1784,6 +1776,134 @@ function __clsx(...args) {
1784
1776
  return args.filter(Boolean).join(' ');
1785
1777
  }
1786
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
+
1787
1907
  class __Component {
1788
1908
  constructor(props = {}) {
1789
1909
  Object.assign(this, props);
@@ -1819,7 +1939,7 @@ class __Component {
1819
1939
 
1820
1940
  // Register on globalThis for runtime deduplication
1821
1941
  if (typeof globalThis !== 'undefined') {
1822
- globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component };
1942
+ globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component };
1823
1943
  }
1824
1944
 
1825
1945
  `;