@estjs/template 0.0.15-beta.11 → 0.0.15-beta.13

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.
@@ -578,4 +578,19 @@ interface ResourceOptions<T> {
578
578
  */
579
579
  declare function createResource<T>(fetcher: () => Promise<T>, options?: ResourceOptions<T>): [Resource<T>, ResourceActions<T>];
580
580
 
581
- export { Component, type ComponentFn, type ComponentProps, Fragment, type FragmentProps, type InjectionKey, Portal, type PortalProps, type Scope, Suspense, type SuspenseProps, addEvent, addEventListener, bindElement, createApp, createComponent, createResource, createScope, delegateEvents, disposeScope, endHydration, getActiveScope, getFirstDOMNode, getHydrationKey, getRenderedElement, hydrate, inject, insert, insertNode, isComponent, isFragment, isHydrating, isPortal, isSameNode, isSuspense, mapNodes, mapSSRNodes, normalizeClass, normalizeNode, omitProps, onCleanup, onDestroy, onMount, onUpdate, patchAttr, patchClass, patchStyle, provide, removeNode, replaceNode, resetHydrationKey, runWithScope, setActiveScope, setStyle, shallowCompare, startHydration, template };
581
+ interface ForProps<T> {
582
+ each: T[] | Signal<T[]> | (() => T[]);
583
+ children: (item: T, index: number) => AnyNode;
584
+ keyFn?: (item: T) => unknown;
585
+ fallback?: () => AnyNode;
586
+ }
587
+ /**
588
+ * Optimized For Component
589
+ * - Uses createDetachedScope to avoid parent.children Set overhead (SolidJS style)
590
+ * - Uses DocumentFragment batching for mass creation
591
+ * - Inlines scope switching for performance
592
+ */
593
+ declare function For<T>(props: ForProps<T>): Node;
594
+ declare namespace For { }
595
+
596
+ export { Component, type ComponentFn, type ComponentProps, For, type ForProps, Fragment, type FragmentProps, type InjectionKey, Portal, type PortalProps, type Scope, Suspense, type SuspenseProps, addEvent, addEventListener, bindElement, createApp, createComponent, createResource, createScope, delegateEvents, disposeScope, endHydration, getActiveScope, getFirstDOMNode, getHydrationKey, getRenderedElement, hydrate, inject, insert, insertNode, isComponent, isFragment, isHydrating, isPortal, isSameNode, isSuspense, mapNodes, mapSSRNodes, normalizeClass, normalizeNode, omitProps, onCleanup, onDestroy, onMount, onUpdate, patchAttr, patchClass, patchStyle, provide, removeNode, replaceNode, resetHydrationKey, runWithScope, setActiveScope, setStyle, shallowCompare, startHydration, template };
@@ -578,4 +578,19 @@ interface ResourceOptions<T> {
578
578
  */
579
579
  declare function createResource<T>(fetcher: () => Promise<T>, options?: ResourceOptions<T>): [Resource<T>, ResourceActions<T>];
580
580
 
581
- export { Component, type ComponentFn, type ComponentProps, Fragment, type FragmentProps, type InjectionKey, Portal, type PortalProps, type Scope, Suspense, type SuspenseProps, addEvent, addEventListener, bindElement, createApp, createComponent, createResource, createScope, delegateEvents, disposeScope, endHydration, getActiveScope, getFirstDOMNode, getHydrationKey, getRenderedElement, hydrate, inject, insert, insertNode, isComponent, isFragment, isHydrating, isPortal, isSameNode, isSuspense, mapNodes, mapSSRNodes, normalizeClass, normalizeNode, omitProps, onCleanup, onDestroy, onMount, onUpdate, patchAttr, patchClass, patchStyle, provide, removeNode, replaceNode, resetHydrationKey, runWithScope, setActiveScope, setStyle, shallowCompare, startHydration, template };
581
+ interface ForProps<T> {
582
+ each: T[] | Signal<T[]> | (() => T[]);
583
+ children: (item: T, index: number) => AnyNode;
584
+ keyFn?: (item: T) => unknown;
585
+ fallback?: () => AnyNode;
586
+ }
587
+ /**
588
+ * Optimized For Component
589
+ * - Uses createDetachedScope to avoid parent.children Set overhead (SolidJS style)
590
+ * - Uses DocumentFragment batching for mass creation
591
+ * - Inlines scope switching for performance
592
+ */
593
+ declare function For<T>(props: ForProps<T>): Node;
594
+ declare namespace For { }
595
+
596
+ export { Component, type ComponentFn, type ComponentProps, For, type ForProps, Fragment, type FragmentProps, type InjectionKey, Portal, type PortalProps, type Scope, Suspense, type SuspenseProps, addEvent, addEventListener, bindElement, createApp, createComponent, createResource, createScope, delegateEvents, disposeScope, endHydration, getActiveScope, getFirstDOMNode, getHydrationKey, getRenderedElement, hydrate, inject, insert, insertNode, isComponent, isFragment, isHydrating, isPortal, isSameNode, isSuspense, mapNodes, mapSSRNodes, normalizeClass, normalizeNode, omitProps, onCleanup, onDestroy, onMount, onUpdate, patchAttr, patchClass, patchStyle, provide, removeNode, replaceNode, resetHydrationKey, runWithScope, setActiveScope, setStyle, shallowCompare, startHydration, template };
@@ -157,6 +157,7 @@ var NORMAL_COMPONENT = /* @__PURE__ */ Symbol("Normal Component" );
157
157
  var FRAGMENT_COMPONENT = /* @__PURE__ */ Symbol("Fragment Component" );
158
158
  var PORTAL_COMPONENT = /* @__PURE__ */ Symbol("Portal Component" );
159
159
  var SUSPENSE_COMPONENT = /* @__PURE__ */ Symbol("Suspense Component" );
160
+ var FOR_COMPONENT = /* @__PURE__ */ Symbol("For Component" );
160
161
  var MAX_KEY_LENGTH = 1e3;
161
162
  var componentKeyPrefixCache = /* @__PURE__ */ new WeakMap();
162
163
  function getComponentKey(type) {
@@ -284,60 +285,46 @@ function shallowCompare(a, b) {
284
285
  if (shared.isArray(a) !== shared.isArray(b)) return false;
285
286
  const aRecord = a;
286
287
  const bRecord = b;
287
- for (const key in aRecord) {
288
- if (aRecord[key] !== bRecord[key]) return false;
289
- }
290
- for (const key in bRecord) {
291
- if (!(key in aRecord)) return false;
288
+ const aKeys = Object.keys(aRecord);
289
+ const bKeys = Object.keys(bRecord);
290
+ if (aKeys.length !== bKeys.length) return false;
291
+ for (const key of aKeys) {
292
+ if (!(key in bRecord) || aRecord[key] !== bRecord[key]) {
293
+ return false;
294
+ }
292
295
  }
293
296
  return true;
294
297
  }
295
298
  function removeNode(node) {
296
299
  if (!node) return;
297
- try {
298
- if (isComponent(node)) {
299
- node.destroy();
300
- } else {
301
- const element = node;
302
- if (element.parentElement) {
303
- element.remove();
304
- }
305
- }
306
- } catch (_error) {
307
- shared.error("Failed to remove node:", _error);
300
+ if (isComponent(node)) {
301
+ node.destroy();
302
+ return;
303
+ }
304
+ const element = node;
305
+ if (element.parentElement) {
306
+ element.remove();
308
307
  }
309
308
  }
310
309
  function insertNode(parent, child, before) {
311
310
  if (!parent || !child) return;
312
- try {
313
- const beforeNode = isComponent(before) ? before.firstChild : before;
314
- if (isComponent(child)) {
315
- child.mount(parent, beforeNode);
316
- return;
317
- }
318
- if (beforeNode) {
319
- parent.insertBefore(child, beforeNode);
320
- } else {
321
- if (true) {
322
- if (!child) {
323
- shared.error("insertNode: child is not a Node", child);
324
- }
325
- }
326
- parent.appendChild(child);
327
- }
328
- } catch (_error) {
329
- shared.error("Failed to insert node:", _error);
311
+ if (isComponent(child)) {
312
+ const beforeNode2 = isComponent(before) ? before.firstChild : before;
313
+ child.mount(parent, beforeNode2);
314
+ return;
315
+ }
316
+ const beforeNode = isComponent(before) ? before.firstChild : before;
317
+ if (beforeNode) {
318
+ parent.insertBefore(child, beforeNode);
319
+ } else {
320
+ parent.appendChild(child);
330
321
  }
331
322
  }
332
323
  function replaceNode(parent, newNode, oldNode) {
333
324
  if (!parent || !newNode || !oldNode || newNode === oldNode) return;
334
- try {
335
- const beforeNode = isComponent(oldNode) ? oldNode.beforeNode : oldNode.nextSibling;
336
- removeNode(oldNode);
337
- insertNode(parent, newNode, beforeNode);
338
- } catch (_error) {
339
- shared.error("Failed to replace node:", _error);
340
- }
325
+ const beforeNode = isComponent(oldNode) ? oldNode.beforeNode : oldNode.nextSibling;
326
+ removeNode(oldNode);
327
+ insertNode(parent, newNode, beforeNode);
341
328
  }
342
329
  function getFirstDOMNode(node) {
343
330
  if (!node) {
@@ -718,10 +705,18 @@ function bindSelectElement(node, setter) {
718
705
  function insert(parent, nodeFactory, before) {
719
706
  if (!parent) return;
720
707
  let renderedNodes = [];
708
+ const currentScope = getActiveScope();
721
709
  const cleanup = signals.effect(() => {
722
- const rawNodes = shared.isFunction(nodeFactory) ? nodeFactory() : nodeFactory;
723
- const nodes = shared.coerceArray(rawNodes).map((item) => shared.isFunction(item) ? item() : item).flatMap(normalizeNode);
724
- renderedNodes = patchChildren(parent, renderedNodes, nodes, before);
710
+ const run = () => {
711
+ const rawNodes = shared.isFunction(nodeFactory) ? nodeFactory() : nodeFactory;
712
+ const nodes = shared.coerceArray(rawNodes).map((item) => shared.isFunction(item) ? item() : item).flatMap(normalizeNode);
713
+ renderedNodes = patchChildren(parent, renderedNodes, nodes, before);
714
+ };
715
+ if (currentScope) {
716
+ runWithScope(currentScope, run);
717
+ } else {
718
+ run();
719
+ }
725
720
  });
726
721
  onCleanup(() => {
727
722
  cleanup();
@@ -893,7 +888,7 @@ var Component = class {
893
888
  return void 0;
894
889
  }
895
890
  mount(parentNode, beforeNode) {
896
- var _a2;
891
+ var _a2, _b;
897
892
  this.parentNode = parentNode;
898
893
  this.beforeNode = beforeNode;
899
894
  this.state = 1 /* MOUNTING */;
@@ -904,27 +899,21 @@ var Component = class {
904
899
  this.state = 2 /* MOUNTED */;
905
900
  return this.renderedNodes;
906
901
  }
907
- const parent = (_a2 = this.parentScope) != null ? _a2 : getActiveScope();
908
- this.scope = createScope(parent);
909
- const renderedNodes = runWithScope(this.scope, () => {
910
- var _a3;
911
- let result = this.component(this.reactiveProps);
912
- if (shared.isFunction(result)) {
913
- result = result(this.reactiveProps);
914
- }
915
- if (signals.isSignal(result) || signals.isComputed(result)) {
916
- result = result.value;
917
- }
918
- return (_a3 = insert(parentNode, result, beforeNode)) != null ? _a3 : [];
919
- });
902
+ const parentScope = (_a2 = this.parentScope) != null ? _a2 : getActiveScope();
903
+ this.scope = createScope(parentScope);
904
+ setActiveScope(this.scope);
905
+ let result = this.component(this.reactiveProps);
906
+ if (shared.isFunction(result)) {
907
+ result = result(this.reactiveProps);
908
+ }
909
+ if (signals.isSignal(result) || signals.isComputed(result)) {
910
+ result = result.value;
911
+ }
912
+ const renderedNodes = (_b = insert(parentNode, result, beforeNode)) != null ? _b : [];
920
913
  this.renderedNodes = renderedNodes;
921
- runWithScope(this.scope, () => {
922
- this.applyProps(this.props);
923
- });
914
+ this.applyProps(this.props);
924
915
  this.state = 2 /* MOUNTED */;
925
- if (this.scope) {
926
- triggerMountHooks(this.scope);
927
- }
916
+ triggerMountHooks(this.scope);
928
917
  return this.renderedNodes;
929
918
  }
930
919
  update(prevNode) {
@@ -946,9 +935,8 @@ var Component = class {
946
935
  return this;
947
936
  }
948
937
  if (this.scope) {
949
- runWithScope(this.scope, () => {
950
- this.applyProps(this.props);
951
- });
938
+ setActiveScope(this.scope);
939
+ this.applyProps(this.props);
952
940
  triggerUpdateHooks(this.scope);
953
941
  }
954
942
  return this;
@@ -1022,9 +1010,9 @@ var Component = class {
1022
1010
  if (this.scope) {
1023
1011
  triggerUpdateHooks(this.scope);
1024
1012
  }
1025
- } catch (error9) {
1013
+ } catch (error8) {
1026
1014
  this.renderedNodes = originalNodes;
1027
- throw error9;
1015
+ throw error8;
1028
1016
  }
1029
1017
  }
1030
1018
  /**
@@ -1049,14 +1037,14 @@ var Component = class {
1049
1037
  return;
1050
1038
  }
1051
1039
  this.state = 4 /* DESTROYING */;
1040
+ for (const node of this.renderedNodes) {
1041
+ removeNode(node);
1042
+ }
1052
1043
  const scope = this.scope;
1053
1044
  if (scope) {
1054
1045
  disposeScope(scope);
1055
1046
  this.scope = null;
1056
1047
  }
1057
- for (const node of this.renderedNodes) {
1058
- removeNode(node);
1059
- }
1060
1048
  this.renderedNodes = [];
1061
1049
  this.parentNode = void 0;
1062
1050
  this.beforeNode = void 0;
@@ -1711,9 +1699,9 @@ function Suspense(props) {
1711
1699
  if (pendingCount === 0) {
1712
1700
  showChildren();
1713
1701
  }
1714
- }).catch((error9) => {
1702
+ }).catch((error8) => {
1715
1703
  {
1716
- shared.warn("[Suspense] Resource failed:", error9);
1704
+ shared.warn("[Suspense] Resource failed:", error8);
1717
1705
  }
1718
1706
  if (!isMounted) return;
1719
1707
  pendingCount--;
@@ -1761,7 +1749,7 @@ function isSuspense(node) {
1761
1749
  function createResource(fetcher, options) {
1762
1750
  const value = signals.signal(options == null ? void 0 : options.initialValue);
1763
1751
  const loading = signals.signal(true);
1764
- const error9 = signals.signal(null);
1752
+ const error8 = signals.signal(null);
1765
1753
  const state = signals.signal("pending");
1766
1754
  let fetchId = 0;
1767
1755
  let currentPromise = null;
@@ -1769,7 +1757,7 @@ function createResource(fetcher, options) {
1769
1757
  const currentFetchId = ++fetchId;
1770
1758
  loading.value = true;
1771
1759
  state.value = "pending";
1772
- error9.value = null;
1760
+ error8.value = null;
1773
1761
  try {
1774
1762
  const promise = fetcher();
1775
1763
  currentPromise = promise.then(() => {
@@ -1783,7 +1771,7 @@ function createResource(fetcher, options) {
1783
1771
  }
1784
1772
  } catch (error_) {
1785
1773
  if (currentFetchId === fetchId) {
1786
- error9.value = error_ instanceof Error ? error_ : new Error(String(error_));
1774
+ error8.value = error_ instanceof Error ? error_ : new Error(String(error_));
1787
1775
  state.value = "errored";
1788
1776
  loading.value = false;
1789
1777
  }
@@ -1800,14 +1788,14 @@ function createResource(fetcher, options) {
1800
1788
  return value.value;
1801
1789
  });
1802
1790
  resource.loading = loading;
1803
- resource.error = error9;
1791
+ resource.error = error8;
1804
1792
  resource.state = state;
1805
1793
  const actions = {
1806
1794
  mutate: (newValue) => {
1807
1795
  value.value = newValue;
1808
1796
  state.value = "ready";
1809
1797
  loading.value = false;
1810
- error9.value = null;
1798
+ error8.value = null;
1811
1799
  },
1812
1800
  refetch: () => __async(null, null, function* () {
1813
1801
  yield fetch();
@@ -1815,8 +1803,175 @@ function createResource(fetcher, options) {
1815
1803
  };
1816
1804
  return [resource, actions];
1817
1805
  }
1806
+ function For(props) {
1807
+ const fragment = document.createDocumentFragment();
1808
+ const marker = document.createComment("");
1809
+ fragment.appendChild(marker);
1810
+ let entries = [];
1811
+ let fallbackNode = null;
1812
+ const keyFn = props.keyFn;
1813
+ const renderFn = props.children;
1814
+ const getList = () => {
1815
+ var _a2, _b;
1816
+ const input = props.each;
1817
+ if (signals.isSignal(input)) return (_a2 = input.value) != null ? _a2 : [];
1818
+ if (typeof input === "function") return (_b = input()) != null ? _b : [];
1819
+ return input != null ? input : [];
1820
+ };
1821
+ const getKey = (item) => keyFn ? keyFn(item) : item;
1822
+ const renderItem = (item, index, parent, before) => {
1823
+ var _a2;
1824
+ const prevScope = getActiveScope();
1825
+ const scope = createScope(prevScope);
1826
+ setActiveScope(scope);
1827
+ let node;
1828
+ try {
1829
+ const result = renderFn(item, index);
1830
+ if (isComponent(result)) {
1831
+ result.mount(parent, before);
1832
+ node = (_a2 = result.firstChild) != null ? _a2 : document.createComment("empty");
1833
+ } else {
1834
+ node = result;
1835
+ if (!node.parentNode) {
1836
+ if (before) {
1837
+ parent.insertBefore(node, before);
1838
+ } else {
1839
+ parent.appendChild(node);
1840
+ }
1841
+ }
1842
+ }
1843
+ } finally {
1844
+ setActiveScope(prevScope);
1845
+ }
1846
+ return { key: getKey(item), node, scope };
1847
+ };
1848
+ const disposeItem = (entry) => {
1849
+ disposeScope(entry.scope);
1850
+ if (entry.node.parentNode) {
1851
+ entry.node.parentNode.removeChild(entry.node);
1852
+ }
1853
+ };
1854
+ signals.memoEffect(
1855
+ ({ prev }) => {
1856
+ var _a2;
1857
+ const newItems = getList();
1858
+ if (prev === newItems) return { prev: newItems };
1859
+ const parent = marker.parentNode;
1860
+ if (!parent) {
1861
+ if (newItems.length === 0) {
1862
+ if (props.fallback) {
1863
+ const fb = props.fallback();
1864
+ if (isComponent(fb)) {
1865
+ fb.mount(fragment, marker);
1866
+ fallbackNode = (_a2 = fb.firstChild) != null ? _a2 : document.createComment("empty");
1867
+ } else {
1868
+ fallbackNode = fb;
1869
+ fragment.insertBefore(fallbackNode, marker);
1870
+ }
1871
+ }
1872
+ return { prev: newItems };
1873
+ }
1874
+ entries = new Array(newItems.length);
1875
+ for (const [i, newItem] of newItems.entries()) {
1876
+ entries[i] = renderItem(newItem, i, fragment, marker);
1877
+ }
1878
+ return { prev: newItems };
1879
+ }
1880
+ signals.untrack(() => reconcile(parent, newItems));
1881
+ return { prev: newItems };
1882
+ },
1883
+ {
1884
+ prev: []
1885
+ }
1886
+ );
1887
+ function reconcile(parent, newItems) {
1888
+ var _a2;
1889
+ const oldLen = entries.length;
1890
+ const newLen = newItems.length;
1891
+ if (newLen === 0) {
1892
+ for (let i = 0; i < oldLen; i++) {
1893
+ disposeItem(entries[i]);
1894
+ }
1895
+ entries = [];
1896
+ if (props.fallback && !fallbackNode) {
1897
+ const fb = props.fallback();
1898
+ if (isComponent(fb)) {
1899
+ fb.mount(parent, marker);
1900
+ fallbackNode = (_a2 = fb.firstChild) != null ? _a2 : document.createComment("empty");
1901
+ } else {
1902
+ fallbackNode = fb;
1903
+ parent.insertBefore(fallbackNode, marker);
1904
+ }
1905
+ }
1906
+ return;
1907
+ }
1908
+ if (oldLen === 0 || fallbackNode) {
1909
+ if (fallbackNode) {
1910
+ if (fallbackNode.parentNode) fallbackNode.parentNode.removeChild(fallbackNode);
1911
+ fallbackNode = null;
1912
+ }
1913
+ entries = new Array(newLen);
1914
+ const batchFragment2 = document.createDocumentFragment();
1915
+ for (let i = 0; i < newLen; i++) {
1916
+ entries[i] = renderItem(newItems[i], i, batchFragment2, null);
1917
+ }
1918
+ parent.insertBefore(batchFragment2, marker);
1919
+ return;
1920
+ }
1921
+ const oldKeyMap = /* @__PURE__ */ new Map();
1922
+ for (let i = 0; i < oldLen; i++) {
1923
+ const entry = entries[i];
1924
+ const list = oldKeyMap.get(entry.key);
1925
+ if (list) {
1926
+ list.push(entry);
1927
+ } else {
1928
+ oldKeyMap.set(entry.key, [entry]);
1929
+ }
1930
+ }
1931
+ const newEntries = new Array(newLen);
1932
+ const toRemove = [];
1933
+ const batchFragment = document.createDocumentFragment();
1934
+ for (let i = 0; i < newLen; i++) {
1935
+ const item = newItems[i];
1936
+ const key = getKey(item);
1937
+ const oldList = oldKeyMap.get(key);
1938
+ if (oldList && oldList.length > 0) {
1939
+ newEntries[i] = oldList.shift();
1940
+ } else {
1941
+ newEntries[i] = renderItem(item, i, batchFragment, null);
1942
+ }
1943
+ }
1944
+ for (const list of oldKeyMap.values()) {
1945
+ for (const entry of list) {
1946
+ toRemove.push(entry);
1947
+ }
1948
+ }
1949
+ for (const entry of toRemove) {
1950
+ disposeItem(entry);
1951
+ }
1952
+ for (let i = 0; i < newLen; i++) {
1953
+ const node = newEntries[i].node;
1954
+ parent.insertBefore(node, marker);
1955
+ }
1956
+ entries = newEntries;
1957
+ }
1958
+ onCleanup(() => {
1959
+ for (const entry of entries) {
1960
+ disposeItem(entry);
1961
+ }
1962
+ if (fallbackNode && fallbackNode.parentNode) {
1963
+ fallbackNode.parentNode.removeChild(fallbackNode);
1964
+ }
1965
+ if (marker.parentNode) {
1966
+ marker.parentNode.removeChild(marker);
1967
+ }
1968
+ });
1969
+ return fragment;
1970
+ }
1971
+ For[FOR_COMPONENT] = true;
1818
1972
 
1819
1973
  exports.Component = Component;
1974
+ exports.For = For;
1820
1975
  exports.Fragment = Fragment;
1821
1976
  exports.Portal = Portal;
1822
1977
  exports.Suspense = Suspense;