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

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/server.js CHANGED
@@ -102,7 +102,7 @@ Feature.RegExp;
102
102
 
103
103
  const HEAD_ELIGIBLE_TAGS = new Set(["title", "meta", "link", "style", "script", "base"]);
104
104
  const HEAD_ATTR_NAME = /^[a-zA-Z_][a-zA-Z0-9_:.-]*$/;
105
- const RESOURCE_LINK_RELS = new Set(["preload", "modulepreload", "prefetch", "preconnect", "dns-prefetch", "icon", "stylesheet"]);
105
+ const RESOURCE_LINK_RELS = new Set(["preload", "modulepreload", "prefetch", "preconnect", "dns-prefetch", "stylesheet"]);
106
106
  const RESOURCE_QUALIFIERS = ["as", "crossorigin", "type", "media", "imagesrcset", "imagesizes"];
107
107
  const STYLESHEET_FETCH_META = new Set(["crossorigin", "integrity", "referrerpolicy", "fetchpriority"]);
108
108
  function evalHeadValue(v) {
@@ -146,12 +146,14 @@ function replaceableIdentity(tag, props, key, unique) {
146
146
  if (tag === "meta" && props.charset != null) return "charset";
147
147
  if (key != null) return tag + ":key:" + key;
148
148
  if (tag === "meta") {
149
- if (props.name != null) return "meta:name:" + props.name;
150
- if (props.property != null) return "meta:property:" + props.property;
151
- if (props["http-equiv"] != null) return "meta:http-equiv:" + props["http-equiv"];
149
+ for (const ns of ["name", "property", "http-equiv"]) if (props[ns] != null) return "meta:" + ns + ":" + props[ns] + (props.media != null ? ":media=" + props.media : "");
152
150
  return unique;
153
151
  }
154
- if (tag === "link") return "link:" + (props.rel || "") + ":" + (props.href || "");
152
+ if (tag === "link") {
153
+ const rel = props.rel || "";
154
+ if (rel === "icon" || rel === "apple-touch-icon") return "link:" + rel + (props.sizes != null ? ":sizes=" + props.sizes : "") + (props.type != null ? ":type=" + props.type : "");
155
+ return "link:" + rel + ":" + (props.href || "");
156
+ }
155
157
  return unique;
156
158
  }
157
159
  function resolveHead(groups) {
@@ -343,6 +345,15 @@ function createHeadRegistry() {
343
345
  }
344
346
  function registerHeadTags(registry, context, tracking, emitResource, nonce, tags) {
345
347
  const boundary = context._currentBoundaryId || "";
348
+ if (typeof tags === "function") {
349
+ registry.pending.push({
350
+ boundary,
351
+ list: tags,
352
+ resource: (desc, rel) => emitHeadResource(registry, context, tracking, emitResource, nonce, desc, rel)
353
+ });
354
+ return;
355
+ }
356
+ if (!Array.isArray(tags)) tags = [tags];
346
357
  let replaceable = null;
347
358
  for (let i = 0; i < tags.length; i++) {
348
359
  const desc = tags[i];
@@ -430,9 +441,39 @@ function commitHeadBoundary(registry, boundary, isPendingFragment) {
430
441
  keep.push(reg);
431
442
  continue;
432
443
  }
444
+ let descs = reg.tags;
445
+ if (reg.list) {
446
+ let resolved;
447
+ try {
448
+ resolved = reg.list();
449
+ } catch (err) {
450
+ console.warn(`useHead: error evaluating head group membership`, err);
451
+ continue;
452
+ }
453
+ if (!Array.isArray(resolved)) resolved = [resolved];
454
+ descs = [];
455
+ for (let j = 0; j < resolved.length; j++) {
456
+ const desc = resolved[j];
457
+ if (!desc || !HEAD_ELIGIBLE_TAGS.has(desc.tag)) {
458
+ console.warn(`useHead: ignoring non-head tag`, desc);
459
+ continue;
460
+ }
461
+ const cls = classifyHeadTag(desc);
462
+ if (cls.resource) {
463
+ reg.resource(desc, cls.rel);
464
+ } else {
465
+ descs.push(cls.rel !== undefined ? {
466
+ tag: desc.tag,
467
+ props: desc.props,
468
+ key: desc.key,
469
+ rel: cls.rel
470
+ } : desc);
471
+ }
472
+ }
473
+ }
433
474
  const tags = [];
434
- for (let j = 0; j < reg.tags.length; j++) {
435
- const desc = reg.tags[j];
475
+ for (let j = 0; j < descs.length; j++) {
476
+ const desc = descs[j];
436
477
  let props, key;
437
478
  try {
438
479
  props = evalHeadProps(desc.props || {}, desc.rel !== undefined ? {
@@ -581,10 +622,10 @@ function useHead(tags) {
581
622
  console.warn("useHead() called outside of a server render; registration ignored.");
582
623
  return;
583
624
  }
584
- ctx.registerHeadTags(Array.isArray(tags) ? tags : [tags]);
625
+ ctx.registerHeadTags(tags);
585
626
  }
586
627
  const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
587
- const REPLACE_SCRIPT = `function $df(e){return _$HY.f?_$HY.f(e):$dfr(e)}function $dfr(e,n,o,t){if(!(n=document.getElementById(e)))return 0;if(!(o=document.getElementById("pl-"+e)))return(_$HY.dq=_$HY.dq||{})[e]=1,0;for(;o&&(8!==o.nodeType||o.nodeValue!=="pl-"+e);)t=o.nextSibling,o.remove(),o=t;t=o.parentNode,o.replaceWith(n.content),n.remove(),_$HY.fe(e,t),_$HY.hp&&_$HY.hp[e]&&($dh(_$HY.hp[e]),delete _$HY.hp[e]),$dfd();return 1}function $dfl(e,o,n){if(!(o=document.getElementById("pl-"+e)))return(_$HY.dlq=_$HY.dlq||{})[e]=1,0;if(o._$fl)return 1;for(n=o.nextSibling;n;){if(8===n.nodeType&&n.nodeValue==="pl-"+e){o.parentNode&&o.parentNode.insertBefore(o.content.cloneNode(!0),n),o._$fl=1,$dfd();return 1}n=n.nextSibling}return 0}function $dflj(e,i){for(i=0;i<e.length;i++)$dfl(e[i])}function $dfd(e,i){if(e=_$HY.dq){_$HY.dq=0;for(i in e)$df(i)}if(e=_$HY.dlq){_$HY.dlq=0;for(i in e)$dfl(i)}}function $dfs(e,c,d){(_$HY.sc=_$HY.sc||{})[e]=c,d&&((_$HY.sd=_$HY.sd||{})[e]=1)}function $dfg(e,g,i,k){if(!(g=_$HY.sg&&_$HY.sg[e]))return;for(i=0;i<g.length;i++)if(_$HY.sc&&_$HY.sc[g[i]]>0)return;for(i=0;i<g.length;i++)k=g[i],delete _$HY.sg[k],$df(k)}function $dfc(e){if(--_$HY.sc[e]<=0){delete _$HY.sc[e],_$HY.sg&&_$HY.sg[e]?$dfg(e):!(_$HY.sd&&_$HY.sd[e])&&$df(e);_$HY.sd&&delete _$HY.sd[e]}}function $dfj(e,i,n){for(i=0;i<e.length;i++)if(_$HY.sc&&_$HY.sc[e[i]]>0){for(n=0;n<e.length;n++)(_$HY.sg=_$HY.sg||{})[e[n]]=e;return}for(i=0;i<e.length;i++)$df(e[i])}`;
628
+ const REPLACE_SCRIPT = `function $df(e){return _$HY.f?_$HY.f(e):$dfr(e)}function $dfr(e,n,o,t){if(!(n=document.getElementById(e)))return 0;if(!(o=document.getElementById("pl-"+e)))return(_$HY.dq=_$HY.dq||{})[e]=1,0;for(;o&&(8!==o.nodeType||o.nodeValue!=="pl-"+e);)t=o.nextSibling,o.remove(),o=t;t=o.parentNode,o.replaceWith(n.content),n.remove(),(_$HY.v=_$HY.v||{})[e]=1,_$HY.fe(e,t),_$HY.hp&&_$HY.hp[e]&&($dh(_$HY.hp[e]),delete _$HY.hp[e]),$dfd();return 1}function $dfl(e,o,n){if(!(o=document.getElementById("pl-"+e)))return(_$HY.dlq=_$HY.dlq||{})[e]=1,0;if(o._$fl)return 1;for(n=o.nextSibling;n;){if(8===n.nodeType&&n.nodeValue==="pl-"+e){o.parentNode&&o.parentNode.insertBefore(o.content.cloneNode(!0),n),o._$fl=1,$dfd();return 1}n=n.nextSibling}return 0}function $dflj(e,i){for(i=0;i<e.length;i++)$dfl(e[i])}function $dfd(e,i){if(e=_$HY.dq){_$HY.dq=0;for(i in e)$df(i)}if(e=_$HY.dlq){_$HY.dlq=0;for(i in e)$dfl(i)}}function $dfs(e,c,d){(_$HY.sc=_$HY.sc||{})[e]=c,d&&((_$HY.sd=_$HY.sd||{})[e]=1)}function $dfg(e,g,i,k){if(!(g=_$HY.sg&&_$HY.sg[e]))return;for(i=0;i<g.length;i++)if(_$HY.sc&&_$HY.sc[g[i]]>0)return;for(i=0;i<g.length;i++)k=g[i],delete _$HY.sg[k],$df(k)}function $dfc(e){if(--_$HY.sc[e]<=0){delete _$HY.sc[e],_$HY.sg&&_$HY.sg[e]?$dfg(e):!(_$HY.sd&&_$HY.sd[e])&&$df(e);_$HY.sd&&delete _$HY.sd[e]}}function $dfj(e,i,n){for(i=0;i<e.length;i++)if(_$HY.sc&&_$HY.sc[e[i]]>0){for(n=0;n<e.length;n++)(_$HY.sg=_$HY.sg||{})[e[n]]=e;return}for(i=0;i<e.length;i++)$df(e[i])}`;
588
629
  const HEAD_SCRIPT = `function $dha(o,i,e,n){for(i=0;i<o.length;i++)e=o[i],"t"==e[0]?((n=document.querySelector("title"))||(n=document.createElement("title"),document.head.appendChild(n)),n.textContent=e[1],n.setAttribute("data-dh","title")):"r"==e[0]?$dhr(e[1]):(n=document.createElement(e[2]),Object.keys(e[3]).forEach(function(a){n.setAttribute(a,e[3][a])}),null!=e[4]&&(n.textContent=e[4]),n.setAttribute("data-dh",e[1]),document.head.appendChild(n))}function $dhr(v,l,i){for(l=document.head.querySelectorAll("[data-dh]"),i=0;i<l.length;i++)l[i].getAttribute("data-dh")==v&&l[i].remove()}function $dh(o){_$HY.h?_$HY.h(o):$dha(o)}`;
589
630
  function renderToString(code, options = {}) {
590
631
  const {
@@ -667,6 +708,41 @@ function renderToStream(code, options = {}) {
667
708
  onHead
668
709
  } = options;
669
710
  let dispose;
711
+ let dead = false;
712
+ const abandon = () => {
713
+ if (dead) return;
714
+ dead = true;
715
+ completed = true;
716
+ buffer = {
717
+ write() {}
718
+ };
719
+ writable = {
720
+ end() {}
721
+ };
722
+ if (dispose) {
723
+ const d = dispose;
724
+ dispose = () => {};
725
+ d();
726
+ }
727
+ };
728
+ const guardSink = w => ({
729
+ write(payload) {
730
+ if (dead) return;
731
+ try {
732
+ w.write(payload);
733
+ } catch (_) {
734
+ abandon();
735
+ }
736
+ },
737
+ end() {
738
+ if (dead) return;
739
+ try {
740
+ w.end();
741
+ } catch (_) {
742
+ abandon();
743
+ }
744
+ }
745
+ });
670
746
  const blockingPromises = new Set();
671
747
  let headerEmitted = false;
672
748
  const pushTask = task => {
@@ -1010,8 +1086,8 @@ function renderToStream(code, options = {}) {
1010
1086
  }
1011
1087
  function doShell() {
1012
1088
  if (shellCompleted) return;
1013
- if (!resolveRootHoles()) return;
1014
1089
  sharedConfig.context = context;
1090
+ if (!resolveRootHoles()) return;
1015
1091
  const assetsHtml = resolveAssetsHtml(context.assets);
1016
1092
  headStyles = new Set();
1017
1093
  for (const url of tracking.emittedAssets) {
@@ -1068,14 +1144,24 @@ function renderToStream(code, options = {}) {
1068
1144
  function flush() {
1069
1145
  allSettled(blockingPromises).then(() => {
1070
1146
  scheduleFlush(() => {
1147
+ if (dead) return resolve();
1071
1148
  doShell();
1072
1149
  if (!shellCompleted) return flush();
1073
1150
  const encoder = new TextEncoder();
1074
1151
  const writer = w.getWriter();
1075
1152
  let pendingWrites = Promise.resolve();
1153
+ let ended = false;
1154
+ const failed = () => {
1155
+ if (!ended) {
1156
+ abandon();
1157
+ resolve();
1158
+ }
1159
+ };
1160
+ writer.closed && writer.closed.catch(failed);
1076
1161
  writable = {
1077
1162
  end() {
1078
1163
  pendingWrites.then(() => {
1164
+ ended = true;
1079
1165
  writer.releaseLock();
1080
1166
  w.close().catch(() => {});
1081
1167
  resolve();
@@ -1084,7 +1170,7 @@ function renderToStream(code, options = {}) {
1084
1170
  };
1085
1171
  buffer = {
1086
1172
  write(payload) {
1087
- pendingWrites = pendingWrites.then(() => writer.write(encoder.encode(payload))).catch(() => {});
1173
+ pendingWrites = pendingWrites.then(() => writer.write(encoder.encode(payload))).catch(failed);
1088
1174
  }
1089
1175
  };
1090
1176
  buffer.write(tmp);
@@ -1127,9 +1213,10 @@ function renderToStream(code, options = {}) {
1127
1213
  function flush() {
1128
1214
  allSettled(blockingPromises).then(() => {
1129
1215
  scheduleFlush(() => {
1216
+ if (dead) return;
1130
1217
  doShell();
1131
1218
  if (!shellCompleted) return flush();
1132
- buffer = writable = w;
1219
+ buffer = writable = guardSink(w);
1133
1220
  buffer.write(tmp);
1134
1221
  firstFlushed = true;
1135
1222
  if (completed) {
package/dist/web.cjs CHANGED
@@ -172,7 +172,7 @@ function reconcileArrays(parentNode, a, b, marker) {
172
172
 
173
173
  const HEAD_ELIGIBLE_TAGS = new Set(["title", "meta", "link", "style", "script", "base"]);
174
174
  const HEAD_ATTR_NAME = /^[a-zA-Z_][a-zA-Z0-9_:.-]*$/;
175
- const RESOURCE_LINK_RELS = new Set(["preload", "modulepreload", "prefetch", "preconnect", "dns-prefetch", "icon", "stylesheet"]);
175
+ const RESOURCE_LINK_RELS = new Set(["preload", "modulepreload", "prefetch", "preconnect", "dns-prefetch", "stylesheet"]);
176
176
  const RESOURCE_QUALIFIERS = ["as", "crossorigin", "type", "media", "imagesrcset", "imagesizes"];
177
177
  function evalHeadValue(v) {
178
178
  return typeof v === "function" ? v() : v;
@@ -215,12 +215,14 @@ function replaceableIdentity(tag, props, key, unique) {
215
215
  if (tag === "meta" && props.charset != null) return "charset";
216
216
  if (key != null) return tag + ":key:" + key;
217
217
  if (tag === "meta") {
218
- if (props.name != null) return "meta:name:" + props.name;
219
- if (props.property != null) return "meta:property:" + props.property;
220
- if (props["http-equiv"] != null) return "meta:http-equiv:" + props["http-equiv"];
218
+ for (const ns of ["name", "property", "http-equiv"]) if (props[ns] != null) return "meta:" + ns + ":" + props[ns] + (props.media != null ? ":media=" + props.media : "");
221
219
  return unique;
222
220
  }
223
- if (tag === "link") return "link:" + (props.rel || "") + ":" + (props.href || "");
221
+ if (tag === "link") {
222
+ const rel = props.rel || "";
223
+ if (rel === "icon" || rel === "apple-touch-icon") return "link:" + rel + (props.sizes != null ? ":sizes=" + props.sizes : "") + (props.type != null ? ":type=" + props.type : "");
224
+ return "link:" + rel + ":" + (props.href || "");
225
+ }
224
226
  return unique;
225
227
  }
226
228
  function resolveHead(groups) {
@@ -831,21 +833,25 @@ function flushHeadRegistry() {
831
833
  headOwned.set(identity, els);
832
834
  }
833
835
  }
834
- function renderHeadElement(t, identity, existing) {
835
- for (let i = 0; i < existing.length; i++) {
836
- if (headElementMatches(existing[i], t)) return existing.splice(i, 1)[0];
837
- }
838
- const el = document.createElement(t.tag);
839
- for (const name in t.props) {
836
+ function createHeadElement(tag, props) {
837
+ const el = document.createElement(tag);
838
+ for (const name in props) {
840
839
  if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
841
840
  if (!HEAD_ATTR_NAME.test(name)) {
842
841
  continue;
843
842
  }
844
- const v = t.props[name];
843
+ const v = props[name];
845
844
  if (v == null || v === false) continue;
846
845
  el.setAttribute(name, v === true ? "" : String(v));
847
846
  }
848
- if (t.props.children != null) el.textContent = String(t.props.children);
847
+ if (props.children != null) el.textContent = String(props.children);
848
+ return el;
849
+ }
850
+ function renderHeadElement(t, identity, existing) {
851
+ for (let i = 0; i < existing.length; i++) {
852
+ if (headElementMatches(existing[i], t)) return existing.splice(i, 1)[0];
853
+ }
854
+ const el = createHeadElement(t.tag, t.props);
849
855
  el.setAttribute("data-dh", identity);
850
856
  document.head.appendChild(el);
851
857
  return el;
@@ -888,23 +894,11 @@ function acquireHeadResource(tag, props) {
888
894
  if (url != null) {
889
895
  if (tag === "link") el = findAssetElement(`link[rel="${props.rel}"]`, "href", url);else if (tag === "script") el = findAssetElement("script[src]", "src", url);else el = findAssetElement("style[href]", "href", url);
890
896
  }
891
- if (!el) {
892
- el = document.createElement(tag);
893
- for (const name in props) {
894
- if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
895
- if (!HEAD_ATTR_NAME.test(name)) continue;
896
- const v = props[name];
897
- if (v == null || v === false) continue;
898
- el.setAttribute(name, v === true ? "" : String(v));
899
- }
900
- if (props.children != null) el.textContent = String(props.children);
901
- document.head.appendChild(el);
902
- }
897
+ if (!el) document.head.appendChild(createHeadElement(tag, props));
903
898
  return noopFn;
904
899
  }
905
900
  function noopFn() {}
906
901
  function useHead(tags) {
907
- const list = Array.isArray(tags) ? tags : [tags];
908
902
  initHeadRegistry();
909
903
  const reg = {
910
904
  seq: -1,
@@ -912,6 +906,8 @@ function useHead(tags) {
912
906
  };
913
907
  const uid = ++headUid;
914
908
  effect(() => {
909
+ let list = typeof tags === "function" ? tags() : tags;
910
+ if (!Array.isArray(list)) list = [list];
915
911
  const replaceable = [];
916
912
  const resources = [];
917
913
  for (let i = 0; i < list.length; i++) {
@@ -1559,19 +1555,29 @@ function portalImpl(props) {
1559
1555
  });
1560
1556
  return treeMarker;
1561
1557
  }
1562
- const COMPONENT_HANDOFF = Symbol.for("dom-expressions.component-handoff");
1563
- function resolveHandoff(next, prev) {
1564
- const handoff = typeof next === "function" && next !== null && next[COMPONENT_HANDOFF];
1565
- return handoff && handoff.take(prev) ? prev : next;
1558
+ const COMPONENT_BINDING = Symbol.for("dom-expressions.component-binding");
1559
+ function bindingOf(value) {
1560
+ return value !== null && (typeof value === "function" || typeof value === "object") && value[COMPONENT_BINDING] || undefined;
1566
1561
  }
1567
1562
  function dynamic(source) {
1568
1563
  let latest = 0;
1564
+ const sites = new Set();
1565
+ const resolveBinding = (next, prev) => {
1566
+ const binding = bindingOf(next);
1567
+ if (!binding) return next;
1568
+ const prevBinding = bindingOf(prev);
1569
+ if (prevBinding && prevBinding.component === binding.component) {
1570
+ for (const deliver of sites) deliver(binding.address);
1571
+ return prev;
1572
+ }
1573
+ return next;
1574
+ };
1569
1575
  const cached = solidJs.createMemo(prev => {
1570
1576
  const next = source();
1571
- if (!next || typeof next.then !== "function") return resolveHandoff(next, prev);
1577
+ if (!next || typeof next.then !== "function") return resolveBinding(next, prev);
1572
1578
  const token = ++latest;
1573
1579
  return {
1574
- then: (onFulfilled, onRejected) => next.then(resolved => onFulfilled(token === latest ? resolveHandoff(resolved, prev) : resolved), onRejected)
1580
+ then: (onFulfilled, onRejected) => next.then(resolved => onFulfilled(token === latest ? resolveBinding(resolved, prev) : resolved), onRejected)
1575
1581
  };
1576
1582
  }, {
1577
1583
  lazy: true
@@ -1581,7 +1587,16 @@ function dynamic(source) {
1581
1587
  const component = cached();
1582
1588
  switch (typeof component) {
1583
1589
  case "function":
1584
- return solidJs.untrack(() => component(props));
1590
+ {
1591
+ const binding = bindingOf(component);
1592
+ if (binding) {
1593
+ const [address, setAddress] = solidJs.createSignal(binding.address);
1594
+ sites.add(setAddress);
1595
+ solidJs.onCleanup(() => sites.delete(setAddress));
1596
+ return solidJs.untrack(() => binding.component(props, address));
1597
+ }
1598
+ return solidJs.untrack(() => component(props));
1599
+ }
1585
1600
  case "string":
1586
1601
  const el = solidJs.sharedConfig.hydrating ? getNextElement() : createElement(component, solidJs.untrack(() => props.is));
1587
1602
  spread(el, props);
@@ -1618,9 +1633,7 @@ _moduleUrl) {
1618
1633
  if ((Comp = solidJs.untrack(comp)) && !solidJs.sharedConfig.hydrating) return Comp(rest);
1619
1634
  const [mounted, setMounted] = solidJs.createSignal(!solidJs.sharedConfig.hydrating);
1620
1635
  const gate = solidJs.createMemo(() => (Comp = comp(), m = mounted(), solidJs.untrack(() => Comp && m ? Comp(rest) : props.fallback)));
1621
- solidJs.onSettled(() => {
1622
- setMounted(true);
1623
- });
1636
+ solidJs.sharedConfig.onHydrationEnd(() => setMounted(true));
1624
1637
  return gate;
1625
1638
  };
1626
1639
  }
package/dist/web.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createRenderEffect, createMemo, sharedConfig, untrack, runWithOwner, flatten, createRoot, merge, createComponent, omit, createOwner, createSignal, onSettled, enableHydration, flush, getOwner, createEffect } from 'solid-js';
1
+ import { createRenderEffect, createMemo, sharedConfig, untrack, runWithOwner, flatten, createRoot, merge, createComponent, omit, createOwner, createSignal, onCleanup, enableHydration, flush, getOwner, createEffect } from 'solid-js';
2
2
  export { Errored, For, Hydration, Loading, Match, NoHydration, Repeat, Reveal, Show, Switch, createComponent, getOwner, untrack } from 'solid-js';
3
3
 
4
4
  const DOMWithState = {
@@ -171,7 +171,7 @@ function reconcileArrays(parentNode, a, b, marker) {
171
171
 
172
172
  const HEAD_ELIGIBLE_TAGS = new Set(["title", "meta", "link", "style", "script", "base"]);
173
173
  const HEAD_ATTR_NAME = /^[a-zA-Z_][a-zA-Z0-9_:.-]*$/;
174
- const RESOURCE_LINK_RELS = new Set(["preload", "modulepreload", "prefetch", "preconnect", "dns-prefetch", "icon", "stylesheet"]);
174
+ const RESOURCE_LINK_RELS = new Set(["preload", "modulepreload", "prefetch", "preconnect", "dns-prefetch", "stylesheet"]);
175
175
  const RESOURCE_QUALIFIERS = ["as", "crossorigin", "type", "media", "imagesrcset", "imagesizes"];
176
176
  function evalHeadValue(v) {
177
177
  return typeof v === "function" ? v() : v;
@@ -214,12 +214,14 @@ function replaceableIdentity(tag, props, key, unique) {
214
214
  if (tag === "meta" && props.charset != null) return "charset";
215
215
  if (key != null) return tag + ":key:" + key;
216
216
  if (tag === "meta") {
217
- if (props.name != null) return "meta:name:" + props.name;
218
- if (props.property != null) return "meta:property:" + props.property;
219
- if (props["http-equiv"] != null) return "meta:http-equiv:" + props["http-equiv"];
217
+ for (const ns of ["name", "property", "http-equiv"]) if (props[ns] != null) return "meta:" + ns + ":" + props[ns] + (props.media != null ? ":media=" + props.media : "");
220
218
  return unique;
221
219
  }
222
- if (tag === "link") return "link:" + (props.rel || "") + ":" + (props.href || "");
220
+ if (tag === "link") {
221
+ const rel = props.rel || "";
222
+ if (rel === "icon" || rel === "apple-touch-icon") return "link:" + rel + (props.sizes != null ? ":sizes=" + props.sizes : "") + (props.type != null ? ":type=" + props.type : "");
223
+ return "link:" + rel + ":" + (props.href || "");
224
+ }
223
225
  return unique;
224
226
  }
225
227
  function resolveHead(groups) {
@@ -830,21 +832,25 @@ function flushHeadRegistry() {
830
832
  headOwned.set(identity, els);
831
833
  }
832
834
  }
833
- function renderHeadElement(t, identity, existing) {
834
- for (let i = 0; i < existing.length; i++) {
835
- if (headElementMatches(existing[i], t)) return existing.splice(i, 1)[0];
836
- }
837
- const el = document.createElement(t.tag);
838
- for (const name in t.props) {
835
+ function createHeadElement(tag, props) {
836
+ const el = document.createElement(tag);
837
+ for (const name in props) {
839
838
  if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
840
839
  if (!HEAD_ATTR_NAME.test(name)) {
841
840
  continue;
842
841
  }
843
- const v = t.props[name];
842
+ const v = props[name];
844
843
  if (v == null || v === false) continue;
845
844
  el.setAttribute(name, v === true ? "" : String(v));
846
845
  }
847
- if (t.props.children != null) el.textContent = String(t.props.children);
846
+ if (props.children != null) el.textContent = String(props.children);
847
+ return el;
848
+ }
849
+ function renderHeadElement(t, identity, existing) {
850
+ for (let i = 0; i < existing.length; i++) {
851
+ if (headElementMatches(existing[i], t)) return existing.splice(i, 1)[0];
852
+ }
853
+ const el = createHeadElement(t.tag, t.props);
848
854
  el.setAttribute("data-dh", identity);
849
855
  document.head.appendChild(el);
850
856
  return el;
@@ -887,23 +893,11 @@ function acquireHeadResource(tag, props) {
887
893
  if (url != null) {
888
894
  if (tag === "link") el = findAssetElement(`link[rel="${props.rel}"]`, "href", url);else if (tag === "script") el = findAssetElement("script[src]", "src", url);else el = findAssetElement("style[href]", "href", url);
889
895
  }
890
- if (!el) {
891
- el = document.createElement(tag);
892
- for (const name in props) {
893
- if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
894
- if (!HEAD_ATTR_NAME.test(name)) continue;
895
- const v = props[name];
896
- if (v == null || v === false) continue;
897
- el.setAttribute(name, v === true ? "" : String(v));
898
- }
899
- if (props.children != null) el.textContent = String(props.children);
900
- document.head.appendChild(el);
901
- }
896
+ if (!el) document.head.appendChild(createHeadElement(tag, props));
902
897
  return noopFn;
903
898
  }
904
899
  function noopFn() {}
905
900
  function useHead(tags) {
906
- const list = Array.isArray(tags) ? tags : [tags];
907
901
  initHeadRegistry();
908
902
  const reg = {
909
903
  seq: -1,
@@ -911,6 +905,8 @@ function useHead(tags) {
911
905
  };
912
906
  const uid = ++headUid;
913
907
  effect(() => {
908
+ let list = typeof tags === "function" ? tags() : tags;
909
+ if (!Array.isArray(list)) list = [list];
914
910
  const replaceable = [];
915
911
  const resources = [];
916
912
  for (let i = 0; i < list.length; i++) {
@@ -1558,19 +1554,29 @@ function portalImpl(props) {
1558
1554
  });
1559
1555
  return treeMarker;
1560
1556
  }
1561
- const COMPONENT_HANDOFF = Symbol.for("dom-expressions.component-handoff");
1562
- function resolveHandoff(next, prev) {
1563
- const handoff = typeof next === "function" && next !== null && next[COMPONENT_HANDOFF];
1564
- return handoff && handoff.take(prev) ? prev : next;
1557
+ const COMPONENT_BINDING = Symbol.for("dom-expressions.component-binding");
1558
+ function bindingOf(value) {
1559
+ return value !== null && (typeof value === "function" || typeof value === "object") && value[COMPONENT_BINDING] || undefined;
1565
1560
  }
1566
1561
  function dynamic(source) {
1567
1562
  let latest = 0;
1563
+ const sites = new Set();
1564
+ const resolveBinding = (next, prev) => {
1565
+ const binding = bindingOf(next);
1566
+ if (!binding) return next;
1567
+ const prevBinding = bindingOf(prev);
1568
+ if (prevBinding && prevBinding.component === binding.component) {
1569
+ for (const deliver of sites) deliver(binding.address);
1570
+ return prev;
1571
+ }
1572
+ return next;
1573
+ };
1568
1574
  const cached = createMemo(prev => {
1569
1575
  const next = source();
1570
- if (!next || typeof next.then !== "function") return resolveHandoff(next, prev);
1576
+ if (!next || typeof next.then !== "function") return resolveBinding(next, prev);
1571
1577
  const token = ++latest;
1572
1578
  return {
1573
- then: (onFulfilled, onRejected) => next.then(resolved => onFulfilled(token === latest ? resolveHandoff(resolved, prev) : resolved), onRejected)
1579
+ then: (onFulfilled, onRejected) => next.then(resolved => onFulfilled(token === latest ? resolveBinding(resolved, prev) : resolved), onRejected)
1574
1580
  };
1575
1581
  }, {
1576
1582
  lazy: true
@@ -1580,7 +1586,16 @@ function dynamic(source) {
1580
1586
  const component = cached();
1581
1587
  switch (typeof component) {
1582
1588
  case "function":
1583
- return untrack(() => component(props));
1589
+ {
1590
+ const binding = bindingOf(component);
1591
+ if (binding) {
1592
+ const [address, setAddress] = createSignal(binding.address);
1593
+ sites.add(setAddress);
1594
+ onCleanup(() => sites.delete(setAddress));
1595
+ return untrack(() => binding.component(props, address));
1596
+ }
1597
+ return untrack(() => component(props));
1598
+ }
1584
1599
  case "string":
1585
1600
  const el = sharedConfig.hydrating ? getNextElement() : createElement(component, untrack(() => props.is));
1586
1601
  spread(el, props);
@@ -1617,9 +1632,7 @@ _moduleUrl) {
1617
1632
  if ((Comp = untrack(comp)) && !sharedConfig.hydrating) return Comp(rest);
1618
1633
  const [mounted, setMounted] = createSignal(!sharedConfig.hydrating);
1619
1634
  const gate = createMemo(() => (Comp = comp(), m = mounted(), untrack(() => Comp && m ? Comp(rest) : props.fallback)));
1620
- onSettled(() => {
1621
- setMounted(true);
1622
- });
1635
+ sharedConfig.onHydrationEnd(() => setMounted(true));
1623
1636
  return gate;
1624
1637
  };
1625
1638
  }