@fluixi/head 0.1.0-alpha.22 → 1.0.0-alpha.101
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/LICENSE +1 -1
- package/README.md +8 -8
- package/dist/components.cjs +91 -0
- package/dist/components.js +1 -1
- package/dist/components.mjs +68 -0
- package/dist/core.cjs +314 -0
- package/dist/core.d.ts +10 -5
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +35 -17
- package/dist/core.mjs +293 -0
- package/dist/index.cjs +672 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +655 -0
- package/dist/jsonld.cjs +94 -0
- package/dist/jsonld.js +1 -1
- package/dist/jsonld.mjs +73 -0
- package/dist/reconcile.cjs +323 -0
- package/dist/reconcile.d.ts +1 -1
- package/dist/reconcile.js +1 -1
- package/dist/reconcile.mjs +302 -0
- package/dist/serialize.cjs +306 -0
- package/dist/serialize.mjs +283 -0
- package/dist/sitemap.cjs +115 -0
- package/dist/sitemap.d.ts +1 -1
- package/dist/sitemap.d.ts.map +1 -1
- package/dist/sitemap.js +1 -1
- package/dist/sitemap.mjs +94 -0
- package/dist/transport.cjs +61 -0
- package/dist/transport.d.ts +3 -3
- package/dist/transport.js +4 -4
- package/dist/transport.mjs +40 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/types.cjs +18 -0
- package/dist/types.d.ts +21 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.mjs +0 -0
- package/package.json +12 -6
package/dist/core.js
CHANGED
|
@@ -42,27 +42,35 @@ export function createHead() {
|
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const slot = (globalThis[Symbol.for('fluixi.head')] ??= {
|
|
46
|
+
active: null,
|
|
47
|
+
scope: null,
|
|
48
|
+
});
|
|
48
49
|
export function setActiveHead(h) {
|
|
49
|
-
active = h;
|
|
50
|
+
slot.active = h;
|
|
50
51
|
}
|
|
51
52
|
export function getActiveHead() {
|
|
52
|
-
return active;
|
|
53
|
+
return slot.scope?.() ?? slot.active;
|
|
53
54
|
}
|
|
54
55
|
export function runWithHead(h, fn) {
|
|
55
|
-
const prev = active;
|
|
56
|
-
active = h;
|
|
56
|
+
const prev = slot.active;
|
|
57
|
+
slot.active = h;
|
|
57
58
|
try {
|
|
58
59
|
return fn();
|
|
59
60
|
}
|
|
60
61
|
finally {
|
|
61
|
-
active = prev;
|
|
62
|
+
slot.active = prev;
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
/**
|
|
65
|
-
*
|
|
66
|
+
* Resolve the active registry from a host's own request scope, e.g. its AsyncLocalStorage.
|
|
67
|
+
* Returning nothing falls back to `setActiveHead`. `null` removes the scope.
|
|
68
|
+
*/
|
|
69
|
+
export function setHeadScope(scope) {
|
|
70
|
+
slot.scope = scope;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Register metadata for the current page/layout. Static, async-resolved, or reactive, values
|
|
66
74
|
* that are signals update the document live on the client. Returns a disposer (client dynamic use).
|
|
67
75
|
*/
|
|
68
76
|
export function useHead(input) {
|
|
@@ -75,18 +83,16 @@ export function useHead(input) {
|
|
|
75
83
|
return () => { };
|
|
76
84
|
}
|
|
77
85
|
const dispose = h.push(input);
|
|
78
|
-
// Drop the source when the owner (page/layout component) unmounts
|
|
86
|
+
// Drop the source when the owner (page/layout component) unmounts, this is what makes route
|
|
79
87
|
// transitions update the head on the client. NOT on the server: there it's a single render and
|
|
80
88
|
// we serialize the head before the root disposes, so the source must survive until then.
|
|
81
89
|
if (typeof window !== 'undefined' && getOwner())
|
|
82
90
|
onCleanup(dispose);
|
|
83
91
|
return dispose;
|
|
84
92
|
}
|
|
85
|
-
/** Ergonomic alias
|
|
93
|
+
/** Ergonomic alias: `seo({ title, description })`. */
|
|
86
94
|
export const seo = useHead;
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
95
|
// Resolution (expand → dedupe)
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
96
|
function djb2(s) {
|
|
91
97
|
let h = 5381;
|
|
92
98
|
for (let i = 0; i < s.length; i++)
|
|
@@ -231,6 +237,11 @@ function expand(input, out, ts) {
|
|
|
231
237
|
const lang = val(input.lang);
|
|
232
238
|
if (lang !== undefined)
|
|
233
239
|
out.push({ tag: 'htmlAttr', key: 'html:lang', name: 'lang', value: lang });
|
|
240
|
+
for (const [name, raw] of Object.entries(input.htmlAttrs ?? {})) {
|
|
241
|
+
const value = val(raw);
|
|
242
|
+
if (value !== undefined)
|
|
243
|
+
out.push({ tag: 'htmlAttr', key: `html:${name}`, name, value });
|
|
244
|
+
}
|
|
234
245
|
if (input.charset !== undefined)
|
|
235
246
|
meta('meta:charset', { charset: input.charset });
|
|
236
247
|
if (input.viewport !== undefined)
|
|
@@ -251,11 +262,18 @@ function expand(input, out, ts) {
|
|
|
251
262
|
meta('meta:name:application-name', { name: 'application-name', content: input.applicationName });
|
|
252
263
|
if (input.referrer !== undefined)
|
|
253
264
|
meta('meta:name:referrer', { name: 'referrer', content: input.referrer });
|
|
254
|
-
|
|
255
|
-
|
|
265
|
+
const scheme = val(input.colorScheme);
|
|
266
|
+
if (scheme !== undefined)
|
|
267
|
+
meta('meta:name:color-scheme', { name: 'color-scheme', content: scheme });
|
|
256
268
|
const theme = val(input.themeColor);
|
|
257
|
-
if (theme
|
|
269
|
+
if (typeof theme === 'string')
|
|
258
270
|
meta('meta:name:theme-color', { name: 'theme-color', content: theme });
|
|
271
|
+
// One tag per media query, keyed by it: a later source replaces the same media, not the others.
|
|
272
|
+
else if (theme) {
|
|
273
|
+
for (const { color, media } of theme) {
|
|
274
|
+
meta(`meta:name:theme-color${media ? `:${media}` : ''}`, media ? { name: 'theme-color', content: color, media } : { name: 'theme-color', content: color });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
259
277
|
const canonical = val(input.canonical);
|
|
260
278
|
if (canonical !== undefined)
|
|
261
279
|
out.push({ tag: 'link', key: 'link:rel:canonical', attrs: { rel: 'canonical', href: canonical } });
|
|
@@ -323,7 +341,7 @@ function applyTemplate(tpl, title) {
|
|
|
323
341
|
}
|
|
324
342
|
/**
|
|
325
343
|
* Flatten every source (in registration order: layouts first, page last) into tags, then
|
|
326
|
-
* deduplicate by key
|
|
344
|
+
* deduplicate by key: last write wins for singletons (title, description, canonical, ...),
|
|
327
345
|
* while repeatable tags (extra og:image, hreflang alternates, json-ld) accumulate.
|
|
328
346
|
*/
|
|
329
347
|
export function resolveHead(head) {
|
package/dist/core.mjs
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// src/core.ts
|
|
2
|
+
import { getOwner, onCleanup, createSignal } from "@fluixi/reactive/signal";
|
|
3
|
+
function val(v) {
|
|
4
|
+
return typeof v === "function" ? v() : v;
|
|
5
|
+
}
|
|
6
|
+
function robotsToString(r) {
|
|
7
|
+
if (typeof r === "string") return r;
|
|
8
|
+
const out = [];
|
|
9
|
+
out.push(r.index === false ? "noindex" : "index");
|
|
10
|
+
out.push(r.follow === false ? "nofollow" : "follow");
|
|
11
|
+
if (r.noarchive) out.push("noarchive");
|
|
12
|
+
if (r.nosnippet) out.push("nosnippet");
|
|
13
|
+
if (r.noimageindex) out.push("noimageindex");
|
|
14
|
+
if (r.maxSnippet != null) out.push(`max-snippet:${r.maxSnippet}`);
|
|
15
|
+
if (r.maxImagePreview) out.push(`max-image-preview:${r.maxImagePreview}`);
|
|
16
|
+
if (r.maxVideoPreview != null) out.push(`max-video-preview:${r.maxVideoPreview}`);
|
|
17
|
+
return out.join(", ");
|
|
18
|
+
}
|
|
19
|
+
function createHead() {
|
|
20
|
+
const sources = [];
|
|
21
|
+
const [version, bump] = createSignal(0);
|
|
22
|
+
return {
|
|
23
|
+
sources,
|
|
24
|
+
version,
|
|
25
|
+
push(input) {
|
|
26
|
+
sources.push(input);
|
|
27
|
+
bump((n) => n + 1);
|
|
28
|
+
return () => {
|
|
29
|
+
const i = sources.indexOf(input);
|
|
30
|
+
if (i !== -1) {
|
|
31
|
+
sources.splice(i, 1);
|
|
32
|
+
bump((n) => n + 1);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
var slot = globalThis[/* @__PURE__ */ Symbol.for("fluixi.head")] ??= {
|
|
39
|
+
active: null,
|
|
40
|
+
scope: null
|
|
41
|
+
};
|
|
42
|
+
function setActiveHead(h) {
|
|
43
|
+
slot.active = h;
|
|
44
|
+
}
|
|
45
|
+
function getActiveHead() {
|
|
46
|
+
return slot.scope?.() ?? slot.active;
|
|
47
|
+
}
|
|
48
|
+
function runWithHead(h, fn) {
|
|
49
|
+
const prev = slot.active;
|
|
50
|
+
slot.active = h;
|
|
51
|
+
try {
|
|
52
|
+
return fn();
|
|
53
|
+
} finally {
|
|
54
|
+
slot.active = prev;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function setHeadScope(scope) {
|
|
58
|
+
slot.scope = scope;
|
|
59
|
+
}
|
|
60
|
+
function useHead(input) {
|
|
61
|
+
const h = getActiveHead();
|
|
62
|
+
if (!h) {
|
|
63
|
+
const proc = globalThis.process;
|
|
64
|
+
if (proc?.env?.NODE_ENV !== "production") {
|
|
65
|
+
console.warn("[fluixi/head] useHead() called with no active head registry — ignored.");
|
|
66
|
+
}
|
|
67
|
+
return () => {
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const dispose = h.push(input);
|
|
71
|
+
if (typeof window !== "undefined" && getOwner()) onCleanup(dispose);
|
|
72
|
+
return dispose;
|
|
73
|
+
}
|
|
74
|
+
var seo = useHead;
|
|
75
|
+
function djb2(s) {
|
|
76
|
+
let h = 5381;
|
|
77
|
+
for (let i = 0; i < s.length; i++) h = h * 33 ^ s.charCodeAt(i);
|
|
78
|
+
return (h >>> 0).toString(36);
|
|
79
|
+
}
|
|
80
|
+
function metaKey(a) {
|
|
81
|
+
if (a.charset != null) return "meta:charset";
|
|
82
|
+
if (a.property) return "meta:property:" + a.property;
|
|
83
|
+
if (a.name) return "meta:name:" + a.name;
|
|
84
|
+
if (a["http-equiv"]) return "meta:http-equiv:" + a["http-equiv"];
|
|
85
|
+
return "meta:" + djb2(JSON.stringify(a));
|
|
86
|
+
}
|
|
87
|
+
function pushImage(out, img) {
|
|
88
|
+
const o = typeof img === "string" ? { url: img } : img;
|
|
89
|
+
out.push({ tag: "meta", key: "meta:property:og:image:" + o.url, attrs: { property: "og:image", content: o.url } });
|
|
90
|
+
if (o.secureUrl) out.push({ tag: "meta", key: "meta:property:og:image:secure:" + o.url, attrs: { property: "og:image:secure_url", content: o.secureUrl } });
|
|
91
|
+
if (o.type) out.push({ tag: "meta", key: "meta:property:og:image:type:" + o.url, attrs: { property: "og:image:type", content: o.type } });
|
|
92
|
+
if (o.width != null) out.push({ tag: "meta", key: "meta:property:og:image:width:" + o.url, attrs: { property: "og:image:width", content: String(o.width) } });
|
|
93
|
+
if (o.height != null) out.push({ tag: "meta", key: "meta:property:og:image:height:" + o.url, attrs: { property: "og:image:height", content: String(o.height) } });
|
|
94
|
+
if (o.alt) out.push({ tag: "meta", key: "meta:property:og:image:alt:" + o.url, attrs: { property: "og:image:alt", content: o.alt } });
|
|
95
|
+
}
|
|
96
|
+
function expandOG(og, out) {
|
|
97
|
+
const m = (p, c) => {
|
|
98
|
+
if (c != null) out.push({ tag: "meta", key: "meta:property:og:" + p, attrs: { property: "og:" + p, content: c } });
|
|
99
|
+
};
|
|
100
|
+
m("title", og.title);
|
|
101
|
+
m("description", og.description);
|
|
102
|
+
m("type", og.type);
|
|
103
|
+
m("url", og.url);
|
|
104
|
+
m("site_name", og.siteName);
|
|
105
|
+
m("locale", og.locale);
|
|
106
|
+
for (const l of og.localeAlternate ?? []) out.push({ tag: "meta", key: "meta:property:og:locale:alt:" + l, attrs: { property: "og:locale:alternate", content: l } });
|
|
107
|
+
if (og.image != null) {
|
|
108
|
+
const imgs = Array.isArray(og.image) ? og.image : [og.image];
|
|
109
|
+
for (const i of imgs) pushImage(out, i);
|
|
110
|
+
}
|
|
111
|
+
if (og.video) {
|
|
112
|
+
const v = typeof og.video === "string" ? { url: og.video } : og.video;
|
|
113
|
+
m("video", v.url);
|
|
114
|
+
if ("type" in v && v.type) m("video:type", v.type);
|
|
115
|
+
if ("width" in v && v.width != null) m("video:width", String(v.width));
|
|
116
|
+
if ("height" in v && v.height != null) m("video:height", String(v.height));
|
|
117
|
+
}
|
|
118
|
+
if (og.audio) {
|
|
119
|
+
const a = typeof og.audio === "string" ? { url: og.audio } : og.audio;
|
|
120
|
+
m("audio", a.url);
|
|
121
|
+
if ("type" in a && a.type) m("audio:type", a.type);
|
|
122
|
+
}
|
|
123
|
+
if (og.article) {
|
|
124
|
+
const ar = og.article;
|
|
125
|
+
m("article:published_time", ar.publishedTime);
|
|
126
|
+
m("article:modified_time", ar.modifiedTime);
|
|
127
|
+
m("article:expiration_time", ar.expirationTime);
|
|
128
|
+
m("article:section", ar.section);
|
|
129
|
+
for (const au of [].concat(ar.author ?? [])) out.push({ tag: "meta", key: "meta:property:og:article:author:" + au, attrs: { property: "article:author", content: au } });
|
|
130
|
+
for (const t of [].concat(ar.tag ?? [])) out.push({ tag: "meta", key: "meta:property:og:article:tag:" + t, attrs: { property: "article:tag", content: t } });
|
|
131
|
+
}
|
|
132
|
+
if (og.book) {
|
|
133
|
+
const b = og.book;
|
|
134
|
+
m("book:isbn", b.isbn);
|
|
135
|
+
m("book:release_date", b.releaseDate);
|
|
136
|
+
for (const au of [].concat(b.author ?? [])) out.push({ tag: "meta", key: "meta:property:og:book:author:" + au, attrs: { property: "book:author", content: au } });
|
|
137
|
+
for (const t of [].concat(b.tag ?? [])) out.push({ tag: "meta", key: "meta:property:og:book:tag:" + t, attrs: { property: "book:tag", content: t } });
|
|
138
|
+
}
|
|
139
|
+
if (og.profile) {
|
|
140
|
+
const p = og.profile;
|
|
141
|
+
m("profile:first_name", p.firstName);
|
|
142
|
+
m("profile:last_name", p.lastName);
|
|
143
|
+
m("profile:username", p.username);
|
|
144
|
+
m("profile:gender", p.gender);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function expandTwitter(t, out) {
|
|
148
|
+
const m = (n, c) => {
|
|
149
|
+
if (c != null) out.push({ tag: "meta", key: "meta:name:twitter:" + n, attrs: { name: "twitter:" + n, content: c } });
|
|
150
|
+
};
|
|
151
|
+
m("card", t.card);
|
|
152
|
+
m("site", t.site);
|
|
153
|
+
m("creator", t.creator);
|
|
154
|
+
m("title", t.title);
|
|
155
|
+
m("description", t.description);
|
|
156
|
+
m("image", t.image);
|
|
157
|
+
m("image:alt", t.imageAlt);
|
|
158
|
+
m("player", t.player);
|
|
159
|
+
if (t.playerWidth != null) m("player:width", String(t.playerWidth));
|
|
160
|
+
if (t.playerHeight != null) m("player:height", String(t.playerHeight));
|
|
161
|
+
if (t.app) {
|
|
162
|
+
m("app:name:iphone", t.app.name);
|
|
163
|
+
m("app:id:iphone", t.app.idIphone);
|
|
164
|
+
m("app:id:ipad", t.app.idIpad);
|
|
165
|
+
m("app:id:googleplay", t.app.idGooglePlay);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function expandIcons(icons, out) {
|
|
169
|
+
const link = (key, attrs) => out.push({ tag: "link", key, attrs });
|
|
170
|
+
if (icons.icon != null) {
|
|
171
|
+
const arr = typeof icons.icon === "string" ? [{ url: icons.icon }] : icons.icon;
|
|
172
|
+
for (const i of arr) link("link:icon:" + i.url, { rel: "icon", href: i.url, ...i.type ? { type: i.type } : {}, ...i.sizes ? { sizes: i.sizes } : {} });
|
|
173
|
+
}
|
|
174
|
+
if (icons.apple != null) {
|
|
175
|
+
const arr = typeof icons.apple === "string" ? [{ url: icons.apple }] : icons.apple;
|
|
176
|
+
for (const i of arr) link("link:apple-touch-icon:" + i.url, { rel: "apple-touch-icon", href: i.url, ...i.sizes ? { sizes: i.sizes } : {} });
|
|
177
|
+
}
|
|
178
|
+
if (icons.mask) link("link:mask-icon", { rel: "mask-icon", href: icons.mask.url, ...icons.mask.color ? { color: icons.mask.color } : {} });
|
|
179
|
+
if (icons.shortcut) link("link:shortcut", { rel: "shortcut icon", href: icons.shortcut });
|
|
180
|
+
if (icons.msTileImage) out.push({ tag: "meta", key: "meta:name:msapplication-TileImage", attrs: { name: "msapplication-TileImage", content: icons.msTileImage } });
|
|
181
|
+
if (icons.msTileColor) out.push({ tag: "meta", key: "meta:name:msapplication-TileColor", attrs: { name: "msapplication-TileColor", content: icons.msTileColor } });
|
|
182
|
+
}
|
|
183
|
+
function expand(input, out, ts) {
|
|
184
|
+
const meta = (key, attrs) => out.push({ tag: "meta", key, attrs });
|
|
185
|
+
const title = val(input.title);
|
|
186
|
+
if (title !== void 0) ts.title = title;
|
|
187
|
+
if (input.titleTemplate !== void 0) ts.template = input.titleTemplate;
|
|
188
|
+
const lang = val(input.lang);
|
|
189
|
+
if (lang !== void 0) out.push({ tag: "htmlAttr", key: "html:lang", name: "lang", value: lang });
|
|
190
|
+
for (const [name, raw] of Object.entries(input.htmlAttrs ?? {})) {
|
|
191
|
+
const value = val(raw);
|
|
192
|
+
if (value !== void 0) out.push({ tag: "htmlAttr", key: `html:${name}`, name, value });
|
|
193
|
+
}
|
|
194
|
+
if (input.charset !== void 0) meta("meta:charset", { charset: input.charset });
|
|
195
|
+
if (input.viewport !== void 0) meta("meta:name:viewport", { name: "viewport", content: input.viewport });
|
|
196
|
+
const desc = val(input.description);
|
|
197
|
+
if (desc !== void 0) meta("meta:name:description", { name: "description", content: desc });
|
|
198
|
+
const kw = val(input.keywords);
|
|
199
|
+
if (kw !== void 0) meta("meta:name:keywords", { name: "keywords", content: Array.isArray(kw) ? kw.join(", ") : kw });
|
|
200
|
+
if (input.author !== void 0) meta("meta:name:author", { name: "author", content: input.author });
|
|
201
|
+
if (input.robots !== void 0) meta("meta:name:robots", { name: "robots", content: robotsToString(input.robots) });
|
|
202
|
+
if (input.generator !== void 0) meta("meta:name:generator", { name: "generator", content: input.generator });
|
|
203
|
+
if (input.applicationName !== void 0) meta("meta:name:application-name", { name: "application-name", content: input.applicationName });
|
|
204
|
+
if (input.referrer !== void 0) meta("meta:name:referrer", { name: "referrer", content: input.referrer });
|
|
205
|
+
const scheme = val(input.colorScheme);
|
|
206
|
+
if (scheme !== void 0) meta("meta:name:color-scheme", { name: "color-scheme", content: scheme });
|
|
207
|
+
const theme = val(input.themeColor);
|
|
208
|
+
if (typeof theme === "string") meta("meta:name:theme-color", { name: "theme-color", content: theme });
|
|
209
|
+
else if (theme) {
|
|
210
|
+
for (const { color, media } of theme) {
|
|
211
|
+
meta(`meta:name:theme-color${media ? `:${media}` : ""}`, media ? { name: "theme-color", content: color, media } : { name: "theme-color", content: color });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const canonical = val(input.canonical);
|
|
215
|
+
if (canonical !== void 0) out.push({ tag: "link", key: "link:rel:canonical", attrs: { rel: "canonical", href: canonical } });
|
|
216
|
+
if (input.manifest !== void 0) out.push({ tag: "link", key: "link:rel:manifest", attrs: { rel: "manifest", href: input.manifest } });
|
|
217
|
+
if (input.base !== void 0) out.push({ tag: "base", key: "base", attrs: { href: input.base } });
|
|
218
|
+
if (input.verification) {
|
|
219
|
+
const map = { google: "google-site-verification", bing: "msvalidate.01", yandex: "yandex-verification", pinterest: "p:domain_verify" };
|
|
220
|
+
for (const [k, v] of Object.entries(input.verification)) {
|
|
221
|
+
if (v == null) continue;
|
|
222
|
+
const name = map[k] ?? k;
|
|
223
|
+
meta("meta:name:" + name, { name, content: v });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const a of input.alternates ?? []) out.push({ tag: "link", key: "link:alternate:hreflang:" + a.hreflang, attrs: { rel: "alternate", hreflang: a.hreflang, href: a.href } });
|
|
227
|
+
for (const f of input.feeds ?? []) out.push({ tag: "link", key: "link:alternate:feed:" + f.href, attrs: { rel: "alternate", type: f.type ?? "application/rss+xml", href: f.href, ...f.title ? { title: f.title } : {} } });
|
|
228
|
+
if (input.og) expandOG(input.og, out);
|
|
229
|
+
if (input.twitter) expandTwitter(input.twitter, out);
|
|
230
|
+
if (input.icons) expandIcons(input.icons, out);
|
|
231
|
+
const ld = val(input.jsonLd);
|
|
232
|
+
if (ld !== void 0) {
|
|
233
|
+
const arr = Array.isArray(ld) ? ld : [ld];
|
|
234
|
+
for (const obj of arr) {
|
|
235
|
+
const json = JSON.stringify(obj);
|
|
236
|
+
out.push({ tag: "script", key: "script:ldjson:" + djb2(json), attrs: { type: "application/ld+json" }, children: json });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
for (const m of input.meta ?? []) {
|
|
240
|
+
const attrs = {};
|
|
241
|
+
for (const [k, v] of Object.entries(m)) if (v != null) attrs[k === "httpEquiv" ? "http-equiv" : k] = v;
|
|
242
|
+
out.push({ tag: "meta", key: metaKey(attrs), attrs });
|
|
243
|
+
}
|
|
244
|
+
for (const l of input.link ?? []) {
|
|
245
|
+
const attrs = {};
|
|
246
|
+
for (const [k, v] of Object.entries(l)) if (v != null) attrs[k] = v;
|
|
247
|
+
out.push({ tag: "link", key: "link:" + (attrs.rel ?? "") + ":" + (attrs.href ?? djb2(JSON.stringify(attrs))), attrs });
|
|
248
|
+
}
|
|
249
|
+
for (const s of input.script ?? []) {
|
|
250
|
+
const attrs = {};
|
|
251
|
+
let children;
|
|
252
|
+
for (const [k, v] of Object.entries(s)) {
|
|
253
|
+
if (v == null) continue;
|
|
254
|
+
if (k === "children") children = v;
|
|
255
|
+
else attrs[k] = v;
|
|
256
|
+
}
|
|
257
|
+
out.push({ tag: "script", key: "script:" + (attrs.src ?? djb2((children ?? "") + JSON.stringify(attrs))), attrs, children });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function applyTemplate(tpl, title) {
|
|
261
|
+
return typeof tpl === "function" ? tpl(title) : tpl.includes("%s") ? tpl.replace("%s", title) : tpl;
|
|
262
|
+
}
|
|
263
|
+
function resolveHead(head) {
|
|
264
|
+
const raw = [];
|
|
265
|
+
const ts = {};
|
|
266
|
+
for (const src of head.sources) {
|
|
267
|
+
const input = typeof src === "function" ? src() : src;
|
|
268
|
+
expand(input, raw, ts);
|
|
269
|
+
}
|
|
270
|
+
if (ts.title !== void 0) {
|
|
271
|
+
raw.push({ tag: "title", key: "title", children: ts.template ? applyTemplate(ts.template, ts.title) : ts.title });
|
|
272
|
+
}
|
|
273
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
274
|
+
const htmlAttrs = {};
|
|
275
|
+
for (const t of raw) {
|
|
276
|
+
if (t.tag === "htmlAttr") {
|
|
277
|
+
htmlAttrs[t.name] = t.value;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
byKey.set(t.key, t);
|
|
281
|
+
}
|
|
282
|
+
return { tags: [...byKey.values()], htmlAttrs };
|
|
283
|
+
}
|
|
284
|
+
export {
|
|
285
|
+
createHead,
|
|
286
|
+
getActiveHead,
|
|
287
|
+
resolveHead,
|
|
288
|
+
runWithHead,
|
|
289
|
+
seo,
|
|
290
|
+
setActiveHead,
|
|
291
|
+
setHeadScope,
|
|
292
|
+
useHead
|
|
293
|
+
};
|