@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.
- package/LICENSE +21 -0
- package/README.md +24 -0
- package/dist/action.js +10 -0
- package/dist/action.js.map +1 -0
- package/dist/client-D67hK4Yy.js +9 -0
- package/dist/client-D67hK4Yy.js.map +1 -0
- package/dist/entry-server-Ckh6zfgm.js +258 -0
- package/dist/entry-server-Ckh6zfgm.js.map +1 -0
- package/dist/entry-server.js +12 -0
- package/dist/entry-server.js.map +1 -0
- package/dist/generateMetadata-C5QsMS-H.js +144 -0
- package/dist/generateMetadata-C5QsMS-H.js.map +1 -0
- package/dist/index-BIt7MJT9.js +163 -0
- package/dist/index-BIt7MJT9.js.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/island/client.js +5 -0
- package/dist/island/client.js.map +1 -0
- package/dist/island/runtime.js +98 -0
- package/dist/island/runtime.js.map +1 -0
- package/dist/island.js +39 -0
- package/dist/island.js.map +1 -0
- package/dist/jsx-runtime-C2Vw67N2.js +256 -0
- package/dist/jsx-runtime-C2Vw67N2.js.map +1 -0
- package/dist/jsx-runtime.js +26 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/middleware.js +9 -0
- package/dist/middleware.js.map +1 -0
- package/dist/seo.js +7 -0
- package/dist/seo.js.map +1 -0
- package/dist/server.js +489 -0
- package/dist/server.js.map +1 -0
- package/dist/swap/server.js +15 -0
- package/dist/swap/server.js.map +1 -0
- package/dist/swap.js +121 -0
- package/dist/swap.js.map +1 -0
- package/dist/tooltip.js +129 -0
- package/dist/tooltip.js.map +1 -0
- package/dist/vite-plugin.js +381 -0
- package/dist/vite-plugin.js.map +1 -0
- package/index.ts +1 -0
- package/package.json +129 -0
- package/src/action/define-action.ts +8 -0
- package/src/action/index.ts +2 -0
- package/src/action/types.ts +21 -0
- package/src/core/bundler.ts +275 -0
- package/src/core/const.ts +2 -0
- package/src/core/entry-server.d.ts +1 -0
- package/src/core/entry-server.ts +381 -0
- package/src/core/exceptions.ts +80 -0
- package/src/core/head-priority.ts +15 -0
- package/src/core/index.ts +40 -0
- package/src/core/jsx-runtime.ts +325 -0
- package/src/core/jsx-types.d.ts +548 -0
- package/src/core/render.ts +181 -0
- package/src/core/request.ts +31 -0
- package/src/core/server.ts +740 -0
- package/src/core/vite-plugin.ts +779 -0
- package/src/island/ClientIsland.ts +71 -0
- package/src/island/client.ts +3 -0
- package/src/island/index.ts +3 -0
- package/src/island/runtime.ts +149 -0
- package/src/island/strategy-registry.ts +10 -0
- package/src/island/types.ts +28 -0
- package/src/middleware/defineMiddleware.ts +5 -0
- package/src/middleware/index.ts +133 -0
- package/src/middleware/sequence.ts +105 -0
- package/src/middleware/types.ts +28 -0
- package/src/seo/generateMetadata.tsx +559 -0
- package/src/seo/index.ts +10 -0
- package/src/seo/mergeMetadata.ts +200 -0
- package/src/seo/types.ts +316 -0
- package/src/swap/SwapResponse.tsx +16 -0
- package/src/swap/create-swap.ts +121 -0
- package/src/swap/index.ts +8 -0
- package/src/swap/parse.ts +12 -0
- package/src/swap/server.ts +1 -0
- package/src/swap/swap.ts +57 -0
- package/src/swap/types.ts +47 -0
- package/src/swap/utils.ts +7 -0
- package/src/tooltip/index.ts +2 -0
- package/src/tooltip/tooltip-loader.ts +108 -0
- package/src/tooltip/tooltip-runtime.ts +173 -0
- 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
|
+
}
|
package/src/seo/index.ts
ADDED