@fluixi/head 1.0.0-alpha.50 → 1.0.0-alpha.52
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/components.cjs +88 -0
- package/dist/components.js +1 -1
- package/dist/components.mjs +65 -0
- package/dist/core.cjs +297 -0
- package/dist/core.d.ts +5 -5
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +4 -4
- package/dist/core.mjs +276 -0
- package/dist/index.cjs +655 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +638 -0
- package/dist/jsonld.cjs +94 -0
- package/dist/jsonld.mjs +73 -0
- package/dist/reconcile.cjs +309 -0
- package/dist/reconcile.d.ts +1 -1
- package/dist/reconcile.js +1 -1
- package/dist/reconcile.mjs +288 -0
- package/dist/serialize.cjs +292 -0
- package/dist/serialize.mjs +269 -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 +2 -2
- package/dist/transport.js +3 -3
- package/dist/transport.mjs +40 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/types.cjs +18 -0
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.mjs +0 -0
- package/package.json +12 -6
|
@@ -0,0 +1,269 @@
|
|
|
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 djb2(s) {
|
|
20
|
+
let h = 5381;
|
|
21
|
+
for (let i = 0; i < s.length; i++) h = h * 33 ^ s.charCodeAt(i);
|
|
22
|
+
return (h >>> 0).toString(36);
|
|
23
|
+
}
|
|
24
|
+
function metaKey(a) {
|
|
25
|
+
if (a.charset != null) return "meta:charset";
|
|
26
|
+
if (a.property) return "meta:property:" + a.property;
|
|
27
|
+
if (a.name) return "meta:name:" + a.name;
|
|
28
|
+
if (a["http-equiv"]) return "meta:http-equiv:" + a["http-equiv"];
|
|
29
|
+
return "meta:" + djb2(JSON.stringify(a));
|
|
30
|
+
}
|
|
31
|
+
function pushImage(out, img) {
|
|
32
|
+
const o = typeof img === "string" ? { url: img } : img;
|
|
33
|
+
out.push({ tag: "meta", key: "meta:property:og:image:" + o.url, attrs: { property: "og:image", content: o.url } });
|
|
34
|
+
if (o.secureUrl) out.push({ tag: "meta", key: "meta:property:og:image:secure:" + o.url, attrs: { property: "og:image:secure_url", content: o.secureUrl } });
|
|
35
|
+
if (o.type) out.push({ tag: "meta", key: "meta:property:og:image:type:" + o.url, attrs: { property: "og:image:type", content: o.type } });
|
|
36
|
+
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) } });
|
|
37
|
+
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) } });
|
|
38
|
+
if (o.alt) out.push({ tag: "meta", key: "meta:property:og:image:alt:" + o.url, attrs: { property: "og:image:alt", content: o.alt } });
|
|
39
|
+
}
|
|
40
|
+
function expandOG(og, out) {
|
|
41
|
+
const m = (p, c) => {
|
|
42
|
+
if (c != null) out.push({ tag: "meta", key: "meta:property:og:" + p, attrs: { property: "og:" + p, content: c } });
|
|
43
|
+
};
|
|
44
|
+
m("title", og.title);
|
|
45
|
+
m("description", og.description);
|
|
46
|
+
m("type", og.type);
|
|
47
|
+
m("url", og.url);
|
|
48
|
+
m("site_name", og.siteName);
|
|
49
|
+
m("locale", og.locale);
|
|
50
|
+
for (const l of og.localeAlternate ?? []) out.push({ tag: "meta", key: "meta:property:og:locale:alt:" + l, attrs: { property: "og:locale:alternate", content: l } });
|
|
51
|
+
if (og.image != null) {
|
|
52
|
+
const imgs = Array.isArray(og.image) ? og.image : [og.image];
|
|
53
|
+
for (const i of imgs) pushImage(out, i);
|
|
54
|
+
}
|
|
55
|
+
if (og.video) {
|
|
56
|
+
const v = typeof og.video === "string" ? { url: og.video } : og.video;
|
|
57
|
+
m("video", v.url);
|
|
58
|
+
if ("type" in v && v.type) m("video:type", v.type);
|
|
59
|
+
if ("width" in v && v.width != null) m("video:width", String(v.width));
|
|
60
|
+
if ("height" in v && v.height != null) m("video:height", String(v.height));
|
|
61
|
+
}
|
|
62
|
+
if (og.audio) {
|
|
63
|
+
const a = typeof og.audio === "string" ? { url: og.audio } : og.audio;
|
|
64
|
+
m("audio", a.url);
|
|
65
|
+
if ("type" in a && a.type) m("audio:type", a.type);
|
|
66
|
+
}
|
|
67
|
+
if (og.article) {
|
|
68
|
+
const ar = og.article;
|
|
69
|
+
m("article:published_time", ar.publishedTime);
|
|
70
|
+
m("article:modified_time", ar.modifiedTime);
|
|
71
|
+
m("article:expiration_time", ar.expirationTime);
|
|
72
|
+
m("article:section", ar.section);
|
|
73
|
+
for (const au of [].concat(ar.author ?? [])) out.push({ tag: "meta", key: "meta:property:og:article:author:" + au, attrs: { property: "article:author", content: au } });
|
|
74
|
+
for (const t of [].concat(ar.tag ?? [])) out.push({ tag: "meta", key: "meta:property:og:article:tag:" + t, attrs: { property: "article:tag", content: t } });
|
|
75
|
+
}
|
|
76
|
+
if (og.book) {
|
|
77
|
+
const b = og.book;
|
|
78
|
+
m("book:isbn", b.isbn);
|
|
79
|
+
m("book:release_date", b.releaseDate);
|
|
80
|
+
for (const au of [].concat(b.author ?? [])) out.push({ tag: "meta", key: "meta:property:og:book:author:" + au, attrs: { property: "book:author", content: au } });
|
|
81
|
+
for (const t of [].concat(b.tag ?? [])) out.push({ tag: "meta", key: "meta:property:og:book:tag:" + t, attrs: { property: "book:tag", content: t } });
|
|
82
|
+
}
|
|
83
|
+
if (og.profile) {
|
|
84
|
+
const p = og.profile;
|
|
85
|
+
m("profile:first_name", p.firstName);
|
|
86
|
+
m("profile:last_name", p.lastName);
|
|
87
|
+
m("profile:username", p.username);
|
|
88
|
+
m("profile:gender", p.gender);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function expandTwitter(t, out) {
|
|
92
|
+
const m = (n, c) => {
|
|
93
|
+
if (c != null) out.push({ tag: "meta", key: "meta:name:twitter:" + n, attrs: { name: "twitter:" + n, content: c } });
|
|
94
|
+
};
|
|
95
|
+
m("card", t.card);
|
|
96
|
+
m("site", t.site);
|
|
97
|
+
m("creator", t.creator);
|
|
98
|
+
m("title", t.title);
|
|
99
|
+
m("description", t.description);
|
|
100
|
+
m("image", t.image);
|
|
101
|
+
m("image:alt", t.imageAlt);
|
|
102
|
+
m("player", t.player);
|
|
103
|
+
if (t.playerWidth != null) m("player:width", String(t.playerWidth));
|
|
104
|
+
if (t.playerHeight != null) m("player:height", String(t.playerHeight));
|
|
105
|
+
if (t.app) {
|
|
106
|
+
m("app:name:iphone", t.app.name);
|
|
107
|
+
m("app:id:iphone", t.app.idIphone);
|
|
108
|
+
m("app:id:ipad", t.app.idIpad);
|
|
109
|
+
m("app:id:googleplay", t.app.idGooglePlay);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function expandIcons(icons, out) {
|
|
113
|
+
const link = (key, attrs) => out.push({ tag: "link", key, attrs });
|
|
114
|
+
if (icons.icon != null) {
|
|
115
|
+
const arr = typeof icons.icon === "string" ? [{ url: icons.icon }] : icons.icon;
|
|
116
|
+
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 } : {} });
|
|
117
|
+
}
|
|
118
|
+
if (icons.apple != null) {
|
|
119
|
+
const arr = typeof icons.apple === "string" ? [{ url: icons.apple }] : icons.apple;
|
|
120
|
+
for (const i of arr) link("link:apple-touch-icon:" + i.url, { rel: "apple-touch-icon", href: i.url, ...i.sizes ? { sizes: i.sizes } : {} });
|
|
121
|
+
}
|
|
122
|
+
if (icons.mask) link("link:mask-icon", { rel: "mask-icon", href: icons.mask.url, ...icons.mask.color ? { color: icons.mask.color } : {} });
|
|
123
|
+
if (icons.shortcut) link("link:shortcut", { rel: "shortcut icon", href: icons.shortcut });
|
|
124
|
+
if (icons.msTileImage) out.push({ tag: "meta", key: "meta:name:msapplication-TileImage", attrs: { name: "msapplication-TileImage", content: icons.msTileImage } });
|
|
125
|
+
if (icons.msTileColor) out.push({ tag: "meta", key: "meta:name:msapplication-TileColor", attrs: { name: "msapplication-TileColor", content: icons.msTileColor } });
|
|
126
|
+
}
|
|
127
|
+
function expand(input, out, ts) {
|
|
128
|
+
const meta = (key, attrs) => out.push({ tag: "meta", key, attrs });
|
|
129
|
+
const title = val(input.title);
|
|
130
|
+
if (title !== void 0) ts.title = title;
|
|
131
|
+
if (input.titleTemplate !== void 0) ts.template = input.titleTemplate;
|
|
132
|
+
const lang = val(input.lang);
|
|
133
|
+
if (lang !== void 0) out.push({ tag: "htmlAttr", key: "html:lang", name: "lang", value: lang });
|
|
134
|
+
if (input.charset !== void 0) meta("meta:charset", { charset: input.charset });
|
|
135
|
+
if (input.viewport !== void 0) meta("meta:name:viewport", { name: "viewport", content: input.viewport });
|
|
136
|
+
const desc = val(input.description);
|
|
137
|
+
if (desc !== void 0) meta("meta:name:description", { name: "description", content: desc });
|
|
138
|
+
const kw = val(input.keywords);
|
|
139
|
+
if (kw !== void 0) meta("meta:name:keywords", { name: "keywords", content: Array.isArray(kw) ? kw.join(", ") : kw });
|
|
140
|
+
if (input.author !== void 0) meta("meta:name:author", { name: "author", content: input.author });
|
|
141
|
+
if (input.robots !== void 0) meta("meta:name:robots", { name: "robots", content: robotsToString(input.robots) });
|
|
142
|
+
if (input.generator !== void 0) meta("meta:name:generator", { name: "generator", content: input.generator });
|
|
143
|
+
if (input.applicationName !== void 0) meta("meta:name:application-name", { name: "application-name", content: input.applicationName });
|
|
144
|
+
if (input.referrer !== void 0) meta("meta:name:referrer", { name: "referrer", content: input.referrer });
|
|
145
|
+
if (input.colorScheme !== void 0) meta("meta:name:color-scheme", { name: "color-scheme", content: input.colorScheme });
|
|
146
|
+
const theme = val(input.themeColor);
|
|
147
|
+
if (theme !== void 0) meta("meta:name:theme-color", { name: "theme-color", content: theme });
|
|
148
|
+
const canonical = val(input.canonical);
|
|
149
|
+
if (canonical !== void 0) out.push({ tag: "link", key: "link:rel:canonical", attrs: { rel: "canonical", href: canonical } });
|
|
150
|
+
if (input.manifest !== void 0) out.push({ tag: "link", key: "link:rel:manifest", attrs: { rel: "manifest", href: input.manifest } });
|
|
151
|
+
if (input.base !== void 0) out.push({ tag: "base", key: "base", attrs: { href: input.base } });
|
|
152
|
+
if (input.verification) {
|
|
153
|
+
const map = { google: "google-site-verification", bing: "msvalidate.01", yandex: "yandex-verification", pinterest: "p:domain_verify" };
|
|
154
|
+
for (const [k, v] of Object.entries(input.verification)) {
|
|
155
|
+
if (v == null) continue;
|
|
156
|
+
const name = map[k] ?? k;
|
|
157
|
+
meta("meta:name:" + name, { name, content: v });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
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 } });
|
|
161
|
+
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 } : {} } });
|
|
162
|
+
if (input.og) expandOG(input.og, out);
|
|
163
|
+
if (input.twitter) expandTwitter(input.twitter, out);
|
|
164
|
+
if (input.icons) expandIcons(input.icons, out);
|
|
165
|
+
const ld = val(input.jsonLd);
|
|
166
|
+
if (ld !== void 0) {
|
|
167
|
+
const arr = Array.isArray(ld) ? ld : [ld];
|
|
168
|
+
for (const obj of arr) {
|
|
169
|
+
const json = JSON.stringify(obj);
|
|
170
|
+
out.push({ tag: "script", key: "script:ldjson:" + djb2(json), attrs: { type: "application/ld+json" }, children: json });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
for (const m of input.meta ?? []) {
|
|
174
|
+
const attrs = {};
|
|
175
|
+
for (const [k, v] of Object.entries(m)) if (v != null) attrs[k === "httpEquiv" ? "http-equiv" : k] = v;
|
|
176
|
+
out.push({ tag: "meta", key: metaKey(attrs), attrs });
|
|
177
|
+
}
|
|
178
|
+
for (const l of input.link ?? []) {
|
|
179
|
+
const attrs = {};
|
|
180
|
+
for (const [k, v] of Object.entries(l)) if (v != null) attrs[k] = v;
|
|
181
|
+
out.push({ tag: "link", key: "link:" + (attrs.rel ?? "") + ":" + (attrs.href ?? djb2(JSON.stringify(attrs))), attrs });
|
|
182
|
+
}
|
|
183
|
+
for (const s of input.script ?? []) {
|
|
184
|
+
const attrs = {};
|
|
185
|
+
let children;
|
|
186
|
+
for (const [k, v] of Object.entries(s)) {
|
|
187
|
+
if (v == null) continue;
|
|
188
|
+
if (k === "children") children = v;
|
|
189
|
+
else attrs[k] = v;
|
|
190
|
+
}
|
|
191
|
+
out.push({ tag: "script", key: "script:" + (attrs.src ?? djb2((children ?? "") + JSON.stringify(attrs))), attrs, children });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function applyTemplate(tpl, title) {
|
|
195
|
+
return typeof tpl === "function" ? tpl(title) : tpl.includes("%s") ? tpl.replace("%s", title) : tpl;
|
|
196
|
+
}
|
|
197
|
+
function resolveHead(head) {
|
|
198
|
+
const raw = [];
|
|
199
|
+
const ts = {};
|
|
200
|
+
for (const src of head.sources) {
|
|
201
|
+
const input = typeof src === "function" ? src() : src;
|
|
202
|
+
expand(input, raw, ts);
|
|
203
|
+
}
|
|
204
|
+
if (ts.title !== void 0) {
|
|
205
|
+
raw.push({ tag: "title", key: "title", children: ts.template ? applyTemplate(ts.template, ts.title) : ts.title });
|
|
206
|
+
}
|
|
207
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
208
|
+
const htmlAttrs = {};
|
|
209
|
+
for (const t of raw) {
|
|
210
|
+
if (t.tag === "htmlAttr") {
|
|
211
|
+
htmlAttrs[t.name] = t.value;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
byKey.set(t.key, t);
|
|
215
|
+
}
|
|
216
|
+
return { tags: [...byKey.values()], htmlAttrs };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/serialize.ts
|
|
220
|
+
function escAttr(s) {
|
|
221
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
222
|
+
}
|
|
223
|
+
function escText(s) {
|
|
224
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
225
|
+
}
|
|
226
|
+
function escScript(s) {
|
|
227
|
+
return s.replace(/<\//g, "<\\/").replace(/<!--/g, "<\\!--");
|
|
228
|
+
}
|
|
229
|
+
var MANAGED_ATTR = "data-fh";
|
|
230
|
+
var KEY_ATTR = "data-fh-key";
|
|
231
|
+
function renderAttrs(attrs) {
|
|
232
|
+
let s = "";
|
|
233
|
+
for (const [k, v] of Object.entries(attrs)) s += ` ${k}="${escAttr(v)}"`;
|
|
234
|
+
return s;
|
|
235
|
+
}
|
|
236
|
+
function renderTag(t) {
|
|
237
|
+
switch (t.tag) {
|
|
238
|
+
case "title":
|
|
239
|
+
return `<title ${MANAGED_ATTR}>${escText(t.children)}</title>`;
|
|
240
|
+
case "meta":
|
|
241
|
+
case "link":
|
|
242
|
+
case "base": {
|
|
243
|
+
const marker = ` ${MANAGED_ATTR} ${KEY_ATTR}="${escAttr(t.key)}"`;
|
|
244
|
+
return `<${t.tag}${renderAttrs(t.attrs)}${marker}>`;
|
|
245
|
+
}
|
|
246
|
+
case "script": {
|
|
247
|
+
const marker = ` ${MANAGED_ATTR} ${KEY_ATTR}="${escAttr(t.key)}"`;
|
|
248
|
+
const body = t.children ? escScript(t.children) : "";
|
|
249
|
+
return `<script${renderAttrs(t.attrs)}${marker}>${body}<\/script>`;
|
|
250
|
+
}
|
|
251
|
+
default:
|
|
252
|
+
return "";
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function renderHead(head) {
|
|
256
|
+
const { tags, htmlAttrs } = resolveHead(head);
|
|
257
|
+
const order = { title: 0, base: 1, meta: 2, link: 3, script: 4 };
|
|
258
|
+
const sorted = [...tags].sort((a, b) => (order[a.tag] ?? 9) - (order[b.tag] ?? 9));
|
|
259
|
+
return { headHtml: sorted.map(renderTag).join(""), htmlAttrs };
|
|
260
|
+
}
|
|
261
|
+
function renderHtmlAttrs(htmlAttrs) {
|
|
262
|
+
return Object.entries(htmlAttrs).map(([k, v]) => ` ${k}="${escAttr(v)}"`).join("");
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
KEY_ATTR,
|
|
266
|
+
MANAGED_ATTR,
|
|
267
|
+
renderHead,
|
|
268
|
+
renderHtmlAttrs
|
|
269
|
+
};
|
package/dist/sitemap.cjs
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/sitemap.ts
|
|
21
|
+
var sitemap_exports = {};
|
|
22
|
+
__export(sitemap_exports, {
|
|
23
|
+
chunkSitemap: () => chunkSitemap,
|
|
24
|
+
generateRobots: () => generateRobots,
|
|
25
|
+
generateSitemap: () => generateSitemap,
|
|
26
|
+
generateSitemapIndex: () => generateSitemapIndex
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(sitemap_exports);
|
|
29
|
+
function esc(s) {
|
|
30
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
31
|
+
}
|
|
32
|
+
function isoDate(d) {
|
|
33
|
+
return d instanceof Date ? d.toISOString() : d;
|
|
34
|
+
}
|
|
35
|
+
function renderEntry(e) {
|
|
36
|
+
let s = " <url>\n";
|
|
37
|
+
s += ` <loc>${esc(e.loc)}</loc>
|
|
38
|
+
`;
|
|
39
|
+
if (e.lastmod != null) s += ` <lastmod>${esc(isoDate(e.lastmod))}</lastmod>
|
|
40
|
+
`;
|
|
41
|
+
if (e.changefreq) s += ` <changefreq>${e.changefreq}</changefreq>
|
|
42
|
+
`;
|
|
43
|
+
if (e.priority != null) s += ` <priority>${e.priority.toFixed(1)}</priority>
|
|
44
|
+
`;
|
|
45
|
+
for (const a of e.alternates ?? []) s += ` <xhtml:link rel="alternate" hreflang="${esc(a.hreflang)}" href="${esc(a.href)}"/>
|
|
46
|
+
`;
|
|
47
|
+
for (const img of e.images ?? []) {
|
|
48
|
+
s += ` <image:image>
|
|
49
|
+
<image:loc>${esc(img.loc)}</image:loc>
|
|
50
|
+
`;
|
|
51
|
+
if (img.title) s += ` <image:title>${esc(img.title)}</image:title>
|
|
52
|
+
`;
|
|
53
|
+
if (img.caption) s += ` <image:caption>${esc(img.caption)}</image:caption>
|
|
54
|
+
`;
|
|
55
|
+
s += ` </image:image>
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
for (const v of e.videos ?? []) {
|
|
59
|
+
s += ` <video:video>
|
|
60
|
+
<video:thumbnail_loc>${esc(v.thumbnailLoc)}</video:thumbnail_loc>
|
|
61
|
+
`;
|
|
62
|
+
s += ` <video:title>${esc(v.title)}</video:title>
|
|
63
|
+
<video:description>${esc(v.description)}</video:description>
|
|
64
|
+
`;
|
|
65
|
+
if (v.contentLoc) s += ` <video:content_loc>${esc(v.contentLoc)}</video:content_loc>
|
|
66
|
+
`;
|
|
67
|
+
if (v.playerLoc) s += ` <video:player_loc>${esc(v.playerLoc)}</video:player_loc>
|
|
68
|
+
`;
|
|
69
|
+
s += ` </video:video>
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
s += " </url>\n";
|
|
73
|
+
return s;
|
|
74
|
+
}
|
|
75
|
+
function generateSitemap(entries) {
|
|
76
|
+
const ns = 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"';
|
|
77
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
78
|
+
<urlset ${ns}>
|
|
79
|
+
${entries.map(renderEntry).join("")}</urlset>
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
function chunkSitemap(entries, limit = 5e4) {
|
|
83
|
+
const out = [];
|
|
84
|
+
for (let i = 0; i < entries.length; i += limit) out.push(entries.slice(i, i + limit));
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
function generateSitemapIndex(sitemaps) {
|
|
88
|
+
let s = '<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n';
|
|
89
|
+
for (const sm of sitemaps) {
|
|
90
|
+
s += ` <sitemap>
|
|
91
|
+
<loc>${esc(sm.loc)}</loc>
|
|
92
|
+
`;
|
|
93
|
+
if (sm.lastmod != null) s += ` <lastmod>${esc(isoDate(sm.lastmod))}</lastmod>
|
|
94
|
+
`;
|
|
95
|
+
s += " </sitemap>\n";
|
|
96
|
+
}
|
|
97
|
+
return s + "</sitemapindex>\n";
|
|
98
|
+
}
|
|
99
|
+
function generateRobots(opts = {}) {
|
|
100
|
+
if (opts.production === false) {
|
|
101
|
+
return "User-agent: *\nDisallow: /\n";
|
|
102
|
+
}
|
|
103
|
+
const lines = [];
|
|
104
|
+
const rules = opts.rules ?? [{ userAgent: "*", allow: "/" }];
|
|
105
|
+
for (const r of rules) {
|
|
106
|
+
for (const ua of [].concat(r.userAgent)) lines.push(`User-agent: ${ua}`);
|
|
107
|
+
for (const a of [].concat(r.allow ?? [])) lines.push(`Allow: ${a}`);
|
|
108
|
+
for (const d of [].concat(r.disallow ?? [])) lines.push(`Disallow: ${d}`);
|
|
109
|
+
if (r.crawlDelay != null) lines.push(`Crawl-delay: ${r.crawlDelay}`);
|
|
110
|
+
lines.push("");
|
|
111
|
+
}
|
|
112
|
+
if (opts.host) lines.push(`Host: ${opts.host}`);
|
|
113
|
+
for (const sm of opts.sitemaps ?? []) lines.push(`Sitemap: ${sm}`);
|
|
114
|
+
return lines.join("\n").replace(/\n+$/, "\n");
|
|
115
|
+
}
|
package/dist/sitemap.d.ts
CHANGED
package/dist/sitemap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../src/sitemap.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../src/sitemap.ts"],"names":[],"mappings":"AAAA,oGAAoG;AAEpG,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AACD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAiCD,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAO/D;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,SAAQ,GAAG,YAAY,EAAE,EAAE,CAIrF;AAED,wDAAwD;AACxD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,GAAG,MAAM,CAQtG;AAMD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AACD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,cAAc,CAAC,IAAI,GAAE,aAAkB,GAAG,MAAM,CAgB/D"}
|
package/dist/sitemap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** sitemap.xml + robots.txt generators. Pure functions
|
|
1
|
+
/** sitemap.xml + robots.txt generators. Pure functions, the build/server wires routes into them. */
|
|
2
2
|
function esc(s) {
|
|
3
3
|
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
4
4
|
}
|
package/dist/sitemap.mjs
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/sitemap.ts
|
|
2
|
+
function esc(s) {
|
|
3
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4
|
+
}
|
|
5
|
+
function isoDate(d) {
|
|
6
|
+
return d instanceof Date ? d.toISOString() : d;
|
|
7
|
+
}
|
|
8
|
+
function renderEntry(e) {
|
|
9
|
+
let s = " <url>\n";
|
|
10
|
+
s += ` <loc>${esc(e.loc)}</loc>
|
|
11
|
+
`;
|
|
12
|
+
if (e.lastmod != null) s += ` <lastmod>${esc(isoDate(e.lastmod))}</lastmod>
|
|
13
|
+
`;
|
|
14
|
+
if (e.changefreq) s += ` <changefreq>${e.changefreq}</changefreq>
|
|
15
|
+
`;
|
|
16
|
+
if (e.priority != null) s += ` <priority>${e.priority.toFixed(1)}</priority>
|
|
17
|
+
`;
|
|
18
|
+
for (const a of e.alternates ?? []) s += ` <xhtml:link rel="alternate" hreflang="${esc(a.hreflang)}" href="${esc(a.href)}"/>
|
|
19
|
+
`;
|
|
20
|
+
for (const img of e.images ?? []) {
|
|
21
|
+
s += ` <image:image>
|
|
22
|
+
<image:loc>${esc(img.loc)}</image:loc>
|
|
23
|
+
`;
|
|
24
|
+
if (img.title) s += ` <image:title>${esc(img.title)}</image:title>
|
|
25
|
+
`;
|
|
26
|
+
if (img.caption) s += ` <image:caption>${esc(img.caption)}</image:caption>
|
|
27
|
+
`;
|
|
28
|
+
s += ` </image:image>
|
|
29
|
+
`;
|
|
30
|
+
}
|
|
31
|
+
for (const v of e.videos ?? []) {
|
|
32
|
+
s += ` <video:video>
|
|
33
|
+
<video:thumbnail_loc>${esc(v.thumbnailLoc)}</video:thumbnail_loc>
|
|
34
|
+
`;
|
|
35
|
+
s += ` <video:title>${esc(v.title)}</video:title>
|
|
36
|
+
<video:description>${esc(v.description)}</video:description>
|
|
37
|
+
`;
|
|
38
|
+
if (v.contentLoc) s += ` <video:content_loc>${esc(v.contentLoc)}</video:content_loc>
|
|
39
|
+
`;
|
|
40
|
+
if (v.playerLoc) s += ` <video:player_loc>${esc(v.playerLoc)}</video:player_loc>
|
|
41
|
+
`;
|
|
42
|
+
s += ` </video:video>
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
s += " </url>\n";
|
|
46
|
+
return s;
|
|
47
|
+
}
|
|
48
|
+
function generateSitemap(entries) {
|
|
49
|
+
const ns = 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"';
|
|
50
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
51
|
+
<urlset ${ns}>
|
|
52
|
+
${entries.map(renderEntry).join("")}</urlset>
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
function chunkSitemap(entries, limit = 5e4) {
|
|
56
|
+
const out = [];
|
|
57
|
+
for (let i = 0; i < entries.length; i += limit) out.push(entries.slice(i, i + limit));
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
function generateSitemapIndex(sitemaps) {
|
|
61
|
+
let s = '<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n';
|
|
62
|
+
for (const sm of sitemaps) {
|
|
63
|
+
s += ` <sitemap>
|
|
64
|
+
<loc>${esc(sm.loc)}</loc>
|
|
65
|
+
`;
|
|
66
|
+
if (sm.lastmod != null) s += ` <lastmod>${esc(isoDate(sm.lastmod))}</lastmod>
|
|
67
|
+
`;
|
|
68
|
+
s += " </sitemap>\n";
|
|
69
|
+
}
|
|
70
|
+
return s + "</sitemapindex>\n";
|
|
71
|
+
}
|
|
72
|
+
function generateRobots(opts = {}) {
|
|
73
|
+
if (opts.production === false) {
|
|
74
|
+
return "User-agent: *\nDisallow: /\n";
|
|
75
|
+
}
|
|
76
|
+
const lines = [];
|
|
77
|
+
const rules = opts.rules ?? [{ userAgent: "*", allow: "/" }];
|
|
78
|
+
for (const r of rules) {
|
|
79
|
+
for (const ua of [].concat(r.userAgent)) lines.push(`User-agent: ${ua}`);
|
|
80
|
+
for (const a of [].concat(r.allow ?? [])) lines.push(`Allow: ${a}`);
|
|
81
|
+
for (const d of [].concat(r.disallow ?? [])) lines.push(`Disallow: ${d}`);
|
|
82
|
+
if (r.crawlDelay != null) lines.push(`Crawl-delay: ${r.crawlDelay}`);
|
|
83
|
+
lines.push("");
|
|
84
|
+
}
|
|
85
|
+
if (opts.host) lines.push(`Host: ${opts.host}`);
|
|
86
|
+
for (const sm of opts.sitemaps ?? []) lines.push(`Sitemap: ${sm}`);
|
|
87
|
+
return lines.join("\n").replace(/\n+$/, "\n");
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
chunkSitemap,
|
|
91
|
+
generateRobots,
|
|
92
|
+
generateSitemap,
|
|
93
|
+
generateSitemapIndex
|
|
94
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/transport.ts
|
|
21
|
+
var transport_exports = {};
|
|
22
|
+
__export(transport_exports, {
|
|
23
|
+
encodeHeadMarker: () => encodeHeadMarker,
|
|
24
|
+
extractHeadMarker: () => extractHeadMarker,
|
|
25
|
+
stripHeadMarker: () => stripHeadMarker
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(transport_exports);
|
|
28
|
+
var RE = /<!--fh:([A-Za-z0-9+/=]+)-->/;
|
|
29
|
+
function b64encode(s) {
|
|
30
|
+
const g = globalThis;
|
|
31
|
+
if (g.Buffer) return g.Buffer.from(s, "utf-8").toString("base64");
|
|
32
|
+
return g.btoa(unescape(encodeURIComponent(s)));
|
|
33
|
+
}
|
|
34
|
+
function b64decode(s) {
|
|
35
|
+
const g = globalThis;
|
|
36
|
+
if (g.Buffer) return g.Buffer.from(s, "base64").toString("utf-8");
|
|
37
|
+
return decodeURIComponent(escape(g.atob(s)));
|
|
38
|
+
}
|
|
39
|
+
function encodeHeadMarker(head) {
|
|
40
|
+
if (!head.headHtml && Object.keys(head.htmlAttrs).length === 0) return "";
|
|
41
|
+
return `<!--fh:${b64encode(JSON.stringify(head))}-->`;
|
|
42
|
+
}
|
|
43
|
+
function stripHeadMarker(container) {
|
|
44
|
+
for (let n = container.firstChild; n; n = n.nextSibling) {
|
|
45
|
+
if (n.nodeType === 8 && n.data.startsWith("fh:")) {
|
|
46
|
+
n.parentNode?.removeChild(n);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function extractHeadMarker(html) {
|
|
52
|
+
const m = html.match(RE);
|
|
53
|
+
if (!m) return { head: null, body: html };
|
|
54
|
+
let head = null;
|
|
55
|
+
try {
|
|
56
|
+
head = JSON.parse(b64decode(m[1]));
|
|
57
|
+
} catch {
|
|
58
|
+
head = null;
|
|
59
|
+
}
|
|
60
|
+
return { head, body: html.replace(m[0], "") };
|
|
61
|
+
}
|
package/dist/transport.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ export declare function encodeHeadMarker(head: SerializedHead): string;
|
|
|
5
5
|
* Client-side safety net: remove any `<!--fh:…-->` transport comment left inside the hydration
|
|
6
6
|
* container. A correct adapter/prerender/dev server lifts the marker into <head> before it reaches
|
|
7
7
|
* the browser, but a streaming or custom server can leave it in the body. Since encodeHeadMarker
|
|
8
|
-
* prepends it, an un-extracted marker is the mount root's first child
|
|
8
|
+
* prepends it, an un-extracted marker is the mount root's first child, where it strands the
|
|
9
9
|
* hydration cursor (the walk expects the app's first element, finds a comment, and rebuilds the
|
|
10
10
|
* whole tree next to the server DOM → duplicate). The client rebuilds the head from seo()/useHead
|
|
11
|
-
* during hydration, so the marker's payload isn't needed here
|
|
11
|
+
* during hydration, so the marker's payload isn't needed here, just drop the node.
|
|
12
12
|
*/
|
|
13
13
|
export declare function stripHeadMarker(container: ParentNode): void;
|
|
14
14
|
/** Pull the head marker out of rendered HTML, returning the head and the cleaned body. */
|
package/dist/transport.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The server can't change the render's return type, so the collected head rides back inside the
|
|
2
2
|
// rendered HTML as one inert comment marker; the adapter/prerender extracts it into <head>. If a
|
|
3
|
-
// custom server doesn't extract it, it's just an invisible comment in the body
|
|
3
|
+
// custom server doesn't extract it, it's just an invisible comment in the body, no breakage.
|
|
4
4
|
const RE = /<!--fh:([A-Za-z0-9+/=]+)-->/;
|
|
5
5
|
function b64encode(s) {
|
|
6
6
|
const g = globalThis;
|
|
@@ -24,10 +24,10 @@ export function encodeHeadMarker(head) {
|
|
|
24
24
|
* Client-side safety net: remove any `<!--fh:…-->` transport comment left inside the hydration
|
|
25
25
|
* container. A correct adapter/prerender/dev server lifts the marker into <head> before it reaches
|
|
26
26
|
* the browser, but a streaming or custom server can leave it in the body. Since encodeHeadMarker
|
|
27
|
-
* prepends it, an un-extracted marker is the mount root's first child
|
|
27
|
+
* prepends it, an un-extracted marker is the mount root's first child, where it strands the
|
|
28
28
|
* hydration cursor (the walk expects the app's first element, finds a comment, and rebuilds the
|
|
29
29
|
* whole tree next to the server DOM → duplicate). The client rebuilds the head from seo()/useHead
|
|
30
|
-
* during hydration, so the marker's payload isn't needed here
|
|
30
|
+
* during hydration, so the marker's payload isn't needed here, just drop the node.
|
|
31
31
|
*/
|
|
32
32
|
export function stripHeadMarker(container) {
|
|
33
33
|
for (let n = container.firstChild; n; n = n.nextSibling) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/transport.ts
|
|
2
|
+
var RE = /<!--fh:([A-Za-z0-9+/=]+)-->/;
|
|
3
|
+
function b64encode(s) {
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
if (g.Buffer) return g.Buffer.from(s, "utf-8").toString("base64");
|
|
6
|
+
return g.btoa(unescape(encodeURIComponent(s)));
|
|
7
|
+
}
|
|
8
|
+
function b64decode(s) {
|
|
9
|
+
const g = globalThis;
|
|
10
|
+
if (g.Buffer) return g.Buffer.from(s, "base64").toString("utf-8");
|
|
11
|
+
return decodeURIComponent(escape(g.atob(s)));
|
|
12
|
+
}
|
|
13
|
+
function encodeHeadMarker(head) {
|
|
14
|
+
if (!head.headHtml && Object.keys(head.htmlAttrs).length === 0) return "";
|
|
15
|
+
return `<!--fh:${b64encode(JSON.stringify(head))}-->`;
|
|
16
|
+
}
|
|
17
|
+
function stripHeadMarker(container) {
|
|
18
|
+
for (let n = container.firstChild; n; n = n.nextSibling) {
|
|
19
|
+
if (n.nodeType === 8 && n.data.startsWith("fh:")) {
|
|
20
|
+
n.parentNode?.removeChild(n);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function extractHeadMarker(html) {
|
|
26
|
+
const m = html.match(RE);
|
|
27
|
+
if (!m) return { head: null, body: html };
|
|
28
|
+
let head = null;
|
|
29
|
+
try {
|
|
30
|
+
head = JSON.parse(b64decode(m[1]));
|
|
31
|
+
} catch {
|
|
32
|
+
head = null;
|
|
33
|
+
}
|
|
34
|
+
return { head, body: html.replace(m[0], "") };
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
encodeHeadMarker,
|
|
38
|
+
extractHeadMarker,
|
|
39
|
+
stripHeadMarker
|
|
40
|
+
};
|