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