@mannisto/astro-metadata 1.0.0-beta.3 → 1.0.0-beta.5
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/README.md +46 -408
- package/package.json +1 -1
- package/src/components/Head.astro +47 -55
- package/src/lib/metadata.ts +3 -0
package/README.md
CHANGED
|
@@ -8,30 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
Astro components for managing your page head — metadata, social sharing, favicons, and SEO.
|
|
10
10
|
|
|
11
|
-
## Table of contents
|
|
12
|
-
|
|
13
|
-
- [Installation](#installation)
|
|
14
|
-
- [Usage](#usage)
|
|
15
|
-
- [Head component](#1-head-component)
|
|
16
|
-
- [Individual components](#2-individual-components)
|
|
17
|
-
- [Metadata utility](#3-metadata-utility)
|
|
18
|
-
- [Components](#components)
|
|
19
|
-
- [Canonical](#canonical)
|
|
20
|
-
- [Description](#description)
|
|
21
|
-
- [Favicon](#favicon)
|
|
22
|
-
- [Head](#head)
|
|
23
|
-
- [Keywords](#keywords)
|
|
24
|
-
- [LanguageAlternates](#languagealternates)
|
|
25
|
-
- [OpenGraph](#opengraph)
|
|
26
|
-
- [Robots](#robots)
|
|
27
|
-
- [Schema](#schema)
|
|
28
|
-
- [Title](#title)
|
|
29
|
-
- [Twitter](#twitter)
|
|
30
|
-
- [Contributing](#contributing)
|
|
31
|
-
- [License](#license)
|
|
32
|
-
|
|
33
11
|
## Installation
|
|
34
|
-
|
|
35
12
|
```bash
|
|
36
13
|
# pnpm
|
|
37
14
|
pnpm add @mannisto/astro-metadata
|
|
@@ -47,104 +24,43 @@ yarn add @mannisto/astro-metadata
|
|
|
47
24
|
|
|
48
25
|
There are three ways to use this package. Pick what suits your project, or combine them freely.
|
|
49
26
|
|
|
50
|
-
###
|
|
51
|
-
|
|
52
|
-
The simplest approach. Use `Head` in your layout and pass props down from your pages. Charset and viewport are included automatically.
|
|
27
|
+
### Head component
|
|
53
28
|
|
|
29
|
+
The simplest approach — use `Head` in your layout and pass props down from pages. Title, description and image flow into Open Graph and Twitter automatically.
|
|
54
30
|
```astro
|
|
55
31
|
---
|
|
56
|
-
// layouts/Layout.astro
|
|
57
32
|
import { Head } from "@mannisto/astro-metadata"
|
|
58
|
-
import type { HeadProps } from "@mannisto/astro-metadata"
|
|
59
|
-
|
|
60
|
-
interface Props extends HeadProps {}
|
|
61
|
-
|
|
62
|
-
const { title, description, ...rest } = Astro.props
|
|
63
33
|
---
|
|
64
34
|
|
|
65
35
|
<html>
|
|
66
|
-
<Head
|
|
36
|
+
<Head
|
|
37
|
+
title="Home"
|
|
38
|
+
titleTemplate="%s | My Site"
|
|
39
|
+
description="Welcome to my site"
|
|
40
|
+
image={{ url: "/og.jpg", alt: "My Site", width: 1200, height: 630 }}
|
|
41
|
+
/>
|
|
67
42
|
<body>
|
|
68
43
|
<slot />
|
|
69
44
|
</body>
|
|
70
45
|
</html>
|
|
71
46
|
```
|
|
72
47
|
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
// pages/index.astro
|
|
76
|
-
import Layout from "../layouts/Layout.astro"
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
<Layout title="Home" description="Welcome to my site">
|
|
80
|
-
<h1>Hello</h1>
|
|
81
|
-
</Layout>
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Best for simple sites where pages pass metadata as props to their layout.
|
|
48
|
+
[Read more →](docs/usage/head.md)
|
|
85
49
|
|
|
86
|
-
###
|
|
87
|
-
|
|
88
|
-
Use components directly inside your own `<head>`. Useful when you only need specific pieces, or want full control over the structure.
|
|
89
|
-
|
|
90
|
-
```astro
|
|
91
|
-
---
|
|
92
|
-
import { Title, Description, OpenGraph, Favicon } from "@mannisto/astro-metadata"
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
<html>
|
|
96
|
-
<head>
|
|
97
|
-
<meta charset="UTF-8" />
|
|
98
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
99
|
-
<Title value="My Page" template="%s | My Site" />
|
|
100
|
-
<Description value="Welcome to my site" />
|
|
101
|
-
<OpenGraph
|
|
102
|
-
title="My Page"
|
|
103
|
-
description="Welcome to my site"
|
|
104
|
-
image={{
|
|
105
|
-
url: "/og.jpg",
|
|
106
|
-
alt: "My Site",
|
|
107
|
-
width: 1200,
|
|
108
|
-
height: 630,
|
|
109
|
-
}}
|
|
110
|
-
/>
|
|
111
|
-
<Favicon icons={[{ path: "/favicon.ico" }, { path: "/favicon.svg" }]} />
|
|
112
|
-
</head>
|
|
113
|
-
<body>
|
|
114
|
-
<slot />
|
|
115
|
-
</body>
|
|
116
|
-
</html>
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Best for when you want to compose only what you need, or when `Head` is too opinionated for your setup.
|
|
120
|
-
|
|
121
|
-
### 3. Metadata utility
|
|
122
|
-
|
|
123
|
-
Set metadata in your page, resolve it in your layout. Eliminates prop drilling through nested layout layers.
|
|
50
|
+
### Metadata API
|
|
124
51
|
|
|
52
|
+
Set metadata in pages, resolve in layouts — no prop drilling.
|
|
125
53
|
```astro
|
|
126
54
|
---
|
|
127
55
|
// pages/about.astro
|
|
128
56
|
import { Metadata } from "@mannisto/astro-metadata"
|
|
129
|
-
import Layout from "../layouts/Layout.astro"
|
|
130
57
|
|
|
131
58
|
Metadata.set({
|
|
132
59
|
title: "About",
|
|
133
60
|
description: "Learn more about us",
|
|
134
|
-
openGraph: {
|
|
135
|
-
image: {
|
|
136
|
-
url: "/og/about.jpg",
|
|
137
|
-
alt: "About",
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
61
|
})
|
|
141
62
|
---
|
|
142
|
-
|
|
143
|
-
<Layout>
|
|
144
|
-
<h1>About</h1>
|
|
145
|
-
</Layout>
|
|
146
63
|
```
|
|
147
|
-
|
|
148
64
|
```astro
|
|
149
65
|
---
|
|
150
66
|
// layouts/Layout.astro
|
|
@@ -152,7 +68,6 @@ import { Head, Metadata } from "@mannisto/astro-metadata"
|
|
|
152
68
|
|
|
153
69
|
const meta = Metadata.resolve({
|
|
154
70
|
title: "My Site",
|
|
155
|
-
description: "Default description",
|
|
156
71
|
titleTemplate: "%s | My Site",
|
|
157
72
|
})
|
|
158
73
|
---
|
|
@@ -165,329 +80,52 @@ const meta = Metadata.resolve({
|
|
|
165
80
|
</html>
|
|
166
81
|
```
|
|
167
82
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
Best for sites with deeply nested layouts, or when you want to keep metadata co-located with page content.
|
|
171
|
-
|
|
172
|
-
## Components
|
|
173
|
-
|
|
174
|
-
### Canonical
|
|
83
|
+
[Read more →](docs/usage/metadata.md)
|
|
175
84
|
|
|
176
|
-
|
|
85
|
+
### Individual components
|
|
177
86
|
|
|
87
|
+
Use components directly in your `<head>` for full control.
|
|
178
88
|
```astro
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
| Prop | Type | Description |
|
|
183
|
-
| ------- | -------- | -------------------------------------------- |
|
|
184
|
-
| `value` | `string` | Canonical URL. Defaults to `Astro.url.href`. |
|
|
185
|
-
|
|
186
|
-
### Description
|
|
187
|
-
|
|
188
|
-
```astro
|
|
189
|
-
<Description value="Welcome to my site" />
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
| Prop | Type | Description |
|
|
193
|
-
| ------- | -------- | ---------------- |
|
|
194
|
-
| `value` | `string` | Page description |
|
|
195
|
-
|
|
196
|
-
### Favicon
|
|
197
|
-
|
|
198
|
-
Favicon support with light and dark mode variants, automatic MIME type detection, and automatic sorting.
|
|
199
|
-
|
|
200
|
-
```astro
|
|
201
|
-
<Favicon
|
|
202
|
-
icons={[
|
|
203
|
-
{ path: "/favicon.ico" },
|
|
204
|
-
{ path: "/favicon.svg" },
|
|
205
|
-
{ path: "/favicon-96x96.png", size: 96 },
|
|
206
|
-
{ path: "/apple-touch-icon.png", size: 180, apple: true },
|
|
207
|
-
{ path: "/favicon-dark.svg", theme: "dark" },
|
|
208
|
-
{ path: "/favicon-light.svg", theme: "light" },
|
|
209
|
-
]}
|
|
210
|
-
manifest="/site.webmanifest"
|
|
211
|
-
/>
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Icons are automatically sorted in the recommended browser order: `ico` → `png` → `svg` → `apple` → themed variants. Pass `sort={false}` to preserve the original order.
|
|
215
|
-
|
|
216
|
-
| Prop | Type | Default | Description |
|
|
217
|
-
| ---------- | --------------- | ------- | --------------------------------------- |
|
|
218
|
-
| `icons` | `FaviconFile[]` | — | List of favicon files |
|
|
219
|
-
| `manifest` | `string` | — | Path to web app manifest |
|
|
220
|
-
| `sort` | `boolean` | `true` | Sort icons in recommended browser order |
|
|
221
|
-
|
|
222
|
-
#### FaviconFile
|
|
223
|
-
|
|
224
|
-
| Prop | Type | Description |
|
|
225
|
-
| ------- | ------------------- | ----------------------------------------------------------- |
|
|
226
|
-
| `path` | `string` | Path to the file. MIME type is detected automatically. |
|
|
227
|
-
| `size` | `number` | Size in pixels. Rendered as `NxN` in the `sizes` attribute. |
|
|
228
|
-
| `theme` | `"light" \| "dark"` | Adds a `prefers-color-scheme` media query |
|
|
229
|
-
| `apple` | `boolean` | Renders as `<link rel="apple-touch-icon">` |
|
|
230
|
-
|
|
231
|
-
### Head
|
|
232
|
-
|
|
233
|
-
Wraps the entire page head and composes all sub-components internally. Charset and viewport are always included and can be overridden if needed.
|
|
234
|
-
|
|
235
|
-
```astro
|
|
236
|
-
<Head
|
|
237
|
-
title="Home"
|
|
238
|
-
titleTemplate="%s | My Site"
|
|
239
|
-
description="Welcome to my site"
|
|
240
|
-
openGraph={{
|
|
241
|
-
image: {
|
|
242
|
-
url: "/og.jpg",
|
|
243
|
-
alt: "My Site",
|
|
244
|
-
width: 1200,
|
|
245
|
-
height: 630,
|
|
246
|
-
},
|
|
247
|
-
}}
|
|
248
|
-
favicon={{
|
|
249
|
-
icons: [{ path: "/favicon.ico" }, { path: "/favicon.svg" }],
|
|
250
|
-
}}
|
|
251
|
-
/>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
| Prop | Type | Default | Description |
|
|
255
|
-
| -------------------- | ---------------------------- | ----------------------------------------- | --------------------------------------------------------- |
|
|
256
|
-
| `title` | `string` | — | Page title. Required. |
|
|
257
|
-
| `titleTemplate` | `` `${string}%s${string}` `` | — | Title template. Must contain `%s`, e.g. `"%s \| My Site"` |
|
|
258
|
-
| `description` | `string` | — | Page description |
|
|
259
|
-
| `canonical` | `string` | `Astro.url.href` | Canonical URL |
|
|
260
|
-
| `keywords` | `string[]` | — | List of keywords |
|
|
261
|
-
| `charset` | `string` | `"UTF-8"` | Document charset |
|
|
262
|
-
| `viewport` | `string` | `"width=device-width, initial-scale=1.0"` | Viewport meta content |
|
|
263
|
-
| `robots` | `RobotsProps` | — | Robots directives |
|
|
264
|
-
| `openGraph` | `OpenGraphProps` | — | Open Graph tags |
|
|
265
|
-
| `twitter` | `TwitterProps` | — | Twitter card tags |
|
|
266
|
-
| `favicon` | `FaviconProps` | — | Favicon configuration |
|
|
267
|
-
| `schema` | `SchemaProps` | — | JSON-LD structured data |
|
|
268
|
-
| `languageAlternates` | `LanguageAlternate[]` | — | Hreflang alternate links |
|
|
269
|
-
|
|
270
|
-
#### Slots
|
|
271
|
-
|
|
272
|
-
```astro
|
|
273
|
-
<Head title="My Site">
|
|
274
|
-
<!-- Renders before charset and viewport -->
|
|
275
|
-
<meta slot="top" http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
276
|
-
|
|
277
|
-
<!-- Renders at the end of <head> -->
|
|
278
|
-
<script src={analyticsUrl}></script>
|
|
279
|
-
</Head>
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### Keywords
|
|
283
|
-
|
|
284
|
-
```astro
|
|
285
|
-
<Keywords value={["astro", "seo", "metadata"]} />
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
| Prop | Type | Description |
|
|
289
|
-
| ------- | ---------- | ---------------- |
|
|
290
|
-
| `value` | `string[]` | List of keywords |
|
|
291
|
-
|
|
292
|
-
### LanguageAlternates
|
|
293
|
-
|
|
294
|
-
Renders `<link rel="alternate" hreflang>` tags for multilingual sites. Tells search engines which language version to serve for a given region.
|
|
295
|
-
|
|
296
|
-
```astro
|
|
297
|
-
<LanguageAlternates
|
|
298
|
-
alternates={[
|
|
299
|
-
{ href: "https://example.com/en", hreflang: "en" },
|
|
300
|
-
{ href: "https://example.com/fi", hreflang: "fi" },
|
|
301
|
-
{ href: "https://example.com", hreflang: "x-default" },
|
|
302
|
-
]}
|
|
303
|
-
/>
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
| Prop | Type | Description |
|
|
307
|
-
| ----------------------- | --------------------- | -------------------------------------------------------------- |
|
|
308
|
-
| `alternates` | `LanguageAlternate[]` | List of alternate language pages |
|
|
309
|
-
| `alternates[].href` | `string` | Full URL of the alternate page |
|
|
310
|
-
| `alternates[].hreflang` | `string` | Language or region code, e.g. `en`, `fi`, `en-US`, `x-default` |
|
|
311
|
-
|
|
312
|
-
### OpenGraph
|
|
313
|
-
|
|
314
|
-
Renders Open Graph meta tags for rich previews when your pages are shared on social platforms. When used inside `Head`, `title`, `description` and `url` fall back to the page values automatically.
|
|
315
|
-
|
|
316
|
-
```astro
|
|
317
|
-
<OpenGraph
|
|
318
|
-
title="My Page"
|
|
319
|
-
description="Welcome to my site"
|
|
320
|
-
url="https://example.com"
|
|
321
|
-
type="website"
|
|
322
|
-
siteName="My Site"
|
|
323
|
-
locale="en_US"
|
|
324
|
-
localeAlternate={["fi_FI", "fr_FR"]}
|
|
325
|
-
image={{
|
|
326
|
-
url: "/og.jpg",
|
|
327
|
-
secureUrl: "https://example.com/og.jpg",
|
|
328
|
-
type: "image/jpeg",
|
|
329
|
-
alt: "My Site",
|
|
330
|
-
width: 1200,
|
|
331
|
-
height: 630,
|
|
332
|
-
}}
|
|
333
|
-
video={{
|
|
334
|
-
url: "https://example.com/video.mp4",
|
|
335
|
-
secureUrl: "https://example.com/video.mp4",
|
|
336
|
-
type: "video/mp4",
|
|
337
|
-
width: 1280,
|
|
338
|
-
height: 720,
|
|
339
|
-
}}
|
|
340
|
-
audio={{
|
|
341
|
-
url: "https://example.com/audio.mp3",
|
|
342
|
-
secureUrl: "https://example.com/audio.mp3",
|
|
343
|
-
type: "audio/mpeg",
|
|
344
|
-
}}
|
|
345
|
-
/>
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
| Prop | Type | Default | Description |
|
|
349
|
-
| ----------------- | ---------------- | ----------- | -------------------------------------------- |
|
|
350
|
-
| `title` | `string` | — | OG title |
|
|
351
|
-
| `description` | `string` | — | OG description |
|
|
352
|
-
| `url` | `string` | — | Canonical URL for the OG object |
|
|
353
|
-
| `type` | `string` | `"website"` | OG type |
|
|
354
|
-
| `siteName` | `string` | — | Name of the site |
|
|
355
|
-
| `locale` | `string` | — | Locale, e.g. `en_US` |
|
|
356
|
-
| `localeAlternate` | `string[]` | — | Alternate locales, e.g. `["fi_FI", "fr_FR"]` |
|
|
357
|
-
| `image` | `OpenGraphImage` | — | Image metadata |
|
|
358
|
-
| `video` | `OpenGraphVideo` | — | Video metadata |
|
|
359
|
-
| `audio` | `OpenGraphAudio` | — | Audio metadata |
|
|
360
|
-
|
|
361
|
-
#### OpenGraphImage
|
|
362
|
-
|
|
363
|
-
| Prop | Type | Description |
|
|
364
|
-
| ----------- | -------- | ------------------------------------------ |
|
|
365
|
-
| `url` | `string` | Image URL. Required if image is set. |
|
|
366
|
-
| `secureUrl` | `string` | HTTPS image URL |
|
|
367
|
-
| `type` | `string` | MIME type, e.g. `"image/jpeg"` |
|
|
368
|
-
| `alt` | `string` | Image alt text |
|
|
369
|
-
| `width` | `number` | Image width in pixels. Recommended: `1200` |
|
|
370
|
-
| `height` | `number` | Image height in pixels. Recommended: `630` |
|
|
371
|
-
|
|
372
|
-
#### OpenGraphVideo
|
|
373
|
-
|
|
374
|
-
| Prop | Type | Description |
|
|
375
|
-
| ----------- | -------- | ------------------------------------ |
|
|
376
|
-
| `url` | `string` | Video URL. Required if video is set. |
|
|
377
|
-
| `secureUrl` | `string` | HTTPS video URL |
|
|
378
|
-
| `type` | `string` | MIME type, e.g. `"video/mp4"` |
|
|
379
|
-
| `width` | `number` | Video width in pixels |
|
|
380
|
-
| `height` | `number` | Video height in pixels |
|
|
381
|
-
|
|
382
|
-
#### OpenGraphAudio
|
|
383
|
-
|
|
384
|
-
| Prop | Type | Description |
|
|
385
|
-
| ----------- | -------- | ------------------------------------ |
|
|
386
|
-
| `url` | `string` | Audio URL. Required if audio is set. |
|
|
387
|
-
| `secureUrl` | `string` | HTTPS audio URL |
|
|
388
|
-
| `type` | `string` | MIME type, e.g. `"audio/mpeg"` |
|
|
389
|
-
|
|
390
|
-
### Robots
|
|
391
|
-
|
|
392
|
-
Controls how search engines crawl and index your page. Defaults to `index, follow`.
|
|
393
|
-
|
|
394
|
-
```astro
|
|
395
|
-
<Robots archive={false} extra="max-snippet:-1, max-image-preview:large, max-video-preview:-1" />
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
| Prop | Type | Default | Description |
|
|
399
|
-
| --------- | --------- | ------- | ----------------------------------------------------------------------- |
|
|
400
|
-
| `index` | `boolean` | `true` | Allow indexing |
|
|
401
|
-
| `follow` | `boolean` | `true` | Allow following links |
|
|
402
|
-
| `archive` | `boolean` | `true` | Allow search engines to cache the page |
|
|
403
|
-
| `snippet` | `boolean` | `true` | Allow text snippets in search results |
|
|
404
|
-
| `extra` | `string` | — | Additional directives, e.g. `"max-snippet:-1, max-image-preview:large"` |
|
|
405
|
-
|
|
406
|
-
### Schema
|
|
407
|
-
|
|
408
|
-
Outputs a `<script type="application/ld+json">` tag for structured data. Use it to help search engines understand your content and qualify for rich results.
|
|
409
|
-
|
|
410
|
-
```astro
|
|
411
|
-
<Schema
|
|
412
|
-
schema={{
|
|
413
|
-
"@context": "https://schema.org",
|
|
414
|
-
"@type": "Person",
|
|
415
|
-
name: "Ere Männistö",
|
|
416
|
-
url: "https://example.com",
|
|
417
|
-
}}
|
|
418
|
-
/>
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
| Prop | Type | Description |
|
|
422
|
-
| -------- | ------------------------- | -------------- |
|
|
423
|
-
| `schema` | `Record<string, unknown>` | JSON-LD object |
|
|
424
|
-
|
|
425
|
-
### Title
|
|
426
|
-
|
|
427
|
-
Renders the `<title>` tag. The template must contain `%s`, which is replaced with the page title — TypeScript enforces this at the type level.
|
|
89
|
+
---
|
|
90
|
+
import { Title, Description, OpenGraph } from "@mannisto/astro-metadata"
|
|
91
|
+
---
|
|
428
92
|
|
|
429
|
-
|
|
430
|
-
<
|
|
93
|
+
<html>
|
|
94
|
+
<head>
|
|
95
|
+
<meta charset="UTF-8" />
|
|
96
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
97
|
+
<Title value="My Page" template="%s | My Site" />
|
|
98
|
+
<Description value="Welcome to my site" />
|
|
99
|
+
<OpenGraph
|
|
100
|
+
title="My Page"
|
|
101
|
+
image={{ url: "/og.jpg", alt: "My Site" }}
|
|
102
|
+
/>
|
|
103
|
+
</head>
|
|
104
|
+
<body>
|
|
105
|
+
<slot />
|
|
106
|
+
</body>
|
|
107
|
+
</html>
|
|
431
108
|
```
|
|
432
109
|
|
|
433
|
-
|
|
434
|
-
| ---------- | ---------------------------- | ----------------------------------- |
|
|
435
|
-
| `value` | `string` | Page title. Required. |
|
|
436
|
-
| `template` | `` `${string}%s${string}` `` | Template string. Must contain `%s`. |
|
|
437
|
-
|
|
438
|
-
### Twitter
|
|
110
|
+
[Read more →](docs/usage/components.md)
|
|
439
111
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
```astro
|
|
443
|
-
<Twitter
|
|
444
|
-
card="summary_large_image"
|
|
445
|
-
site="@mysite"
|
|
446
|
-
creator="@myhandle"
|
|
447
|
-
url="https://example.com"
|
|
448
|
-
image={{
|
|
449
|
-
url: "/og.jpg",
|
|
450
|
-
alt: "My Site",
|
|
451
|
-
}}
|
|
452
|
-
/>
|
|
453
|
-
```
|
|
112
|
+
## Components
|
|
454
113
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
114
|
+
- [Canonical](docs/components/canonical.md)
|
|
115
|
+
- [Description](docs/components/description.md)
|
|
116
|
+
- [Favicon](docs/components/favicon.md)
|
|
117
|
+
- [Head](docs/components/head.md)
|
|
118
|
+
- [Keywords](docs/components/keywords.md)
|
|
119
|
+
- [LanguageAlternates](docs/components/language-alternates.md)
|
|
120
|
+
- [OpenGraph](docs/components/open-graph.md)
|
|
121
|
+
- [Robots](docs/components/robots.md)
|
|
122
|
+
- [Schema](docs/components/schema.md)
|
|
123
|
+
- [Title](docs/components/title.md)
|
|
124
|
+
- [Twitter](docs/components/twitter.md)
|
|
465
125
|
|
|
466
126
|
## Contributing
|
|
467
127
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
### Project structure
|
|
471
|
-
|
|
472
|
-
```
|
|
473
|
-
astro-metadata/
|
|
474
|
-
src/
|
|
475
|
-
components/
|
|
476
|
-
lib/
|
|
477
|
-
tests/
|
|
478
|
-
e2e/
|
|
479
|
-
components/
|
|
480
|
-
fixtures/
|
|
481
|
-
unit/
|
|
482
|
-
metadata.test.ts
|
|
483
|
-
scripts/
|
|
484
|
-
init.sh
|
|
485
|
-
index.ts
|
|
486
|
-
playwright.config.ts
|
|
487
|
-
vitest.config.ts
|
|
488
|
-
biome.json
|
|
489
|
-
prettier.config.ts
|
|
490
|
-
```
|
|
128
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and contribution guidelines.
|
|
491
129
|
|
|
492
130
|
## License
|
|
493
131
|
|
package/package.json
CHANGED
|
@@ -10,21 +10,23 @@ import Twitter from "./Twitter.astro"
|
|
|
10
10
|
import Favicon from "./Favicon.astro"
|
|
11
11
|
import Schema from "./Schema.astro"
|
|
12
12
|
import LanguageAlternates from "./LanguageAlternates.astro"
|
|
13
|
+
import type { OpenGraphImage } from "./OpenGraph.astro"
|
|
13
14
|
|
|
14
15
|
export type Props = {
|
|
15
16
|
title: string
|
|
16
17
|
titleTemplate?: `${string}%s${string}`
|
|
17
|
-
description?: string
|
|
18
|
-
canonical?: string
|
|
19
|
-
keywords?: string[]
|
|
18
|
+
description?: string | false
|
|
19
|
+
canonical?: string | false
|
|
20
|
+
keywords?: string[] | false
|
|
20
21
|
charset?: string
|
|
21
22
|
viewport?: string
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
image?: OpenGraphImage | false
|
|
24
|
+
robots?: ComponentProps<typeof Robots> | false
|
|
25
|
+
openGraph?: ComponentProps<typeof OpenGraph> | false
|
|
26
|
+
twitter?: ComponentProps<typeof Twitter> | false
|
|
27
|
+
favicon?: ComponentProps<typeof Favicon> | false
|
|
28
|
+
schema?: ComponentProps<typeof Schema> | false
|
|
29
|
+
languageAlternates?: ComponentProps<typeof LanguageAlternates>["alternates"] | false
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
const {
|
|
@@ -35,6 +37,7 @@ const {
|
|
|
35
37
|
keywords,
|
|
36
38
|
charset = "UTF-8",
|
|
37
39
|
viewport = "width=device-width, initial-scale=1.0",
|
|
40
|
+
image,
|
|
38
41
|
robots,
|
|
39
42
|
openGraph,
|
|
40
43
|
twitter,
|
|
@@ -42,6 +45,32 @@ const {
|
|
|
42
45
|
schema,
|
|
43
46
|
languageAlternates,
|
|
44
47
|
} = Astro.props
|
|
48
|
+
|
|
49
|
+
const enabled = <T,>(value: T | false | undefined): value is T => {
|
|
50
|
+
return value !== false && value !== undefined
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const og =
|
|
54
|
+
openGraph !== false
|
|
55
|
+
? {
|
|
56
|
+
title,
|
|
57
|
+
description: enabled(description) ? description : undefined,
|
|
58
|
+
image: enabled(image) ? image : undefined,
|
|
59
|
+
url: enabled(canonical) ? canonical : undefined,
|
|
60
|
+
...openGraph,
|
|
61
|
+
}
|
|
62
|
+
: false
|
|
63
|
+
|
|
64
|
+
const tw =
|
|
65
|
+
twitter !== false
|
|
66
|
+
? {
|
|
67
|
+
title,
|
|
68
|
+
description: enabled(description) ? description : undefined,
|
|
69
|
+
image: enabled(image) ? image : undefined,
|
|
70
|
+
url: enabled(canonical) ? canonical : undefined,
|
|
71
|
+
...twitter,
|
|
72
|
+
}
|
|
73
|
+
: false
|
|
45
74
|
---
|
|
46
75
|
|
|
47
76
|
<head>
|
|
@@ -51,53 +80,16 @@ const {
|
|
|
51
80
|
<meta name="viewport" content={viewport} />
|
|
52
81
|
|
|
53
82
|
<Title value={title} template={titleTemplate} />
|
|
54
|
-
<Description value={description} />
|
|
55
|
-
<Canonical value={canonical} />
|
|
56
|
-
<Keywords value={keywords} />
|
|
57
|
-
<Robots
|
|
58
|
-
index={robots?.index}
|
|
59
|
-
follow={robots?.follow}
|
|
60
|
-
archive={robots?.archive}
|
|
61
|
-
snippet={robots?.snippet}
|
|
62
|
-
extra={robots?.extra}
|
|
63
|
-
/>
|
|
64
|
-
|
|
65
|
-
{languageAlternates && <LanguageAlternates alternates={languageAlternates} />}
|
|
66
|
-
|
|
67
|
-
{
|
|
68
|
-
openGraph && (
|
|
69
|
-
<OpenGraph
|
|
70
|
-
title={openGraph.title ?? title}
|
|
71
|
-
description={openGraph.description ?? description}
|
|
72
|
-
url={openGraph.url ?? canonical}
|
|
73
|
-
type={openGraph.type}
|
|
74
|
-
siteName={openGraph.siteName}
|
|
75
|
-
locale={openGraph.locale}
|
|
76
|
-
localeAlternate={openGraph.localeAlternate}
|
|
77
|
-
image={openGraph.image}
|
|
78
|
-
video={openGraph.video}
|
|
79
|
-
audio={openGraph.audio}
|
|
80
|
-
/>
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
twitter && (
|
|
86
|
-
<Twitter
|
|
87
|
-
title={twitter.title ?? title}
|
|
88
|
-
description={twitter.description ?? description}
|
|
89
|
-
image={twitter.image}
|
|
90
|
-
card={twitter.card}
|
|
91
|
-
site={twitter.site}
|
|
92
|
-
creator={twitter.creator}
|
|
93
|
-
url={twitter.url ?? canonical}
|
|
94
|
-
/>
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
{favicon && <Favicon icons={favicon.icons} manifest={favicon.manifest} sort={favicon.sort} />}
|
|
99
83
|
|
|
100
|
-
{
|
|
84
|
+
{enabled(description) && <Description value={description} />}
|
|
85
|
+
{enabled(canonical) && <Canonical value={canonical} />}
|
|
86
|
+
{enabled(keywords) && <Keywords value={keywords} />}
|
|
87
|
+
{enabled(robots) && <Robots {...robots} />}
|
|
88
|
+
{enabled(og) && <OpenGraph {...og} />}
|
|
89
|
+
{enabled(tw) && <Twitter {...tw} />}
|
|
90
|
+
{enabled(favicon) && <Favicon {...favicon} />}
|
|
91
|
+
{enabled(schema) && <Schema {...schema} />}
|
|
92
|
+
{enabled(languageAlternates) && <LanguageAlternates alternates={languageAlternates} />}
|
|
101
93
|
|
|
102
94
|
<slot />
|
|
103
95
|
</head>
|