@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/web.cjs CHANGED
@@ -170,6 +170,88 @@ function reconcileArrays(parentNode, a, b, marker) {
170
170
  }
171
171
  }
172
172
 
173
+ const HEAD_ELIGIBLE_TAGS = new Set(["title", "meta", "link", "style", "script", "base"]);
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"]);
176
+ const RESOURCE_QUALIFIERS = ["as", "crossorigin", "type", "media", "imagesrcset", "imagesizes"];
177
+ function evalHeadValue(v) {
178
+ return typeof v === "function" ? v() : v;
179
+ }
180
+ function evalHeadProps(props, presets) {
181
+ const out = {};
182
+ for (const name in props) out[name] = presets && name in presets ? presets[name] : evalHeadValue(props[name]);
183
+ return out;
184
+ }
185
+ function classifyHeadTag(desc) {
186
+ const tag = desc.tag;
187
+ if (tag === "link") {
188
+ const rel = evalHeadValue(desc.props && desc.props.rel);
189
+ return {
190
+ resource: RESOURCE_LINK_RELS.has(rel),
191
+ rel
192
+ };
193
+ }
194
+ if (tag === "style") return {
195
+ resource: !!(desc.props && "href" in desc.props)
196
+ };
197
+ if (tag === "script") return {
198
+ resource: !!(desc.props && "src" in desc.props)
199
+ };
200
+ return {
201
+ resource: false
202
+ };
203
+ }
204
+ function resourceIdentity(tag, props) {
205
+ let id = "res:" + tag + ":" + (props.rel || "") + ":" + (props.href || props.src || "");
206
+ for (let i = 0; i < RESOURCE_QUALIFIERS.length; i++) {
207
+ const q = RESOURCE_QUALIFIERS[i];
208
+ if (props[q] != null) id += ":" + q + "=" + props[q];
209
+ }
210
+ return id;
211
+ }
212
+ function replaceableIdentity(tag, props, key, unique) {
213
+ if (tag === "title") return "title";
214
+ if (tag === "base") return "base";
215
+ if (tag === "meta" && props.charset != null) return "charset";
216
+ if (key != null) return tag + ":key:" + key;
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"];
221
+ return unique;
222
+ }
223
+ if (tag === "link") return "link:" + (props.rel || "") + ":" + (props.href || "");
224
+ return unique;
225
+ }
226
+ function resolveHead(groups) {
227
+ const winners = new Map();
228
+ const sorted = groups.slice().sort((a, b) => a.seq - b.seq);
229
+ for (let i = 0; i < sorted.length; i++) {
230
+ const group = sorted[i];
231
+ const byIdentity = new Map();
232
+ for (let j = 0; j < group.tags.length; j++) {
233
+ const t = group.tags[j];
234
+ let list = byIdentity.get(t.identity);
235
+ if (!list) byIdentity.set(t.identity, list = []);
236
+ list.push(t);
237
+ }
238
+ for (const [identity, tags] of byIdentity) {
239
+ if (identity === "title") {
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
+
173
255
  const $$EVENT_OWNER = "_$DX_EVENT_OWNER";
174
256
  const INNER_OWNED = {};
175
257
  const delegatedEvents = new Set();
@@ -650,6 +732,232 @@ function acquireAsset(descriptor) {
650
732
  }, ASSET_REMOVAL_GRACE);
651
733
  };
652
734
  }
735
+ let headRegistrations = null;
736
+ let headSeq = 0;
737
+ let headUid = 0;
738
+ let headOwned = null;
739
+ let headApplied = null;
740
+ let headFallbackTitle = null;
741
+ let headScheduled = false;
742
+ const headMountedResources = new Set();
743
+ function initHeadRegistry() {
744
+ if (headRegistrations) return;
745
+ headRegistrations = [];
746
+ headOwned = new Map();
747
+ headApplied = new Map();
748
+ const t = document.querySelector("title");
749
+ headFallbackTitle = t && !t.hasAttribute("data-dh") ? t.textContent : null;
750
+ if (globalThis._$HY) globalThis._$HY.h = applyServerHeadOps;
751
+ }
752
+ function applyServerHeadOps(ops) {
753
+ for (let i = 0; i < ops.length; i++) {
754
+ const op = ops[i];
755
+ const identity = op[0] === "t" ? "title" : op[1];
756
+ if (headOwned.has(identity)) continue;
757
+ if (op[0] === "t") setHeadTitle(op[1]);else if (op[0] === "r") {
758
+ const els = headMarkedElements(identity);
759
+ for (let j = 0; j < els.length; j++) els[j].remove();
760
+ } else {
761
+ const el = document.createElement(op[2]);
762
+ for (const name in op[3]) el.setAttribute(name, op[3][name]);
763
+ if (op[4] != null) el.textContent = op[4];
764
+ el.setAttribute("data-dh", identity);
765
+ document.head.appendChild(el);
766
+ }
767
+ }
768
+ }
769
+ function headMarkedElements(identity) {
770
+ const nodes = document.head.querySelectorAll("[data-dh]");
771
+ const out = [];
772
+ for (let i = 0; i < nodes.length; i++) {
773
+ if (nodes[i].getAttribute("data-dh") === identity) out.push(nodes[i]);
774
+ }
775
+ return out;
776
+ }
777
+ function setHeadTitle(text) {
778
+ let el = document.querySelector("title");
779
+ if (!el) {
780
+ el = document.createElement("title");
781
+ document.head.appendChild(el);
782
+ }
783
+ el.textContent = text;
784
+ el.setAttribute("data-dh", "title");
785
+ return el;
786
+ }
787
+ function scheduleHeadApply() {
788
+ if (headScheduled) return;
789
+ headScheduled = true;
790
+ queueMicrotask(flushHeadRegistry);
791
+ }
792
+ function flushHeadRegistry() {
793
+ if (solidJs.sharedConfig.hydrating) {
794
+ setTimeout(flushHeadRegistry, 0);
795
+ return;
796
+ }
797
+ headScheduled = false;
798
+ const winners = resolveHead(headRegistrations);
799
+ for (const [identity, els] of headOwned) {
800
+ if (winners.has(identity)) continue;
801
+ headOwned.delete(identity);
802
+ headApplied.delete(identity);
803
+ if (identity === "title") {
804
+ if (headFallbackTitle != null) setHeadTitle(headFallbackTitle).removeAttribute("data-dh");
805
+ } else {
806
+ for (let i = 0; i < els.length; i++) els[i].remove();
807
+ }
808
+ }
809
+ for (const [identity, winner] of winners) {
810
+ let sig = "";
811
+ for (let i = 0; i < winner.tags.length; i++) sig += winner.tags[i].tag + JSON.stringify(winner.tags[i].props) + "|";
812
+ if (headApplied.get(identity) === sig) continue;
813
+ headApplied.set(identity, sig);
814
+ if (identity === "base" || identity === "charset") {
815
+ continue;
816
+ }
817
+ if (identity === "title") {
818
+ const children = winner.tags[0].props.children;
819
+ setHeadTitle(children == null ? "" : String(children));
820
+ headOwned.set(identity, []);
821
+ continue;
822
+ }
823
+ const existing = headMarkedElements(identity);
824
+ const els = [];
825
+ for (let i = 0; i < winner.tags.length; i++) {
826
+ els.push(renderHeadElement(winner.tags[i], identity, existing));
827
+ }
828
+ for (let i = 0; i < existing.length; i++) {
829
+ if (els.indexOf(existing[i]) === -1) existing[i].remove();
830
+ }
831
+ headOwned.set(identity, els);
832
+ }
833
+ }
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) {
840
+ if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
841
+ if (!HEAD_ATTR_NAME.test(name)) {
842
+ continue;
843
+ }
844
+ const v = t.props[name];
845
+ if (v == null || v === false) continue;
846
+ el.setAttribute(name, v === true ? "" : String(v));
847
+ }
848
+ if (t.props.children != null) el.textContent = String(t.props.children);
849
+ el.setAttribute("data-dh", identity);
850
+ document.head.appendChild(el);
851
+ return el;
852
+ }
853
+ function headElementMatches(el, t) {
854
+ if (el.tagName.toLowerCase() !== t.tag) return false;
855
+ for (const name in t.props) {
856
+ if (name === "children" || name === "ref" || name.slice(0, 2) === "on") continue;
857
+ if (!HEAD_ATTR_NAME.test(name)) continue;
858
+ const v = t.props[name];
859
+ if (v == null || v === false) {
860
+ if (el.hasAttribute(name)) return false;
861
+ } else if (el.getAttribute(name) !== (v === true ? "" : String(v))) return false;
862
+ }
863
+ const children = t.props.children;
864
+ return (children == null ? "" : String(children)) === el.textContent;
865
+ }
866
+ function acquireHeadResource(tag, props) {
867
+ if (tag === "link" && (props.rel === "stylesheet" || props.rel === "modulepreload")) {
868
+ const descriptor = {
869
+ type: props.rel === "stylesheet" ? "style" : "module",
870
+ href: props.href
871
+ };
872
+ let attrs = null;
873
+ for (const name in props) {
874
+ if (name === "rel" || name === "href") continue;
875
+ if (!HEAD_ATTR_NAME.test(name)) continue;
876
+ const v = props[name];
877
+ if (v == null || v === false) continue;
878
+ (attrs || (attrs = {}))[name] = v === true ? "" : String(v);
879
+ }
880
+ if (attrs) descriptor.attrs = attrs;
881
+ return acquireAsset(descriptor);
882
+ }
883
+ const identity = resourceIdentity(tag, props);
884
+ if (headMountedResources.has(identity)) return noopFn;
885
+ headMountedResources.add(identity);
886
+ const url = props.href || props.src;
887
+ let el = null;
888
+ if (url != null) {
889
+ 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
+ }
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
+ }
903
+ return noopFn;
904
+ }
905
+ function noopFn() {}
906
+ function useHead(tags) {
907
+ const list = Array.isArray(tags) ? tags : [tags];
908
+ initHeadRegistry();
909
+ const reg = {
910
+ seq: -1,
911
+ tags: null
912
+ };
913
+ const uid = ++headUid;
914
+ effect(() => {
915
+ const replaceable = [];
916
+ const resources = [];
917
+ for (let i = 0; i < list.length; i++) {
918
+ const desc = list[i];
919
+ if (!desc || !HEAD_ELIGIBLE_TAGS.has(desc.tag)) {
920
+ continue;
921
+ }
922
+ const cls = classifyHeadTag(desc);
923
+ const props = evalHeadProps(desc.props || {}, cls.rel !== undefined ? {
924
+ rel: cls.rel
925
+ } : undefined);
926
+ if (cls.resource) {
927
+ resources.push({
928
+ tag: desc.tag,
929
+ props
930
+ });
931
+ } else {
932
+ const key = evalHeadValue(desc.key);
933
+ replaceable.push({
934
+ tag: desc.tag,
935
+ props,
936
+ identity: replaceableIdentity(desc.tag, props, key, "u:c" + uid + ":" + i)
937
+ });
938
+ }
939
+ }
940
+ return {
941
+ replaceable,
942
+ resources
943
+ };
944
+ }, evaluated => {
945
+ const releases = [];
946
+ for (let i = 0; i < evaluated.resources.length; i++) {
947
+ releases.push(acquireHeadResource(evaluated.resources[i].tag, evaluated.resources[i].props));
948
+ }
949
+ reg.tags = evaluated.replaceable;
950
+ if (reg.seq < 0) reg.seq = ++headSeq;
951
+ if (headRegistrations.indexOf(reg) === -1) headRegistrations.push(reg);
952
+ scheduleHeadApply();
953
+ return () => {
954
+ const idx = headRegistrations.indexOf(reg);
955
+ if (idx > -1) headRegistrations.splice(idx, 1);
956
+ for (let i = 0; i < releases.length; i++) releases[i]();
957
+ scheduleHeadApply();
958
+ };
959
+ });
960
+ }
653
961
  function loadModuleAssets(mapping) {
654
962
  const hy = globalThis._$HY;
655
963
  if (!hy) return;
@@ -1251,8 +1559,21 @@ function portalImpl(props) {
1251
1559
  });
1252
1560
  return treeMarker;
1253
1561
  }
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;
1566
+ }
1254
1567
  function dynamic(source) {
1255
- const cached = solidJs.createMemo(source, {
1568
+ let latest = 0;
1569
+ const cached = solidJs.createMemo(prev => {
1570
+ const next = source();
1571
+ if (!next || typeof next.then !== "function") return resolveHandoff(next, prev);
1572
+ const token = ++latest;
1573
+ return {
1574
+ then: (onFulfilled, onRejected) => next.then(resolved => onFulfilled(token === latest ? resolveHandoff(resolved, prev) : resolved), onRejected)
1575
+ };
1576
+ }, {
1256
1577
  lazy: true
1257
1578
  });
1258
1579
  return props => {
@@ -1281,7 +1602,8 @@ function createElement(tagName, is = undefined) {
1281
1602
  function loadClientOnly(fn, setComp) {
1282
1603
  fn().then(m => setComp(() => m.default));
1283
1604
  }
1284
- function clientOnly(fn, options = {}) {
1605
+ function clientOnly(fn, options = {},
1606
+ _moduleUrl) {
1285
1607
  const [comp, setComp] = solidJs.createSignal();
1286
1608
  let started = !options.lazy;
1287
1609
  started && loadClientOnly(fn, setComp);
@@ -1438,3 +1760,4 @@ exports.template = template;
1438
1760
  exports.unregisterDelegatedContainer = unregisterDelegatedContainer;
1439
1761
  exports.unregisterDelegatedRoot = unregisterDelegatedRoot;
1440
1762
  exports.useAssets = voidFn;
1763
+ exports.useHead = useHead;