@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.
package/dist/dev.js CHANGED
@@ -169,6 +169,89 @@ function reconcileArrays(parentNode, a, b, marker) {
169
169
  }
170
170
  }
171
171
 
172
+ const HEAD_ELIGIBLE_TAGS = new Set(["title", "meta", "link", "style", "script", "base"]);
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"]);
175
+ const RESOURCE_QUALIFIERS = ["as", "crossorigin", "type", "media", "imagesrcset", "imagesizes"];
176
+ function evalHeadValue(v) {
177
+ return typeof v === "function" ? v() : v;
178
+ }
179
+ function evalHeadProps(props, presets) {
180
+ const out = {};
181
+ for (const name in props) out[name] = presets && name in presets ? presets[name] : evalHeadValue(props[name]);
182
+ return out;
183
+ }
184
+ function classifyHeadTag(desc) {
185
+ const tag = desc.tag;
186
+ if (tag === "link") {
187
+ const rel = evalHeadValue(desc.props && desc.props.rel);
188
+ return {
189
+ resource: RESOURCE_LINK_RELS.has(rel),
190
+ rel
191
+ };
192
+ }
193
+ if (tag === "style") return {
194
+ resource: !!(desc.props && "href" in desc.props)
195
+ };
196
+ if (tag === "script") return {
197
+ resource: !!(desc.props && "src" in desc.props)
198
+ };
199
+ return {
200
+ resource: false
201
+ };
202
+ }
203
+ function resourceIdentity(tag, props) {
204
+ let id = "res:" + tag + ":" + (props.rel || "") + ":" + (props.href || props.src || "");
205
+ for (let i = 0; i < RESOURCE_QUALIFIERS.length; i++) {
206
+ const q = RESOURCE_QUALIFIERS[i];
207
+ if (props[q] != null) id += ":" + q + "=" + props[q];
208
+ }
209
+ return id;
210
+ }
211
+ function replaceableIdentity(tag, props, key, unique) {
212
+ if (tag === "title") return "title";
213
+ if (tag === "base") return "base";
214
+ if (tag === "meta" && props.charset != null) return "charset";
215
+ if (key != null) return tag + ":key:" + key;
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"];
220
+ return unique;
221
+ }
222
+ if (tag === "link") return "link:" + (props.rel || "") + ":" + (props.href || "");
223
+ return unique;
224
+ }
225
+ function resolveHead(groups) {
226
+ const winners = new Map();
227
+ const sorted = groups.slice().sort((a, b) => a.seq - b.seq);
228
+ for (let i = 0; i < sorted.length; i++) {
229
+ const group = sorted[i];
230
+ const byIdentity = new Map();
231
+ for (let j = 0; j < group.tags.length; j++) {
232
+ const t = group.tags[j];
233
+ let list = byIdentity.get(t.identity);
234
+ if (!list) byIdentity.set(t.identity, list = []);
235
+ list.push(t);
236
+ }
237
+ for (const [identity, tags] of byIdentity) {
238
+ if (identity === "title") {
239
+ if (tags.length > 1) console.warn("Multiple <title> tags in one head group; the last one wins.");
240
+ winners.set(identity, {
241
+ seq: group.seq,
242
+ tags: [tags[tags.length - 1]]
243
+ });
244
+ } else {
245
+ winners.set(identity, {
246
+ seq: group.seq,
247
+ tags
248
+ });
249
+ }
250
+ }
251
+ }
252
+ return winners;
253
+ }
254
+
172
255
  const $$EVENT_OWNER = "_$DX_EVENT_OWNER";
173
256
  const INNER_OWNED = {};
174
257
  const delegatedEvents = new Set();
@@ -654,6 +737,235 @@ function acquireAsset(descriptor) {
654
737
  }, ASSET_REMOVAL_GRACE);
655
738
  };
656
739
  }
740
+ let headRegistrations = null;
741
+ let headSeq = 0;
742
+ let headUid = 0;
743
+ let headOwned = null;
744
+ let headApplied = null;
745
+ let headFallbackTitle = null;
746
+ let headScheduled = false;
747
+ const headMountedResources = new Set();
748
+ function initHeadRegistry() {
749
+ if (headRegistrations) return;
750
+ headRegistrations = [];
751
+ headOwned = new Map();
752
+ headApplied = new Map();
753
+ const t = document.querySelector("title");
754
+ headFallbackTitle = t && !t.hasAttribute("data-dh") ? t.textContent : null;
755
+ if (globalThis._$HY) globalThis._$HY.h = applyServerHeadOps;
756
+ }
757
+ function applyServerHeadOps(ops) {
758
+ for (let i = 0; i < ops.length; i++) {
759
+ const op = ops[i];
760
+ const identity = op[0] === "t" ? "title" : op[1];
761
+ if (headOwned.has(identity)) continue;
762
+ if (op[0] === "t") setHeadTitle(op[1]);else if (op[0] === "r") {
763
+ const els = headMarkedElements(identity);
764
+ for (let j = 0; j < els.length; j++) els[j].remove();
765
+ } else {
766
+ const el = document.createElement(op[2]);
767
+ for (const name in op[3]) el.setAttribute(name, op[3][name]);
768
+ if (op[4] != null) el.textContent = op[4];
769
+ el.setAttribute("data-dh", identity);
770
+ document.head.appendChild(el);
771
+ }
772
+ }
773
+ }
774
+ function headMarkedElements(identity) {
775
+ const nodes = document.head.querySelectorAll("[data-dh]");
776
+ const out = [];
777
+ for (let i = 0; i < nodes.length; i++) {
778
+ if (nodes[i].getAttribute("data-dh") === identity) out.push(nodes[i]);
779
+ }
780
+ return out;
781
+ }
782
+ function setHeadTitle(text) {
783
+ let el = document.querySelector("title");
784
+ if (!el) {
785
+ el = document.createElement("title");
786
+ document.head.appendChild(el);
787
+ }
788
+ el.textContent = text;
789
+ el.setAttribute("data-dh", "title");
790
+ return el;
791
+ }
792
+ function scheduleHeadApply() {
793
+ if (headScheduled) return;
794
+ headScheduled = true;
795
+ queueMicrotask(flushHeadRegistry);
796
+ }
797
+ function flushHeadRegistry() {
798
+ if (sharedConfig.hydrating) {
799
+ setTimeout(flushHeadRegistry, 0);
800
+ return;
801
+ }
802
+ headScheduled = false;
803
+ const winners = resolveHead(headRegistrations);
804
+ for (const [identity, els] of headOwned) {
805
+ if (winners.has(identity)) continue;
806
+ headOwned.delete(identity);
807
+ headApplied.delete(identity);
808
+ if (identity === "title") {
809
+ if (headFallbackTitle != null) setHeadTitle(headFallbackTitle).removeAttribute("data-dh");
810
+ } else {
811
+ for (let i = 0; i < els.length; i++) els[i].remove();
812
+ }
813
+ }
814
+ for (const [identity, winner] of winners) {
815
+ let sig = "";
816
+ for (let i = 0; i < winner.tags.length; i++) sig += winner.tags[i].tag + JSON.stringify(winner.tags[i].props) + "|";
817
+ if (headApplied.get(identity) === sig) continue;
818
+ headApplied.set(identity, sig);
819
+ if (identity === "base" || identity === "charset") {
820
+ console.warn(`useHead: <${winner.tags[0].tag}> (${identity}) is shell-only and ignored on the client`);
821
+ continue;
822
+ }
823
+ if (identity === "title") {
824
+ const children = winner.tags[0].props.children;
825
+ setHeadTitle(children == null ? "" : String(children));
826
+ headOwned.set(identity, []);
827
+ continue;
828
+ }
829
+ const existing = headMarkedElements(identity);
830
+ const els = [];
831
+ for (let i = 0; i < winner.tags.length; i++) {
832
+ els.push(renderHeadElement(winner.tags[i], identity, existing));
833
+ }
834
+ for (let i = 0; i < existing.length; i++) {
835
+ if (els.indexOf(existing[i]) === -1) existing[i].remove();
836
+ }
837
+ headOwned.set(identity, els);
838
+ }
839
+ }
840
+ function renderHeadElement(t, identity, existing) {
841
+ for (let i = 0; i < existing.length; i++) {
842
+ if (headElementMatches(existing[i], t)) return existing.splice(i, 1)[0];
843
+ }
844
+ const el = document.createElement(t.tag);
845
+ for (const name in t.props) {
846
+ if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
847
+ if (!HEAD_ATTR_NAME.test(name)) {
848
+ console.warn(`useHead: ignoring invalid attribute name "${name}"`);
849
+ continue;
850
+ }
851
+ const v = t.props[name];
852
+ if (v == null || v === false) continue;
853
+ el.setAttribute(name, v === true ? "" : String(v));
854
+ }
855
+ if (t.props.children != null) el.textContent = String(t.props.children);
856
+ el.setAttribute("data-dh", identity);
857
+ document.head.appendChild(el);
858
+ return el;
859
+ }
860
+ function headElementMatches(el, t) {
861
+ if (el.tagName.toLowerCase() !== t.tag) return false;
862
+ for (const name in t.props) {
863
+ if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
864
+ if (!HEAD_ATTR_NAME.test(name)) continue;
865
+ const v = t.props[name];
866
+ if (v == null || v === false) {
867
+ if (el.hasAttribute(name)) return false;
868
+ } else if (el.getAttribute(name) !== (v === true ? "" : String(v))) return false;
869
+ }
870
+ const children = t.props.children;
871
+ return (children == null ? "" : String(children)) === el.textContent;
872
+ }
873
+ function acquireHeadResource(tag, props) {
874
+ if (tag === "link" && (props.rel === "stylesheet" || props.rel === "modulepreload")) {
875
+ const descriptor = {
876
+ type: props.rel === "stylesheet" ? "style" : "module",
877
+ href: props.href
878
+ };
879
+ let attrs = null;
880
+ for (const name in props) {
881
+ if (name === "rel" || name === "href") continue;
882
+ if (!HEAD_ATTR_NAME.test(name)) continue;
883
+ const v = props[name];
884
+ if (v == null || v === false) continue;
885
+ (attrs || (attrs = {}))[name] = v === true ? "" : String(v);
886
+ }
887
+ if (attrs) descriptor.attrs = attrs;
888
+ return acquireAsset(descriptor);
889
+ }
890
+ const identity = resourceIdentity(tag, props);
891
+ if (headMountedResources.has(identity)) return noopFn;
892
+ headMountedResources.add(identity);
893
+ const url = props.href || props.src;
894
+ let el = null;
895
+ if (url != null) {
896
+ 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);
897
+ }
898
+ if (!el) {
899
+ el = document.createElement(tag);
900
+ for (const name in props) {
901
+ if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
902
+ if (!HEAD_ATTR_NAME.test(name)) continue;
903
+ const v = props[name];
904
+ if (v == null || v === false) continue;
905
+ el.setAttribute(name, v === true ? "" : String(v));
906
+ }
907
+ if (props.children != null) el.textContent = String(props.children);
908
+ document.head.appendChild(el);
909
+ }
910
+ return noopFn;
911
+ }
912
+ function noopFn() {}
913
+ function useHead(tags) {
914
+ const list = Array.isArray(tags) ? tags : [tags];
915
+ initHeadRegistry();
916
+ const reg = {
917
+ seq: -1,
918
+ tags: null
919
+ };
920
+ const uid = ++headUid;
921
+ effect(() => {
922
+ const replaceable = [];
923
+ const resources = [];
924
+ for (let i = 0; i < list.length; i++) {
925
+ const desc = list[i];
926
+ if (!desc || !HEAD_ELIGIBLE_TAGS.has(desc.tag)) {
927
+ console.warn(`useHead: ignoring non-head tag`, desc);
928
+ continue;
929
+ }
930
+ const cls = classifyHeadTag(desc);
931
+ const props = evalHeadProps(desc.props || {}, cls.rel !== undefined ? {
932
+ rel: cls.rel
933
+ } : undefined);
934
+ if (cls.resource) {
935
+ resources.push({
936
+ tag: desc.tag,
937
+ props
938
+ });
939
+ } else {
940
+ const key = evalHeadValue(desc.key);
941
+ replaceable.push({
942
+ tag: desc.tag,
943
+ props,
944
+ identity: replaceableIdentity(desc.tag, props, key, "u:c" + uid + ":" + i)
945
+ });
946
+ }
947
+ }
948
+ return {
949
+ replaceable,
950
+ resources
951
+ };
952
+ }, evaluated => {
953
+ const releases = [];
954
+ for (let i = 0; i < evaluated.resources.length; i++) {
955
+ releases.push(acquireHeadResource(evaluated.resources[i].tag, evaluated.resources[i].props));
956
+ }
957
+ reg.tags = evaluated.replaceable;
958
+ if (reg.seq < 0) reg.seq = ++headSeq;
959
+ if (headRegistrations.indexOf(reg) === -1) headRegistrations.push(reg);
960
+ scheduleHeadApply();
961
+ return () => {
962
+ const idx = headRegistrations.indexOf(reg);
963
+ if (idx > -1) headRegistrations.splice(idx, 1);
964
+ for (let i = 0; i < releases.length; i++) releases[i]();
965
+ scheduleHeadApply();
966
+ };
967
+ });
968
+ }
657
969
  function loadModuleAssets(mapping) {
658
970
  const hy = globalThis._$HY;
659
971
  if (!hy) return;
@@ -1309,8 +1621,21 @@ function portalImpl(props) {
1309
1621
  });
1310
1622
  return treeMarker;
1311
1623
  }
1624
+ const COMPONENT_HANDOFF = Symbol.for("dom-expressions.component-handoff");
1625
+ function resolveHandoff(next, prev) {
1626
+ const handoff = typeof next === "function" && next !== null && next[COMPONENT_HANDOFF];
1627
+ return handoff && handoff.take(prev) ? prev : next;
1628
+ }
1312
1629
  function dynamic(source) {
1313
- const cached = createMemo(source, {
1630
+ let latest = 0;
1631
+ const cached = createMemo(prev => {
1632
+ const next = source();
1633
+ if (!next || typeof next.then !== "function") return resolveHandoff(next, prev);
1634
+ const token = ++latest;
1635
+ return {
1636
+ then: (onFulfilled, onRejected) => next.then(resolved => onFulfilled(token === latest ? resolveHandoff(resolved, prev) : resolved), onRejected)
1637
+ };
1638
+ }, {
1314
1639
  lazy: true
1315
1640
  });
1316
1641
  return props => {
@@ -1342,7 +1667,8 @@ function createElement(tagName, is = undefined) {
1342
1667
  function loadClientOnly(fn, setComp) {
1343
1668
  fn().then(m => setComp(() => m.default));
1344
1669
  }
1345
- function clientOnly(fn, options = {}) {
1670
+ function clientOnly(fn, options = {},
1671
+ _moduleUrl) {
1346
1672
  const [comp, setComp] = createSignal();
1347
1673
  let started = !options.lazy;
1348
1674
  started && loadClientOnly(fn, setComp);
@@ -1366,4 +1692,4 @@ function clientOnly(fn, options = {}) {
1366
1692
  function httpStatus(_code, _text) {}
1367
1693
  function httpHeader(_name, _value, _options) {}
1368
1694
 
1369
- export { voidFn as Assets, ChildProperties, DOMElements, DOMWithState, DelegatedEvents, Dynamic, HREF, voidFn as HydrationScript, MathMLElements, Namespaces, Portal, REVALIDATE_HEADER, RawTextElements, RequestContext, ResponseEnvelope, SVGElements, VoidElements, acquireAsset, addEvent, applyRef, assign, claimElement, claimElementTree, className, clientOnly, delegateEvents, dynamic, dynamicProperty, effect, escape, voidFn as generateHydrationScript, voidFn as getAssets, getDelegatedRoot, getFirstChild, getHydrationKey, getNextElement, getNextMarker, getNextMatch, getNextSibling, voidFn as getRequestEvent, httpHeader, httpStatus, hydrate, insert, installHydrationRuntime, isDev, isHref, isResponseEnvelope, isServer, memo, mergeProps, redirect, ref, registerDelegatedContainer, registerDelegatedRoot, registerElementClaim, reload, render, renderToStream, renderToString, renderToStringAsync, resolveSSRNode, respond, runHydrationEvents, scope, setAttribute, setAttributeNS, setProperty, setStyleProperty, spread, ssr, ssrAttribute, ssrClassList, ssrElement, ssrHydrationKey, ssrStyle, style, template, unregisterDelegatedContainer, unregisterDelegatedRoot, voidFn as useAssets };
1695
+ export { voidFn as Assets, ChildProperties, DOMElements, DOMWithState, DelegatedEvents, Dynamic, HREF, voidFn as HydrationScript, MathMLElements, Namespaces, Portal, REVALIDATE_HEADER, RawTextElements, RequestContext, ResponseEnvelope, SVGElements, VoidElements, acquireAsset, addEvent, applyRef, assign, claimElement, claimElementTree, className, clientOnly, delegateEvents, dynamic, dynamicProperty, effect, escape, voidFn as generateHydrationScript, voidFn as getAssets, getDelegatedRoot, getFirstChild, getHydrationKey, getNextElement, getNextMarker, getNextMatch, getNextSibling, voidFn as getRequestEvent, httpHeader, httpStatus, hydrate, insert, installHydrationRuntime, isDev, isHref, isResponseEnvelope, isServer, memo, mergeProps, redirect, ref, registerDelegatedContainer, registerDelegatedRoot, registerElementClaim, reload, render, renderToStream, renderToString, renderToStringAsync, resolveSSRNode, respond, runHydrationEvents, scope, setAttribute, setAttributeNS, setProperty, setStyleProperty, spread, ssr, ssrAttribute, ssrClassList, ssrElement, ssrHydrationKey, ssrStyle, style, template, unregisterDelegatedContainer, unregisterDelegatedRoot, voidFn as useAssets, useHead };