@solidjs/web 2.0.0-rc.1 → 2.0.0-rc.2

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.
@@ -1,5 +1,5 @@
1
1
  import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig, materializeContainerTrace } from 'solid-js';
2
- import { insert } from '@solidjs/web';
2
+ import { delegateEvents, insert } from '@solidjs/web';
3
3
  import { ChunkReader, frameAddress, SINGLE_FLIGHT_HEADER, deserializeStream, getServerFunctionsCodec, createChunk, getFlightDataConsumer, ERROR_HEADER, REVALIDATE_HEADER, configureServerFunctionsClient } from '@solidjs/web/server-functions/client';
4
4
 
5
5
  const ELEMENT_NODE = 1;
@@ -15,6 +15,74 @@ function claimNode(handlers, el) {
15
15
  for (let i = 0; i < handlers.length; i++) handlers[i](el);
16
16
  }
17
17
  const claimedAttr = name => name === "href" || name === "action";
18
+ const BOUND_SEAM = Symbol.for("dx.bnd");
19
+ const boundSeam = globalThis[BOUND_SEAM] || (globalThis[BOUND_SEAM] = {});
20
+ const BND_ATTR = "_bnd";
21
+ const BND_SELECTOR = "[_bnd]";
22
+ function bndMap(el) {
23
+ const s = el.getAttribute(BND_ATTR);
24
+ if (!s) return undefined;
25
+ const map = {};
26
+ for (const entry of s.split(",")) {
27
+ const eq = entry.indexOf("=");
28
+ if (eq < 1) continue;
29
+ const pos = entry.slice(0, eq);
30
+ const prop = decodeURIComponent(entry.slice(eq + 1));
31
+ const prev = map[pos];
32
+ if (prev === undefined) map[pos] = prop;else if (Array.isArray(prev)) prev.push(prop);else map[pos] = [prev, prop];
33
+ }
34
+ return map;
35
+ }
36
+ boundSeam.resolve = (el, type) => {
37
+ const frame = el._$bndFrame;
38
+ if (!frame) return undefined;
39
+ const map = bndMap(el);
40
+ const prop = map && map[type];
41
+ if (typeof prop !== "string") return undefined;
42
+ return claimFn(frame, type, prop);
43
+ };
44
+ function claimFn(frame, pos, prop) {
45
+ const fn = frame.clientProp(prop);
46
+ if (typeof fn === "function") return fn;
47
+ if (fn !== undefined) {
48
+ console.warn(`A server element claims \`${pos}\` from client prop \`${prop}\`, but the mounted ` + `frame's prop is not a function.`);
49
+ }
50
+ return undefined;
51
+ }
52
+ function sweepBound(root, frame, delegate, scope) {
53
+ const isElement = root.nodeType === ELEMENT_NODE;
54
+ if (!isElement && root.nodeType !== 11 ) return;
55
+ let els;
56
+ if (isElement && root.hasAttribute(BND_ATTR)) (els = []).push(root);
57
+ const found = root.querySelectorAll(BND_SELECTOR);
58
+ if (found.length) {
59
+ els || (els = []);
60
+ for (let i = 0; i < found.length; i++) els.push(found[i]);
61
+ }
62
+ if (!els) return;
63
+ const run = () => {
64
+ let types;
65
+ for (const el of els) {
66
+ el._$bndFrame = frame;
67
+ const map = bndMap(el);
68
+ if (!map) continue;
69
+ for (const pos in map) {
70
+ if (pos === "ref") fireRefs(frame, el, map.ref);else (types || (types = [])).push(pos);
71
+ }
72
+ }
73
+ if (types && delegate) delegate(types);
74
+ };
75
+ scope ? scope(run) : run();
76
+ }
77
+ function fireRefs(frame, el, prop) {
78
+ const fired = el._$bndFired || (el._$bndFired = new Set());
79
+ for (const p of Array.isArray(prop) ? prop : [prop]) {
80
+ if (fired.has(p)) continue;
81
+ fired.add(p);
82
+ const fn = claimFn(frame, "ref", p);
83
+ if (fn) fn(el);
84
+ }
85
+ }
18
86
  function claimTree(handlers, root) {
19
87
  const isElement = root.nodeType === ELEMENT_NODE;
20
88
  if (!isElement && root.nodeType !== 11 ) return;
@@ -114,6 +182,7 @@ function createFrameHost(options = {}) {
114
182
  return true;
115
183
  };
116
184
  return {
185
+ delegate: options.delegate,
117
186
  register(id, frame) {
118
187
  let set = frames.get(id);
119
188
  if (!set) frames.set(id, set = new Set());
@@ -208,10 +277,18 @@ class FrameImpl {
208
277
  if (!this.#disposed) this.#flush();
209
278
  };
210
279
  #claimTree = (node, direct) => {
280
+ if (!direct && node.nodeType !== TEXT_NODE && node.nodeType !== COMMENT_NODE) {
281
+ const o = this.#options;
282
+ sweepBound(node, this, o.delegate || o.host && o.host.delegate, o.ownerScope);
283
+ }
211
284
  const handlers = claimHandlers();
212
285
  if (!handlers) return;
213
286
  this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
214
287
  };
288
+ clientProp(name) {
289
+ const props = this.#options.props;
290
+ return props ? props[name] : undefined;
291
+ }
215
292
  #scoped(fn) {
216
293
  const scope = this.#options.ownerScope;
217
294
  return scope ? scope(fn) : fn();
@@ -661,7 +738,7 @@ class FrameImpl {
661
738
  parent.insertBefore(fragment, this.#end);
662
739
  this.#hasContent = true;
663
740
  } else {
664
- const claim = claimHandlers() ? this.#claimTree : null;
741
+ const claim = this.#claimTree;
665
742
  const ranges = new Map();
666
743
  this.#collectSlots(ranges);
667
744
  const grafts = [];
@@ -680,8 +757,7 @@ class FrameImpl {
680
757
  }
681
758
  return false;
682
759
  }
683
- const claim = claimHandlers() ? this.#claimTree : null;
684
- reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
760
+ reconcileChildren(open.parentNode, parseFragment(html), open, close, this.#claimTree);
685
761
  return true;
686
762
  }
687
763
  #applyAttrs(addr, text, removed) {
@@ -705,7 +781,6 @@ class FrameImpl {
705
781
  removeUntil(this.#parent(), this.#firstContent(), this.#end);
706
782
  }
707
783
  #claimContent() {
708
- if (!claimHandlers()) return;
709
784
  let n = this.#firstContent();
710
785
  while (n && n !== this.#end) {
711
786
  this.#claimTree(n);
@@ -1421,7 +1496,9 @@ function isMaterializedContainer(value) {
1421
1496
  return value !== null && typeof value === "object" && state.materializedValues.has(value);
1422
1497
  }
1423
1498
  function isContainerTraceMarker(value) {
1424
- return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
1499
+ if (value == null || typeof value !== "object" || value.$tr == null) return false;
1500
+ const tr = value.$tr;
1501
+ return tr.__SEROVAL_STREAM__ === true || typeof tr[Symbol.asyncIterator] === "function";
1425
1502
  }
1426
1503
  function reviveContainerTraces(value) {
1427
1504
  if (!state.materializeTrace || value == null || typeof value !== "object") return value;
@@ -1467,7 +1544,8 @@ function getFrameHost() {
1467
1544
  applyData: c => tableFor(c.id)?.apply(c),
1468
1545
  resolve: (ref, id) => tableFor(id)?.resolve(ref),
1469
1546
  revive: reviveContainerTraces,
1470
- isContainer: isMaterializedContainer
1547
+ isContainer: isMaterializedContainer,
1548
+ delegate: delegateEvents
1471
1549
  });
1472
1550
  }
1473
1551
  return sharedHost;
@@ -1543,7 +1621,16 @@ function slotArgsProxy(args) {
1543
1621
  if (!isAsyncValue(v)) return v;
1544
1622
  let read = asyncReads.get(key);
1545
1623
  if (!read) {
1546
- const make = () => createMemo(() => args()[key]);
1624
+ const make = () => createMemo(() => {
1625
+ const raw = args()[key];
1626
+ if (raw != null && typeof raw.then === "function") {
1627
+ if (raw.s === 1) return raw.v;
1628
+ if (raw.s === 2) throw raw.v;
1629
+ }
1630
+ return raw;
1631
+ }, {
1632
+ transparent: true
1633
+ });
1547
1634
  read = owner ? runWithOwner(owner, make) : make();
1548
1635
  asyncReads.set(key, read);
1549
1636
  }
@@ -1662,6 +1749,7 @@ function boundaryComponent(host, fnId) {
1662
1749
  host,
1663
1750
  id,
1664
1751
  slots: slotsFor(props),
1752
+ props,
1665
1753
  ownerScope: boundaryScope(owner),
1666
1754
  reveal: revealSeam(owner),
1667
1755
  onApply: () => {
@@ -1846,6 +1934,7 @@ function adoptBoundary(host, id, el, props, binding) {
1846
1934
  host,
1847
1935
  id: address,
1848
1936
  slots: slotsFor(props),
1937
+ props,
1849
1938
  ownerScope: boundaryScope(owner),
1850
1939
  reveal: revealSeam(owner),
1851
1940
  onApply: () => {
@@ -1,5 +1,5 @@
1
1
  import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig, materializeContainerTrace } from 'solid-js';
2
- import { insert } from '@solidjs/web';
2
+ import { delegateEvents, insert } from '@solidjs/web';
3
3
  import { ChunkReader, frameAddress, SINGLE_FLIGHT_HEADER, deserializeStream, getServerFunctionsCodec, createChunk, getFlightDataConsumer, ERROR_HEADER, REVALIDATE_HEADER, configureServerFunctionsClient } from '@solidjs/web/server-functions/client';
4
4
 
5
5
  const ELEMENT_NODE = 1;
@@ -15,6 +15,71 @@ function claimNode(handlers, el) {
15
15
  for (let i = 0; i < handlers.length; i++) handlers[i](el);
16
16
  }
17
17
  const claimedAttr = name => name === "href" || name === "action";
18
+ const BOUND_SEAM = Symbol.for("dx.bnd");
19
+ const boundSeam = globalThis[BOUND_SEAM] || (globalThis[BOUND_SEAM] = {});
20
+ const BND_ATTR = "_bnd";
21
+ const BND_SELECTOR = "[_bnd]";
22
+ function bndMap(el) {
23
+ const s = el.getAttribute(BND_ATTR);
24
+ if (!s) return undefined;
25
+ const map = {};
26
+ for (const entry of s.split(",")) {
27
+ const eq = entry.indexOf("=");
28
+ if (eq < 1) continue;
29
+ const pos = entry.slice(0, eq);
30
+ const prop = decodeURIComponent(entry.slice(eq + 1));
31
+ const prev = map[pos];
32
+ if (prev === undefined) map[pos] = prop;else if (Array.isArray(prev)) prev.push(prop);else map[pos] = [prev, prop];
33
+ }
34
+ return map;
35
+ }
36
+ boundSeam.resolve = (el, type) => {
37
+ const frame = el._$bndFrame;
38
+ if (!frame) return undefined;
39
+ const map = bndMap(el);
40
+ const prop = map && map[type];
41
+ if (typeof prop !== "string") return undefined;
42
+ return claimFn(frame, type, prop);
43
+ };
44
+ function claimFn(frame, pos, prop) {
45
+ const fn = frame.clientProp(prop);
46
+ if (typeof fn === "function") return fn;
47
+ return undefined;
48
+ }
49
+ function sweepBound(root, frame, delegate, scope) {
50
+ const isElement = root.nodeType === ELEMENT_NODE;
51
+ if (!isElement && root.nodeType !== 11 ) return;
52
+ let els;
53
+ if (isElement && root.hasAttribute(BND_ATTR)) (els = []).push(root);
54
+ const found = root.querySelectorAll(BND_SELECTOR);
55
+ if (found.length) {
56
+ els || (els = []);
57
+ for (let i = 0; i < found.length; i++) els.push(found[i]);
58
+ }
59
+ if (!els) return;
60
+ const run = () => {
61
+ let types;
62
+ for (const el of els) {
63
+ el._$bndFrame = frame;
64
+ const map = bndMap(el);
65
+ if (!map) continue;
66
+ for (const pos in map) {
67
+ if (pos === "ref") fireRefs(frame, el, map.ref);else (types || (types = [])).push(pos);
68
+ }
69
+ }
70
+ if (types && delegate) delegate(types);
71
+ };
72
+ scope ? scope(run) : run();
73
+ }
74
+ function fireRefs(frame, el, prop) {
75
+ const fired = el._$bndFired || (el._$bndFired = new Set());
76
+ for (const p of Array.isArray(prop) ? prop : [prop]) {
77
+ if (fired.has(p)) continue;
78
+ fired.add(p);
79
+ const fn = claimFn(frame, "ref", p);
80
+ if (fn) fn(el);
81
+ }
82
+ }
18
83
  function claimTree(handlers, root) {
19
84
  const isElement = root.nodeType === ELEMENT_NODE;
20
85
  if (!isElement && root.nodeType !== 11 ) return;
@@ -114,6 +179,7 @@ function createFrameHost(options = {}) {
114
179
  return true;
115
180
  };
116
181
  return {
182
+ delegate: options.delegate,
117
183
  register(id, frame) {
118
184
  let set = frames.get(id);
119
185
  if (!set) frames.set(id, set = new Set());
@@ -208,10 +274,18 @@ class FrameImpl {
208
274
  if (!this.#disposed) this.#flush();
209
275
  };
210
276
  #claimTree = (node, direct) => {
277
+ if (!direct && node.nodeType !== TEXT_NODE && node.nodeType !== COMMENT_NODE) {
278
+ const o = this.#options;
279
+ sweepBound(node, this, o.delegate || o.host && o.host.delegate, o.ownerScope);
280
+ }
211
281
  const handlers = claimHandlers();
212
282
  if (!handlers) return;
213
283
  this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
214
284
  };
285
+ clientProp(name) {
286
+ const props = this.#options.props;
287
+ return props ? props[name] : undefined;
288
+ }
215
289
  #scoped(fn) {
216
290
  const scope = this.#options.ownerScope;
217
291
  return scope ? scope(fn) : fn();
@@ -660,7 +734,7 @@ class FrameImpl {
660
734
  parent.insertBefore(fragment, this.#end);
661
735
  this.#hasContent = true;
662
736
  } else {
663
- const claim = claimHandlers() ? this.#claimTree : null;
737
+ const claim = this.#claimTree;
664
738
  const ranges = new Map();
665
739
  this.#collectSlots(ranges);
666
740
  const grafts = [];
@@ -676,8 +750,7 @@ class FrameImpl {
676
750
  if (!close) {
677
751
  return false;
678
752
  }
679
- const claim = claimHandlers() ? this.#claimTree : null;
680
- reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
753
+ reconcileChildren(open.parentNode, parseFragment(html), open, close, this.#claimTree);
681
754
  return true;
682
755
  }
683
756
  #applyAttrs(addr, text, removed) {
@@ -701,7 +774,6 @@ class FrameImpl {
701
774
  removeUntil(this.#parent(), this.#firstContent(), this.#end);
702
775
  }
703
776
  #claimContent() {
704
- if (!claimHandlers()) return;
705
777
  let n = this.#firstContent();
706
778
  while (n && n !== this.#end) {
707
779
  this.#claimTree(n);
@@ -1404,7 +1476,9 @@ function isMaterializedContainer(value) {
1404
1476
  return value !== null && typeof value === "object" && state.materializedValues.has(value);
1405
1477
  }
1406
1478
  function isContainerTraceMarker(value) {
1407
- return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
1479
+ if (value == null || typeof value !== "object" || value.$tr == null) return false;
1480
+ const tr = value.$tr;
1481
+ return tr.__SEROVAL_STREAM__ === true || typeof tr[Symbol.asyncIterator] === "function";
1408
1482
  }
1409
1483
  function reviveContainerTraces(value) {
1410
1484
  if (!state.materializeTrace || value == null || typeof value !== "object") return value;
@@ -1450,7 +1524,8 @@ function getFrameHost() {
1450
1524
  applyData: c => tableFor(c.id)?.apply(c),
1451
1525
  resolve: (ref, id) => tableFor(id)?.resolve(ref),
1452
1526
  revive: reviveContainerTraces,
1453
- isContainer: isMaterializedContainer
1527
+ isContainer: isMaterializedContainer,
1528
+ delegate: delegateEvents
1454
1529
  });
1455
1530
  }
1456
1531
  return sharedHost;
@@ -1526,7 +1601,16 @@ function slotArgsProxy(args) {
1526
1601
  if (!isAsyncValue(v)) return v;
1527
1602
  let read = asyncReads.get(key);
1528
1603
  if (!read) {
1529
- const make = () => createMemo(() => args()[key]);
1604
+ const make = () => createMemo(() => {
1605
+ const raw = args()[key];
1606
+ if (raw != null && typeof raw.then === "function") {
1607
+ if (raw.s === 1) return raw.v;
1608
+ if (raw.s === 2) throw raw.v;
1609
+ }
1610
+ return raw;
1611
+ }, {
1612
+ transparent: true
1613
+ });
1530
1614
  read = owner ? runWithOwner(owner, make) : make();
1531
1615
  asyncReads.set(key, read);
1532
1616
  }
@@ -1645,6 +1729,7 @@ function boundaryComponent(host, fnId) {
1645
1729
  host,
1646
1730
  id,
1647
1731
  slots: slotsFor(props),
1732
+ props,
1648
1733
  ownerScope: boundaryScope(owner),
1649
1734
  reveal: revealSeam(owner),
1650
1735
  onApply: () => {
@@ -1829,6 +1914,7 @@ function adoptBoundary(host, id, el, props, binding) {
1829
1914
  host,
1830
1915
  id: address,
1831
1916
  slots: slotsFor(props),
1917
+ props,
1832
1918
  ownerScope: boundaryScope(owner),
1833
1919
  reveal: revealSeam(owner),
1834
1920
  onApply: () => {