@flightdev/seo 0.2.0
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 +21 -0
- package/README.md +403 -0
- package/dist/index.d.ts +282 -0
- package/dist/index.js +1239 -0
- package/dist/index.js.map +1 -0
- package/dist/json-ld/index.d.ts +341 -0
- package/dist/json-ld/index.js +82 -0
- package/dist/json-ld/index.js.map +1 -0
- package/dist/react/index.d.ts +161 -0
- package/dist/react/index.js +1300 -0
- package/dist/react/index.js.map +1 -0
- package/dist/robots/index.d.ts +105 -0
- package/dist/robots/index.js +186 -0
- package/dist/robots/index.js.map +1 -0
- package/dist/sitemap/index.d.ts +224 -0
- package/dist/sitemap/index.js +242 -0
- package/dist/sitemap/index.js.map +1 -0
- package/dist/solid/index.d.ts +50 -0
- package/dist/solid/index.js +1217 -0
- package/dist/solid/index.js.map +1 -0
- package/dist/svelte/index.d.ts +131 -0
- package/dist/svelte/index.js +1253 -0
- package/dist/svelte/index.js.map +1 -0
- package/dist/types-BBIMJIqz.d.ts +433 -0
- package/dist/vue/index.d.ts +130 -0
- package/dist/vue/index.js +1293 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +129 -0
|
@@ -0,0 +1,1253 @@
|
|
|
1
|
+
// src/head.ts
|
|
2
|
+
var VOID_TAGS = /* @__PURE__ */ new Set(["meta", "link", "base"]);
|
|
3
|
+
var BOOLEAN_ATTRS = /* @__PURE__ */ new Set(["async", "defer", "nomodule", "crossorigin"]);
|
|
4
|
+
function escapeHtml(str) {
|
|
5
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6
|
+
}
|
|
7
|
+
function escapeAttr(str) {
|
|
8
|
+
return str.replace(/"/g, """);
|
|
9
|
+
}
|
|
10
|
+
function renderTag(tag) {
|
|
11
|
+
const parts = [`<${tag.tag}`];
|
|
12
|
+
if (tag.attrs) {
|
|
13
|
+
for (const [key, value] of Object.entries(tag.attrs)) {
|
|
14
|
+
if (value === void 0 || value === false) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (value === true) {
|
|
18
|
+
parts.push(` ${key}`);
|
|
19
|
+
} else if (BOOLEAN_ATTRS.has(key) && value === "") {
|
|
20
|
+
parts.push(` ${key}`);
|
|
21
|
+
} else {
|
|
22
|
+
parts.push(` ${key}="${escapeAttr(String(value))}"`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (VOID_TAGS.has(tag.tag)) {
|
|
27
|
+
parts.push(" />");
|
|
28
|
+
} else if (tag.content !== void 0) {
|
|
29
|
+
const isJsonLd = tag.tag === "script" && tag.attrs?.type === "application/ld+json";
|
|
30
|
+
const content = isJsonLd ? tag.content : escapeHtml(tag.content);
|
|
31
|
+
parts.push(`>${content}</${tag.tag}>`);
|
|
32
|
+
} else {
|
|
33
|
+
parts.push(`></${tag.tag}>`);
|
|
34
|
+
}
|
|
35
|
+
return parts.join("");
|
|
36
|
+
}
|
|
37
|
+
function renderTags(tags) {
|
|
38
|
+
return tags.map((tag) => renderTag(tag)).join("\n");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/meta.ts
|
|
42
|
+
function generateTitleTag(title, template, defaultTitle) {
|
|
43
|
+
let resolvedTitle = title || defaultTitle;
|
|
44
|
+
if (!resolvedTitle) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (template && title) {
|
|
48
|
+
resolvedTitle = template.replace("%s", title);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
tag: "title",
|
|
52
|
+
content: resolvedTitle,
|
|
53
|
+
key: "title"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function generateCharsetTag(charset = "utf-8") {
|
|
57
|
+
return {
|
|
58
|
+
tag: "meta",
|
|
59
|
+
attrs: { charset },
|
|
60
|
+
key: "meta:charset"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function generateViewportTag(viewport) {
|
|
64
|
+
if (!viewport) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
let content;
|
|
68
|
+
if (typeof viewport === "string") {
|
|
69
|
+
content = viewport;
|
|
70
|
+
} else {
|
|
71
|
+
const parts = [];
|
|
72
|
+
if (viewport.width !== void 0) {
|
|
73
|
+
parts.push(`width=${viewport.width}`);
|
|
74
|
+
}
|
|
75
|
+
if (viewport.height !== void 0) {
|
|
76
|
+
parts.push(`height=${viewport.height}`);
|
|
77
|
+
}
|
|
78
|
+
if (viewport.initialScale !== void 0) {
|
|
79
|
+
parts.push(`initial-scale=${viewport.initialScale}`);
|
|
80
|
+
}
|
|
81
|
+
if (viewport.minimumScale !== void 0) {
|
|
82
|
+
parts.push(`minimum-scale=${viewport.minimumScale}`);
|
|
83
|
+
}
|
|
84
|
+
if (viewport.maximumScale !== void 0) {
|
|
85
|
+
parts.push(`maximum-scale=${viewport.maximumScale}`);
|
|
86
|
+
}
|
|
87
|
+
if (viewport.userScalable !== void 0) {
|
|
88
|
+
parts.push(`user-scalable=${viewport.userScalable ? "yes" : "no"}`);
|
|
89
|
+
}
|
|
90
|
+
if (viewport.viewportFit !== void 0) {
|
|
91
|
+
parts.push(`viewport-fit=${viewport.viewportFit}`);
|
|
92
|
+
}
|
|
93
|
+
content = parts.join(", ");
|
|
94
|
+
}
|
|
95
|
+
if (!content) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
tag: "meta",
|
|
100
|
+
attrs: { name: "viewport", content },
|
|
101
|
+
key: "meta:name:viewport"
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function generateDescriptionTag(description) {
|
|
105
|
+
if (!description) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
tag: "meta",
|
|
110
|
+
attrs: { name: "description", content: description },
|
|
111
|
+
key: "meta:name:description"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function generateKeywordsTag(keywords) {
|
|
115
|
+
if (!keywords) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const content = Array.isArray(keywords) ? keywords.join(", ") : keywords;
|
|
119
|
+
return {
|
|
120
|
+
tag: "meta",
|
|
121
|
+
attrs: { name: "keywords", content },
|
|
122
|
+
key: "meta:name:keywords"
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function generateAuthorTag(author) {
|
|
126
|
+
if (!author) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
tag: "meta",
|
|
131
|
+
attrs: { name: "author", content: author },
|
|
132
|
+
key: "meta:name:author"
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function generateGeneratorTag(generator) {
|
|
136
|
+
if (!generator) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
tag: "meta",
|
|
141
|
+
attrs: { name: "generator", content: generator },
|
|
142
|
+
key: "meta:name:generator"
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function generateApplicationNameTag(name) {
|
|
146
|
+
if (!name) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
tag: "meta",
|
|
151
|
+
attrs: { name: "application-name", content: name },
|
|
152
|
+
key: "meta:name:application-name"
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function generateReferrerTag(referrer) {
|
|
156
|
+
if (!referrer) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
tag: "meta",
|
|
161
|
+
attrs: { name: "referrer", content: referrer },
|
|
162
|
+
key: "meta:name:referrer"
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function generateThemeColorTags(themeColor) {
|
|
166
|
+
if (!themeColor) {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
if (typeof themeColor === "string") {
|
|
170
|
+
return [
|
|
171
|
+
{
|
|
172
|
+
tag: "meta",
|
|
173
|
+
attrs: { name: "theme-color", content: themeColor },
|
|
174
|
+
key: "meta:name:theme-color"
|
|
175
|
+
}
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
return themeColor.map((tc, index) => ({
|
|
179
|
+
tag: "meta",
|
|
180
|
+
attrs: {
|
|
181
|
+
name: "theme-color",
|
|
182
|
+
content: tc.color,
|
|
183
|
+
media: tc.media
|
|
184
|
+
},
|
|
185
|
+
key: `meta:name:theme-color:${index}`
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
function generateColorSchemeTag(colorScheme) {
|
|
189
|
+
if (!colorScheme) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
tag: "meta",
|
|
194
|
+
attrs: { name: "color-scheme", content: colorScheme },
|
|
195
|
+
key: "meta:name:color-scheme"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function robotsMetaToString(robots) {
|
|
199
|
+
const directives = [];
|
|
200
|
+
if (robots.index === false) {
|
|
201
|
+
directives.push("noindex");
|
|
202
|
+
} else if (robots.index === true) {
|
|
203
|
+
directives.push("index");
|
|
204
|
+
}
|
|
205
|
+
if (robots.follow === false) {
|
|
206
|
+
directives.push("nofollow");
|
|
207
|
+
} else if (robots.follow === true) {
|
|
208
|
+
directives.push("follow");
|
|
209
|
+
}
|
|
210
|
+
if (robots.noarchive) {
|
|
211
|
+
directives.push("noarchive");
|
|
212
|
+
}
|
|
213
|
+
if (robots.nosnippet) {
|
|
214
|
+
directives.push("nosnippet");
|
|
215
|
+
}
|
|
216
|
+
if (robots.notranslate) {
|
|
217
|
+
directives.push("notranslate");
|
|
218
|
+
}
|
|
219
|
+
if (robots.maxSnippet !== void 0) {
|
|
220
|
+
directives.push(`max-snippet:${robots.maxSnippet}`);
|
|
221
|
+
}
|
|
222
|
+
if (robots.maxImagePreview !== void 0) {
|
|
223
|
+
directives.push(`max-image-preview:${robots.maxImagePreview}`);
|
|
224
|
+
}
|
|
225
|
+
if (robots.maxVideoPreview !== void 0) {
|
|
226
|
+
directives.push(`max-video-preview:${robots.maxVideoPreview}`);
|
|
227
|
+
}
|
|
228
|
+
if (robots.unavailableAfter) {
|
|
229
|
+
directives.push(`unavailable_after:${robots.unavailableAfter}`);
|
|
230
|
+
}
|
|
231
|
+
return directives.join(", ");
|
|
232
|
+
}
|
|
233
|
+
function generateRobotsTag(robots) {
|
|
234
|
+
if (!robots) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
const content = typeof robots === "string" ? robots : robotsMetaToString(robots);
|
|
238
|
+
if (!content) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
tag: "meta",
|
|
243
|
+
attrs: { name: "robots", content },
|
|
244
|
+
key: "meta:name:robots"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function generateGooglebotTag(googleBot) {
|
|
248
|
+
if (!googleBot) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
const content = typeof googleBot === "string" ? googleBot : robotsMetaToString(googleBot);
|
|
252
|
+
if (!content) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
tag: "meta",
|
|
257
|
+
attrs: { name: "googlebot", content },
|
|
258
|
+
key: "meta:name:googlebot"
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function generateVerificationTags(verification) {
|
|
262
|
+
if (!verification) {
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
const tags = [];
|
|
266
|
+
if (verification.google) {
|
|
267
|
+
tags.push({
|
|
268
|
+
tag: "meta",
|
|
269
|
+
attrs: { name: "google-site-verification", content: verification.google },
|
|
270
|
+
key: "meta:name:google-site-verification"
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (verification.bing) {
|
|
274
|
+
tags.push({
|
|
275
|
+
tag: "meta",
|
|
276
|
+
attrs: { name: "msvalidate.01", content: verification.bing },
|
|
277
|
+
key: "meta:name:msvalidate.01"
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
if (verification.yandex) {
|
|
281
|
+
tags.push({
|
|
282
|
+
tag: "meta",
|
|
283
|
+
attrs: { name: "yandex-verification", content: verification.yandex },
|
|
284
|
+
key: "meta:name:yandex-verification"
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
if (verification.pinterest) {
|
|
288
|
+
tags.push({
|
|
289
|
+
tag: "meta",
|
|
290
|
+
attrs: { name: "p:domain_verify", content: verification.pinterest },
|
|
291
|
+
key: "meta:name:p:domain_verify"
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (verification.norton) {
|
|
295
|
+
tags.push({
|
|
296
|
+
tag: "meta",
|
|
297
|
+
attrs: { name: "norton-safeweb-site-verification", content: verification.norton },
|
|
298
|
+
key: "meta:name:norton-safeweb-site-verification"
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
return tags;
|
|
302
|
+
}
|
|
303
|
+
function generateCanonicalTag(canonical) {
|
|
304
|
+
if (!canonical) {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
tag: "link",
|
|
309
|
+
attrs: { rel: "canonical", href: canonical },
|
|
310
|
+
key: "link:canonical"
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function generateBaseTag(base) {
|
|
314
|
+
if (!base) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
tag: "base",
|
|
319
|
+
attrs: { href: base },
|
|
320
|
+
key: "base"
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function generateAlternateTags(alternates) {
|
|
324
|
+
if (!alternates || alternates.length === 0) {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
return alternates.map((alt, index) => {
|
|
328
|
+
const attrs = {
|
|
329
|
+
rel: "alternate",
|
|
330
|
+
href: alt.href
|
|
331
|
+
};
|
|
332
|
+
if (alt.hreflang) {
|
|
333
|
+
attrs.hreflang = alt.hreflang;
|
|
334
|
+
}
|
|
335
|
+
if (alt.media) {
|
|
336
|
+
attrs.media = alt.media;
|
|
337
|
+
}
|
|
338
|
+
if (alt.type) {
|
|
339
|
+
attrs.type = alt.type;
|
|
340
|
+
}
|
|
341
|
+
if (alt.title) {
|
|
342
|
+
attrs.title = alt.title;
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
tag: "link",
|
|
346
|
+
attrs,
|
|
347
|
+
key: `link:alternate:${alt.hreflang || index}`
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
function generateManifestTag(manifest) {
|
|
352
|
+
if (!manifest) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
tag: "link",
|
|
357
|
+
attrs: { rel: "manifest", href: manifest },
|
|
358
|
+
key: "link:manifest"
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function generateMetaTags(metadata, options) {
|
|
362
|
+
const tags = [];
|
|
363
|
+
tags.push(generateCharsetTag(metadata.charset));
|
|
364
|
+
const base = generateBaseTag(metadata.base);
|
|
365
|
+
if (base) tags.push(base);
|
|
366
|
+
const viewport = generateViewportTag(metadata.viewport);
|
|
367
|
+
if (viewport) tags.push(viewport);
|
|
368
|
+
const title = generateTitleTag(
|
|
369
|
+
metadata.title,
|
|
370
|
+
metadata.titleTemplate || options?.titleTemplate,
|
|
371
|
+
metadata.defaultTitle || options?.defaultTitle
|
|
372
|
+
);
|
|
373
|
+
if (title) tags.push(title);
|
|
374
|
+
const description = generateDescriptionTag(metadata.description);
|
|
375
|
+
if (description) tags.push(description);
|
|
376
|
+
const keywords = generateKeywordsTag(metadata.keywords);
|
|
377
|
+
if (keywords) tags.push(keywords);
|
|
378
|
+
const author = generateAuthorTag(metadata.author);
|
|
379
|
+
if (author) tags.push(author);
|
|
380
|
+
const generator = generateGeneratorTag(metadata.generator);
|
|
381
|
+
if (generator) tags.push(generator);
|
|
382
|
+
const appName = generateApplicationNameTag(metadata.applicationName);
|
|
383
|
+
if (appName) tags.push(appName);
|
|
384
|
+
const referrer = generateReferrerTag(metadata.referrer);
|
|
385
|
+
if (referrer) tags.push(referrer);
|
|
386
|
+
tags.push(...generateThemeColorTags(metadata.themeColor));
|
|
387
|
+
const colorScheme = generateColorSchemeTag(metadata.colorScheme);
|
|
388
|
+
if (colorScheme) tags.push(colorScheme);
|
|
389
|
+
const robots = generateRobotsTag(metadata.robots);
|
|
390
|
+
if (robots) tags.push(robots);
|
|
391
|
+
const googleBot = generateGooglebotTag(metadata.googleBot);
|
|
392
|
+
if (googleBot) tags.push(googleBot);
|
|
393
|
+
tags.push(...generateVerificationTags(metadata.verification));
|
|
394
|
+
const canonical = generateCanonicalTag(metadata.canonical);
|
|
395
|
+
if (canonical) tags.push(canonical);
|
|
396
|
+
tags.push(...generateAlternateTags(metadata.alternates));
|
|
397
|
+
const manifest = generateManifestTag(metadata.manifest);
|
|
398
|
+
if (manifest) tags.push(manifest);
|
|
399
|
+
if (metadata.other) {
|
|
400
|
+
for (const [name, content] of Object.entries(metadata.other)) {
|
|
401
|
+
tags.push({
|
|
402
|
+
tag: "meta",
|
|
403
|
+
attrs: { name, content },
|
|
404
|
+
key: `meta:name:${name}`
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return tags;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/opengraph.ts
|
|
412
|
+
function ogTag(property, content) {
|
|
413
|
+
if (content === void 0 || content === null || content === "") {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
tag: "meta",
|
|
418
|
+
attrs: { property: `og:${property}`, content: String(content) },
|
|
419
|
+
key: `meta:property:og:${property}`
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function articleTag(property, content) {
|
|
423
|
+
if (!content) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
return {
|
|
427
|
+
tag: "meta",
|
|
428
|
+
attrs: { property: `article:${property}`, content },
|
|
429
|
+
key: `meta:property:article:${property}`
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
function profileTag(property, content) {
|
|
433
|
+
if (!content) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
tag: "meta",
|
|
438
|
+
attrs: { property: `profile:${property}`, content },
|
|
439
|
+
key: `meta:property:profile:${property}`
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function bookTag(property, content) {
|
|
443
|
+
if (!content) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
tag: "meta",
|
|
448
|
+
attrs: { property: `book:${property}`, content },
|
|
449
|
+
key: `meta:property:book:${property}`
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function productTag(property, content) {
|
|
453
|
+
if (content === void 0 || content === null || content === "") {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
tag: "meta",
|
|
458
|
+
attrs: { property: `product:${property}`, content: String(content) },
|
|
459
|
+
key: `meta:property:product:${property}`
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function generateImageTags(images) {
|
|
463
|
+
if (!images || images.length === 0) {
|
|
464
|
+
return [];
|
|
465
|
+
}
|
|
466
|
+
const tags = [];
|
|
467
|
+
images.forEach((image, index) => {
|
|
468
|
+
const suffix = index > 0 ? `:${index}` : "";
|
|
469
|
+
tags.push({
|
|
470
|
+
tag: "meta",
|
|
471
|
+
attrs: { property: "og:image", content: image.url },
|
|
472
|
+
key: `meta:property:og:image${suffix}`
|
|
473
|
+
});
|
|
474
|
+
if (image.secureUrl) {
|
|
475
|
+
tags.push({
|
|
476
|
+
tag: "meta",
|
|
477
|
+
attrs: { property: "og:image:secure_url", content: image.secureUrl },
|
|
478
|
+
key: `meta:property:og:image:secure_url${suffix}`
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
if (image.type) {
|
|
482
|
+
tags.push({
|
|
483
|
+
tag: "meta",
|
|
484
|
+
attrs: { property: "og:image:type", content: image.type },
|
|
485
|
+
key: `meta:property:og:image:type${suffix}`
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
if (image.width) {
|
|
489
|
+
tags.push({
|
|
490
|
+
tag: "meta",
|
|
491
|
+
attrs: { property: "og:image:width", content: String(image.width) },
|
|
492
|
+
key: `meta:property:og:image:width${suffix}`
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
if (image.height) {
|
|
496
|
+
tags.push({
|
|
497
|
+
tag: "meta",
|
|
498
|
+
attrs: { property: "og:image:height", content: String(image.height) },
|
|
499
|
+
key: `meta:property:og:image:height${suffix}`
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
if (image.alt) {
|
|
503
|
+
tags.push({
|
|
504
|
+
tag: "meta",
|
|
505
|
+
attrs: { property: "og:image:alt", content: image.alt },
|
|
506
|
+
key: `meta:property:og:image:alt${suffix}`
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
return tags;
|
|
511
|
+
}
|
|
512
|
+
function generateVideoTags(videos) {
|
|
513
|
+
if (!videos || videos.length === 0) {
|
|
514
|
+
return [];
|
|
515
|
+
}
|
|
516
|
+
const tags = [];
|
|
517
|
+
videos.forEach((video, index) => {
|
|
518
|
+
const suffix = index > 0 ? `:${index}` : "";
|
|
519
|
+
tags.push({
|
|
520
|
+
tag: "meta",
|
|
521
|
+
attrs: { property: "og:video", content: video.url },
|
|
522
|
+
key: `meta:property:og:video${suffix}`
|
|
523
|
+
});
|
|
524
|
+
if (video.secureUrl) {
|
|
525
|
+
tags.push({
|
|
526
|
+
tag: "meta",
|
|
527
|
+
attrs: { property: "og:video:secure_url", content: video.secureUrl },
|
|
528
|
+
key: `meta:property:og:video:secure_url${suffix}`
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
if (video.type) {
|
|
532
|
+
tags.push({
|
|
533
|
+
tag: "meta",
|
|
534
|
+
attrs: { property: "og:video:type", content: video.type },
|
|
535
|
+
key: `meta:property:og:video:type${suffix}`
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
if (video.width) {
|
|
539
|
+
tags.push({
|
|
540
|
+
tag: "meta",
|
|
541
|
+
attrs: { property: "og:video:width", content: String(video.width) },
|
|
542
|
+
key: `meta:property:og:video:width${suffix}`
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
if (video.height) {
|
|
546
|
+
tags.push({
|
|
547
|
+
tag: "meta",
|
|
548
|
+
attrs: { property: "og:video:height", content: String(video.height) },
|
|
549
|
+
key: `meta:property:og:video:height${suffix}`
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
return tags;
|
|
554
|
+
}
|
|
555
|
+
function generateAudioTags(audio) {
|
|
556
|
+
if (!audio || audio.length === 0) {
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
const tags = [];
|
|
560
|
+
audio.forEach((a, index) => {
|
|
561
|
+
const suffix = index > 0 ? `:${index}` : "";
|
|
562
|
+
tags.push({
|
|
563
|
+
tag: "meta",
|
|
564
|
+
attrs: { property: "og:audio", content: a.url },
|
|
565
|
+
key: `meta:property:og:audio${suffix}`
|
|
566
|
+
});
|
|
567
|
+
if (a.secureUrl) {
|
|
568
|
+
tags.push({
|
|
569
|
+
tag: "meta",
|
|
570
|
+
attrs: { property: "og:audio:secure_url", content: a.secureUrl },
|
|
571
|
+
key: `meta:property:og:audio:secure_url${suffix}`
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
if (a.type) {
|
|
575
|
+
tags.push({
|
|
576
|
+
tag: "meta",
|
|
577
|
+
attrs: { property: "og:audio:type", content: a.type },
|
|
578
|
+
key: `meta:property:og:audio:type${suffix}`
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
return tags;
|
|
583
|
+
}
|
|
584
|
+
function generateArticleTags(article) {
|
|
585
|
+
if (!article) {
|
|
586
|
+
return [];
|
|
587
|
+
}
|
|
588
|
+
const tags = [];
|
|
589
|
+
const publishedTime = articleTag("published_time", article.publishedTime);
|
|
590
|
+
if (publishedTime) tags.push(publishedTime);
|
|
591
|
+
const modifiedTime = articleTag("modified_time", article.modifiedTime);
|
|
592
|
+
if (modifiedTime) tags.push(modifiedTime);
|
|
593
|
+
const expirationTime = articleTag("expiration_time", article.expirationTime);
|
|
594
|
+
if (expirationTime) tags.push(expirationTime);
|
|
595
|
+
const section = articleTag("section", article.section);
|
|
596
|
+
if (section) tags.push(section);
|
|
597
|
+
if (article.authors) {
|
|
598
|
+
article.authors.forEach((author, index) => {
|
|
599
|
+
tags.push({
|
|
600
|
+
tag: "meta",
|
|
601
|
+
attrs: { property: "article:author", content: author },
|
|
602
|
+
key: `meta:property:article:author:${index}`
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
if (article.tags) {
|
|
607
|
+
article.tags.forEach((tag, index) => {
|
|
608
|
+
tags.push({
|
|
609
|
+
tag: "meta",
|
|
610
|
+
attrs: { property: "article:tag", content: tag },
|
|
611
|
+
key: `meta:property:article:tag:${index}`
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
return tags;
|
|
616
|
+
}
|
|
617
|
+
function generateProfileTags(profile) {
|
|
618
|
+
if (!profile) {
|
|
619
|
+
return [];
|
|
620
|
+
}
|
|
621
|
+
const tags = [];
|
|
622
|
+
const firstName = profileTag("first_name", profile.firstName);
|
|
623
|
+
if (firstName) tags.push(firstName);
|
|
624
|
+
const lastName = profileTag("last_name", profile.lastName);
|
|
625
|
+
if (lastName) tags.push(lastName);
|
|
626
|
+
const username = profileTag("username", profile.username);
|
|
627
|
+
if (username) tags.push(username);
|
|
628
|
+
const gender = profileTag("gender", profile.gender);
|
|
629
|
+
if (gender) tags.push(gender);
|
|
630
|
+
return tags;
|
|
631
|
+
}
|
|
632
|
+
function generateBookTags(book) {
|
|
633
|
+
if (!book) {
|
|
634
|
+
return [];
|
|
635
|
+
}
|
|
636
|
+
const tags = [];
|
|
637
|
+
if (book.authors) {
|
|
638
|
+
book.authors.forEach((author, index) => {
|
|
639
|
+
tags.push({
|
|
640
|
+
tag: "meta",
|
|
641
|
+
attrs: { property: "book:author", content: author },
|
|
642
|
+
key: `meta:property:book:author:${index}`
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
const isbn = bookTag("isbn", book.isbn);
|
|
647
|
+
if (isbn) tags.push(isbn);
|
|
648
|
+
const releaseDate = bookTag("release_date", book.releaseDate);
|
|
649
|
+
if (releaseDate) tags.push(releaseDate);
|
|
650
|
+
if (book.tags) {
|
|
651
|
+
book.tags.forEach((tag, index) => {
|
|
652
|
+
tags.push({
|
|
653
|
+
tag: "meta",
|
|
654
|
+
attrs: { property: "book:tag", content: tag },
|
|
655
|
+
key: `meta:property:book:tag:${index}`
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
return tags;
|
|
660
|
+
}
|
|
661
|
+
function generateProductTags(product) {
|
|
662
|
+
if (!product) {
|
|
663
|
+
return [];
|
|
664
|
+
}
|
|
665
|
+
const tags = [];
|
|
666
|
+
const price = productTag("price:amount", product.priceAmount);
|
|
667
|
+
if (price) tags.push(price);
|
|
668
|
+
const currency = productTag("price:currency", product.priceCurrency);
|
|
669
|
+
if (currency) tags.push(currency);
|
|
670
|
+
const availability = productTag("availability", product.availability);
|
|
671
|
+
if (availability) tags.push(availability);
|
|
672
|
+
const condition = productTag("condition", product.condition);
|
|
673
|
+
if (condition) tags.push(condition);
|
|
674
|
+
const retailerId = productTag("retailer_item_id", product.retailerItemId);
|
|
675
|
+
if (retailerId) tags.push(retailerId);
|
|
676
|
+
return tags;
|
|
677
|
+
}
|
|
678
|
+
function generateOpenGraphTags(og) {
|
|
679
|
+
if (!og) {
|
|
680
|
+
return [];
|
|
681
|
+
}
|
|
682
|
+
const tags = [];
|
|
683
|
+
const type = ogTag("type", og.type || "website");
|
|
684
|
+
if (type) tags.push(type);
|
|
685
|
+
const title = ogTag("title", og.title);
|
|
686
|
+
if (title) tags.push(title);
|
|
687
|
+
const description = ogTag("description", og.description);
|
|
688
|
+
if (description) tags.push(description);
|
|
689
|
+
const url = ogTag("url", og.url);
|
|
690
|
+
if (url) tags.push(url);
|
|
691
|
+
const siteName = ogTag("site_name", og.siteName);
|
|
692
|
+
if (siteName) tags.push(siteName);
|
|
693
|
+
const locale = ogTag("locale", og.locale);
|
|
694
|
+
if (locale) tags.push(locale);
|
|
695
|
+
if (og.alternateLocales) {
|
|
696
|
+
og.alternateLocales.forEach((alternateLocale, index) => {
|
|
697
|
+
tags.push({
|
|
698
|
+
tag: "meta",
|
|
699
|
+
attrs: { property: "og:locale:alternate", content: alternateLocale },
|
|
700
|
+
key: `meta:property:og:locale:alternate:${index}`
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
const determiner = ogTag("determiner", og.determiner);
|
|
705
|
+
if (determiner) tags.push(determiner);
|
|
706
|
+
tags.push(...generateImageTags(og.images));
|
|
707
|
+
tags.push(...generateVideoTags(og.videos));
|
|
708
|
+
tags.push(...generateAudioTags(og.audio));
|
|
709
|
+
tags.push(...generateArticleTags(og.article));
|
|
710
|
+
tags.push(...generateProfileTags(og.profile));
|
|
711
|
+
tags.push(...generateBookTags(og.book));
|
|
712
|
+
tags.push(...generateProductTags(og.product));
|
|
713
|
+
return tags;
|
|
714
|
+
}
|
|
715
|
+
function generateOpenGraphFallback(title, description, url) {
|
|
716
|
+
const tags = [];
|
|
717
|
+
const typeTag = ogTag("type", "website");
|
|
718
|
+
if (typeTag) tags.push(typeTag);
|
|
719
|
+
const titleTag = ogTag("title", title);
|
|
720
|
+
if (titleTag) tags.push(titleTag);
|
|
721
|
+
const descTag = ogTag("description", description);
|
|
722
|
+
if (descTag) tags.push(descTag);
|
|
723
|
+
const urlTag = ogTag("url", url);
|
|
724
|
+
if (urlTag) tags.push(urlTag);
|
|
725
|
+
return tags;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// src/twitter.ts
|
|
729
|
+
function twitterTag(name, content) {
|
|
730
|
+
if (!content) {
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
return {
|
|
734
|
+
tag: "meta",
|
|
735
|
+
attrs: { name: `twitter:${name}`, content },
|
|
736
|
+
key: `meta:name:twitter:${name}`
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
function normalizeImage(image) {
|
|
740
|
+
if (!image) {
|
|
741
|
+
return void 0;
|
|
742
|
+
}
|
|
743
|
+
if (typeof image === "string") {
|
|
744
|
+
return { url: image };
|
|
745
|
+
}
|
|
746
|
+
return image;
|
|
747
|
+
}
|
|
748
|
+
function generateTwitterTags(twitter) {
|
|
749
|
+
if (!twitter) {
|
|
750
|
+
return [];
|
|
751
|
+
}
|
|
752
|
+
const tags = [];
|
|
753
|
+
const card = twitterTag("card", twitter.card || "summary");
|
|
754
|
+
if (card) tags.push(card);
|
|
755
|
+
const title = twitterTag("title", twitter.title);
|
|
756
|
+
if (title) tags.push(title);
|
|
757
|
+
const description = twitterTag("description", twitter.description);
|
|
758
|
+
if (description) tags.push(description);
|
|
759
|
+
const site = twitterTag("site", twitter.site);
|
|
760
|
+
if (site) tags.push(site);
|
|
761
|
+
const siteId = twitterTag("site:id", twitter.siteId);
|
|
762
|
+
if (siteId) tags.push(siteId);
|
|
763
|
+
const creator = twitterTag("creator", twitter.creator);
|
|
764
|
+
if (creator) tags.push(creator);
|
|
765
|
+
const creatorId = twitterTag("creator:id", twitter.creatorId);
|
|
766
|
+
if (creatorId) tags.push(creatorId);
|
|
767
|
+
const image = normalizeImage(twitter.image);
|
|
768
|
+
if (image) {
|
|
769
|
+
const imageTag = twitterTag("image", image.url);
|
|
770
|
+
if (imageTag) tags.push(imageTag);
|
|
771
|
+
if (image.alt) {
|
|
772
|
+
const imageAlt = twitterTag("image:alt", image.alt);
|
|
773
|
+
if (imageAlt) tags.push(imageAlt);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (twitter.images && twitter.images.length > 0) {
|
|
777
|
+
twitter.images.forEach((img, index) => {
|
|
778
|
+
if (index >= 4) return;
|
|
779
|
+
const normalizedImg = typeof img === "string" ? { url: img } : img;
|
|
780
|
+
tags.push({
|
|
781
|
+
tag: "meta",
|
|
782
|
+
attrs: { name: `twitter:image${index}`, content: normalizedImg.url },
|
|
783
|
+
key: `meta:name:twitter:image${index}`
|
|
784
|
+
});
|
|
785
|
+
if (normalizedImg.alt) {
|
|
786
|
+
tags.push({
|
|
787
|
+
tag: "meta",
|
|
788
|
+
attrs: { name: `twitter:image${index}:alt`, content: normalizedImg.alt },
|
|
789
|
+
key: `meta:name:twitter:image${index}:alt`
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
if (twitter.player) {
|
|
795
|
+
const playerUrl = twitterTag("player", twitter.player.url);
|
|
796
|
+
if (playerUrl) tags.push(playerUrl);
|
|
797
|
+
const playerWidth = twitterTag("player:width", String(twitter.player.width));
|
|
798
|
+
if (playerWidth) tags.push(playerWidth);
|
|
799
|
+
const playerHeight = twitterTag("player:height", String(twitter.player.height));
|
|
800
|
+
if (playerHeight) tags.push(playerHeight);
|
|
801
|
+
if (twitter.player.stream) {
|
|
802
|
+
const stream = twitterTag("player:stream", twitter.player.stream);
|
|
803
|
+
if (stream) tags.push(stream);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (twitter.app) {
|
|
807
|
+
const appName = twitterTag("app:name:iphone", twitter.app.name);
|
|
808
|
+
if (appName) tags.push(appName);
|
|
809
|
+
tags.push({
|
|
810
|
+
tag: "meta",
|
|
811
|
+
attrs: { name: "twitter:app:name:ipad", content: twitter.app.name },
|
|
812
|
+
key: "meta:name:twitter:app:name:ipad"
|
|
813
|
+
});
|
|
814
|
+
tags.push({
|
|
815
|
+
tag: "meta",
|
|
816
|
+
attrs: { name: "twitter:app:name:googleplay", content: twitter.app.name },
|
|
817
|
+
key: "meta:name:twitter:app:name:googleplay"
|
|
818
|
+
});
|
|
819
|
+
if (twitter.app.id.iphone) {
|
|
820
|
+
const iphoneId = twitterTag("app:id:iphone", twitter.app.id.iphone);
|
|
821
|
+
if (iphoneId) tags.push(iphoneId);
|
|
822
|
+
}
|
|
823
|
+
if (twitter.app.id.ipad) {
|
|
824
|
+
const ipadId = twitterTag("app:id:ipad", twitter.app.id.ipad);
|
|
825
|
+
if (ipadId) tags.push(ipadId);
|
|
826
|
+
}
|
|
827
|
+
if (twitter.app.id.googleplay) {
|
|
828
|
+
const googleId = twitterTag("app:id:googleplay", twitter.app.id.googleplay);
|
|
829
|
+
if (googleId) tags.push(googleId);
|
|
830
|
+
}
|
|
831
|
+
if (twitter.app.url) {
|
|
832
|
+
if (twitter.app.url.iphone) {
|
|
833
|
+
const iphoneUrl = twitterTag("app:url:iphone", twitter.app.url.iphone);
|
|
834
|
+
if (iphoneUrl) tags.push(iphoneUrl);
|
|
835
|
+
}
|
|
836
|
+
if (twitter.app.url.ipad) {
|
|
837
|
+
const ipadUrl = twitterTag("app:url:ipad", twitter.app.url.ipad);
|
|
838
|
+
if (ipadUrl) tags.push(ipadUrl);
|
|
839
|
+
}
|
|
840
|
+
if (twitter.app.url.googleplay) {
|
|
841
|
+
const googleUrl = twitterTag("app:url:googleplay", twitter.app.url.googleplay);
|
|
842
|
+
if (googleUrl) tags.push(googleUrl);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (twitter.label1) {
|
|
847
|
+
const label1 = twitterTag("label1", twitter.label1);
|
|
848
|
+
if (label1) tags.push(label1);
|
|
849
|
+
}
|
|
850
|
+
if (twitter.data1) {
|
|
851
|
+
const data1 = twitterTag("data1", twitter.data1);
|
|
852
|
+
if (data1) tags.push(data1);
|
|
853
|
+
}
|
|
854
|
+
if (twitter.label2) {
|
|
855
|
+
const label2 = twitterTag("label2", twitter.label2);
|
|
856
|
+
if (label2) tags.push(label2);
|
|
857
|
+
}
|
|
858
|
+
if (twitter.data2) {
|
|
859
|
+
const data2 = twitterTag("data2", twitter.data2);
|
|
860
|
+
if (data2) tags.push(data2);
|
|
861
|
+
}
|
|
862
|
+
return tags;
|
|
863
|
+
}
|
|
864
|
+
function generateTwitterFallback(title, description, image, cardType = "summary") {
|
|
865
|
+
const tags = [];
|
|
866
|
+
const card = twitterTag("card", cardType);
|
|
867
|
+
if (card) tags.push(card);
|
|
868
|
+
const titleTag = twitterTag("title", title);
|
|
869
|
+
if (titleTag) tags.push(titleTag);
|
|
870
|
+
const descTag = twitterTag("description", description);
|
|
871
|
+
if (descTag) tags.push(descTag);
|
|
872
|
+
const imageTag = twitterTag("image", image);
|
|
873
|
+
if (imageTag) tags.push(imageTag);
|
|
874
|
+
return tags;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// src/index.ts
|
|
878
|
+
function createSEO(config = {}) {
|
|
879
|
+
const fullConfig = {
|
|
880
|
+
baseUrl: config.baseUrl || "",
|
|
881
|
+
titleTemplate: config.titleTemplate,
|
|
882
|
+
defaultTitle: config.defaultTitle,
|
|
883
|
+
defaultOpenGraph: config.defaultOpenGraph,
|
|
884
|
+
defaultTwitter: config.defaultTwitter,
|
|
885
|
+
features: {
|
|
886
|
+
openGraph: config.features?.openGraph ?? true,
|
|
887
|
+
twitter: config.features?.twitter ?? true,
|
|
888
|
+
robots: config.features?.robots ?? true
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
return {
|
|
892
|
+
config: fullConfig,
|
|
893
|
+
render(metadata) {
|
|
894
|
+
const tags = generateAllTags(metadata, fullConfig);
|
|
895
|
+
const html = renderTags(tags);
|
|
896
|
+
return { tags, html };
|
|
897
|
+
},
|
|
898
|
+
merge(parent, child) {
|
|
899
|
+
return mergeMetadata(parent, child);
|
|
900
|
+
},
|
|
901
|
+
resolveTitle(metadata) {
|
|
902
|
+
return resolveTitleWithTemplate(
|
|
903
|
+
metadata.title,
|
|
904
|
+
metadata.titleTemplate || fullConfig.titleTemplate,
|
|
905
|
+
metadata.defaultTitle || fullConfig.defaultTitle
|
|
906
|
+
);
|
|
907
|
+
},
|
|
908
|
+
canonical(path) {
|
|
909
|
+
if (!fullConfig.baseUrl) {
|
|
910
|
+
return path;
|
|
911
|
+
}
|
|
912
|
+
const base = fullConfig.baseUrl.endsWith("/") ? fullConfig.baseUrl.slice(0, -1) : fullConfig.baseUrl;
|
|
913
|
+
const normalizedPath = path.startsWith("/") ? path : "/" + path;
|
|
914
|
+
return base + normalizedPath;
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
function mergeMetadata(parent, child) {
|
|
919
|
+
return {
|
|
920
|
+
...parent,
|
|
921
|
+
...child,
|
|
922
|
+
// Merge Open Graph
|
|
923
|
+
openGraph: child.openGraph ? { ...parent.openGraph, ...child.openGraph } : parent.openGraph,
|
|
924
|
+
// Merge Twitter
|
|
925
|
+
twitter: child.twitter ? { ...parent.twitter, ...child.twitter } : parent.twitter,
|
|
926
|
+
// Merge Apple
|
|
927
|
+
apple: child.apple ? { ...parent.apple, ...child.apple } : parent.apple,
|
|
928
|
+
// Merge Icons
|
|
929
|
+
icons: child.icons ? { ...parent.icons, ...child.icons } : parent.icons,
|
|
930
|
+
// Merge verification
|
|
931
|
+
verification: child.verification ? { ...parent.verification, ...child.verification } : parent.verification,
|
|
932
|
+
// Merge other
|
|
933
|
+
other: child.other ? { ...parent.other, ...child.other } : parent.other
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
function resolveTitleWithTemplate(title, template, defaultTitle) {
|
|
937
|
+
const resolvedTitle = title || defaultTitle || "";
|
|
938
|
+
if (!resolvedTitle) {
|
|
939
|
+
return "";
|
|
940
|
+
}
|
|
941
|
+
if (template && title) {
|
|
942
|
+
return template.replace("%s", title);
|
|
943
|
+
}
|
|
944
|
+
return resolvedTitle;
|
|
945
|
+
}
|
|
946
|
+
function generateAllTags(metadata, config) {
|
|
947
|
+
const tags = [];
|
|
948
|
+
tags.push(...generateMetaTags(metadata, {
|
|
949
|
+
titleTemplate: config.titleTemplate,
|
|
950
|
+
defaultTitle: config.defaultTitle
|
|
951
|
+
}));
|
|
952
|
+
if (config.features?.openGraph !== false) {
|
|
953
|
+
if (metadata.openGraph) {
|
|
954
|
+
const og = { ...config.defaultOpenGraph, ...metadata.openGraph };
|
|
955
|
+
tags.push(...generateOpenGraphTags(og));
|
|
956
|
+
} else if (metadata.title || metadata.description) {
|
|
957
|
+
tags.push(...generateOpenGraphFallback(
|
|
958
|
+
metadata.title,
|
|
959
|
+
metadata.description,
|
|
960
|
+
metadata.canonical
|
|
961
|
+
));
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
if (config.features?.twitter !== false) {
|
|
965
|
+
if (metadata.twitter) {
|
|
966
|
+
const twitter = { ...config.defaultTwitter, ...metadata.twitter };
|
|
967
|
+
tags.push(...generateTwitterTags(twitter));
|
|
968
|
+
} else if (metadata.title || metadata.description) {
|
|
969
|
+
const ogImage = metadata.openGraph?.images?.[0];
|
|
970
|
+
const imageUrl = typeof ogImage === "string" ? ogImage : ogImage?.url;
|
|
971
|
+
tags.push(...generateTwitterFallback(
|
|
972
|
+
metadata.title,
|
|
973
|
+
metadata.description,
|
|
974
|
+
imageUrl
|
|
975
|
+
));
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
if (metadata.icons) {
|
|
979
|
+
tags.push(...generateIconTags(metadata.icons));
|
|
980
|
+
}
|
|
981
|
+
if (metadata.apple) {
|
|
982
|
+
tags.push(...generateAppleTags(metadata.apple));
|
|
983
|
+
}
|
|
984
|
+
return tags;
|
|
985
|
+
}
|
|
986
|
+
function generateIconTags(icons) {
|
|
987
|
+
if (!icons) return [];
|
|
988
|
+
const tags = [];
|
|
989
|
+
if (icons.icon) {
|
|
990
|
+
const iconList = Array.isArray(icons.icon) ? icons.icon : [icons.icon];
|
|
991
|
+
iconList.forEach((icon, index) => {
|
|
992
|
+
const iconData = typeof icon === "string" ? { url: icon } : icon;
|
|
993
|
+
tags.push({
|
|
994
|
+
tag: "link",
|
|
995
|
+
attrs: {
|
|
996
|
+
rel: "icon",
|
|
997
|
+
href: iconData.url,
|
|
998
|
+
type: iconData.type,
|
|
999
|
+
sizes: iconData.sizes
|
|
1000
|
+
},
|
|
1001
|
+
key: `link:icon:${index}`
|
|
1002
|
+
});
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
if (icons.shortcut) {
|
|
1006
|
+
const shortcut = typeof icons.shortcut === "string" ? { url: icons.shortcut } : icons.shortcut;
|
|
1007
|
+
tags.push({
|
|
1008
|
+
tag: "link",
|
|
1009
|
+
attrs: {
|
|
1010
|
+
rel: "shortcut icon",
|
|
1011
|
+
href: shortcut.url,
|
|
1012
|
+
type: shortcut.type
|
|
1013
|
+
},
|
|
1014
|
+
key: "link:shortcut-icon"
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
if (icons.apple) {
|
|
1018
|
+
const appleList = Array.isArray(icons.apple) ? icons.apple : [icons.apple];
|
|
1019
|
+
appleList.forEach((icon, index) => {
|
|
1020
|
+
const iconData = typeof icon === "string" ? { url: icon } : icon;
|
|
1021
|
+
tags.push({
|
|
1022
|
+
tag: "link",
|
|
1023
|
+
attrs: {
|
|
1024
|
+
rel: "apple-touch-icon",
|
|
1025
|
+
href: iconData.url,
|
|
1026
|
+
sizes: iconData.sizes
|
|
1027
|
+
},
|
|
1028
|
+
key: `link:apple-touch-icon:${index}`
|
|
1029
|
+
});
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
return tags;
|
|
1033
|
+
}
|
|
1034
|
+
function generateAppleTags(apple) {
|
|
1035
|
+
if (!apple) return [];
|
|
1036
|
+
const tags = [];
|
|
1037
|
+
if (apple.mobileWebAppCapable) {
|
|
1038
|
+
tags.push({
|
|
1039
|
+
tag: "meta",
|
|
1040
|
+
attrs: { name: "apple-mobile-web-app-capable", content: "yes" },
|
|
1041
|
+
key: "meta:name:apple-mobile-web-app-capable"
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
if (apple.statusBarStyle) {
|
|
1045
|
+
tags.push({
|
|
1046
|
+
tag: "meta",
|
|
1047
|
+
attrs: { name: "apple-mobile-web-app-status-bar-style", content: apple.statusBarStyle },
|
|
1048
|
+
key: "meta:name:apple-mobile-web-app-status-bar-style"
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
if (apple.title) {
|
|
1052
|
+
tags.push({
|
|
1053
|
+
tag: "meta",
|
|
1054
|
+
attrs: { name: "apple-mobile-web-app-title", content: apple.title },
|
|
1055
|
+
key: "meta:name:apple-mobile-web-app-title"
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
if (apple.touchIcon) {
|
|
1059
|
+
const touchIcon = typeof apple.touchIcon === "string" ? { url: apple.touchIcon } : apple.touchIcon;
|
|
1060
|
+
tags.push({
|
|
1061
|
+
tag: "link",
|
|
1062
|
+
attrs: {
|
|
1063
|
+
rel: "apple-touch-icon",
|
|
1064
|
+
href: touchIcon.url,
|
|
1065
|
+
sizes: touchIcon.sizes
|
|
1066
|
+
},
|
|
1067
|
+
key: "link:apple-touch-icon-from-apple"
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
return tags;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// src/json-ld/index.ts
|
|
1074
|
+
function createJsonLd(schema, options) {
|
|
1075
|
+
const data = {
|
|
1076
|
+
"@context": "https://schema.org",
|
|
1077
|
+
...schema
|
|
1078
|
+
};
|
|
1079
|
+
return JSON.stringify(data);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// src/svelte/index.ts
|
|
1083
|
+
function generateMetadataHtml(metadata) {
|
|
1084
|
+
const seo = createSEO();
|
|
1085
|
+
const { html } = seo.render(metadata);
|
|
1086
|
+
return html;
|
|
1087
|
+
}
|
|
1088
|
+
function generateTitleHtml(title, template) {
|
|
1089
|
+
const resolvedTitle = template ? template.replace("%s", title) : title;
|
|
1090
|
+
return `<title>${escapeHtml2(resolvedTitle)}</title>`;
|
|
1091
|
+
}
|
|
1092
|
+
function generateDescriptionHtml(description) {
|
|
1093
|
+
return `<meta name="description" content="${escapeAttr2(description)}" />`;
|
|
1094
|
+
}
|
|
1095
|
+
function generateCanonicalHtml(url) {
|
|
1096
|
+
return `<link rel="canonical" href="${escapeAttr2(url)}" />`;
|
|
1097
|
+
}
|
|
1098
|
+
function generateOpenGraphHtml(og) {
|
|
1099
|
+
const tags = generateOpenGraphTags(og);
|
|
1100
|
+
return renderTags(tags);
|
|
1101
|
+
}
|
|
1102
|
+
function generateTwitterHtml(twitter) {
|
|
1103
|
+
const tags = generateTwitterTags(twitter);
|
|
1104
|
+
return renderTags(tags);
|
|
1105
|
+
}
|
|
1106
|
+
function createJsonLdScript(schema, id) {
|
|
1107
|
+
const jsonLd = createJsonLd(schema);
|
|
1108
|
+
const idAttr = id ? ` id="json-ld-${id}"` : "";
|
|
1109
|
+
return `<script type="application/ld+json"${idAttr}>${jsonLd}</script>`;
|
|
1110
|
+
}
|
|
1111
|
+
function createMetadataStore(initialMetadata = {}) {
|
|
1112
|
+
let metadata = { ...initialMetadata };
|
|
1113
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
1114
|
+
return {
|
|
1115
|
+
subscribe(callback) {
|
|
1116
|
+
subscribers.add(callback);
|
|
1117
|
+
callback(metadata);
|
|
1118
|
+
return () => subscribers.delete(callback);
|
|
1119
|
+
},
|
|
1120
|
+
set(newMetadata) {
|
|
1121
|
+
metadata = { ...newMetadata };
|
|
1122
|
+
subscribers.forEach((cb) => cb(metadata));
|
|
1123
|
+
},
|
|
1124
|
+
update(updater) {
|
|
1125
|
+
metadata = updater(metadata);
|
|
1126
|
+
subscribers.forEach((cb) => cb(metadata));
|
|
1127
|
+
},
|
|
1128
|
+
get() {
|
|
1129
|
+
return metadata;
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
function createJsonLdStore(initialSchema) {
|
|
1134
|
+
let schema = { ...initialSchema };
|
|
1135
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
1136
|
+
return {
|
|
1137
|
+
subscribe(callback) {
|
|
1138
|
+
subscribers.add(callback);
|
|
1139
|
+
callback(schema);
|
|
1140
|
+
return () => subscribers.delete(callback);
|
|
1141
|
+
},
|
|
1142
|
+
set(newSchema) {
|
|
1143
|
+
schema = { ...newSchema };
|
|
1144
|
+
subscribers.forEach((cb) => cb(schema));
|
|
1145
|
+
},
|
|
1146
|
+
update(updater) {
|
|
1147
|
+
schema = updater(schema);
|
|
1148
|
+
subscribers.forEach((cb) => cb(schema));
|
|
1149
|
+
},
|
|
1150
|
+
get() {
|
|
1151
|
+
return schema;
|
|
1152
|
+
},
|
|
1153
|
+
toScript(id) {
|
|
1154
|
+
return createJsonLdScript(schema, id);
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function updateDocumentHead(metadata) {
|
|
1159
|
+
if (typeof document === "undefined") return;
|
|
1160
|
+
if (metadata.title) {
|
|
1161
|
+
const title = metadata.titleTemplate ? metadata.titleTemplate.replace("%s", metadata.title) : metadata.title;
|
|
1162
|
+
document.title = title;
|
|
1163
|
+
}
|
|
1164
|
+
if (metadata.description) {
|
|
1165
|
+
updateMetaTag("description", metadata.description);
|
|
1166
|
+
}
|
|
1167
|
+
if (metadata.canonical) {
|
|
1168
|
+
updateLinkTag("canonical", metadata.canonical);
|
|
1169
|
+
}
|
|
1170
|
+
if (metadata.openGraph) {
|
|
1171
|
+
updateOpenGraphTags(metadata.openGraph);
|
|
1172
|
+
}
|
|
1173
|
+
if (metadata.twitter) {
|
|
1174
|
+
updateTwitterTags(metadata.twitter);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
function updateJsonLd(schema, id = "default") {
|
|
1178
|
+
if (typeof document === "undefined") return;
|
|
1179
|
+
const scriptId = `json-ld-${id}`;
|
|
1180
|
+
let script = document.getElementById(scriptId);
|
|
1181
|
+
if (!script) {
|
|
1182
|
+
script = document.createElement("script");
|
|
1183
|
+
script.type = "application/ld+json";
|
|
1184
|
+
script.id = scriptId;
|
|
1185
|
+
document.head.appendChild(script);
|
|
1186
|
+
}
|
|
1187
|
+
script.textContent = createJsonLd(schema);
|
|
1188
|
+
}
|
|
1189
|
+
function escapeHtml2(str) {
|
|
1190
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1191
|
+
}
|
|
1192
|
+
function escapeAttr2(str) {
|
|
1193
|
+
return str.replace(/"/g, """);
|
|
1194
|
+
}
|
|
1195
|
+
function updateMetaTag(name, content) {
|
|
1196
|
+
let meta = document.querySelector(`meta[name="${name}"]`);
|
|
1197
|
+
if (!meta) {
|
|
1198
|
+
meta = document.createElement("meta");
|
|
1199
|
+
meta.setAttribute("name", name);
|
|
1200
|
+
document.head.appendChild(meta);
|
|
1201
|
+
}
|
|
1202
|
+
meta.setAttribute("content", content);
|
|
1203
|
+
}
|
|
1204
|
+
function updateLinkTag(rel, href) {
|
|
1205
|
+
let link = document.querySelector(`link[rel="${rel}"]`);
|
|
1206
|
+
if (!link) {
|
|
1207
|
+
link = document.createElement("link");
|
|
1208
|
+
link.setAttribute("rel", rel);
|
|
1209
|
+
document.head.appendChild(link);
|
|
1210
|
+
}
|
|
1211
|
+
link.setAttribute("href", href);
|
|
1212
|
+
}
|
|
1213
|
+
function updateOpenGraphTags(og) {
|
|
1214
|
+
const properties = {
|
|
1215
|
+
"og:title": og.title,
|
|
1216
|
+
"og:description": og.description,
|
|
1217
|
+
"og:type": og.type,
|
|
1218
|
+
"og:url": og.url
|
|
1219
|
+
};
|
|
1220
|
+
for (const [property, content] of Object.entries(properties)) {
|
|
1221
|
+
if (content) {
|
|
1222
|
+
let meta = document.querySelector(`meta[property="${property}"]`);
|
|
1223
|
+
if (!meta) {
|
|
1224
|
+
meta = document.createElement("meta");
|
|
1225
|
+
meta.setAttribute("property", property);
|
|
1226
|
+
document.head.appendChild(meta);
|
|
1227
|
+
}
|
|
1228
|
+
meta.setAttribute("content", content);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
function updateTwitterTags(twitter) {
|
|
1233
|
+
const names = {
|
|
1234
|
+
"twitter:card": twitter.card,
|
|
1235
|
+
"twitter:title": twitter.title,
|
|
1236
|
+
"twitter:description": twitter.description
|
|
1237
|
+
};
|
|
1238
|
+
for (const [name, content] of Object.entries(names)) {
|
|
1239
|
+
if (content) {
|
|
1240
|
+
let meta = document.querySelector(`meta[name="${name}"]`);
|
|
1241
|
+
if (!meta) {
|
|
1242
|
+
meta = document.createElement("meta");
|
|
1243
|
+
meta.setAttribute("name", name);
|
|
1244
|
+
document.head.appendChild(meta);
|
|
1245
|
+
}
|
|
1246
|
+
meta.setAttribute("content", content);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
export { createJsonLdScript, createJsonLdStore, createMetadataStore, generateCanonicalHtml, generateDescriptionHtml, generateMetadataHtml, generateOpenGraphHtml, generateTitleHtml, generateTwitterHtml, updateDocumentHead, updateJsonLd };
|
|
1252
|
+
//# sourceMappingURL=index.js.map
|
|
1253
|
+
//# sourceMappingURL=index.js.map
|