@mannisto/astro-metadata 1.0.2 → 2.0.0-alpha.1
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 +427 -82
- package/package.json +25 -14
- package/src/assets/icons/LICENSE.md +35 -0
- package/src/assets/icons/binoculars.svg +1 -0
- package/src/assets/icons/caret-right.svg +1 -0
- package/src/assets/icons/check.svg +1 -0
- package/src/assets/icons/copy.svg +1 -0
- package/src/assets/icons/seal-check-duotone.svg +1 -0
- package/src/assets/icons/seal-question-duotone.svg +1 -0
- package/src/assets/icons/warning-circle-duotone.svg +1 -0
- package/src/assets/icons/warning-duotone.svg +1 -0
- package/src/assets/icons/x.svg +1 -0
- package/src/components/Metadata.astro +145 -0
- package/src/components/index.ts +2 -0
- package/src/integration.d.ts +112 -0
- package/src/integration.js +508 -0
- package/src/lib/checks.d.ts +70 -0
- package/src/lib/checks.js +193 -0
- package/src/lib/config.ts +111 -0
- package/src/lib/favicon-files.d.ts +29 -0
- package/src/lib/favicon-files.js +103 -0
- package/src/lib/favicon-image.ts +124 -0
- package/src/lib/favicon.ts +167 -0
- package/src/lib/head.d.ts +25 -0
- package/src/lib/head.js +141 -0
- package/src/lib/manifest.ts +126 -0
- package/src/lib/rules.d.ts +73 -0
- package/src/lib/rules.js +902 -0
- package/src/lib/schema.ts +208 -0
- package/src/lib/site.ts +72 -0
- package/src/lib/url.ts +79 -0
- package/src/routes/favicon.ts +14 -0
- package/src/routes/manifest.ts +18 -0
- package/src/runtime.ts +16 -0
- package/src/toolbar/app.ts +72 -0
- package/src/toolbar/assets.ts +81 -0
- package/src/toolbar/data.ts +282 -0
- package/src/toolbar/define.ts +7 -0
- package/src/toolbar/env.d.ts +10 -0
- package/src/toolbar/highlight.ts +83 -0
- package/src/toolbar/styles/shared.css +18 -0
- package/src/toolbar/styles/tokens.css +182 -0
- package/src/toolbar/toolbar-badge.ts +78 -0
- package/src/toolbar/ui/alert/alert.css +126 -0
- package/src/toolbar/ui/alert/alert.ts +102 -0
- package/src/toolbar/ui/badge/badge.css +41 -0
- package/src/toolbar/ui/badge/badge.ts +24 -0
- package/src/toolbar/ui/code-block/code-block.css +90 -0
- package/src/toolbar/ui/code-block/code-block.ts +68 -0
- package/src/toolbar/ui/color-swatch/color-swatch.css +15 -0
- package/src/toolbar/ui/color-swatch/color-swatch.ts +31 -0
- package/src/toolbar/ui/icon/icon.css +11 -0
- package/src/toolbar/ui/icon/icon.ts +46 -0
- package/src/toolbar/ui/image/image.css +18 -0
- package/src/toolbar/ui/image/image.ts +58 -0
- package/src/toolbar/ui/inline-code/inline-code.css +16 -0
- package/src/toolbar/ui/inline-code/inline-code.ts +41 -0
- package/src/toolbar/ui/section/section.css +88 -0
- package/src/toolbar/ui/section/section.ts +78 -0
- package/src/toolbar/ui/tab/tab.css +7 -0
- package/src/toolbar/ui/tab/tab.ts +47 -0
- package/src/toolbar/ui/tab-group/tab-group.css +42 -0
- package/src/toolbar/ui/tab-group/tab-group.ts +101 -0
- package/src/toolbar/ui/table/table.css +124 -0
- package/src/toolbar/ui/table/table.ts +155 -0
- package/src/toolbar/views/code-blocks.ts +35 -0
- package/src/toolbar/views/metadata-panel.css +101 -0
- package/src/toolbar/views/metadata-panel.ts +125 -0
- package/src/toolbar/views/overview-view.css +27 -0
- package/src/toolbar/views/overview-view.ts +401 -0
- package/src/toolbar/views/previews/discord.css +70 -0
- package/src/toolbar/views/previews/discord.ts +60 -0
- package/src/toolbar/views/previews/facebook.css +65 -0
- package/src/toolbar/views/previews/facebook.ts +36 -0
- package/src/toolbar/views/previews/google.css +75 -0
- package/src/toolbar/views/previews/google.ts +36 -0
- package/src/toolbar/views/previews/linkedin.css +58 -0
- package/src/toolbar/views/previews/linkedin.ts +34 -0
- package/src/toolbar/views/previews/page.ts +129 -0
- package/src/toolbar/views/previews/platform.ts +14 -0
- package/src/toolbar/views/previews/previews-view.css +37 -0
- package/src/toolbar/views/previews/previews-view.ts +52 -0
- package/src/toolbar/views/previews/slack.css +61 -0
- package/src/toolbar/views/previews/slack.ts +47 -0
- package/src/toolbar/views/previews/twitter.css +89 -0
- package/src/toolbar/views/previews/twitter.ts +57 -0
- package/src/toolbar/views/previews/whatsapp.css +88 -0
- package/src/toolbar/views/previews/whatsapp.ts +65 -0
- package/src/toolbar/views/raw-view.ts +41 -0
- package/index.ts +0 -31
- package/src/components/Canonical.astro +0 -11
- package/src/components/Description.astro +0 -9
- package/src/components/Favicon.astro +0 -96
- package/src/components/Head.astro +0 -94
- package/src/components/Keywords.astro +0 -9
- package/src/components/LanguageAlternates.astro +0 -18
- package/src/components/OpenGraph.astro +0 -75
- package/src/components/Robots.astro +0 -25
- package/src/components/Schema.astro +0 -18
- package/src/components/Title.astro +0 -13
- package/src/components/Twitter.astro +0 -27
- package/src/lib/metadata.ts +0 -77
package/README.md
CHANGED
|
@@ -2,131 +2,476 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-

|
|
6
|
+

|
|
7
|
+

|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The page metadata for Astro: the title, the description, the canonical URL, social cards, favicons, the web app manifest and structured data. Set the site values one time, and give each page only its own values.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
|
|
11
|
+
- [Install](#install)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
- [Metadata component](#metadata-component)
|
|
14
|
+
- [Favicons](#favicons)
|
|
15
|
+
- [Web app manifest](#web-app-manifest)
|
|
16
|
+
- [Structured data](#structured-data)
|
|
17
|
+
- [Checks](#checks)
|
|
18
|
+
- [API](#api)
|
|
19
|
+
- [Good to know](#good-to-know)
|
|
20
|
+
- [Migrate from v1](#migrate-from-v1)
|
|
21
|
+
- [Contributing](#contributing)
|
|
22
|
+
- [License](#license)
|
|
23
|
+
|
|
24
|
+
## Install
|
|
15
25
|
|
|
16
|
-
|
|
26
|
+
```bash
|
|
17
27
|
npm install @mannisto/astro-metadata
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The integration requires Astro 6.4.6 or later, or Astro 7.
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// astro.config.ts
|
|
36
|
+
import metadata from "@mannisto/astro-metadata"
|
|
37
|
+
import { defineConfig } from "astro/config"
|
|
38
|
+
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
site: "https://example.com",
|
|
41
|
+
integrations: [
|
|
42
|
+
metadata({
|
|
43
|
+
siteName: "Acme Studio",
|
|
44
|
+
titleTemplate: "%s | Acme Studio",
|
|
45
|
+
description: "Tools for small teams.",
|
|
46
|
+
image: { url: "/og.jpg", alt: "Acme Studio", width: 1200, height: 630 },
|
|
47
|
+
twitter: { site: "@acmestudio" },
|
|
48
|
+
themeColor: "#111111",
|
|
49
|
+
feeds: [{ href: "/rss.xml", title: "News" }],
|
|
50
|
+
favicon: { source: "./src/assets/logo.svg" },
|
|
51
|
+
manifest: { name: "Acme Studio" },
|
|
52
|
+
}),
|
|
53
|
+
],
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Set `site` in the Astro config. The canonical URL and the social images must be absolute URLs, and the package uses `site` to make them.
|
|
58
|
+
|
|
59
|
+
| Option | Default | Description |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `siteName` | — | The name of the site: the title of a page without a title, and `og:site_name`. |
|
|
62
|
+
| `titleTemplate` | — | The title template, for example `"%s \| My Site"`. `%s` is the page title. |
|
|
63
|
+
| `description` | — | The description of a page without a description. |
|
|
64
|
+
| `image` | — | The social image of a page without an image: `url`, `alt`, `width` and `height`. |
|
|
65
|
+
| `twitter` | — | `site`: the Twitter (X) handle of the site. `card`: `"summary"` or `"summary_large_image"` (default) for pages with an image. |
|
|
66
|
+
| `themeColor` | — | The color of the browser interface. Give one color, or `{ light, dark }`. |
|
|
67
|
+
| `feeds` | — | The RSS, Atom or JSON feeds: `href`, `title` and `type` (`"rss"` by default). |
|
|
68
|
+
| `robots` | — | `index: false` or `follow: false` for all pages, for example on a staging site. `extra`: other directives, for example `"max-image-preview:large"`. |
|
|
69
|
+
| `favicon` | — | Generates the favicons from one image. See [Favicons](#favicons). |
|
|
70
|
+
| `manifest` | — | Generates the web app manifest, or links your own. See [Web app manifest](#web-app-manifest). |
|
|
71
|
+
| `debug` | `true` | The Metadata app in the dev toolbar (`client`) and the checks in the build log (`build`). Set `false` to turn off both, or `{ client: false }` or `{ build: false }` to turn off one. See [Checks](#checks). |
|
|
72
|
+
| `rules` | — | `ignore`: the ids of the check rules to turn off. `custom`: your own rules. See [Checks](#checks). |
|
|
73
|
+
| `pages` | — | `ignore`: the pages without checks, as path patterns, for example `["/404", "/drafts/**"]`. See [Checks](#checks). |
|
|
18
74
|
|
|
19
|
-
|
|
20
|
-
|
|
75
|
+
### Translations
|
|
76
|
+
|
|
77
|
+
Each text can be one string, or an object with one string for each locale:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
metadata({
|
|
81
|
+
siteName: { en: "Acme Studio", fi: "Acme Studio Suomi" },
|
|
82
|
+
titleTemplate: { en: "%s | Acme Studio", fi: "%s | Acme Studio Suomi" },
|
|
83
|
+
})
|
|
21
84
|
```
|
|
22
85
|
|
|
23
|
-
|
|
86
|
+
`<Metadata>` uses the text of `Astro.currentLocale`, or of its `locale` prop. A locale without a text uses the first text.
|
|
24
87
|
|
|
25
|
-
|
|
88
|
+
## Metadata component
|
|
26
89
|
|
|
27
|
-
|
|
90
|
+
Put `<Metadata>` into the `<head>` of your layout, and give it the values of the page:
|
|
28
91
|
|
|
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.
|
|
30
92
|
```astro
|
|
31
93
|
---
|
|
32
|
-
|
|
94
|
+
// src/layouts/Layout.astro
|
|
95
|
+
import { Metadata, type MetadataProps } from "@mannisto/astro-metadata/components"
|
|
96
|
+
|
|
97
|
+
type Props = MetadataProps
|
|
33
98
|
---
|
|
34
99
|
|
|
35
|
-
<html>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
description="Welcome to my site"
|
|
40
|
-
image={{ url: "/og.jpg", alt: "My Site", width: 1200, height: 630 }}
|
|
41
|
-
/>
|
|
100
|
+
<html lang="en">
|
|
101
|
+
<head>
|
|
102
|
+
<Metadata {...Astro.props} />
|
|
103
|
+
</head>
|
|
42
104
|
<body>
|
|
43
105
|
<slot />
|
|
44
106
|
</body>
|
|
45
107
|
</html>
|
|
46
108
|
```
|
|
47
109
|
|
|
48
|
-
[Read more →](docs/usage/head.md)
|
|
49
|
-
|
|
50
|
-
### Metadata API
|
|
51
|
-
|
|
52
|
-
Set metadata in pages, resolve in layouts — no prop drilling.
|
|
53
110
|
```astro
|
|
54
111
|
---
|
|
55
|
-
// pages/about.astro
|
|
56
|
-
import
|
|
112
|
+
// src/pages/about.astro
|
|
113
|
+
import Layout from "../layouts/Layout.astro"
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
<Layout title="About" description="The story of Acme Studio.">
|
|
117
|
+
<h1>About</h1>
|
|
118
|
+
</Layout>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The page values override the site values. For example, `title="About"` gives:
|
|
57
122
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
123
|
+
```html
|
|
124
|
+
<meta charset="UTF-8">
|
|
125
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
126
|
+
<title>About | Acme Studio</title>
|
|
127
|
+
<meta name="description" content="The story of Acme Studio.">
|
|
128
|
+
<link rel="canonical" href="https://example.com/about/">
|
|
129
|
+
<meta property="og:title" content="About">
|
|
130
|
+
<meta property="og:description" content="The story of Acme Studio.">
|
|
131
|
+
<meta property="og:url" content="https://example.com/about/">
|
|
132
|
+
<meta property="og:type" content="website">
|
|
133
|
+
<meta property="og:site_name" content="Acme Studio">
|
|
134
|
+
<meta property="og:image" content="https://example.com/og.jpg">
|
|
135
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
136
|
+
<meta name="twitter:site" content="@acmestudio">
|
|
137
|
+
...
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
| Prop | Default | Description |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `title` | `siteName` | The page title. The title template adds the site name. |
|
|
143
|
+
| `description` | `description` | The page description. |
|
|
144
|
+
| `image` | `image` | The social image: `url`, `alt`, `width` and `height`. |
|
|
145
|
+
| `type` | `"website"` | The Open Graph type, for example `"article"`. |
|
|
146
|
+
| `author` | — | The Twitter (X) handle of the author, for example `"@acmewriter"`. |
|
|
147
|
+
| `canonical` | The current page | The canonical path or URL. |
|
|
148
|
+
| `index` | `true` | `false` keeps the page out of search results. |
|
|
149
|
+
| `follow` | `true` | `false` tells search engines not to follow the links. |
|
|
150
|
+
| `hreflang` | — | The hreflang links: the page in each locale, and `x-default`: `{ hreflang, href }[]`. |
|
|
151
|
+
| `schema` | — | Structured data. See [Structured data](#structured-data). |
|
|
152
|
+
| `locale` | `Astro.currentLocale` | The locale of the translated site values. |
|
|
153
|
+
|
|
154
|
+
A tag is rendered only when it has a value. For example, the robots tag is necessary only with `index` or `follow` set to `false`.
|
|
155
|
+
|
|
156
|
+
## Favicons
|
|
157
|
+
|
|
158
|
+
Give one square source image. An SVG gives the best result. Use a PNG, WebP or JPEG of at least 512 × 512 px, or the large icons become blurred.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
metadata({
|
|
162
|
+
favicon: {
|
|
163
|
+
source: "./src/assets/logo.svg",
|
|
164
|
+
background: "#ffffff",
|
|
165
|
+
},
|
|
61
166
|
})
|
|
62
|
-
---
|
|
63
167
|
```
|
|
64
|
-
```astro
|
|
65
|
-
---
|
|
66
|
-
// layouts/Layout.astro
|
|
67
|
-
import { Head, Metadata } from "@mannisto/astro-metadata"
|
|
68
168
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
169
|
+
The integration generates these files, and `<Metadata>` renders their tags in the recommended order:
|
|
170
|
+
|
|
171
|
+
| File | Use |
|
|
172
|
+
|---|---|
|
|
173
|
+
| `/favicon.ico` | 16 and 32 px, for old browsers and tools |
|
|
174
|
+
| `/favicon.svg` | Modern browsers. Only for an SVG source. |
|
|
175
|
+
| `/favicon-16.png`, `/favicon-32.png` | Browser tabs |
|
|
176
|
+
| `/apple-touch-icon.png` | 180 px, for the iOS home screen |
|
|
177
|
+
| `/favicon-192.png`, `/favicon-512.png` | The web app manifest |
|
|
178
|
+
|
|
179
|
+
iOS shows transparent pixels as black, so the Apple icon shows the logo on the `background` color (white by default).
|
|
180
|
+
|
|
181
|
+
To generate other files, give a `files` list. It replaces the default files:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
favicon: {
|
|
185
|
+
source: "./src/assets/logo.svg",
|
|
186
|
+
files: [
|
|
187
|
+
{ path: "/favicon.ico", sizes: [16, 32, 48] },
|
|
188
|
+
{ path: "/favicon.svg" },
|
|
189
|
+
{ path: "/apple-touch-icon.png", size: 180 },
|
|
190
|
+
{ path: "/favicon-96.png", size: 96 },
|
|
191
|
+
{ path: "/favicon-512.png", size: 512 },
|
|
192
|
+
],
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The extension sets the format: `.ico`, `.png` or `.svg`. The name and the size set the use:
|
|
197
|
+
|
|
198
|
+
- `.ico` and `.svg` files, and `.png` files smaller than 192 px, get an icon link tag.
|
|
199
|
+
- `apple-touch-icon.png` gets the Apple link tag.
|
|
200
|
+
- `.png` files of 192 px or larger go into the web app manifest.
|
|
201
|
+
|
|
202
|
+
Keep the names `favicon.ico` and `apple-touch-icon.png`. Browsers and iOS ask for these URLs directly.
|
|
203
|
+
|
|
204
|
+
## Web app manifest
|
|
205
|
+
|
|
206
|
+
Give the manifest as an object, and the integration serves it at `/manifest.webmanifest` with the generated icons:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
metadata({
|
|
210
|
+
manifest: {
|
|
211
|
+
name: "Acme Studio",
|
|
212
|
+
shortName: "Acme",
|
|
213
|
+
backgroundColor: "#ffffff",
|
|
214
|
+
},
|
|
72
215
|
})
|
|
73
|
-
|
|
216
|
+
```
|
|
74
217
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
218
|
+
| Value | Default | Description |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| `name` | — | The name of the app. |
|
|
221
|
+
| `shortName` | — | The name on the home screen. |
|
|
222
|
+
| `description` | — | The description of the app. |
|
|
223
|
+
| `startUrl` | `"/"` | The page that the installed app opens. |
|
|
224
|
+
| `display` | `"standalone"` | `"standalone"`, `"fullscreen"`, `"minimal-ui"` or `"browser"`. |
|
|
225
|
+
| `themeColor` | `themeColor` | The color of the app interface. |
|
|
226
|
+
| `backgroundColor` | — | The color of the start screen. |
|
|
227
|
+
| `extra` | — | Other manifest fields, copied into the file unchanged. |
|
|
228
|
+
|
|
229
|
+
With translated texts, each locale gets its own manifest: the first locale at `/manifest.webmanifest`, the others at `/fi/manifest.webmanifest` and so on.
|
|
230
|
+
|
|
231
|
+
To use your own manifest, give its path. The integration then only renders the link tag:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
metadata({
|
|
235
|
+
manifest: "/site.webmanifest",
|
|
236
|
+
})
|
|
81
237
|
```
|
|
82
238
|
|
|
83
|
-
|
|
239
|
+
Your own manifest endpoint can use the generated icons with `Favicon.icons()`.
|
|
240
|
+
|
|
241
|
+
## Structured data
|
|
84
242
|
|
|
85
|
-
|
|
243
|
+
The `Schema` builders make the most common JSON-LD types. They fill in the site name and make all URLs absolute.
|
|
86
244
|
|
|
87
|
-
Use components directly in your `<head>` for full control.
|
|
88
245
|
```astro
|
|
89
246
|
---
|
|
90
|
-
import {
|
|
247
|
+
import { Metadata } from "@mannisto/astro-metadata/components"
|
|
248
|
+
import { Schema } from "@mannisto/astro-metadata/runtime"
|
|
91
249
|
---
|
|
92
250
|
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
251
|
+
<Metadata
|
|
252
|
+
title="Hello"
|
|
253
|
+
type="article"
|
|
254
|
+
schema={[
|
|
255
|
+
Schema.article({
|
|
256
|
+
title: "Hello",
|
|
257
|
+
published: new Date("2026-09-01"),
|
|
258
|
+
author: { name: "Acme Writer", url: "/about" },
|
|
259
|
+
url: "/blog/hello",
|
|
260
|
+
}),
|
|
261
|
+
Schema.breadcrumbs([
|
|
262
|
+
{ name: "Home", url: "/" },
|
|
263
|
+
{ name: "Blog", url: "/blog" },
|
|
264
|
+
]),
|
|
265
|
+
]}
|
|
266
|
+
/>
|
|
108
267
|
```
|
|
109
268
|
|
|
110
|
-
|
|
269
|
+
The `schema` prop also takes your own objects. The types come from [`schema-dts`](https://github.com/google/schema-dts), so your editor completes the schema.org fields.
|
|
270
|
+
|
|
271
|
+
## Checks
|
|
111
272
|
|
|
112
|
-
|
|
273
|
+
The integration checks the metadata of each page. Each problem has a level:
|
|
113
274
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
124
|
-
-
|
|
275
|
+
| Level | Meaning | Examples |
|
|
276
|
+
| --- | --- | --- |
|
|
277
|
+
| Error | The metadata is broken. | No title, placeholder text such as `undefined`, two canonical links, invalid JSON-LD, an `og:image` that does not load |
|
|
278
|
+
| Warning | Fix it for better results. | A long title, the same title on two pages, no `twitter:card`, an `og:image` over 600 KB, an hreflang page that does not link back |
|
|
279
|
+
| Info | Good to know. | `noindex`, a short description, no `og:image:alt` |
|
|
280
|
+
|
|
281
|
+
The checks look at three things:
|
|
282
|
+
|
|
283
|
+
- **The head of each page:** the tags, the structured data and the hreflang links. A page with `noindex` gets no search and sharing checks.
|
|
284
|
+
- **The files of each page:** the `og:image` and the favicons. The checks load them, and compare the real image size, format and file size with the tags. Files on other sites are not checked.
|
|
285
|
+
- **All pages together:** the same title or description on two pages, and hreflang links that do not link back. Only the build can compare the pages.
|
|
286
|
+
|
|
287
|
+
The results show in two places:
|
|
288
|
+
|
|
289
|
+
- **In dev:** Open the Metadata app in the Astro dev toolbar. It shows the problems of the current page. Click a problem to open the row of its tag. The app also shows the link previews of Google, Twitter (X), Facebook, LinkedIn, WhatsApp, Discord and Slack, and all metadata tags.
|
|
290
|
+
- **In the build:** The integration logs the errors and the warnings of the prerendered pages:
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
[WARN] [@mannisto/astro-metadata] /about/index.html
|
|
294
|
+
- The page has no description.
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Turn off rules
|
|
298
|
+
|
|
299
|
+
All rules and their ids are in [`src/lib/rules.js`](./src/lib/rules.js). Give the ids of the rules to turn off in `rules.ignore`. This applies to the dev toolbar app and to the build:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
metadata({
|
|
303
|
+
rules: {
|
|
304
|
+
ignore: ["description-short", "og-image-alt-missing"],
|
|
305
|
+
},
|
|
306
|
+
})
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Custom rules
|
|
310
|
+
|
|
311
|
+
Add your own rules in `rules.custom`. A rule has an `id`, a `level` (`"error"`, `"warning"` or `"info"`), a `message`, and a `check` function that returns `true` when the page has the problem:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
metadata({
|
|
315
|
+
rules: {
|
|
316
|
+
custom: [
|
|
317
|
+
{
|
|
318
|
+
id: "title-brand",
|
|
319
|
+
level: "warning",
|
|
320
|
+
message: "The title must contain the brand name.",
|
|
321
|
+
check(head) {
|
|
322
|
+
return !head.titles[0]?.includes("Acme Studio")
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
})
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The `head` has the metadata of the page:
|
|
331
|
+
|
|
332
|
+
| Value | Description |
|
|
333
|
+
|---|---|
|
|
334
|
+
| `titles` | The texts of all title tags. |
|
|
335
|
+
| `meta` | The first `content` of each meta tag, by its `name` or `property`, for example `head.meta["og:image"]`. |
|
|
336
|
+
| `metaCount` | The number of meta tags with each name. |
|
|
337
|
+
| `links` | The attributes of all link tags, for example `{ rel: "canonical", href: "…" }`. |
|
|
338
|
+
| `schemas` | The texts of the JSON-LD scripts. |
|
|
339
|
+
| `lang`, `charset` | The `lang` attribute of the html tag, and the charset. |
|
|
340
|
+
|
|
341
|
+
The rule can also have:
|
|
342
|
+
- `message` as a function, `(head) => string`, for a message with values of the page.
|
|
343
|
+
- `field`, the tag of the problem, for example `"og:title"`. A click on the problem in the dev toolbar app opens its row.
|
|
344
|
+
- `indexed: true`, to skip the rule on a noindex page.
|
|
345
|
+
|
|
346
|
+
The custom rules run in the build and in the dev toolbar app. In dev, the dev server runs them, because they are functions in the Astro config. Text between backticks in a message shows as code in the dev toolbar app. The `MetadataRule` type gives your editor completion.
|
|
347
|
+
|
|
348
|
+
### Ignore pages
|
|
349
|
+
|
|
350
|
+
Some pages do not need checks, for example a 404 page or the drafts. Give their paths in `pages.ignore`, without the base. In a pattern, `*` matches one part of the path, and `**` matches any number of parts:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
metadata({
|
|
354
|
+
pages: {
|
|
355
|
+
ignore: ["/404", "/drafts/**"],
|
|
356
|
+
},
|
|
357
|
+
})
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
The build does not check these pages, and does not compare them with the other pages. The dev toolbar app shows all values of these pages, but no problems.
|
|
361
|
+
|
|
362
|
+
Set `debug: { build: false }` to hide the build warnings, `debug: { client: false }` to remove the dev toolbar app, and `debug: false` to turn off both.
|
|
363
|
+
|
|
364
|
+
## API
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
import { Favicon, Manifest, Schema, Site } from "@mannisto/astro-metadata/runtime"
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
| Method | Returns | Description |
|
|
371
|
+
|---|---|---|
|
|
372
|
+
| `Site.config` | `MetadataConfig` | The site values of the integration |
|
|
373
|
+
| `Site.name("fi")` | `string` | The site name of a locale |
|
|
374
|
+
| `Site.title("About", "fi")` | `string` | The page title in the title template |
|
|
375
|
+
| `Site.url("/og.jpg")` | `string` | The absolute URL of a path |
|
|
376
|
+
| `Site.pageUrl("/about")` | `string` | The absolute URL of a page, with the trailing slash of Astro |
|
|
377
|
+
| `Favicon.config` | `FaviconConfig` | The favicon config, or `undefined` without the `favicon` option |
|
|
378
|
+
| `Favicon.files` | `ResolvedFaviconFile[]` | The generated files |
|
|
379
|
+
| `Favicon.links()` | `FaviconLink[]` | The link tags of the generated files |
|
|
380
|
+
| `Favicon.icons()` | `ManifestIcon[]` | The icons for a web app manifest |
|
|
381
|
+
| `Favicon.url("/favicon.ico")` | `string` | The URL of a file, with the base and a hash |
|
|
382
|
+
| `Favicon.generate("/favicon-32.png")` | `Promise<Response>` | Generates a file of the config |
|
|
383
|
+
| `Manifest.config` | `ManifestConfig \| string` | The `manifest` option |
|
|
384
|
+
| `Manifest.url("fi")` | `string` | The URL of the manifest of a locale |
|
|
385
|
+
| `Manifest.build("fi")` | `object` | The manifest of a locale |
|
|
386
|
+
| `Manifest.paths()` | `ManifestPath[]` | The generated manifest files |
|
|
387
|
+
| `Schema.website()` | `WebSite` | The website, with the site name and URL |
|
|
388
|
+
| `Schema.organization()` | `Organization` | The organization: `name`, `url`, `logo`, `sameAs` |
|
|
389
|
+
| `Schema.article()` | `Article` | An article: `title`, `description`, `image`, `published`, `modified`, `author`, `url`, `type` |
|
|
390
|
+
| `Schema.breadcrumbs()` | `BreadcrumbList` | The path to the current page: `{ name, url }[]` |
|
|
391
|
+
| `Schema.stringify(data)` | `string` | JSON that is safe inside a script tag |
|
|
392
|
+
|
|
393
|
+
| Component | Props |
|
|
394
|
+
|---|---|
|
|
395
|
+
| `<Metadata>` | See [Metadata component](#metadata-component) |
|
|
396
|
+
|
|
397
|
+
Import the types from `@mannisto/astro-metadata/runtime`, for example `import type { MetadataImage } from "@mannisto/astro-metadata/runtime"`.
|
|
398
|
+
|
|
399
|
+
## Good to know
|
|
400
|
+
|
|
401
|
+
- **CMS data.** Give CMS values to `<Metadata>` as props. The site values of the integration are fixed at startup, and a change restarts the dev server.
|
|
402
|
+
- **Trailing slash.** The canonical URL and the `hreflang` links follow the `trailingSlash` setting of Astro. With `"ignore"`, `build.format` decides.
|
|
403
|
+
- **`sharp`.** The favicons use the `sharp` package that Astro installs. If you install Astro without its optional dependencies, install `sharp` yourself.
|
|
404
|
+
- **Files in `public/`.** A file in `public/` with the same path as a generated favicon replaces the generated file. The integration shows a warning.
|
|
405
|
+
- **With @mannisto/astro-i18n.** Give the locale and the hreflang links to `<Metadata>`:
|
|
406
|
+
|
|
407
|
+
```astro
|
|
408
|
+
---
|
|
409
|
+
import { Locale } from "@mannisto/astro-i18n/runtime"
|
|
410
|
+
import { Metadata } from "@mannisto/astro-metadata/components"
|
|
411
|
+
|
|
412
|
+
const { code } = Locale.use(Astro)
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
<Metadata locale={code} hreflang={Locale.hreflang(Astro.url, Astro.site!)} />
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Migrate from v1
|
|
419
|
+
|
|
420
|
+
Version 2 is a new API. The main changes:
|
|
421
|
+
|
|
422
|
+
### Move the site values into the integration
|
|
423
|
+
|
|
424
|
+
```diff
|
|
425
|
+
- <Head title="About" titleTemplate="%s | My Site" description="..." />
|
|
426
|
+
+ metadata({ titleTemplate: "%s | My Site", description: "..." })
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Use `<Metadata>` inside your own `<head>`
|
|
430
|
+
|
|
431
|
+
`<Head>` rendered the `<head>` element. `<Metadata>` renders only the tags.
|
|
432
|
+
|
|
433
|
+
```diff
|
|
434
|
+
- import { Head } from "@mannisto/astro-metadata"
|
|
435
|
+
+ import { Metadata } from "@mannisto/astro-metadata/components"
|
|
436
|
+
|
|
437
|
+
- <Head title="About" />
|
|
438
|
+
+ <head>
|
|
439
|
+
+ <Metadata title="About" />
|
|
440
|
+
+ </head>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Replace the removed components
|
|
444
|
+
|
|
445
|
+
`Title`, `Description`, `Canonical`, `Robots`, `OpenGraph`, `Twitter`, `Favicon`, `LanguageAlternates`, `Keywords` and `Schema` are removed. `<Metadata>` renders their tags.
|
|
446
|
+
|
|
447
|
+
```diff
|
|
448
|
+
- <LanguageAlternates alternates={links} />
|
|
449
|
+
- <Schema schema={data} />
|
|
450
|
+
+ <Metadata hreflang={links} schema={data} />
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Replace the `Metadata` store
|
|
454
|
+
|
|
455
|
+
`Metadata.set()` and `Metadata.resolve()` are removed. Give the values as props to your layout.
|
|
456
|
+
|
|
457
|
+
### Replace the robots object and the `false` props
|
|
458
|
+
|
|
459
|
+
```diff
|
|
460
|
+
- <Head robots={{ index: false }} twitter={false} />
|
|
461
|
+
+ <Metadata index={false} />
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Generate the favicons
|
|
465
|
+
|
|
466
|
+
```diff
|
|
467
|
+
- <Head favicon={{ icons: [{ path: "/favicon.ico" }] }} />
|
|
468
|
+
+ metadata({ favicon: { source: "./src/assets/logo.svg" } })
|
|
469
|
+
```
|
|
125
470
|
|
|
126
471
|
## Contributing
|
|
127
472
|
|
|
128
|
-
|
|
473
|
+
Read [CONTRIBUTING.md](./CONTRIBUTING.md) for the setup and the test commands. Report bugs and ideas in [GitHub issues](https://github.com/eremannisto/astro-metadata/issues).
|
|
129
474
|
|
|
130
475
|
## License
|
|
131
476
|
|
|
132
|
-
MIT © [Ere Männistö](https://github.com/eremannisto)
|
|
477
|
+
MIT © [Ere Männistö](https://github.com/eremannisto)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mannisto/astro-metadata",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0-alpha.1",
|
|
4
|
+
"description": "Metadata for Astro: page tags, social cards, favicons, the web app manifest and structured data.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/eremannisto/astro-metadata#readme",
|
|
@@ -18,10 +18,16 @@
|
|
|
18
18
|
"seo",
|
|
19
19
|
"metadata"
|
|
20
20
|
],
|
|
21
|
-
"exports":
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./src/integration.d.ts",
|
|
24
|
+
"default": "./src/integration.js"
|
|
25
|
+
},
|
|
26
|
+
"./runtime": "./src/runtime.ts",
|
|
27
|
+
"./components": "./src/components/index.ts"
|
|
28
|
+
},
|
|
22
29
|
"files": [
|
|
23
|
-
"src"
|
|
24
|
-
"index.ts"
|
|
30
|
+
"src"
|
|
25
31
|
],
|
|
26
32
|
"scripts": {
|
|
27
33
|
"lint": "eslint . --fix",
|
|
@@ -33,21 +39,26 @@
|
|
|
33
39
|
"test:fixtures": "pnpm --filter \"./tests/e2e/fixtures/**\" exec astro build"
|
|
34
40
|
},
|
|
35
41
|
"peerDependencies": {
|
|
36
|
-
"astro": "^4.
|
|
42
|
+
"astro": "^6.4.6 || ^7.0.0"
|
|
37
43
|
},
|
|
38
44
|
"devDependencies": {
|
|
39
45
|
"@eslint/js": "^10.0.1",
|
|
40
46
|
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
|
|
41
|
-
"@playwright/test": "^1.
|
|
42
|
-
"@types/node": "^22.
|
|
43
|
-
"astro": "^
|
|
44
|
-
"eslint": "^10.0
|
|
47
|
+
"@playwright/test": "^1.63.0",
|
|
48
|
+
"@types/node": "^22.20.5",
|
|
49
|
+
"astro": "^7.3.5",
|
|
50
|
+
"eslint": "^10.11.0",
|
|
45
51
|
"eslint-config-prettier": "^10.1.8",
|
|
46
|
-
"prettier": "^3.
|
|
52
|
+
"prettier": "^3.9.9",
|
|
47
53
|
"prettier-plugin-astro": "^0.14.1",
|
|
54
|
+
"sharp": "^0.35.5",
|
|
48
55
|
"typescript": "^5.9.3",
|
|
49
|
-
"typescript-eslint": "^8.
|
|
50
|
-
"vitest": "^4.1.
|
|
56
|
+
"typescript-eslint": "^8.71.0",
|
|
57
|
+
"vitest": "^4.1.11"
|
|
51
58
|
},
|
|
52
|
-
"packageManager": "pnpm@10.32.1"
|
|
59
|
+
"packageManager": "pnpm@10.32.1",
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"lit": "^3.3.3",
|
|
62
|
+
"schema-dts": "^2.0.0"
|
|
63
|
+
}
|
|
53
64
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
The icons in this folder are from [Phosphor Icons](https://phosphoricons.com).
|
|
2
|
+
|
|
3
|
+
| File | Phosphor icon | Weight |
|
|
4
|
+
| ---------------------------- | ---------------- | ------- |
|
|
5
|
+
| `binoculars.svg` | `binoculars` | Bold |
|
|
6
|
+
| `caret-right.svg` | `caret-right` | Bold |
|
|
7
|
+
| `check.svg` | `check` | Bold |
|
|
8
|
+
| `copy.svg` | `copy` | Bold |
|
|
9
|
+
| `x.svg` | `x` | Bold |
|
|
10
|
+
| `seal-check-duotone.svg` | `seal-check` | Duotone |
|
|
11
|
+
| `seal-question-duotone.svg` | `seal-question` | Duotone |
|
|
12
|
+
| `warning-circle-duotone.svg` | `warning-circle` | Duotone |
|
|
13
|
+
| `warning-duotone.svg` | `warning` | Duotone |
|
|
14
|
+
|
|
15
|
+
MIT License
|
|
16
|
+
|
|
17
|
+
Copyright (c) 2023 Phosphor Icons
|
|
18
|
+
|
|
19
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
20
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
21
|
+
in the Software without restriction, including without limitation the rights
|
|
22
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
23
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
24
|
+
furnished to do so, subject to the following conditions:
|
|
25
|
+
|
|
26
|
+
The above copyright notice and this permission notice shall be included in all
|
|
27
|
+
copies or substantial portions of the Software.
|
|
28
|
+
|
|
29
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
33
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
34
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
35
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M241,150.65s0,0,0-.05a51.33,51.33,0,0,0-2.53-5.9L196.93,50.18a12,12,0,0,0-2.5-3.65,36,36,0,0,0-50.92,0A12,12,0,0,0,140,55V76H116V55a12,12,0,0,0-3.51-8.48,36,36,0,0,0-50.92,0,12,12,0,0,0-2.5,3.65L17.53,144.7A51.33,51.33,0,0,0,15,150.6s0,0,0,.05A52,52,0,1,0,116,168V100h24v68a52,52,0,1,0,101-17.35ZM80,62.28a12,12,0,0,1,12-1.22v63.15a51.9,51.9,0,0,0-35.9-7.62ZM64,196a28,28,0,1,1,28-28A28,28,0,0,1,64,196ZM164,61.06a12.06,12.06,0,0,1,12,1.22l23.87,54.31a51.9,51.9,0,0,0-35.9,7.62ZM192,196a28,28,0,1,1,28-28A28,28,0,0,1,192,196Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M184.49,136.49l-80,80a12,12,0,0,1-17-17L159,128,87.51,56.49a12,12,0,1,1,17-17l80,80A12,12,0,0,1,184.49,136.49Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z"/></svg>
|