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 +2 -2
- package/package.json +1 -1
- package/src/compiler.js +2 -2
- package/src/components.js +149 -29
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
|
@@ -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
|
|
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(`
|
|
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
|
`;
|