@solidjs/web 2.0.0-beta.29 → 2.0.0-beta.30

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.
@@ -102,12 +102,11 @@ function createFrameHost(options = {}) {
102
102
  const sibling = set.size ? set.values().next().value : undefined;
103
103
  set.add(frame);
104
104
  if (sibling) {
105
- if (sibling.version !== undefined) {
106
- frame.apply({
107
- version: sibling.version,
108
- r: sibling.store
109
- });
110
- }
105
+ frame.apply({
106
+ version: sibling.version ?? 0,
107
+ r: sibling.store
108
+ });
109
+ if (sibling.version === undefined) frame.rebase && frame.rebase();
111
110
  return;
112
111
  }
113
112
  const kept = retained.get(id);
@@ -117,6 +116,7 @@ function createFrameHost(options = {}) {
117
116
  version: kept.version,
118
117
  r: kept.records
119
118
  });
119
+ frame.rebase && frame.rebase();
120
120
  }
121
121
  const buffered = pending.get(id);
122
122
  if (buffered) {
@@ -205,6 +205,7 @@ class FrameImpl {
205
205
  #mountedSlots = new Set();
206
206
  #slotCleanups = new Map();
207
207
  #slotArgs = new Map();
208
+ #slotUpdaters = new Map();
208
209
  #slotRegions = new Map();
209
210
  #slotResolvedRefs = new Map();
210
211
  #slotNodes = new Map();
@@ -382,6 +383,21 @@ class FrameImpl {
382
383
  } else if (record !== this.#slotArgs.get(occurrence)) {
383
384
  if (this.#refArgsUnchanged(occurrence, record)) {
384
385
  this.#slotArgs.set(occurrence, record);
386
+ this.#reconcileRegions(occurrence, record);
387
+ continue;
388
+ }
389
+ const update = this.#slotUpdaters.get(occurrence);
390
+ if (update) {
391
+ const props = this.#resolveArgs(occurrence, record.args);
392
+ const regions = this.#slotRegions.get(occurrence);
393
+ if (regions) {
394
+ for (const [argKey, entry] of regions) {
395
+ if (!(argKey in props)) props[argKey] = entry.element;
396
+ }
397
+ }
398
+ this.#slotArgs.set(occurrence, record);
399
+ update(props);
400
+ this.#bindRegions(occurrence);
385
401
  continue;
386
402
  }
387
403
  const nodes = this.#invokeSlot(occurrence, callback, record, start);
@@ -397,6 +413,7 @@ class FrameImpl {
397
413
  }
398
414
  }
399
415
  #invokeSlot(occurrence, callback, record, start, adopted) {
416
+ this.#slotUpdaters.delete(occurrence);
400
417
  const cleanups = this.#slotCleanups.get(occurrence) ?? [];
401
418
  let existing = [];
402
419
  let end = null;
@@ -415,6 +432,7 @@ class FrameImpl {
415
432
  adopted: !!adopted,
416
433
  invoked: !!(record && record.kind === "slot"),
417
434
  onCleanup: fn => cleanups.push(fn),
435
+ onUpdate: fn => this.#slotUpdaters.set(occurrence, fn),
418
436
  existing,
419
437
  range: end ? {
420
438
  start,
@@ -424,8 +442,7 @@ class FrameImpl {
424
442
  const props = record && record.kind === "slot" ? this.#resolveArgs(occurrence, record.args) : {};
425
443
  const regions = this.#slotRegions.get(occurrence);
426
444
  if (regions) {
427
- for (const entry of regions.values()) {
428
- const argKey = entry.childId.slice(entry.childId.lastIndexOf(".") + 1);
445
+ for (const [argKey, entry] of regions) {
429
446
  if (!(argKey in props)) props[argKey] = entry.element;
430
447
  }
431
448
  }
@@ -451,6 +468,7 @@ class FrameImpl {
451
468
  this.#mountedSlots.delete(key);
452
469
  this.#slotNodes.delete(key);
453
470
  this.#slotArgs.delete(key);
471
+ this.#slotUpdaters.delete(key);
454
472
  this.#slotResolvedRefs.delete(key);
455
473
  this.#removeSlotRecord(key);
456
474
  this.#runSlotCleanups(key);
@@ -485,7 +503,7 @@ class FrameImpl {
485
503
  cache[key] = resolved;
486
504
  props[key] = resolved;
487
505
  } else if (isFrameRef(value)) {
488
- let entry = regions.get(value.$frame);
506
+ let entry = regions.get(key);
489
507
  if (!entry) {
490
508
  const element = makeFrameElement(value.$frame);
491
509
  entry = {
@@ -493,7 +511,9 @@ class FrameImpl {
493
511
  element,
494
512
  frame: undefined
495
513
  };
496
- regions.set(value.$frame, entry);
514
+ regions.set(key, entry);
515
+ } else if (entry.childId !== value.$frame) {
516
+ renameRegion(entry, value.$frame);
497
517
  }
498
518
  props[key] = entry.element;
499
519
  } else {
@@ -510,9 +530,10 @@ class FrameImpl {
510
530
  this.#slotRegions.set(slotKey, regions);
511
531
  }
512
532
  const endData = slotEnd(slotKey);
533
+ const prefix = `${this.#options.id}.`;
513
534
  let n = start.nextSibling;
514
535
  while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
515
- collectRegionElements(n, regions);
536
+ collectRegionElements(n, regions, prefix);
516
537
  n = n.nextSibling;
517
538
  }
518
539
  }
@@ -531,7 +552,7 @@ class FrameImpl {
531
552
  if (key in a) continue;
532
553
  const vb = b[key];
533
554
  if (!vb || typeof vb !== "object" || typeof vb.$frame !== "string") return false;
534
- if (!regions || !regions.has(vb.$frame)) return false;
555
+ if (!regions || !regions.has(key)) return false;
535
556
  }
536
557
  }
537
558
  const cache = this.#slotResolvedRefs.get(occurrence);
@@ -540,7 +561,7 @@ class FrameImpl {
540
561
  const vb = b[key];
541
562
  if (va === vb) continue;
542
563
  if (!va || !vb || typeof va !== "object" || typeof vb !== "object") return false;
543
- if (typeof va.$frame === "string" && va.$frame === vb.$frame) continue;
564
+ if (typeof va.$frame === "string" && typeof vb.$frame === "string") continue;
544
565
  if (typeof va.$ref === "string" && typeof vb.$ref === "string" && cache && key in cache) {
545
566
  const host = this.#options.host;
546
567
  const next = host ? host.resolve(vb, this.#options.id) : undefined;
@@ -554,6 +575,18 @@ class FrameImpl {
554
575
  }
555
576
  return true;
556
577
  }
578
+ #reconcileRegions(occurrence, record) {
579
+ const args = record && record.args;
580
+ if (!args) return;
581
+ const regions = this.#slotRegions.get(occurrence);
582
+ if (!regions) return;
583
+ for (const key of Object.keys(args)) {
584
+ const value = args[key];
585
+ if (!isFrameRef(value)) continue;
586
+ const entry = regions.get(key);
587
+ if (entry && entry.childId !== value.$frame) renameRegion(entry, value.$frame);
588
+ }
589
+ }
557
590
  #bindRegions(slotKey) {
558
591
  const regions = this.#slotRegions.get(slotKey);
559
592
  if (!regions) return;
@@ -595,6 +628,31 @@ class FrameImpl {
595
628
  records
596
629
  };
597
630
  }
631
+ rebind(id) {
632
+ if (this.#disposed || id === this.#options.id) return;
633
+ const {
634
+ host,
635
+ id: oldId
636
+ } = this.#options;
637
+ if (host && oldId !== undefined) host.unregister(oldId, this);
638
+ this.#options = {
639
+ ...this.#options,
640
+ id
641
+ };
642
+ if (this.#element && this.#element.nodeType === ELEMENT_NODE) {
643
+ this.#element.setAttribute(FRAME_ID_ATTR, id);
644
+ }
645
+ this.#version = undefined;
646
+ this.#revealed.clear();
647
+ this.#fallbackShown.clear();
648
+ for (const key of Object.keys(this.#store)) {
649
+ if (key.startsWith("seg:") || key === ":error") delete this.#store[key];
650
+ }
651
+ if (host) host.register(id, this);
652
+ }
653
+ rebase() {
654
+ this.#version = undefined;
655
+ }
598
656
  dispose() {
599
657
  if (this.#disposed) return;
600
658
  const {
@@ -626,6 +684,7 @@ class FrameImpl {
626
684
  const ranges = new Map();
627
685
  this.#collectSlots(ranges);
628
686
  reconcileChildren(parent, fragment, this.#start, this.#end, claim, ranges);
687
+ if (ranges.size) restoreDisplacedRanges(this.#firstContent(), this.#end, ranges);
629
688
  }
630
689
  }
631
690
  #clearContent() {
@@ -652,8 +711,8 @@ class FrameImpl {
652
711
  const assets = this.#store[`seg:${name}:assets`];
653
712
  if (assets && assets.styles) {
654
713
  let ready = true;
655
- for (const href of assets.styles) {
656
- if (!ensureStylesheet(href, this.#styleFlush)) ready = false;
714
+ for (const entry of assets.styles) {
715
+ if (!ensureStylesheet(entry, this.#styleFlush)) ready = false;
657
716
  }
658
717
  if (!ready) return false;
659
718
  }
@@ -766,11 +825,15 @@ function findHeadElement(selector, attr, value) {
766
825
  }
767
826
  return null;
768
827
  }
769
- function ensureStylesheet(href, onSettle) {
828
+ function ensureStylesheet(entry, onSettle) {
829
+ const href = typeof entry === "string" ? entry : entry.href;
770
830
  let link = findHeadElement('link[rel="stylesheet"]', "href", href);
771
831
  if (!link) {
772
832
  link = document.createElement("link");
773
833
  link.rel = "stylesheet";
834
+ if (typeof entry !== "string" && entry.attrs) {
835
+ for (const name in entry.attrs) link.setAttribute(name, entry.attrs[name]);
836
+ }
774
837
  link.href = href;
775
838
  const waiters = new Set();
776
839
  link._$frWaiters = waiters;
@@ -840,21 +903,28 @@ function collectSlots(n, end, out) {
840
903
  n = n.nextSibling;
841
904
  }
842
905
  }
843
- function collectRegionElements(node, regions) {
906
+ function collectRegionElements(node, regions, prefix) {
844
907
  if (node.nodeType !== ELEMENT_NODE) return;
845
908
  if (isFrameElement(node)) {
846
909
  const childId = node.getAttribute(FRAME_ID_ATTR);
847
- if (childId && !regions.has(childId)) {
848
- regions.set(childId, {
849
- childId,
850
- element: node,
851
- frame: undefined,
852
- adopt: true
853
- });
910
+ if (childId && childId.startsWith(prefix)) {
911
+ const argKey = childId.slice(childId.lastIndexOf(".") + 1);
912
+ if (!regions.has(argKey)) {
913
+ regions.set(argKey, {
914
+ childId,
915
+ element: node,
916
+ frame: undefined,
917
+ adopt: true
918
+ });
919
+ }
854
920
  }
855
921
  return;
856
922
  }
857
- for (let c = node.firstChild; c; c = c.nextSibling) collectRegionElements(c, regions);
923
+ for (let c = node.firstChild; c; c = c.nextSibling) collectRegionElements(c, regions, prefix);
924
+ }
925
+ function renameRegion(entry, childId) {
926
+ entry.childId = childId;
927
+ if (entry.frame) entry.frame.rebind(childId);else entry.element.setAttribute(FRAME_ID_ATTR, childId);
858
928
  }
859
929
  function argsEquivalent(a, b) {
860
930
  if (a === b) return true;
@@ -1050,6 +1120,22 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1050
1120
  oldChild = next;
1051
1121
  }
1052
1122
  }
1123
+ function restoreDisplacedRanges(first, end, ranges) {
1124
+ const found = new Map();
1125
+ collectSlots(first, end, found);
1126
+ for (const [id, start] of found) {
1127
+ const displaced = ranges.get(id);
1128
+ if (!displaced || displaced === start) continue;
1129
+ ranges.delete(id);
1130
+ const parent = start.parentNode;
1131
+ if (displaced.nodeType === 11 ) {
1132
+ parent.insertBefore(displaced, start);
1133
+ } else {
1134
+ moveRangeBefore(parent, displaced, id, start);
1135
+ }
1136
+ stashRange(document.createDocumentFragment(), start, id);
1137
+ }
1138
+ }
1053
1139
  function stashRange(frag, start, id) {
1054
1140
  const end = slotEnd(id);
1055
1141
  let n = start;
@@ -1093,6 +1179,7 @@ async function applyFrameResponse(response, host, options = {}) {
1093
1179
  }
1094
1180
  const SERVER_COMPONENT = /*#__PURE__*/Symbol.for("dom-expressions.server-component");
1095
1181
  const SERVER_COMPONENT_ADDRESS = /*#__PURE__*/Symbol.for("dom-expressions.server-component-address");
1182
+ const COMPONENT_HANDOFF = /*#__PURE__*/Symbol.for("dom-expressions.component-handoff");
1096
1183
  let resolveServerComponent;
1097
1184
  function parseServerComponent(value, ctx) {
1098
1185
  return {
@@ -1147,18 +1234,48 @@ function createServerComponentHandler({
1147
1234
  codec = client.getServerFunctionsCodec
1148
1235
  }) {
1149
1236
  const byAddress = new Map();
1150
- const boundaryFor = address => {
1237
+ const forwards = new Map();
1238
+ const brand = (comp, fnId, frameId) => {
1239
+ const brandable = typeof comp === "function" || typeof comp === "object" && comp !== null;
1240
+ if (brandable && !comp[COMPONENT_HANDOFF]) {
1241
+ comp[COMPONENT_HANDOFF] = {
1242
+ fnId,
1243
+ frameId,
1244
+ take(prev) {
1245
+ const meta = prev !== null && (typeof prev === "function" || typeof prev === "object") && prev[COMPONENT_HANDOFF];
1246
+ if (!meta || meta.fnId !== fnId) return false;
1247
+ const root = meta.frameId;
1248
+ let cur = forwards.get(root) ?? root;
1249
+ if (cur !== root && !host.get(cur)) {
1250
+ forwards.delete(root);
1251
+ cur = root;
1252
+ }
1253
+ if (cur === frameId) return true;
1254
+ let frame = host.get(cur);
1255
+ if (!frame) return false;
1256
+ while (frame) {
1257
+ frame.rebind(frameId);
1258
+ frame = host.get(cur);
1259
+ }
1260
+ if (frameId === root) forwards.delete(root);else forwards.set(root, frameId);
1261
+ return true;
1262
+ }
1263
+ };
1264
+ }
1265
+ return comp;
1266
+ };
1267
+ const boundaryFor = (address, fnId) => {
1151
1268
  let entry = byAddress.get(address);
1152
1269
  if (!entry) {
1153
1270
  entry = {
1154
1271
  frameId: address,
1155
- component: component(address)
1272
+ component: brand(component(address), fnId, address)
1156
1273
  };
1157
1274
  byAddress.set(address, entry);
1158
1275
  }
1159
1276
  return entry;
1160
1277
  };
1161
- const resolveAddress = (id, address) => boundaryFor(address).component;
1278
+ const resolveAddress = (id, address) => boundaryFor(address, id).component;
1162
1279
  resolveServerComponent = resolveAddress;
1163
1280
  const versions = new Map();
1164
1281
  const bump = frameId => {
@@ -1173,7 +1290,7 @@ function createServerComponentHandler({
1173
1290
  const address = client.frameAddress(info.id, info.args);
1174
1291
  byAddress.set(address, {
1175
1292
  frameId: info.id,
1176
- component: hit
1293
+ component: brand(hit, info.id, info.id)
1177
1294
  });
1178
1295
  }
1179
1296
  return hit;
@@ -1187,11 +1304,11 @@ function createServerComponentHandler({
1187
1304
  if (adopted) {
1188
1305
  entry = {
1189
1306
  frameId: ctx.id,
1190
- component: adopted
1307
+ component: brand(adopted, ctx.id, ctx.id)
1191
1308
  };
1192
1309
  byAddress.set(address, entry);
1193
1310
  } else {
1194
- entry = boundaryFor(address);
1311
+ entry = boundaryFor(address, ctx.id);
1195
1312
  }
1196
1313
  }
1197
1314
  if (response.headers.has(client.SINGLE_FLIGHT_HEADER)) {
@@ -1216,7 +1333,7 @@ function createServerComponentHandler({
1216
1333
  if (!byAddress.has(address)) {
1217
1334
  byAddress.set(address, {
1218
1335
  frameId: functionId,
1219
- component,
1336
+ component: brand(component, functionId, functionId),
1220
1337
  address
1221
1338
  });
1222
1339
  }
@@ -1348,6 +1465,15 @@ function gatherClaims(el, registry) {
1348
1465
  if (el.hasAttribute(FRAME_ID_ATTR)) return;
1349
1466
  for (let c = el.firstElementChild; c; c = c.nextElementSibling) gatherClaims(c, registry);
1350
1467
  }
1468
+ function hasPendingFragment(existing) {
1469
+ for (const n of existing) {
1470
+ if (n.nodeType !== 1) continue;
1471
+ const el = n;
1472
+ if (el.tagName === "TEMPLATE" && el.id.startsWith("pl-")) return true;
1473
+ if (el.querySelector?.('template[id^="pl-"]')) return true;
1474
+ }
1475
+ return false;
1476
+ }
1351
1477
  function claimRender(prefix, existing, render) {
1352
1478
  const sc = solidJs.sharedConfig;
1353
1479
  if (!sc.getNextContextId) return render();
@@ -1356,10 +1482,11 @@ function claimRender(prefix, existing, render) {
1356
1482
  if (n.nodeType !== 1) continue;
1357
1483
  gatherClaims(n, registry);
1358
1484
  }
1359
- if (!registry.size) return render();
1485
+ if (!registry.size && !hasPendingFragment(existing)) return render();
1360
1486
  const prevRegistry = sc.registry;
1361
1487
  const prevHydrating = sc.hydrating;
1362
1488
  const prevClaimRoots = sc.claimRoots;
1489
+ if (prevRegistry) for (const key of registry.keys()) prevRegistry.delete(key);
1363
1490
  sc.registry = registry;
1364
1491
  sc.hydrating = true;
1365
1492
  sc.claimRoots = existing;
@@ -1373,6 +1500,19 @@ function claimRender(prefix, existing, render) {
1373
1500
  sc.claimRoots = prevClaimRoots;
1374
1501
  }
1375
1502
  }
1503
+ function liveSlotProps(initial, ctx) {
1504
+ const [args, setArgs] = solidJs.createSignal(initial);
1505
+ ctx.onUpdate(next => setArgs(() => next));
1506
+ return new Proxy({}, {
1507
+ get: (_, key) => args()[key],
1508
+ has: (_, key) => key in args(),
1509
+ ownKeys: () => Reflect.ownKeys(args()),
1510
+ getOwnPropertyDescriptor: (_, key) => key in args() ? {
1511
+ enumerable: true,
1512
+ configurable: true
1513
+ } : undefined
1514
+ });
1515
+ }
1376
1516
  function isReactiveContent(value) {
1377
1517
  if (typeof value === "function") return true;
1378
1518
  if (Array.isArray(value)) {
@@ -1403,7 +1543,10 @@ function slotsFor(props) {
1403
1543
  };
1404
1544
  const evaluate = () => {
1405
1545
  const v = props[prop];
1406
- return typeof v === "function" && ctx && ctx.invoked ? v(slotProps) : v;
1546
+ if (typeof v === "function" && ctx && ctx.invoked) {
1547
+ return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
1548
+ }
1549
+ return v;
1407
1550
  };
1408
1551
  const adopted = !!(ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length);
1409
1552
  const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";