unhead 3.1.6 → 3.1.7

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.
Files changed (61) hide show
  1. package/dist/client.d.mts +5 -6
  2. package/dist/client.d.ts +5 -6
  3. package/dist/client.mjs +7 -12
  4. package/dist/index.d.mts +3 -3
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.mjs +15 -10
  7. package/dist/legacy.d.mts +2 -2
  8. package/dist/legacy.d.ts +2 -2
  9. package/dist/legacy.mjs +9 -9
  10. package/dist/parser.mjs +11 -7
  11. package/dist/plugins.d.mts +2 -2
  12. package/dist/plugins.d.ts +2 -2
  13. package/dist/plugins.mjs +7 -7
  14. package/dist/scripts.d.mts +3 -3
  15. package/dist/scripts.d.ts +3 -3
  16. package/dist/scripts.mjs +2 -3
  17. package/dist/server.d.mts +3 -3
  18. package/dist/server.d.ts +3 -3
  19. package/dist/server.mjs +5 -5
  20. package/dist/shared/{unhead.fg-0ge_u.mjs → unhead.1eoQpFT1.mjs} +6 -1
  21. package/dist/shared/{unhead.CRVUlr67.d.mts → unhead.BIArU35P.d.mts} +1 -1
  22. package/dist/shared/{unhead.BSqa2bYq.mjs → unhead.BNhb5t7_.mjs} +4 -6
  23. package/dist/shared/{unhead.BeIvDlYY.d.mts → unhead.BZy3nXHx.d.mts} +1 -1
  24. package/dist/shared/{unhead.DvIxXxuO.mjs → unhead.Bm4Y6XQI.mjs} +3 -0
  25. package/dist/shared/{unhead.B37CPNIq.d.mts → unhead.Bx2bAQEg.d.mts} +1 -1
  26. package/dist/shared/{unhead.D-W0R71w.mjs → unhead.C-RMAxyM.mjs} +27 -38
  27. package/dist/shared/{unhead.CS8tFxG3.d.mts → unhead.CELAVVhh.d.mts} +3 -5
  28. package/dist/shared/unhead.CHEy9ana.mjs +7 -0
  29. package/dist/shared/{unhead.BhvKU-Uu.d.ts → unhead.CN7FHaDF.d.ts} +1 -1
  30. package/dist/shared/{unhead.H5gLMTHA.mjs → unhead.CR32jx68.mjs} +125 -35
  31. package/dist/shared/{unhead.CBkhoTGw.mjs → unhead.CT1xoGPh.mjs} +1 -1
  32. package/dist/shared/unhead.CaGVnLUe.mjs +231 -0
  33. package/dist/shared/{unhead.YBmQyFpm.d.ts → unhead.Cnhe7n9-.d.ts} +1 -1
  34. package/dist/shared/{unhead.Blkhu0Cr.d.ts → unhead.DQadHCLi.d.ts} +1 -1
  35. package/dist/shared/{unhead.CBj56GgY.d.ts → unhead.Dv-MkoX9.d.ts} +3 -5
  36. package/dist/shared/{unhead.CfgPMHXt.mjs → unhead.WK9wg_ep.mjs} +2 -0
  37. package/dist/shared/{unhead.Doc2Fa-a.mjs → unhead.YIW3bMYK.mjs} +64 -33
  38. package/dist/shared/{unhead.BZYKjYpn.mjs → unhead.b15Kg1OH.mjs} +3 -3
  39. package/dist/shared/{unhead.J7mPNB3E.mjs → unhead.tpXvlPwt.mjs} +53 -34
  40. package/dist/shared/{unhead.BWKj-eGY.d.ts → unhead.xa1ebPBp.d.ts} +1 -1
  41. package/dist/shared/{unhead.BO2PdEL4.d.mts → unhead.zyZmXwAG.d.mts} +1 -1
  42. package/dist/stream/client.d.mts +2 -2
  43. package/dist/stream/client.d.ts +2 -2
  44. package/dist/stream/client.mjs +3 -8
  45. package/dist/stream/iife.d.cts +2 -0
  46. package/dist/stream/iife.global.js +1 -1
  47. package/dist/stream/iife.mjs +2 -2
  48. package/dist/stream/server.d.mts +2 -2
  49. package/dist/stream/server.d.ts +2 -2
  50. package/dist/stream/server.mjs +5 -5
  51. package/dist/stream/unplugin.d.mts +3 -1
  52. package/dist/stream/unplugin.d.ts +3 -1
  53. package/dist/stream/unplugin.mjs +28 -6
  54. package/dist/types.d.mts +2 -2
  55. package/dist/types.d.ts +2 -2
  56. package/dist/utils.d.mts +1 -1
  57. package/dist/utils.d.ts +1 -1
  58. package/dist/utils.mjs +4 -4
  59. package/package.json +2 -2
  60. package/dist/shared/unhead.CT3gGqve.mjs +0 -154
  61. package/dist/shared/unhead.CUXLLRtV.mjs +0 -5
@@ -1,10 +1,14 @@
1
- import { U as UniqueTags, c as TagsWithInnerContent, M as MetaTagsArrayable, H as HasElementTags, T as TagConfigKeys, D as DupeableTags, d as UsesMergeStrategy, V as ValidHeadTags } from './unhead.fg-0ge_u.mjs';
2
- import { a as callHook } from './unhead.DvIxXxuO.mjs';
1
+ import { U as UniqueTags, c as TagsWithInnerContent, M as MetaTagsArrayable, i as isUnsafeKey, H as HasElementTags, T as TagConfigKeys, D as DupeableTags, d as UsesMergeStrategy, V as ValidHeadTags } from './unhead.1eoQpFT1.mjs';
2
+ import { a as callHook } from './unhead.Bm4Y6XQI.mjs';
3
3
 
4
4
  const META_NOREWRITE_RE = /^(?:viewport|description|keywords|robots)$/;
5
5
  const META_KEY_ATTRS = ["name", "property", "http-equiv"];
6
6
  function isMetaArrayDupeKey(v) {
7
- return MetaTagsArrayable.has(v.split(":")[1]);
7
+ const i = v.indexOf(":");
8
+ if (i === -1)
9
+ return false;
10
+ const j = v.indexOf(":", i + 1);
11
+ return MetaTagsArrayable.has(v.slice(i + 1, j === -1 ? v.length : j));
8
12
  }
9
13
  function dedupeKey(tag) {
10
14
  const { props, tag: t, key } = tag;
@@ -36,7 +40,18 @@ function dedupeKey(tag) {
36
40
  return TagsWithInnerContent.has(t) && (tag.textContent || tag.innerHTML) ? `${t}:content:${tag.textContent || tag.innerHTML}` : void 0;
37
41
  }
38
42
  function hashTag(tag) {
39
- return tag._h || tag._d || tag.textContent || tag.innerHTML || `${tag.tag}:${Object.entries(tag.props).map(([k, v]) => `${k}:${String(v)}`).join()}`;
43
+ const identity = tag._h || tag._d || tag.textContent || tag.innerHTML;
44
+ if (identity)
45
+ return identity;
46
+ let hash = `${tag.tag}:`;
47
+ let separator = "";
48
+ for (const key in tag.props) {
49
+ if (Object.hasOwn(tag.props, key)) {
50
+ hash += `${separator}${key}:${String(tag.props[key])}`;
51
+ separator = ",";
52
+ }
53
+ }
54
+ return hash;
40
55
  }
41
56
 
42
57
  function walkResolver(val, resolve, key) {
@@ -45,20 +60,42 @@ function walkResolver(val, resolve, key) {
45
60
  if (typeof val === "function" && (!key || key !== "titleTemplate" && !key.startsWith("on")))
46
61
  val = val();
47
62
  const v = resolve ? resolve(key, val) : val;
48
- if (Array.isArray(v))
49
- return v.map((r) => walkResolver(r, resolve));
63
+ if (Array.isArray(v)) {
64
+ let out;
65
+ for (let i = 0; i < v.length; i++) {
66
+ const r = walkResolver(v[i], resolve);
67
+ if (out) {
68
+ out[i] = r;
69
+ } else if (r !== v[i]) {
70
+ out = v.slice(0, i);
71
+ out[i] = r;
72
+ }
73
+ }
74
+ return out || v;
75
+ }
50
76
  if (v?.constructor === Object) {
51
- const next = {};
77
+ let next;
52
78
  for (const k in v) {
53
- if (k === "__proto__" || k === "constructor" || k === "prototype")
54
- continue;
55
- next[k] = walkResolver(v[k], resolve, k);
79
+ const unsafe = isUnsafeKey(k);
80
+ const r = unsafe ? void 0 : walkResolver(v[k], resolve, k);
81
+ if (!next && (unsafe || r !== v[k])) {
82
+ next = {};
83
+ for (const pk in v) {
84
+ if (pk === k)
85
+ break;
86
+ next[pk] = v[pk];
87
+ }
88
+ }
89
+ if (next && !unsafe)
90
+ next[k] = r;
56
91
  }
57
- return next;
92
+ return next || v;
58
93
  }
59
94
  return v;
60
95
  }
61
96
 
97
+ const INVALID_ATTR_NAME_RE = /[\s"'<>/=\x00-\x1F\x7F]/;
98
+
62
99
  function normalizeStyleClassProps(key, value) {
63
100
  const isStyle = key === "style";
64
101
  const store = isStyle ? /* @__PURE__ */ new Map() : /* @__PURE__ */ new Set();
@@ -94,11 +131,16 @@ function normalizeProps(tag, input) {
94
131
  }
95
132
  const isHtmlTag = HasElementTags.has(tag.tag) || tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs";
96
133
  for (const prop in input) {
97
- if (prop === "__proto__" || prop === "constructor" || prop === "prototype")
134
+ if (isUnsafeKey(prop))
135
+ continue;
136
+ const isData = prop.startsWith("data-");
137
+ const isHtmlAttr = isHtmlTag && !TagConfigKeys.has(prop);
138
+ const key = isHtmlAttr && !isData ? prop.toLowerCase() : prop;
139
+ if (isHtmlAttr && (!key || INVALID_ATTR_NAME_RE.test(key)))
98
140
  continue;
99
141
  const value = input[prop];
100
142
  if (value === null) {
101
- tag.props[prop] = null;
143
+ tag.props[key] = null;
102
144
  } else if (prop === "class" || prop === "style") {
103
145
  tag.props[prop] = normalizeStyleClassProps(prop, value);
104
146
  } else if (TagConfigKeys.has(prop)) {
@@ -112,8 +154,6 @@ function normalizeProps(tag, input) {
112
154
  tag[prop] = value;
113
155
  }
114
156
  } else if (value !== void 0) {
115
- const isData = prop.startsWith("data-");
116
- const key = isHtmlTag && !isData ? prop.toLowerCase() : prop;
117
157
  const str = String(value);
118
158
  const isMeta = tag.tag === "meta" && key === "content";
119
159
  tag.props[key] = str === "true" || str === "" ? isData || isMeta ? str : true : !value && isData && str === "false" ? "false" : value;
@@ -130,7 +170,21 @@ function normalizeTag(tagName, _input) {
130
170
  tag.innerHTML = JSON.stringify(tag.innerHTML);
131
171
  tag.props.type = tag.props.type || "application/json";
132
172
  }
133
- return Array.isArray(tag.props.content) ? tag.props.content.map((v) => ({ ...tag, props: { ...tag.props, content: v } })) : tag;
173
+ if (Array.isArray(tag.props.content)) {
174
+ const tags = [];
175
+ for (const content of tag.props.content) {
176
+ tags.push({ ...tag, props: { ...tag.props, content } });
177
+ }
178
+ return tags;
179
+ }
180
+ return tag;
181
+ }
182
+ function pushNormalizedTag(tags, tag) {
183
+ if (Array.isArray(tag)) {
184
+ for (const t of tag) tags.push(t);
185
+ } else {
186
+ tags.push(tag);
187
+ }
134
188
  }
135
189
  function normalizeEntryToTags(input, propResolvers) {
136
190
  if (!input)
@@ -151,21 +205,60 @@ function normalizeEntryToTags(input, propResolvers) {
151
205
  for (const key in input) {
152
206
  const value = input[key];
153
207
  if (value !== void 0) {
154
- for (const v of Array.isArray(value) ? value : [value]) tags.push(normalizeTag(key, v));
208
+ if (Array.isArray(value)) {
209
+ for (const v of value) pushNormalizedTag(tags, normalizeTag(key, v));
210
+ } else {
211
+ pushNormalizedTag(tags, normalizeTag(key, value));
212
+ }
155
213
  }
156
214
  }
157
- return tags.flat();
215
+ return tags;
158
216
  }
159
217
 
160
218
  const LT_RE = /</g;
161
219
  const SCRIPT_END_RE = /<\/script/g;
162
220
  const sortTags = (a, b) => a._w === b._w ? a._p - b._p : a._w - b._w;
163
221
  const DEFAULT_TAG_WEIGHT = () => 100;
222
+ function isEmptyProps(props) {
223
+ for (const _ in props)
224
+ return false;
225
+ return true;
226
+ }
164
227
  const TAG_MUTATING_HOOK_RE = /^tags:|:render/;
228
+ function pushEntryTags(ctx, entries, needsClone) {
229
+ for (const tags of entries) {
230
+ for (const t of tags) {
231
+ if (needsClone) {
232
+ const props = { ...t.props };
233
+ if (props.class instanceof Set)
234
+ props.class = new Set(props.class);
235
+ if (props.style instanceof Map)
236
+ props.style = new Map(props.style);
237
+ ctx.tags.push({ ...t, props });
238
+ } else {
239
+ ctx.tags.push(t);
240
+ }
241
+ }
242
+ }
243
+ }
244
+ function valuesToTags(ctx, sortFlatMeta) {
245
+ ctx.tags = [];
246
+ for (const value of ctx.tagMap.values()) {
247
+ if (Array.isArray(value)) {
248
+ for (const tag of value) ctx.tags.push(tag);
249
+ } else {
250
+ ctx.tags.push(value);
251
+ }
252
+ }
253
+ if (sortFlatMeta)
254
+ ctx.tags.sort(sortTags);
255
+ }
165
256
  function dedupeTags(ctx) {
166
257
  let hasFlatMeta = false;
167
258
  for (const next of ctx.tags.sort(sortTags)) {
168
259
  const k = next._d || hashTag(next);
260
+ if (!k)
261
+ continue;
169
262
  const prev = ctx.tagMap.get(k);
170
263
  if (!prev) {
171
264
  ctx.tagMap.set(k, next);
@@ -210,7 +303,7 @@ function sanitizeTags(tags) {
210
303
  const out = [];
211
304
  for (let t of tags) {
212
305
  const { innerHTML, tag, props } = t;
213
- if (!ValidHeadTags.has(tag) || !Object.keys(props).length && !innerHTML && !t.textContent)
306
+ if (!ValidHeadTags.has(tag) || isEmptyProps(props) && !innerHTML && !t.textContent)
214
307
  continue;
215
308
  if (tag === "meta" && !props.content && !props["http-equiv"] && !props.charset)
216
309
  continue;
@@ -242,22 +335,28 @@ function resolveTags(head, options) {
242
335
  }
243
336
  }
244
337
  callHook(head, "entries:resolve", { entries, ...ctx });
338
+ const entryTags = [];
245
339
  for (const e of entries) {
246
340
  if (!e._tags) {
341
+ const tags = normalizeEntryToTags(e.input, head.resolvedOptions.propResolvers || []);
342
+ for (const t of tags)
343
+ Object.assign(t, e.options);
247
344
  const normalizeCtx = {
248
- tags: normalizeEntryToTags(e.input, head.resolvedOptions.propResolvers || []).map((t) => Object.assign(t, e.options)),
345
+ tags,
249
346
  entry: e
250
347
  };
251
348
  callHook(head, "entries:normalize", normalizeCtx);
252
- e._tags = normalizeCtx.tags.map((t, i) => {
349
+ for (let i = 0; i < normalizeCtx.tags.length; i++) {
350
+ const t = normalizeCtx.tags[i];
253
351
  t._w = weightFn(t);
254
352
  t._p = (e._i << 10) + i;
255
353
  t._d = dedupeKey(t);
256
354
  if (!t._d)
257
355
  t._h = hashTag(t);
258
- return t;
259
- });
356
+ }
357
+ e._tags = normalizeCtx.tags;
260
358
  }
359
+ entryTags.push(e._tags);
261
360
  }
262
361
  let needsClone = false;
263
362
  for (const k in hooks) {
@@ -266,23 +365,14 @@ function resolveTags(head, options) {
266
365
  break;
267
366
  }
268
367
  }
269
- ctx.tags = needsClone ? entries.flatMap((e) => (e._tags || []).map((t) => {
270
- const props = { ...t.props };
271
- if (props.class instanceof Set)
272
- props.class = new Set(props.class);
273
- if (props.style instanceof Map)
274
- props.style = new Map(props.style);
275
- return { ...t, props };
276
- })) : entries.flatMap((e) => e._tags || []);
368
+ pushEntryTags(ctx, entryTags, needsClone);
277
369
  const hasFlatMeta = dedupeTags(ctx);
278
370
  resolveTitleTemplate(ctx, head);
279
- ctx.tags = [...ctx.tagMap.values()];
280
- if (hasFlatMeta)
281
- ctx.tags = ctx.tags.flat().sort(sortTags);
371
+ valuesToTags(ctx, hasFlatMeta);
282
372
  callHook(head, "tags:beforeResolve", ctx);
283
373
  callHook(head, "tags:resolve", ctx);
284
374
  callHook(head, "tags:afterResolve", ctx);
285
375
  return sanitizeTags(ctx.tags);
286
376
  }
287
377
 
288
- export { dedupeTags as a, normalizeProps as b, resolveTitleTemplate as c, dedupeKey as d, hashTag as h, isMetaArrayDupeKey as i, normalizeEntryToTags as n, resolveTags as r, sanitizeTags as s, walkResolver as w };
378
+ export { INVALID_ATTR_NAME_RE as I, dedupeTags as a, normalizeProps as b, resolveTitleTemplate as c, dedupeKey as d, hashTag as h, isMetaArrayDupeKey as i, normalizeEntryToTags as n, resolveTags as r, sanitizeTags as s, walkResolver as w };
@@ -1,4 +1,4 @@
1
- import { M as MetaTagsArrayable } from './unhead.fg-0ge_u.mjs';
1
+ import { M as MetaTagsArrayable } from './unhead.1eoQpFT1.mjs';
2
2
 
3
3
  const NAMESPACES = {
4
4
  META: /* @__PURE__ */ new Set(["twitter", "fediverse"]),
@@ -0,0 +1,231 @@
1
+ import { H as HasElementTags } from './unhead.1eoQpFT1.mjs';
2
+ import { r as resolveTags, i as isMetaArrayDupeKey, b as normalizeProps, d as dedupeKey, h as hashTag } from './unhead.CR32jx68.mjs';
3
+ import { a as callHook } from './unhead.Bm4Y6XQI.mjs';
4
+
5
+ const WHITESPACE_RE = /\s+/;
6
+ // @__NO_SIDE_EFFECTS__
7
+ function createDomRenderer(options = {}) {
8
+ return (head) => _renderDOMHead(head, options);
9
+ }
10
+ function renderDOMHead(head, options = {}) {
11
+ return _renderDOMHead(head, options);
12
+ }
13
+ function hasPendingEntries(head) {
14
+ for (const entry of head.entries.values()) {
15
+ if (entry._pending !== void 0)
16
+ return true;
17
+ }
18
+ return false;
19
+ }
20
+ function cleanupDomState(state) {
21
+ for (const k in state._s) state._s[k]();
22
+ for (const k in state._p) state._p[k]();
23
+ state._s = {};
24
+ state._p = {};
25
+ state._e.clear();
26
+ state._l.clear();
27
+ }
28
+ function createDomState(head, dom) {
29
+ const state = { _d: dom, _t: dom.title, _e: /* @__PURE__ */ new Map([["htmlAttrs", dom.documentElement], ["bodyAttrs", dom.body]]), _p: {}, _s: {}, _l: /* @__PURE__ */ new Map() };
30
+ for (const el of [...dom.body.children, ...dom.head.children]) {
31
+ const tag = el.tagName.toLowerCase();
32
+ if (!HasElementTags.has(tag))
33
+ continue;
34
+ const props = { innerHTML: el.innerHTML };
35
+ for (const n of el.getAttributeNames())
36
+ props[n] = el.getAttribute(n);
37
+ const next = normalizeProps({ tag, props: {} }, props);
38
+ next.key = el.getAttribute("data-hid") || void 0;
39
+ const dedupe = dedupeKey(next) || hashTag(next);
40
+ let k = dedupe;
41
+ let c = 1;
42
+ while (state._e.has(k))
43
+ k = `${dedupe}:${c++}`;
44
+ state._e.set(k, el);
45
+ }
46
+ for (const entry of head.entries.values()) {
47
+ if (entry._o !== void 0) {
48
+ const orig = entry._o;
49
+ for (const t of ["bodyAttrs", "htmlAttrs"]) {
50
+ const cls = orig[t]?.class;
51
+ if (typeof cls === "string") {
52
+ const $el = state._e.get(t);
53
+ for (const c of cls.split(WHITESPACE_RE)) {
54
+ if (c)
55
+ state._p[`${t}:attr:class:${c}`] = () => $el.classList.remove(c);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ return state;
62
+ }
63
+ function _renderDOMHead(head, options = {}) {
64
+ const dom = options.document || head.resolvedOptions.document;
65
+ const activeState = head._dom;
66
+ const documentChanged = !!activeState && activeState._d !== dom;
67
+ if (!dom || !documentChanged && !head.dirty && !hasPendingEntries(head))
68
+ return false;
69
+ if (head._du)
70
+ return false;
71
+ const defaultView = dom.defaultView;
72
+ head._du = true;
73
+ let didRender = false;
74
+ try {
75
+ let track = function(id, scope, fn, fresh) {
76
+ const k = `${id}:${scope}`;
77
+ renderState._s[k] = !fresh && renderState._p[k] || fn;
78
+ delete renderState._p[k];
79
+ }, reclaim = function(key) {
80
+ const prev = renderState._p[key];
81
+ delete renderState._p[key];
82
+ return prev;
83
+ }, trackEvent = function(id, k, ev, source, $el, target) {
84
+ const scope = `event:${k}`;
85
+ const key = `${id}:${scope}`;
86
+ const prev = renderState._l.get(key);
87
+ if (prev && prev[0] === target && prev[1] === ev && prev[2] === source) {
88
+ track(id, scope, prev[4]);
89
+ return;
90
+ }
91
+ prev?.[4]();
92
+ const dk = `data-${k}`;
93
+ const handler = ((e) => source.call($el, e));
94
+ const cleanup = () => {
95
+ target.removeEventListener(ev, handler);
96
+ if ($el.getAttribute(dk) === "")
97
+ $el.removeAttribute(dk);
98
+ if (renderState._l.get(key)?.[3] === handler)
99
+ renderState._l.delete(key);
100
+ };
101
+ target.addEventListener(ev, handler);
102
+ renderState._l.set(key, [target, ev, source, handler, cleanup]);
103
+ $el.setAttribute(dk, "");
104
+ track(id, scope, cleanup, true);
105
+ }, trackCtx = function({ id, $el, tag }) {
106
+ const isAttr = tag.tag.endsWith("Attrs");
107
+ renderState._e.set(id, $el);
108
+ if (!isAttr) {
109
+ const text = tag.textContent;
110
+ if (text != null && text !== "") {
111
+ if (text !== $el.textContent)
112
+ $el.textContent = text;
113
+ track(id, "text", () => {
114
+ if ($el.textContent === text)
115
+ $el.textContent = "";
116
+ }, true);
117
+ }
118
+ const html = tag.innerHTML;
119
+ if (html != null && html !== "") {
120
+ if (html !== $el.innerHTML)
121
+ $el.innerHTML = html;
122
+ track(id, "html", () => {
123
+ if ($el.innerHTML === html)
124
+ $el.innerHTML = "";
125
+ }, true);
126
+ }
127
+ const elKey = `${id}:el`;
128
+ renderState._s[elKey] = reclaim(elKey) || (() => {
129
+ $el?.remove();
130
+ renderState._e.delete(id);
131
+ });
132
+ }
133
+ for (const k in tag.props) {
134
+ const v = tag.props[k];
135
+ if (k[0] === "o" && k[1] === "n" && typeof v === "function") {
136
+ const ev = k.slice(2);
137
+ if ($el?.dataset?.[`${k}fired`])
138
+ v.call($el, new (defaultView?.Event || Event)(ev));
139
+ trackEvent(id, k, ev, v, $el, tag.tag === "bodyAttrs" && defaultView ? defaultView : $el);
140
+ continue;
141
+ }
142
+ const ck = `${id}:attr:${k}`;
143
+ if (k === "class" && v) {
144
+ for (const c of v) {
145
+ const key = `${ck}:${c}`;
146
+ renderState._s[key] = reclaim(key) || (() => $el.classList.remove(c));
147
+ if (!$el.classList.contains(c))
148
+ $el.classList.add(c);
149
+ }
150
+ } else if (k === "style" && v) {
151
+ for (const [sk, sv] of v) {
152
+ const key = `${ck}:${sk}`;
153
+ renderState._s[key] = reclaim(key) || (() => $el.style.removeProperty(sk));
154
+ $el.style.setProperty(sk, sv);
155
+ }
156
+ } else if (v !== false && v !== null) {
157
+ if ($el.getAttribute(k) !== v)
158
+ $el.setAttribute(k, v === true ? "" : String(v));
159
+ renderState._s[ck] = reclaim(ck) || (() => $el.removeAttribute(k));
160
+ }
161
+ }
162
+ };
163
+ const beforeRenderCtx = { shouldRender: true, tags: [] };
164
+ callHook(head, "dom:beforeRender", beforeRenderCtx);
165
+ if (!beforeRenderCtx.shouldRender)
166
+ return false;
167
+ let state = head._dom;
168
+ if (state?._d !== dom) {
169
+ if (state)
170
+ cleanupDomState(state);
171
+ state = void 0;
172
+ }
173
+ if (!state) {
174
+ state = createDomState(head, dom);
175
+ } else {
176
+ state._p = state._s;
177
+ }
178
+ state._s = {};
179
+ const renderState = state;
180
+ const pending = [];
181
+ const frag = {};
182
+ head.dirty = false;
183
+ const rawTags = resolveTags(head, options.tagWeight ? { tagWeight: options.tagWeight } : void 0);
184
+ const tags = [];
185
+ const dupeKeyCounter = {};
186
+ for (const tag of rawTags) {
187
+ const count = dupeKeyCounter[tag._d] || 0;
188
+ const id = (count ? `${tag._d}:${count}` : tag._d) || tag._h;
189
+ const ctx = { tag, id, shouldRender: true };
190
+ if (tag._d && isMetaArrayDupeKey(tag._d))
191
+ dupeKeyCounter[tag._d] = count + 1;
192
+ tags.push(ctx);
193
+ if (tag.tag === "title") {
194
+ dom.title = tag.textContent;
195
+ track("title", "", () => dom.title = renderState._t);
196
+ continue;
197
+ }
198
+ ctx.$el = renderState._e.get(id);
199
+ if (ctx.$el)
200
+ trackCtx(ctx);
201
+ else if (HasElementTags.has(tag.tag))
202
+ pending.push(ctx);
203
+ }
204
+ for (const ctx of pending) {
205
+ ctx.$el = dom.createElement(ctx.tag.tag);
206
+ trackCtx(ctx);
207
+ (frag[ctx.tag.tagPosition || "head"] ??= dom.createDocumentFragment()).appendChild(ctx.$el);
208
+ }
209
+ if (frag.head)
210
+ dom.head.appendChild(frag.head);
211
+ if (frag.bodyOpen)
212
+ dom.body.insertBefore(frag.bodyOpen, dom.body.firstChild);
213
+ if (frag.bodyClose)
214
+ dom.body.appendChild(frag.bodyClose);
215
+ for (const k in renderState._p)
216
+ renderState._p[k]();
217
+ head._dom = renderState;
218
+ didRender = true;
219
+ callHook(head, "dom:rendered", { renders: tags });
220
+ } catch (e) {
221
+ head.dirty = true;
222
+ throw e;
223
+ } finally {
224
+ head._du = false;
225
+ }
226
+ if (didRender && (head.dirty || hasPendingEntries(head)))
227
+ _renderDOMHead(head, options);
228
+ return didRender;
229
+ }
230
+
231
+ export { createDomRenderer as c, renderDOMHead as r };
@@ -1,4 +1,4 @@
1
- import { U as Unhead, F as UseScriptInput, G as UseScriptOptions, J as UseScriptReturn } from './unhead.CBj56GgY.js';
1
+ import { U as Unhead, F as UseScriptInput, G as UseScriptOptions, J as UseScriptReturn } from './unhead.Dv-MkoX9.js';
2
2
 
3
3
  /**
4
4
  * Load third-party scripts with SSR support and a proxied API.
@@ -1,5 +1,5 @@
1
1
  import { HookableCore } from 'hookable';
2
- import { U as Unhead, S as SSRHeadPayload, f as ServerHeadHooks, a as CreateServerHeadOptions } from './unhead.CBj56GgY.js';
2
+ import { U as Unhead, S as SSRHeadPayload, f as ServerHeadHooks, a as CreateServerHeadOptions } from './unhead.Dv-MkoX9.js';
3
3
  import { R as ResolvableHead } from './unhead.Dl7UHSN0.js';
4
4
 
5
5
  interface ServerUnhead<T = ResolvableHead> extends Unhead<T, SSRHeadPayload> {
@@ -263,8 +263,10 @@ interface HeadPluginOptions extends CreateHeadOptions {
263
263
  }
264
264
  type HeadPluginInput = (HeadPluginOptions & {
265
265
  key: string;
266
- }) | ((head: Unhead) => HeadPluginOptions & {
266
+ }) | (((head: Unhead) => HeadPluginOptions & {
267
267
  key: string;
268
+ }) & {
269
+ key?: string;
268
270
  });
269
271
  type HeadPlugin = HeadPluginOptions & {
270
272
  key: string;
@@ -484,10 +486,6 @@ interface Unhead<Input = ResolvableHead, RenderResult = unknown> {
484
486
  * @internal
485
487
  */
486
488
  _titleTemplate?: string;
487
- /**
488
- * @internal
489
- */
490
- _ssrPayload?: ResolvableHead;
491
489
  /**
492
490
  * @internal
493
491
  */
@@ -1,4 +1,6 @@
1
1
  function registerPlugin(head, p) {
2
+ if (typeof p === "function" && p.key && head.plugins.has(p.key))
3
+ return;
2
4
  const plugin = typeof p === "function" ? p(head) : p;
3
5
  const key = plugin.key || String(head.plugins.size + 1);
4
6
  if (!head.plugins.get(key)) {