@solidjs/web 2.0.0-beta.15 → 2.0.0-beta.17

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/dist/dev.js CHANGED
@@ -59,9 +59,9 @@ const syncOptions = {
59
59
  sync: true
60
60
  };
61
61
  const effect = (fn, effectFn, options) => createRenderEffect(fn, effectFn, options ? {
62
- transparent: true,
63
62
  sync: true,
64
- ...options
63
+ ...options,
64
+ transparent: !options.scope
65
65
  } : transparentOptions);
66
66
  const memo = fn => createMemo(() => fn(), syncOptions);
67
67
 
@@ -334,20 +334,41 @@ function addEvent(node, name, handler, delegate) {
334
334
  }
335
335
  function style(node, value, prev) {
336
336
  if (!value) {
337
- if (prev) setAttribute(node, "style");
337
+ if (prev || node._$styles) {
338
+ setAttribute(node, "style");
339
+ node._$styles = undefined;
340
+ }
338
341
  return;
339
342
  }
340
343
  const nodeStyle = node.style;
341
- if (typeof value === "string") return nodeStyle.cssText = value;
342
- typeof prev === "string" && (nodeStyle.cssText = prev = undefined);
343
- prev || (prev = {});
344
+ if (typeof value === "string") {
345
+ node._$styles = undefined;
346
+ return nodeStyle.cssText = value;
347
+ }
348
+ if (typeof prev === "string") {
349
+ nodeStyle.cssText = "";
350
+ prev = undefined;
351
+ }
352
+ let applied = node._$styles;
353
+ if (!applied) {
354
+ applied = node._$styles = prev ? {
355
+ ...prev
356
+ } : {};
357
+ }
344
358
  let v, s;
359
+ for (s in applied) {
360
+ if (value[s] == null) {
361
+ nodeStyle.removeProperty(s);
362
+ delete applied[s];
363
+ }
364
+ }
345
365
  for (s in value) {
346
366
  v = value[s];
347
- if (v !== prev[s]) nodeStyle.setProperty(s, v);
348
- delete prev[s];
367
+ if (v != null && v !== applied[s]) {
368
+ nodeStyle.setProperty(s, v);
369
+ applied[s] = v;
370
+ }
349
371
  }
350
- for (s in prev) value[s] == null && nodeStyle.removeProperty(s);
351
372
  }
352
373
  function setStyleProperty(node, name, value) {
353
374
  value != null ? node.style.setProperty(name, value) : node.style.removeProperty(name);
@@ -386,19 +407,28 @@ function ref(fn, element) {
386
407
  const resolved = untrack(fn);
387
408
  runWithOwner(null, () => applyRef(resolved, element));
388
409
  }
410
+ function scope(fn) {
411
+ fn.$s = true;
412
+ return fn;
413
+ }
414
+ const SCOPE_OPTIONS = {
415
+ scope: true
416
+ };
417
+ function stripTextSeparators(nodes) {
418
+ let j = 0;
419
+ for (let i = 0; i < nodes.length; i++) {
420
+ if (nodes[i].nodeType === 8 && nodes[i].nodeValue === "!$") nodes[i].remove();else nodes[j++] = nodes[i];
421
+ }
422
+ nodes.length = j;
423
+ return nodes;
424
+ }
389
425
  function insert(parent, accessor, marker, initial, options) {
390
426
  const multi = marker !== undefined;
391
427
  const host = options && options.host;
392
428
  if (multi && !initial) initial = [];
393
429
  if (isHydrating(parent)) {
394
430
  if (!multi && initial === undefined && parent) initial = [...parent.childNodes];
395
- if (Array.isArray(initial)) {
396
- let j = 0;
397
- for (let i = 0; i < initial.length; i++) {
398
- if (initial[i].nodeType === 8 && initial[i].nodeValue === "!$") initial[i].remove();else initial[j++] = initial[i];
399
- }
400
- initial.length = j;
401
- }
431
+ if (Array.isArray(initial)) stripTextSeparators(initial);
402
432
  }
403
433
  if (typeof accessor !== "function") {
404
434
  accessor = normalize(accessor, initial, multi, true);
@@ -414,10 +444,34 @@ function insert(parent, accessor, marker, initial, options) {
414
444
  initial = [placeholder];
415
445
  }
416
446
  let current = initial;
447
+ function reclaimSwappedRegion() {
448
+ if (!sharedConfig.hydrating || !current || !parent.isConnected) return;
449
+ const first = Array.isArray(current) ? current[0] : current;
450
+ if (!first || !first.nodeType || first.isConnected) return;
451
+ let nodes;
452
+ if (marker) {
453
+ nodes = [];
454
+ let node = marker.previousSibling,
455
+ depth = 0;
456
+ while (node) {
457
+ if (node.nodeType === 8) {
458
+ const v = node.nodeValue;
459
+ if (v === "/") depth++;else if (v === "$") {
460
+ if (depth === 0) break;
461
+ depth--;
462
+ }
463
+ }
464
+ nodes.unshift(node);
465
+ node = node.previousSibling;
466
+ }
467
+ } else nodes = [...parent.childNodes];
468
+ current = stripTextSeparators(nodes);
469
+ }
417
470
  effect(prev => {
471
+ reclaimSwappedRegion();
418
472
  const value = normalize(accessor(), current, multi, true);
419
473
  if (typeof value !== "function") return value;
420
- effect(() => normalize(value, current, multi), inner => {
474
+ effect(() => (reclaimSwappedRegion(), normalize(value, current, multi)), inner => {
421
475
  insertExpression(parent, inner, current, marker);
422
476
  current = inner;
423
477
  host && tagHost(current, host);
@@ -431,7 +485,11 @@ function insert(parent, accessor, marker, initial, options) {
431
485
  insertExpression(parent, value, current, marker);
432
486
  current = value;
433
487
  host && tagHost(current, host);
434
- }, options);
488
+ },
489
+ accessor.$s ? options ? {
490
+ ...options,
491
+ scope: true
492
+ } : SCOPE_OPTIONS : options);
435
493
  }
436
494
  function assign(node, props, skipChildren, prevProps = {}, skipRef = false) {
437
495
  const nodeName = node.nodeName;
@@ -450,19 +508,117 @@ function assign(node, props, skipChildren, prevProps = {}, skipRef = false) {
450
508
  prevProps[prop] = assignProp(node, prop, props[prop], prevProps[prop], skipRef, nodeName);
451
509
  }
452
510
  }
511
+ const ASSET_REMOVAL_GRACE = 100;
512
+ const assetRegistry = new Map();
513
+ function assetEntryKey(descriptor) {
514
+ if (descriptor.policy === "exclusive") return "x|" + descriptor.key;
515
+ return descriptor.type === "inline-style" ? "i|" + descriptor.id : descriptor.type + "|" + descriptor.href;
516
+ }
517
+ function findAssetElement(selector, attr, value) {
518
+ const nodes = document.querySelectorAll(selector);
519
+ for (let i = 0; i < nodes.length; i++) {
520
+ if (nodes[i].getAttribute(attr) === value) return nodes[i];
521
+ }
522
+ return null;
523
+ }
524
+ function mountAssetElement(descriptor) {
525
+ let el;
526
+ if (descriptor.type === "inline-style") {
527
+ el = findAssetElement("style[data-asset]", "data-asset", descriptor.id);
528
+ if (!el) {
529
+ el = document.createElement("style");
530
+ el.setAttribute("data-asset", descriptor.id);
531
+ el.textContent = descriptor.content || "";
532
+ }
533
+ } else {
534
+ const rel = descriptor.type === "module" ? "modulepreload" : "stylesheet";
535
+ el = findAssetElement(`link[rel="${rel}"]`, "href", descriptor.href);
536
+ if (!el) {
537
+ el = document.createElement("link");
538
+ el.rel = rel;
539
+ el.href = descriptor.href;
540
+ }
541
+ }
542
+ if (descriptor.attrs) {
543
+ for (const name in descriptor.attrs) el.setAttribute(name, descriptor.attrs[name]);
544
+ }
545
+ if (!el.isConnected) document.head.appendChild(el);
546
+ return el;
547
+ }
548
+ function acquireAsset(descriptor) {
549
+ const key = assetEntryKey(descriptor);
550
+ let entry = assetRegistry.get(key);
551
+ if (descriptor.policy === "exclusive") {
552
+ if (!entry) {
553
+ entry = {
554
+ original: descriptor.get(),
555
+ set: descriptor.set,
556
+ writers: []
557
+ };
558
+ assetRegistry.set(key, entry);
559
+ }
560
+ const writer = {
561
+ value: descriptor.value
562
+ };
563
+ entry.writers.push(writer);
564
+ entry.set(writer.value);
565
+ let released = false;
566
+ return () => {
567
+ if (released) return;
568
+ released = true;
569
+ const index = entry.writers.indexOf(writer);
570
+ const wasTop = index === entry.writers.length - 1;
571
+ entry.writers.splice(index, 1);
572
+ if (!wasTop) return;
573
+ if (entry.writers.length) {
574
+ entry.set(entry.writers[entry.writers.length - 1].value);
575
+ } else {
576
+ entry.set(entry.original);
577
+ assetRegistry.delete(key);
578
+ }
579
+ };
580
+ }
581
+ if (!entry) {
582
+ entry = {
583
+ count: 0,
584
+ element: null,
585
+ timer: null
586
+ };
587
+ assetRegistry.set(key, entry);
588
+ }
589
+ if (entry.timer) {
590
+ clearTimeout(entry.timer);
591
+ entry.timer = null;
592
+ }
593
+ entry.count++;
594
+ if (!entry.element || !entry.element.isConnected) entry.element = mountAssetElement(descriptor);
595
+ let released = false;
596
+ return () => {
597
+ if (released) return;
598
+ released = true;
599
+ if (--entry.count > 0) return;
600
+ entry.timer = setTimeout(() => {
601
+ assetRegistry.delete(key);
602
+ entry.element && entry.element.remove();
603
+ }, ASSET_REMOVAL_GRACE);
604
+ };
605
+ }
453
606
  function loadModuleAssets(mapping) {
454
607
  const hy = globalThis._$HY;
455
608
  if (!hy) return;
456
609
  const pending = [];
457
- for (const moduleUrl in mapping) {
458
- if (hy.modules[moduleUrl]) continue;
459
- const entryUrl = mapping[moduleUrl];
460
- if (!hy.loading[moduleUrl]) {
461
- hy.loading[moduleUrl] = import(entryUrl).then(mod => {
462
- hy.modules[moduleUrl] = mod;
610
+ for (const key in mapping) {
611
+ if (hy.modules[key]) continue;
612
+ const entryUrl = mapping[key];
613
+ if (!hy.loading[key]) {
614
+ hy.loading[key] = import(entryUrl).then(mod => {
615
+ hy.modules[key] = mod;
616
+ }, err => {
617
+ delete hy.loading[key];
618
+ throw err;
463
619
  });
464
620
  }
465
- pending.push(hy.loading[moduleUrl]);
621
+ pending.push(hy.loading[key]);
466
622
  }
467
623
  return pending.length ? Promise.all(pending).then(() => {}) : undefined;
468
624
  }
@@ -517,8 +673,11 @@ function hydrate$1(code, element, options = {}) {
517
673
  } finally {
518
674
  sharedConfig.hydrating = false;
519
675
  }
520
- }, () => {
676
+ }, err => {
677
+ console.error("Hydration module preload failed, falling back to client render:", err);
521
678
  sharedConfig.hydrating = false;
679
+ sharedConfig.registry = undefined;
680
+ disposer = render$1(code, element, [...element.childNodes], options);
522
681
  });
523
682
  return () => disposer && disposer();
524
683
  }
@@ -630,17 +789,32 @@ function runHydrationEvents() {
630
789
  const [el, e] = events[0];
631
790
  if (!completed.has(el)) return;
632
791
  events.shift();
633
- let match;
792
+ let matchContainer, matchState, matchDistance, matches;
634
793
  for (const [container, state] of delegatedContainers) {
635
794
  if (!state.handlers.has(e.type)) continue;
636
795
  const entry = findOwner(e.target, state);
637
- if (entry && (!match || entry.distance < match.distance)) match = {
638
- container,
639
- state,
640
- distance: entry.distance
641
- };
796
+ if (!entry) continue;
797
+ if (matchContainer) {
798
+ if (!matches) matches = [{
799
+ container: matchContainer,
800
+ state: matchState,
801
+ distance: matchDistance
802
+ }];
803
+ matches.push({
804
+ container,
805
+ state,
806
+ distance: entry.distance
807
+ });
808
+ } else {
809
+ matchContainer = container;
810
+ matchState = state;
811
+ matchDistance = entry.distance;
812
+ }
642
813
  }
643
- if (match) eventHandler(e, match.container, match.state);
814
+ if (matches) {
815
+ matches.sort((a, b) => a.distance - b.distance);
816
+ for (let i = 0; i < matches.length; i++) eventHandler(e, matches[i].container, matches[i].state);
817
+ } else if (matchContainer) eventHandler(e, matchContainer, matchState);
644
818
  }
645
819
  if (sharedConfig.done) {
646
820
  sharedConfig.events = _$HY.events = null;
@@ -711,11 +885,16 @@ function eventHandler(e, container, state) {
711
885
  if (sharedConfig.registry && sharedConfig.events) {
712
886
  if (sharedConfig.events.find(([el, ev]) => ev === e)) return;
713
887
  }
714
- if (e[$$EVENT_OWNER]) return;
888
+ const prev = e[$$EVENT_OWNER];
889
+ let resumeNode;
890
+ if (prev) {
891
+ if (prev === true || prev === container || !container.contains(prev)) return;
892
+ resumeNode = prev;
893
+ }
715
894
  const owner = state && (state.owners.size === 1 && state.owners.has(container) ? container : findOwner(e.target, state)?.owner);
716
895
  if (state && !owner) return;
717
896
  e[$$EVENT_OWNER] = owner || true;
718
- let node = e.target;
897
+ let node = resumeNode || e.target;
719
898
  const key = `$$${e.type}`;
720
899
  const oriTarget = e.target;
721
900
  const boundary = owner || container || e.currentTarget;
@@ -745,7 +924,10 @@ function eventHandler(e, container, state) {
745
924
  return node || boundary || document;
746
925
  }
747
926
  });
748
- if (e.composedPath) {
927
+ if (resumeNode) {
928
+ if (resumeNode === e.target) node = resumeNode._$host || resumeNode.parentNode || resumeNode.host;
929
+ if (node && node !== boundary) walkUpTree();
930
+ } else if (e.composedPath) {
749
931
  const path = e.composedPath();
750
932
  if (path.length) {
751
933
  retarget(path[0]);
@@ -775,7 +957,13 @@ function insertExpression(parent, value, current, marker) {
775
957
  const tc = typeof current;
776
958
  if (tc === "string" || tc === "number") {
777
959
  parent.firstChild.data = value;
778
- } else parent.textContent = value;
960
+ } else {
961
+ const owned = ownedChildCount(current);
962
+ if (owned === -1 || owned === parent.childNodes.length) parent.textContent = value;else {
963
+ removeOwnedChildren(parent, current);
964
+ parent.insertBefore(document.createTextNode(value), parent.firstChild);
965
+ }
966
+ }
779
967
  } else if (value === undefined) {
780
968
  cleanChildren(parent, current, marker);
781
969
  } else if (value.nodeType) {
@@ -798,7 +986,7 @@ function insertExpression(parent, value, current, marker) {
798
986
  appendNodes(parent, value, marker);
799
987
  } else reconcileArrays(parent, current, value, marker);
800
988
  } else {
801
- current && cleanChildren(parent);
989
+ current && cleanChildren(parent, current);
802
990
  appendNodes(parent, value);
803
991
  }
804
992
  } else console.warn(`Unrecognized value. Skipped inserting`, value);
@@ -838,8 +1026,31 @@ function appendNodes(parent, array, marker = null) {
838
1026
  if (marker) n[$$SLOT] = marker;
839
1027
  }
840
1028
  }
1029
+ function ownedChildCount(current) {
1030
+ if (Array.isArray(current)) return current.length;
1031
+ if (current == null) return -1;
1032
+ if (current === "") return 0;
1033
+ return 1;
1034
+ }
1035
+ function removeOwnedChildren(parent, current) {
1036
+ if (Array.isArray(current)) {
1037
+ for (let i = 0; i < current.length; i++) {
1038
+ const el = current[i];
1039
+ if (el.parentNode === parent) el.remove();
1040
+ }
1041
+ } else if (current.nodeType) {
1042
+ if (current.parentNode === parent) current.remove();
1043
+ } else {
1044
+ const first = parent.firstChild;
1045
+ if (first && first.nodeType === 3) first.remove();
1046
+ }
1047
+ }
841
1048
  function cleanChildren(parent, current, marker, replacement) {
842
- if (marker === undefined) return parent.textContent = "";
1049
+ if (marker === undefined) {
1050
+ const owned = ownedChildCount(current);
1051
+ if (owned === -1 || owned === parent.childNodes.length) return parent.textContent = "";
1052
+ return removeOwnedChildren(parent, current);
1053
+ }
843
1054
  if (current.length) {
844
1055
  let inserted = false;
845
1056
  for (let i = current.length - 1; i >= 0; i--) {
@@ -930,9 +1141,10 @@ function Portal(props) {
930
1141
  return () => {
931
1142
  dispose();
932
1143
  let c = startMarker;
933
- while (c && c !== endMarker) {
1144
+ while (c) {
934
1145
  const n = c.nextSibling;
935
1146
  m.removeChild(c);
1147
+ if (c === endMarker) break;
936
1148
  c = n;
937
1149
  }
938
1150
  };
@@ -979,4 +1191,4 @@ function createElement(tagName, is = undefined) {
979
1191
  });
980
1192
  }
981
1193
 
982
- export { voidFn as Assets, ChildProperties, DOMElements, DOMWithState, DelegatedEvents, Dynamic, voidFn as HydrationScript, MathMLElements, Namespaces, Portal, RawTextElements, RequestContext, SVGElements, VoidElements, addEvent, applyRef, assign, className, delegateEvents, dynamic, dynamicProperty, effect, escape, voidFn as generateHydrationScript, voidFn as getAssets, getDelegatedRoot, getFirstChild, getHydrationKey, getNextElement, getNextMarker, getNextMatch, getNextSibling, voidFn as getRequestEvent, hydrate, insert, isDev, isServer, memo, mergeProps, ref, registerDelegatedContainer, registerDelegatedRoot, render, renderToStream, renderToString, renderToStringAsync, resolveSSRNode, runHydrationEvents, setAttribute, setAttributeNS, setProperty, setStyleProperty, spread, ssr, ssrAttribute, ssrClassList, ssrElement, ssrHydrationKey, ssrStyle, style, template, unregisterDelegatedContainer, unregisterDelegatedRoot, voidFn as useAssets };
1194
+ export { voidFn as Assets, ChildProperties, DOMElements, DOMWithState, DelegatedEvents, Dynamic, voidFn as HydrationScript, MathMLElements, Namespaces, Portal, RawTextElements, RequestContext, SVGElements, VoidElements, acquireAsset, addEvent, applyRef, assign, className, delegateEvents, dynamic, dynamicProperty, effect, escape, voidFn as generateHydrationScript, voidFn as getAssets, getDelegatedRoot, getFirstChild, getHydrationKey, getNextElement, getNextMarker, getNextMatch, getNextSibling, voidFn as getRequestEvent, hydrate, insert, isDev, isServer, memo, mergeProps, ref, registerDelegatedContainer, registerDelegatedRoot, render, renderToStream, renderToString, renderToStringAsync, resolveSSRNode, runHydrationEvents, scope, setAttribute, setAttributeNS, setProperty, setStyleProperty, spread, ssr, ssrAttribute, ssrClassList, ssrElement, ssrHydrationKey, ssrStyle, style, template, unregisterDelegatedContainer, unregisterDelegatedRoot, voidFn as useAssets };