@withl5e/l5e 0.1.0-alpha.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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -0
  3. package/dist/action.js +10 -0
  4. package/dist/action.js.map +1 -0
  5. package/dist/client-D67hK4Yy.js +9 -0
  6. package/dist/client-D67hK4Yy.js.map +1 -0
  7. package/dist/entry-server-Ckh6zfgm.js +258 -0
  8. package/dist/entry-server-Ckh6zfgm.js.map +1 -0
  9. package/dist/entry-server.js +12 -0
  10. package/dist/entry-server.js.map +1 -0
  11. package/dist/generateMetadata-C5QsMS-H.js +144 -0
  12. package/dist/generateMetadata-C5QsMS-H.js.map +1 -0
  13. package/dist/index-BIt7MJT9.js +163 -0
  14. package/dist/index-BIt7MJT9.js.map +1 -0
  15. package/dist/index.js +49 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/island/client.js +5 -0
  18. package/dist/island/client.js.map +1 -0
  19. package/dist/island/runtime.js +98 -0
  20. package/dist/island/runtime.js.map +1 -0
  21. package/dist/island.js +39 -0
  22. package/dist/island.js.map +1 -0
  23. package/dist/jsx-runtime-C2Vw67N2.js +256 -0
  24. package/dist/jsx-runtime-C2Vw67N2.js.map +1 -0
  25. package/dist/jsx-runtime.js +26 -0
  26. package/dist/jsx-runtime.js.map +1 -0
  27. package/dist/middleware.js +9 -0
  28. package/dist/middleware.js.map +1 -0
  29. package/dist/seo.js +7 -0
  30. package/dist/seo.js.map +1 -0
  31. package/dist/server.js +489 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/swap/server.js +15 -0
  34. package/dist/swap/server.js.map +1 -0
  35. package/dist/swap.js +121 -0
  36. package/dist/swap.js.map +1 -0
  37. package/dist/tooltip.js +129 -0
  38. package/dist/tooltip.js.map +1 -0
  39. package/dist/vite-plugin.js +381 -0
  40. package/dist/vite-plugin.js.map +1 -0
  41. package/index.ts +1 -0
  42. package/package.json +129 -0
  43. package/src/action/define-action.ts +8 -0
  44. package/src/action/index.ts +2 -0
  45. package/src/action/types.ts +21 -0
  46. package/src/core/bundler.ts +275 -0
  47. package/src/core/const.ts +2 -0
  48. package/src/core/entry-server.d.ts +1 -0
  49. package/src/core/entry-server.ts +381 -0
  50. package/src/core/exceptions.ts +80 -0
  51. package/src/core/head-priority.ts +15 -0
  52. package/src/core/index.ts +40 -0
  53. package/src/core/jsx-runtime.ts +325 -0
  54. package/src/core/jsx-types.d.ts +548 -0
  55. package/src/core/render.ts +181 -0
  56. package/src/core/request.ts +31 -0
  57. package/src/core/server.ts +740 -0
  58. package/src/core/vite-plugin.ts +779 -0
  59. package/src/island/ClientIsland.ts +71 -0
  60. package/src/island/client.ts +3 -0
  61. package/src/island/index.ts +3 -0
  62. package/src/island/runtime.ts +149 -0
  63. package/src/island/strategy-registry.ts +10 -0
  64. package/src/island/types.ts +28 -0
  65. package/src/middleware/defineMiddleware.ts +5 -0
  66. package/src/middleware/index.ts +133 -0
  67. package/src/middleware/sequence.ts +105 -0
  68. package/src/middleware/types.ts +28 -0
  69. package/src/seo/generateMetadata.tsx +559 -0
  70. package/src/seo/index.ts +10 -0
  71. package/src/seo/mergeMetadata.ts +200 -0
  72. package/src/seo/types.ts +316 -0
  73. package/src/swap/SwapResponse.tsx +16 -0
  74. package/src/swap/create-swap.ts +121 -0
  75. package/src/swap/index.ts +8 -0
  76. package/src/swap/parse.ts +12 -0
  77. package/src/swap/server.ts +1 -0
  78. package/src/swap/swap.ts +57 -0
  79. package/src/swap/types.ts +47 -0
  80. package/src/swap/utils.ts +7 -0
  81. package/src/tooltip/index.ts +2 -0
  82. package/src/tooltip/tooltip-loader.ts +108 -0
  83. package/src/tooltip/tooltip-runtime.ts +173 -0
  84. package/types.d.ts +14 -0
@@ -0,0 +1,559 @@
1
+ /// <reference types="../core/jsx-types" />
2
+ import { HEAD_PRIORITY } from '../core/head-priority';
3
+ import { Fragment, Head, resolveMetadata, useRequest } from '../core/jsx-runtime';
4
+
5
+ import { mergeMetadata } from './mergeMetadata';
6
+ import type { Metadata } from './types';
7
+
8
+ /**
9
+ * Converts Metadata object to SEO meta tags
10
+ * This is the core function that transforms the metadata object into actual HTML tags
11
+ *
12
+ * If metadata is provided, it will be merged with metadata from the stack (from global/page loaders)
13
+ * If no metadata is provided, it will use resolved metadata from the stack
14
+ */
15
+ export function MetadataRenderer({ metadata }: { metadata?: Metadata }) {
16
+ const { request } = useRequest();
17
+
18
+ // Resolve metadata from stack (hierarchical metadata from loaders)
19
+ const stackMetadata = resolveMetadata();
20
+
21
+ // Merge: stack metadata (parent) + provided metadata (child)
22
+ // If metadata is provided, it overrides stack metadata
23
+ // If not provided, use stack metadata only
24
+ const resolvedMetadata = metadata ? mergeMetadata(stackMetadata, metadata) : stackMetadata;
25
+
26
+ if (!resolvedMetadata) return null;
27
+
28
+ // Helper to normalize title
29
+ const getTitle = () => {
30
+ if (!resolvedMetadata.title) return null;
31
+ if (typeof resolvedMetadata.title === 'string') return resolvedMetadata.title;
32
+ // Handle TemplateString
33
+ return resolvedMetadata.title.default;
34
+ };
35
+
36
+ // Helper to normalize robots
37
+ const getRobots = () => {
38
+ // tránh tiêm query
39
+ const queryNames = Object.keys(request.query || {});
40
+ // mặc định nếu có query sẽ ép robots về noindex
41
+ // tuy nhiên 1 số trường hợp đặc biệt ví dụ như page=1 có chủ đích `allowQueryNames` sẽ bỏ qua nguyên tắc này
42
+
43
+ const noindex =
44
+ queryNames.filter((name) => {
45
+ if (typeof resolvedMetadata.robots === 'string') return false;
46
+ if (Array.isArray(resolvedMetadata.robots?.allowQueryNames))
47
+ return !resolvedMetadata.robots.allowQueryNames.includes(name);
48
+ return name !== resolvedMetadata.robots?.allowQueryNames;
49
+ }).length > 0;
50
+
51
+ if (noindex) return 'noindex, follow';
52
+
53
+ if (!resolvedMetadata.robots) return null;
54
+ if (typeof resolvedMetadata.robots === 'string') return resolvedMetadata.robots;
55
+
56
+ const parts: string[] = [];
57
+ if (resolvedMetadata.robots.index === false) parts.push('noindex');
58
+ else if (resolvedMetadata.robots.index === true) parts.push('index');
59
+
60
+ if (resolvedMetadata.robots.follow === false) parts.push('nofollow');
61
+ else if (resolvedMetadata.robots.follow === true) parts.push('follow');
62
+
63
+ if (resolvedMetadata.robots.noarchive) parts.push('noarchive');
64
+ if (resolvedMetadata.robots.nosnippet) parts.push('nosnippet');
65
+ if (resolvedMetadata.robots.noimageindex) parts.push('noimageindex');
66
+ if (resolvedMetadata.robots.nocache) parts.push('nocache');
67
+
68
+ return parts.length > 0 ? parts.join(', ') : null;
69
+ };
70
+
71
+ // Helper to normalize viewport
72
+ const getViewport = () => {
73
+ if (!resolvedMetadata.viewport) return null;
74
+ if (typeof resolvedMetadata.viewport === 'string') return resolvedMetadata.viewport;
75
+
76
+ const parts: string[] = [];
77
+ if (resolvedMetadata.viewport.width) parts.push(`width=${resolvedMetadata.viewport.width}`);
78
+ if (resolvedMetadata.viewport.height) parts.push(`height=${resolvedMetadata.viewport.height}`);
79
+ if (resolvedMetadata.viewport.initialScale)
80
+ parts.push(`initial-scale=${resolvedMetadata.viewport.initialScale}`);
81
+ if (resolvedMetadata.viewport.maximumScale)
82
+ parts.push(`maximum-scale=${resolvedMetadata.viewport.maximumScale}`);
83
+ if (resolvedMetadata.viewport.minimumScale)
84
+ parts.push(`minimum-scale=${resolvedMetadata.viewport.minimumScale}`);
85
+ if (resolvedMetadata.viewport.userScalable !== undefined) {
86
+ parts.push(`user-scalable=${resolvedMetadata.viewport.userScalable ? 'yes' : 'no'}`);
87
+ }
88
+
89
+ return parts.length > 0 ? parts.join(', ') : null;
90
+ };
91
+
92
+ // Helper to normalize keywords
93
+ const getKeywords = () => {
94
+ if (!resolvedMetadata.keywords) return null;
95
+ if (Array.isArray(resolvedMetadata.keywords)) return resolvedMetadata.keywords.join(', ');
96
+ return resolvedMetadata.keywords;
97
+ };
98
+
99
+ // Helper to normalize authors
100
+ const getAuthors = () => {
101
+ if (!resolvedMetadata.author) return [];
102
+ if (typeof resolvedMetadata.author === 'string') return [{ name: resolvedMetadata.author }];
103
+ if (Array.isArray(resolvedMetadata.author)) return resolvedMetadata.author;
104
+ return [resolvedMetadata.author];
105
+ };
106
+
107
+ // Helper to normalize format detection
108
+ const getFormatDetection = () => {
109
+ if (!resolvedMetadata.formatDetection) return null;
110
+
111
+ const parts: string[] = [];
112
+ if (resolvedMetadata.formatDetection.telephone === false) parts.push('telephone=no');
113
+ if (resolvedMetadata.formatDetection.date === false) parts.push('date=no');
114
+ if (resolvedMetadata.formatDetection.address === false) parts.push('address=no');
115
+ if (resolvedMetadata.formatDetection.email === false) parts.push('email=no');
116
+
117
+ return parts.length > 0 ? parts.join(', ') : null;
118
+ };
119
+
120
+ // Helper to normalize Open Graph images
121
+ const getOgImages = () => {
122
+ if (!resolvedMetadata.openGraph?.images) return [];
123
+
124
+ const images = Array.isArray(resolvedMetadata.openGraph.images)
125
+ ? resolvedMetadata.openGraph.images
126
+ : [resolvedMetadata.openGraph.images];
127
+
128
+ return images.map((img) => {
129
+ if (typeof img === 'string') return { url: img };
130
+ return img;
131
+ });
132
+ };
133
+
134
+ // Helper to normalize Twitter images
135
+ const getTwitterImages = () => {
136
+ if (!resolvedMetadata.twitter?.images) return [];
137
+
138
+ const images = Array.isArray(resolvedMetadata.twitter.images)
139
+ ? resolvedMetadata.twitter.images
140
+ : [resolvedMetadata.twitter.images];
141
+
142
+ return images.map((img) => {
143
+ if (typeof img === 'string') return { url: img };
144
+ return img;
145
+ });
146
+ };
147
+
148
+ // Helper to get icon URLs
149
+ const getIcons = () => {
150
+ if (!resolvedMetadata.icons) return { icon: [], apple: [], shortcut: null, other: [] };
151
+
152
+ const iconUrls = resolvedMetadata.icons.icon
153
+ ? Array.isArray(resolvedMetadata.icons.icon)
154
+ ? resolvedMetadata.icons.icon
155
+ : [resolvedMetadata.icons.icon]
156
+ : [];
157
+
158
+ const appleUrls = resolvedMetadata.icons.apple
159
+ ? Array.isArray(resolvedMetadata.icons.apple)
160
+ ? resolvedMetadata.icons.apple
161
+ : [resolvedMetadata.icons.apple]
162
+ : [];
163
+
164
+ return {
165
+ icon: iconUrls,
166
+ apple: appleUrls,
167
+ shortcut: resolvedMetadata.icons.shortcut || null,
168
+ other: resolvedMetadata.icons.other || [],
169
+ };
170
+ };
171
+
172
+ const title = getTitle();
173
+ const robots = getRobots();
174
+ const viewport = getViewport();
175
+ const keywords = getKeywords();
176
+ const authors = getAuthors();
177
+ const formatDetection = getFormatDetection();
178
+ const ogImages = getOgImages();
179
+ const twitterImages = getTwitterImages();
180
+ const icons = getIcons();
181
+ const themeColors = Array.isArray(resolvedMetadata.themeColor)
182
+ ? resolvedMetadata.themeColor
183
+ : resolvedMetadata.themeColor
184
+ ? [resolvedMetadata.themeColor]
185
+ : [];
186
+
187
+ return (
188
+ <>
189
+ {/* Critical: charset, viewport - must be first */}
190
+ {(resolvedMetadata.charset || viewport) && (
191
+ <Head priority={HEAD_PRIORITY.CRITICAL}>
192
+ {resolvedMetadata.charset && <meta charset={resolvedMetadata.charset} />}
193
+ {viewport && <meta name="viewport" content={viewport} />}
194
+ </Head>
195
+ )}
196
+
197
+ {/* High priority: title, description, canonical, prev/next */}
198
+ {(title ||
199
+ resolvedMetadata.description ||
200
+ resolvedMetadata.canonical ||
201
+ resolvedMetadata.prev ||
202
+ resolvedMetadata.next) && (
203
+ <Head priority={HEAD_PRIORITY.HIGH}>
204
+ {title && <title>{title}</title>}
205
+ {resolvedMetadata.description && (
206
+ <meta name="description" content={resolvedMetadata.description} />
207
+ )}
208
+ {resolvedMetadata.canonical && <link rel="canonical" href={resolvedMetadata.canonical} />}
209
+ {resolvedMetadata.prev && <link rel="prev" href={resolvedMetadata.prev} />}
210
+ {resolvedMetadata.next && <link rel="next" href={resolvedMetadata.next} />}
211
+ </Head>
212
+ )}
213
+
214
+ {/* Medium priority: meta tags, robots */}
215
+ <Head priority={HEAD_PRIORITY.MEDIUM}>
216
+ {keywords && <meta name="keywords" content={keywords} />}
217
+ {robots && <meta name="robots" content={robots} />}
218
+ {resolvedMetadata.colorScheme && (
219
+ <meta name="color-scheme" content={resolvedMetadata.colorScheme} />
220
+ )}
221
+
222
+ {/* Theme colors */}
223
+ {themeColors.map((color, index) => (
224
+ <meta name="theme-color" content={color} />
225
+ ))}
226
+
227
+ {resolvedMetadata.generator && (
228
+ <meta name="generator" content={resolvedMetadata.generator} />
229
+ )}
230
+ {resolvedMetadata.creator && <meta name="creator" content={resolvedMetadata.creator} />}
231
+ {resolvedMetadata.publisher && (
232
+ <meta name="publisher" content={resolvedMetadata.publisher} />
233
+ )}
234
+ {resolvedMetadata.referrer && <meta name="referrer" content={resolvedMetadata.referrer} />}
235
+ {resolvedMetadata.category && <meta name="category" content={resolvedMetadata.category} />}
236
+ {resolvedMetadata.classification && (
237
+ <meta name="classification" content={resolvedMetadata.classification} />
238
+ )}
239
+ {formatDetection && <meta name="format-detection" content={formatDetection} />}
240
+
241
+ {/* Authors */}
242
+ {authors.map((author, index) => (
243
+ <Fragment>
244
+ <meta name="author" content={author.name} />
245
+ {author.url && <link rel="author" href={author.url} />}
246
+ </Fragment>
247
+ ))}
248
+
249
+ {/* Icons */}
250
+ {icons.icon.map((url, index) => (
251
+ <link rel="icon" href={url} />
252
+ ))}
253
+ {icons.shortcut && <link rel="shortcut icon" href={icons.shortcut} />}
254
+ {icons.apple.map((url, index) => (
255
+ <link rel="apple-touch-icon" href={url} />
256
+ ))}
257
+ {icons.other.map((icon, index) => (
258
+ <link
259
+ rel={icon.rel || 'icon'}
260
+ href={icon.url}
261
+ {...(icon.sizes && { sizes: icon.sizes })}
262
+ {...(icon.type && { type: icon.type })}
263
+ />
264
+ ))}
265
+
266
+ {/* Manifest */}
267
+ {resolvedMetadata.manifest && <link rel="manifest" href={resolvedMetadata.manifest} />}
268
+
269
+ {/* Feeds */}
270
+ {resolvedMetadata.feed && (
271
+ <link rel="alternate" type="application/rss+xml" href={resolvedMetadata.feed} />
272
+ )}
273
+
274
+ {/* Verification */}
275
+ {resolvedMetadata.verification?.google && (
276
+ <meta name="google-site-verification" content={resolvedMetadata.verification.google} />
277
+ )}
278
+ {resolvedMetadata.verification?.yahoo && (
279
+ <meta name="y_key" content={resolvedMetadata.verification.yahoo} />
280
+ )}
281
+ {resolvedMetadata.verification?.yandex && (
282
+ <meta name="yandex-verification" content={resolvedMetadata.verification.yandex} />
283
+ )}
284
+ {resolvedMetadata.verification?.me &&
285
+ (Array.isArray(resolvedMetadata.verification.me) ? (
286
+ resolvedMetadata.verification.me.map((url, index) => <link rel="me" href={url} />)
287
+ ) : (
288
+ <link rel="me" href={resolvedMetadata.verification.me} />
289
+ ))}
290
+ {resolvedMetadata.verification?.other &&
291
+ Object.entries(resolvedMetadata.verification.other).map(([key, value]) =>
292
+ Array.isArray(value) ? (
293
+ value.map((v, index) => <meta name={key} content={v} />)
294
+ ) : (
295
+ <meta name={key} content={value} />
296
+ ),
297
+ )}
298
+
299
+ {/* App Links */}
300
+ {resolvedMetadata.appLinks && (
301
+ <Fragment>
302
+ {resolvedMetadata.appLinks.ios?.url && (
303
+ <meta property="al:ios:url" content={resolvedMetadata.appLinks.ios.url} />
304
+ )}
305
+ {resolvedMetadata.appLinks.ios?.app_store_id && (
306
+ <meta
307
+ property="al:ios:app_store_id"
308
+ content={resolvedMetadata.appLinks.ios.app_store_id}
309
+ />
310
+ )}
311
+ {resolvedMetadata.appLinks.ios?.app_name && (
312
+ <meta property="al:ios:app_name" content={resolvedMetadata.appLinks.ios.app_name} />
313
+ )}
314
+ {resolvedMetadata.appLinks.android?.package && (
315
+ <meta
316
+ property="al:android:package"
317
+ content={resolvedMetadata.appLinks.android.package}
318
+ />
319
+ )}
320
+ {resolvedMetadata.appLinks.android?.app_name && (
321
+ <meta
322
+ property="al:android:app_name"
323
+ content={resolvedMetadata.appLinks.android.app_name}
324
+ />
325
+ )}
326
+ {resolvedMetadata.appLinks.android?.url && (
327
+ <meta property="al:android:url" content={resolvedMetadata.appLinks.android.url} />
328
+ )}
329
+ {resolvedMetadata.appLinks.web?.url && (
330
+ <meta property="al:web:url" content={resolvedMetadata.appLinks.web.url} />
331
+ )}
332
+ {resolvedMetadata.appLinks.web?.should_fallback !== undefined && (
333
+ <meta
334
+ property="al:web:should_fallback"
335
+ content={String(resolvedMetadata.appLinks.web.should_fallback)}
336
+ />
337
+ )}
338
+ </Fragment>
339
+ )}
340
+
341
+ {/* Additional meta tags */}
342
+ {resolvedMetadata.other &&
343
+ Object.entries(resolvedMetadata.other).map(([key, value]) =>
344
+ Array.isArray(value) ? (
345
+ value.map((v, index) => <meta name={key} content={String(v)} />)
346
+ ) : (
347
+ <meta name={key} content={String(value)} />
348
+ ),
349
+ )}
350
+ </Head>
351
+
352
+ {/* SEO: OpenGraph, Twitter - lower priority */}
353
+ {(resolvedMetadata.openGraph || resolvedMetadata.twitter) && (
354
+ <Head priority={HEAD_PRIORITY.SEO}>
355
+ {/* Open Graph */}
356
+ {resolvedMetadata.openGraph && (
357
+ <Fragment>
358
+ {resolvedMetadata.openGraph.title && (
359
+ <meta property="og:title" content={resolvedMetadata.openGraph.title} />
360
+ )}
361
+ {resolvedMetadata.openGraph.description && (
362
+ <meta property="og:description" content={resolvedMetadata.openGraph.description} />
363
+ )}
364
+ {resolvedMetadata.openGraph.type && (
365
+ <meta property="og:type" content={resolvedMetadata.openGraph.type} />
366
+ )}
367
+ {resolvedMetadata.openGraph.url && (
368
+ <meta property="og:url" content={resolvedMetadata.openGraph.url} />
369
+ )}
370
+ {resolvedMetadata.openGraph.siteName && (
371
+ <meta property="og:site_name" content={resolvedMetadata.openGraph.siteName} />
372
+ )}
373
+ {resolvedMetadata.openGraph.locale && (
374
+ <meta property="og:locale" content={resolvedMetadata.openGraph.locale} />
375
+ )}
376
+ {resolvedMetadata.openGraph.alternateLocale &&
377
+ (Array.isArray(resolvedMetadata.openGraph.alternateLocale) ? (
378
+ resolvedMetadata.openGraph.alternateLocale.map((locale, index) => (
379
+ <meta property="og:locale:alternate" content={locale} />
380
+ ))
381
+ ) : (
382
+ <meta
383
+ property="og:locale:alternate"
384
+ content={resolvedMetadata.openGraph.alternateLocale}
385
+ />
386
+ ))}
387
+ {resolvedMetadata.openGraph.determiner && (
388
+ <meta property="og:determiner" content={resolvedMetadata.openGraph.determiner} />
389
+ )}
390
+
391
+ {/* OG Images */}
392
+ {ogImages.map((image, index) => (
393
+ <Fragment>
394
+ <meta property="og:image" content={image.url} />
395
+ {image.secureUrl && (
396
+ <meta property="og:image:secure_url" content={image.secureUrl} />
397
+ )}
398
+ {image.alt && <meta property="og:image:alt" content={image.alt} />}
399
+ {image.type && <meta property="og:image:type" content={image.type} />}
400
+ {image.width && <meta property="og:image:width" content={String(image.width)} />}
401
+ {image.height && (
402
+ <meta property="og:image:height" content={String(image.height)} />
403
+ )}
404
+ </Fragment>
405
+ ))}
406
+
407
+ {/* Article specific */}
408
+ {resolvedMetadata.openGraph.publishedTime && (
409
+ <meta
410
+ property="article:published_time"
411
+ content={resolvedMetadata.openGraph.publishedTime}
412
+ />
413
+ )}
414
+ {resolvedMetadata.openGraph.modifiedTime && (
415
+ <meta
416
+ property="article:modified_time"
417
+ content={resolvedMetadata.openGraph.modifiedTime}
418
+ />
419
+ )}
420
+ {resolvedMetadata.openGraph.expirationTime && (
421
+ <meta
422
+ property="article:expiration_time"
423
+ content={resolvedMetadata.openGraph.expirationTime}
424
+ />
425
+ )}
426
+ {resolvedMetadata.openGraph.authors &&
427
+ (Array.isArray(resolvedMetadata.openGraph.authors) ? (
428
+ resolvedMetadata.openGraph.authors.map((author, index) => (
429
+ <meta property="article:author" content={author} />
430
+ ))
431
+ ) : (
432
+ <meta property="article:author" content={resolvedMetadata.openGraph.authors} />
433
+ ))}
434
+ {resolvedMetadata.openGraph.section && (
435
+ <meta property="article:section" content={resolvedMetadata.openGraph.section} />
436
+ )}
437
+ {resolvedMetadata.openGraph.tags &&
438
+ (Array.isArray(resolvedMetadata.openGraph.tags) ? (
439
+ resolvedMetadata.openGraph.tags.map((tag, index) => (
440
+ <meta property="article:tag" content={tag} />
441
+ ))
442
+ ) : (
443
+ <meta property="article:tag" content={resolvedMetadata.openGraph.tags} />
444
+ ))}
445
+
446
+ {/* Book specific */}
447
+ {resolvedMetadata.openGraph.isbn && (
448
+ <meta property="book:isbn" content={resolvedMetadata.openGraph.isbn} />
449
+ )}
450
+ {resolvedMetadata.openGraph.releaseDate && (
451
+ <meta
452
+ property="book:release_date"
453
+ content={resolvedMetadata.openGraph.releaseDate}
454
+ />
455
+ )}
456
+
457
+ {/* Profile specific */}
458
+ {resolvedMetadata.openGraph.firstName && (
459
+ <meta
460
+ property="profile:first_name"
461
+ content={resolvedMetadata.openGraph.firstName}
462
+ />
463
+ )}
464
+ {resolvedMetadata.openGraph.lastName && (
465
+ <meta property="profile:last_name" content={resolvedMetadata.openGraph.lastName} />
466
+ )}
467
+ {resolvedMetadata.openGraph.username && (
468
+ <meta property="profile:username" content={resolvedMetadata.openGraph.username} />
469
+ )}
470
+ {resolvedMetadata.openGraph.gender && (
471
+ <meta property="profile:gender" content={resolvedMetadata.openGraph.gender} />
472
+ )}
473
+ </Fragment>
474
+ )}
475
+
476
+ {/* Twitter Card */}
477
+ {resolvedMetadata.twitter && (
478
+ <Fragment>
479
+ {resolvedMetadata.twitter.card && (
480
+ <meta name="twitter:card" content={resolvedMetadata.twitter.card} />
481
+ )}
482
+ {resolvedMetadata.twitter.site && (
483
+ <meta name="twitter:site" content={resolvedMetadata.twitter.site} />
484
+ )}
485
+ {resolvedMetadata.twitter.siteId && (
486
+ <meta name="twitter:site:id" content={resolvedMetadata.twitter.siteId} />
487
+ )}
488
+ {resolvedMetadata.twitter.creator && (
489
+ <meta name="twitter:creator" content={resolvedMetadata.twitter.creator} />
490
+ )}
491
+ {resolvedMetadata.twitter.creatorId && (
492
+ <meta name="twitter:creator:id" content={resolvedMetadata.twitter.creatorId} />
493
+ )}
494
+ {resolvedMetadata.twitter.title && (
495
+ <meta name="twitter:title" content={resolvedMetadata.twitter.title} />
496
+ )}
497
+ {resolvedMetadata.twitter.description && (
498
+ <meta name="twitter:description" content={resolvedMetadata.twitter.description} />
499
+ )}
500
+
501
+ {/* Twitter Images */}
502
+ {twitterImages.map((image, index) => (
503
+ <Fragment>
504
+ <meta name="twitter:image" content={image.url} />
505
+ {image.alt && <meta name="twitter:image:alt" content={image.alt} />}
506
+ </Fragment>
507
+ ))}
508
+
509
+ {/* Twitter App */}
510
+ {resolvedMetadata.twitter.app && (
511
+ <Fragment>
512
+ {resolvedMetadata.twitter.app.name && (
513
+ <meta name="twitter:app:name" content={resolvedMetadata.twitter.app.name} />
514
+ )}
515
+ {resolvedMetadata.twitter.app.id.iphone && (
516
+ <meta
517
+ name="twitter:app:id:iphone"
518
+ content={resolvedMetadata.twitter.app.id.iphone}
519
+ />
520
+ )}
521
+ {resolvedMetadata.twitter.app.id.ipad && (
522
+ <meta
523
+ name="twitter:app:id:ipad"
524
+ content={resolvedMetadata.twitter.app.id.ipad}
525
+ />
526
+ )}
527
+ {resolvedMetadata.twitter.app.id.googleplay && (
528
+ <meta
529
+ name="twitter:app:id:googleplay"
530
+ content={resolvedMetadata.twitter.app.id.googleplay}
531
+ />
532
+ )}
533
+ {resolvedMetadata.twitter.app.url?.iphone && (
534
+ <meta
535
+ name="twitter:app:url:iphone"
536
+ content={resolvedMetadata.twitter.app.url.iphone}
537
+ />
538
+ )}
539
+ {resolvedMetadata.twitter.app.url?.ipad && (
540
+ <meta
541
+ name="twitter:app:url:ipad"
542
+ content={resolvedMetadata.twitter.app.url.ipad}
543
+ />
544
+ )}
545
+ {resolvedMetadata.twitter.app.url?.googleplay && (
546
+ <meta
547
+ name="twitter:app:url:googleplay"
548
+ content={resolvedMetadata.twitter.app.url.googleplay}
549
+ />
550
+ )}
551
+ </Fragment>
552
+ )}
553
+ </Fragment>
554
+ )}
555
+ </Head>
556
+ )}
557
+ </>
558
+ );
559
+ }
@@ -0,0 +1,10 @@
1
+ export { MetadataRenderer } from './generateMetadata';
2
+ export { mergeMetadata } from './mergeMetadata';
3
+ export type {
4
+ BreadcrumbSchema,
5
+ Metadata,
6
+ MetadataProps,
7
+ OrganizationSchema,
8
+ WebPageSchema,
9
+ WebSiteSchema,
10
+ } from './types';