@mannisto/astro-metadata 1.0.0-alpha.4 → 1.0.0-beta.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 +225 -129
- package/index.ts +30 -25
- package/package.json +30 -7
- package/src/components/Canonical.astro +2 -6
- package/src/components/Description.astro +2 -6
- package/src/components/Favicon.astro +51 -43
- package/src/components/Head.astro +57 -56
- package/src/components/Keywords.astro +2 -6
- package/src/components/LanguageAlternates.astro +7 -9
- package/src/components/OpenGraph.astro +59 -23
- package/src/components/Robots.astro +12 -16
- package/src/components/Schema.astro +2 -6
- package/src/components/Title.astro +4 -9
- package/src/components/Twitter.astro +17 -24
- package/src/lib/metadata.ts +35 -13
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mannisto/astro-metadata",
|
|
3
|
-
"version": "1.0.0-
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
5
4
|
"description": "Astro components for managing your page head — metadata, social sharing, favicons, and SEO.",
|
|
6
5
|
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/eremannisto/astro-metadata#readme",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -12,14 +12,37 @@
|
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/eremannisto/astro-metadata/issues"
|
|
14
14
|
},
|
|
15
|
-
"
|
|
15
|
+
"keywords": [
|
|
16
|
+
"astro-component",
|
|
17
|
+
"astro",
|
|
18
|
+
"seo",
|
|
19
|
+
"metadata"
|
|
20
|
+
],
|
|
16
21
|
"exports": "./index.ts",
|
|
22
|
+
"files": [
|
|
23
|
+
"src",
|
|
24
|
+
"index.ts"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"init": "bash scripts/init.sh",
|
|
28
|
+
"lint": "biome lint ./src ./index.ts",
|
|
29
|
+
"format": "prettier --write .",
|
|
30
|
+
"check": "biome lint ./src ./index.ts && prettier --check .",
|
|
31
|
+
"test:unit": "vitest run",
|
|
32
|
+
"test:e2e": "playwright test",
|
|
33
|
+
"test:all": "pnpm run test:unit && pnpm run test:e2e"
|
|
34
|
+
},
|
|
17
35
|
"peerDependencies": {
|
|
18
36
|
"astro": "^4.3.0 || ^5.0.0"
|
|
19
37
|
},
|
|
20
38
|
"devDependencies": {
|
|
39
|
+
"@biomejs/biome": "^2.4.3",
|
|
40
|
+
"@playwright/test": "^1.58.2",
|
|
41
|
+
"@types/node": "^25.3.0",
|
|
21
42
|
"astro": "^5.17.2",
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
"prettier": "^3.8.1",
|
|
44
|
+
"prettier-plugin-astro": "^0.14.1",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vitest": "^4.0.18"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
export type Props = {
|
|
4
3
|
value?: string
|
|
5
4
|
}
|
|
6
5
|
|
|
7
|
-
const {
|
|
8
|
-
value
|
|
9
|
-
} = Astro.props
|
|
6
|
+
const { value } = Astro.props
|
|
10
7
|
|
|
11
8
|
const canonical = value ?? Astro.url.href
|
|
12
|
-
|
|
13
9
|
---
|
|
14
10
|
|
|
15
|
-
<link rel="canonical" href={canonical} />
|
|
11
|
+
<link rel="canonical" href={canonical} />
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
export type FaviconFile = {
|
|
4
|
-
path
|
|
5
|
-
size
|
|
6
|
-
theme
|
|
7
|
-
apple
|
|
3
|
+
path: string
|
|
4
|
+
size?: number
|
|
5
|
+
theme?: "light" | "dark"
|
|
6
|
+
apple?: boolean
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
export type Props = {
|
|
11
|
-
icons
|
|
12
|
-
manifest
|
|
10
|
+
icons: FaviconFile[]
|
|
11
|
+
manifest?: string
|
|
12
|
+
sort?: boolean
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const {
|
|
16
|
-
icons,
|
|
17
|
-
manifest
|
|
18
|
-
} = Astro.props
|
|
15
|
+
const { icons, manifest, sort = true } = Astro.props
|
|
19
16
|
|
|
20
17
|
/**
|
|
21
18
|
* Detects the MIME type of a favicon file from its extension.
|
|
@@ -24,12 +21,11 @@ const {
|
|
|
24
21
|
* @returns The MIME type of the file.
|
|
25
22
|
*/
|
|
26
23
|
function getMimeType(path: string): string {
|
|
27
|
-
if (path.endsWith(".svg"))
|
|
28
|
-
if (path.endsWith(".png"))
|
|
24
|
+
if (path.endsWith(".svg")) return "image/svg+xml"
|
|
25
|
+
if (path.endsWith(".png")) return "image/png"
|
|
29
26
|
if (path.endsWith(".webp")) return "image/webp"
|
|
30
|
-
if (path.endsWith(".jpg") ||
|
|
31
|
-
|
|
32
|
-
if (path.endsWith(".ico")) return "image/x-icon"
|
|
27
|
+
if (path.endsWith(".jpg") || path.endsWith(".jpeg")) return "image/jpeg"
|
|
28
|
+
if (path.endsWith(".ico")) return "image/x-icon"
|
|
33
29
|
return "image/x-icon"
|
|
34
30
|
}
|
|
35
31
|
|
|
@@ -41,40 +37,52 @@ function getMimeType(path: string): string {
|
|
|
41
37
|
*/
|
|
42
38
|
function getMedia(theme?: "light" | "dark"): string | undefined {
|
|
43
39
|
if (theme === "light") return "(prefers-color-scheme: light)"
|
|
44
|
-
if (theme === "dark")
|
|
40
|
+
if (theme === "dark") return "(prefers-color-scheme: dark)"
|
|
45
41
|
return undefined
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Returns a sort priority for a given favicon file.
|
|
46
|
+
* Lower number = rendered first.
|
|
47
|
+
*
|
|
48
|
+
* Order: ico → png → svg → apple → themed variants
|
|
49
|
+
*/
|
|
50
|
+
function getSortOrder(file: FaviconFile): number {
|
|
51
|
+
if (file.apple) return 4
|
|
52
|
+
if (file.theme) return 5
|
|
53
|
+
if (file.path.endsWith(".ico")) return 0
|
|
54
|
+
if (
|
|
55
|
+
file.path.endsWith(".png") ||
|
|
56
|
+
file.path.endsWith(".webp") ||
|
|
57
|
+
file.path.endsWith(".jpg") ||
|
|
58
|
+
file.path.endsWith(".jpeg")
|
|
59
|
+
)
|
|
60
|
+
return 1
|
|
61
|
+
if (file.path.endsWith(".svg")) return 2
|
|
62
|
+
return 3
|
|
63
|
+
}
|
|
55
64
|
|
|
65
|
+
const sorted = sort ? [...icons].sort((a, b) => getSortOrder(a) - getSortOrder(b)) : icons
|
|
66
|
+
|
|
67
|
+
const prepared = sorted.map((file) => ({
|
|
68
|
+
path: file.path,
|
|
69
|
+
size: file.size ? `${file.size}x${file.size}` : undefined,
|
|
70
|
+
type: getMimeType(file.path),
|
|
71
|
+
media: getMedia(file.theme),
|
|
72
|
+
apple: file.apple ?? false,
|
|
73
|
+
}))
|
|
56
74
|
---
|
|
57
75
|
|
|
58
76
|
{manifest && <link rel="manifest" href={manifest} />}
|
|
59
77
|
|
|
60
|
-
{
|
|
61
|
-
|
|
78
|
+
{
|
|
79
|
+
prepared.map((icon) => {
|
|
80
|
+
if (icon.apple) {
|
|
81
|
+
return <link rel="apple-touch-icon" href={icon.path} sizes={icon.size} />
|
|
82
|
+
}
|
|
83
|
+
|
|
62
84
|
return (
|
|
63
|
-
<link
|
|
64
|
-
rel="apple-touch-icon"
|
|
65
|
-
href={icon.path}
|
|
66
|
-
sizes={icon.size}
|
|
67
|
-
/>
|
|
85
|
+
<link rel="icon" type={icon.type} href={icon.path} sizes={icon.size} media={icon.media} />
|
|
68
86
|
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<link
|
|
73
|
-
rel="icon"
|
|
74
|
-
type={icon.type}
|
|
75
|
-
href={icon.path}
|
|
76
|
-
sizes={icon.size}
|
|
77
|
-
media={icon.media}
|
|
78
|
-
/>
|
|
79
|
-
)
|
|
80
|
-
})}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { ComponentProps } from "astro/types"
|
|
3
|
-
import Title
|
|
4
|
-
import Description
|
|
5
|
-
import Canonical
|
|
6
|
-
import Keywords
|
|
7
|
-
import Robots
|
|
8
|
-
import OpenGraph
|
|
9
|
-
import Twitter
|
|
10
|
-
import Favicon
|
|
11
|
-
import Schema
|
|
3
|
+
import Title from "./Title.astro"
|
|
4
|
+
import Description from "./Description.astro"
|
|
5
|
+
import Canonical from "./Canonical.astro"
|
|
6
|
+
import Keywords from "./Keywords.astro"
|
|
7
|
+
import Robots from "./Robots.astro"
|
|
8
|
+
import OpenGraph from "./OpenGraph.astro"
|
|
9
|
+
import Twitter from "./Twitter.astro"
|
|
10
|
+
import Favicon from "./Favicon.astro"
|
|
11
|
+
import Schema from "./Schema.astro"
|
|
12
12
|
import LanguageAlternates from "./LanguageAlternates.astro"
|
|
13
13
|
|
|
14
14
|
export type Props = {
|
|
15
|
-
title
|
|
16
|
-
titleTemplate
|
|
17
|
-
description
|
|
18
|
-
canonical
|
|
19
|
-
keywords
|
|
20
|
-
charset
|
|
21
|
-
viewport
|
|
22
|
-
robots
|
|
23
|
-
openGraph
|
|
24
|
-
twitter
|
|
25
|
-
favicon
|
|
26
|
-
schema
|
|
27
|
-
languageAlternates
|
|
15
|
+
title: string
|
|
16
|
+
titleTemplate?: `${string}%s${string}`
|
|
17
|
+
description?: string
|
|
18
|
+
canonical?: string
|
|
19
|
+
keywords?: string[]
|
|
20
|
+
charset?: string
|
|
21
|
+
viewport?: string
|
|
22
|
+
robots?: ComponentProps<typeof Robots>
|
|
23
|
+
openGraph?: ComponentProps<typeof OpenGraph>
|
|
24
|
+
twitter?: ComponentProps<typeof Twitter>
|
|
25
|
+
favicon?: ComponentProps<typeof Favicon>
|
|
26
|
+
schema?: ComponentProps<typeof Schema>
|
|
27
|
+
languageAlternates?: ComponentProps<typeof LanguageAlternates>["alternates"]
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const {
|
|
@@ -33,7 +33,7 @@ const {
|
|
|
33
33
|
description,
|
|
34
34
|
canonical,
|
|
35
35
|
keywords,
|
|
36
|
-
charset
|
|
36
|
+
charset = "UTF-8",
|
|
37
37
|
viewport = "width=device-width, initial-scale=1.0",
|
|
38
38
|
robots,
|
|
39
39
|
openGraph,
|
|
@@ -57,46 +57,47 @@ const {
|
|
|
57
57
|
<Robots
|
|
58
58
|
index={robots?.index}
|
|
59
59
|
follow={robots?.follow}
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
archive={robots?.archive}
|
|
61
|
+
snippet={robots?.snippet}
|
|
62
62
|
extra={robots?.extra}
|
|
63
63
|
/>
|
|
64
64
|
|
|
65
|
-
{languageAlternates &&
|
|
66
|
-
<LanguageAlternates alternates={languageAlternates} />
|
|
67
|
-
)}
|
|
65
|
+
{languageAlternates && <LanguageAlternates alternates={languageAlternates} />}
|
|
68
66
|
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
+
}
|
|
80
83
|
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
}
|
|
91
97
|
|
|
92
|
-
{favicon &&
|
|
93
|
-
<Favicon
|
|
94
|
-
icons={favicon.icons}
|
|
95
|
-
manifest={favicon.manifest}
|
|
96
|
-
/>
|
|
97
|
-
)}
|
|
98
|
+
{favicon && <Favicon icons={favicon.icons} manifest={favicon.manifest} sort={favicon.sort} />}
|
|
98
99
|
|
|
99
100
|
{schema && <Schema schema={schema.schema} />}
|
|
100
101
|
|
|
101
102
|
<slot />
|
|
102
|
-
</head>
|
|
103
|
+
</head>
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
export type Props = {
|
|
4
3
|
value?: string[]
|
|
5
4
|
}
|
|
6
5
|
|
|
7
|
-
const {
|
|
8
|
-
value = []
|
|
9
|
-
} = Astro.props
|
|
10
|
-
|
|
6
|
+
const { value = [] } = Astro.props
|
|
11
7
|
---
|
|
12
8
|
|
|
13
|
-
{value.length > 0 && <meta name="keywords" content={value.join(", ")} />}
|
|
9
|
+
{value.length > 0 && <meta name="keywords" content={value.join(", ")} />}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
export type LanguageAlternate = {
|
|
3
|
-
href
|
|
4
|
-
hreflang
|
|
3
|
+
href: string
|
|
4
|
+
hreflang: string
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export type Props = {
|
|
@@ -11,10 +11,8 @@ export type Props = {
|
|
|
11
11
|
const { alternates } = Astro.props
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
-
rel="alternate"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/>
|
|
20
|
-
))}
|
|
14
|
+
{
|
|
15
|
+
alternates.map((alternate) => (
|
|
16
|
+
<link rel="alternate" href={alternate.href} hreflang={alternate.hreflang} />
|
|
17
|
+
))
|
|
18
|
+
}
|
|
@@ -1,39 +1,75 @@
|
|
|
1
1
|
---
|
|
2
|
+
export type OpenGraphImage = {
|
|
3
|
+
url: string
|
|
4
|
+
secureUrl?: string
|
|
5
|
+
type?: string
|
|
6
|
+
alt?: string
|
|
7
|
+
width?: number
|
|
8
|
+
height?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type OpenGraphVideo = {
|
|
12
|
+
url: string
|
|
13
|
+
secureUrl?: string
|
|
14
|
+
type?: string
|
|
15
|
+
width?: number
|
|
16
|
+
height?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type OpenGraphAudio = {
|
|
20
|
+
url: string
|
|
21
|
+
secureUrl?: string
|
|
22
|
+
type?: string
|
|
23
|
+
}
|
|
2
24
|
|
|
3
25
|
export type Props = {
|
|
4
|
-
title
|
|
5
|
-
description
|
|
6
|
-
url
|
|
7
|
-
type
|
|
8
|
-
siteName
|
|
9
|
-
locale
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
height? : number
|
|
15
|
-
}
|
|
26
|
+
title?: string
|
|
27
|
+
description?: string
|
|
28
|
+
url?: string
|
|
29
|
+
type?: string
|
|
30
|
+
siteName?: string
|
|
31
|
+
locale?: string
|
|
32
|
+
localeAlternate?: string[]
|
|
33
|
+
image?: OpenGraphImage
|
|
34
|
+
video?: OpenGraphVideo
|
|
35
|
+
audio?: OpenGraphAudio
|
|
16
36
|
}
|
|
17
37
|
|
|
18
38
|
const {
|
|
19
39
|
title,
|
|
20
40
|
description,
|
|
21
|
-
image,
|
|
22
41
|
url,
|
|
23
42
|
type = "website",
|
|
24
43
|
siteName,
|
|
25
44
|
locale,
|
|
45
|
+
localeAlternate,
|
|
46
|
+
image,
|
|
47
|
+
video,
|
|
48
|
+
audio,
|
|
26
49
|
} = Astro.props
|
|
27
|
-
|
|
28
50
|
---
|
|
29
51
|
|
|
30
|
-
{title
|
|
31
|
-
{description
|
|
32
|
-
{
|
|
33
|
-
{
|
|
34
|
-
{
|
|
52
|
+
{title && <meta property="og:title" content={title} />}
|
|
53
|
+
{description && <meta property="og:description" content={description} />}
|
|
54
|
+
{url && <meta property="og:url" content={url} />}
|
|
55
|
+
{type && <meta property="og:type" content={type} />}
|
|
56
|
+
{siteName && <meta property="og:site_name" content={siteName} />}
|
|
57
|
+
{locale && <meta property="og:locale" content={locale} />}
|
|
58
|
+
{localeAlternate?.map((loc: string) => <meta property="og:locale:alternate" content={loc} />)}
|
|
59
|
+
|
|
60
|
+
{image?.url && <meta property="og:image" content={image.url} />}
|
|
61
|
+
{image?.secureUrl && <meta property="og:image:secure_url" content={image.secureUrl} />}
|
|
62
|
+
{image?.type && <meta property="og:image:type" content={image.type} />}
|
|
63
|
+
{image?.alt && <meta property="og:image:alt" content={image.alt} />}
|
|
64
|
+
{image?.width && <meta property="og:image:width" content={String(image.width)} />}
|
|
35
65
|
{image?.height && <meta property="og:image:height" content={String(image.height)} />}
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
{
|
|
39
|
-
{
|
|
66
|
+
|
|
67
|
+
{video?.url && <meta property="og:video" content={video.url} />}
|
|
68
|
+
{video?.secureUrl && <meta property="og:video:secure_url" content={video.secureUrl} />}
|
|
69
|
+
{video?.type && <meta property="og:video:type" content={video.type} />}
|
|
70
|
+
{video?.width && <meta property="og:video:width" content={String(video.width)} />}
|
|
71
|
+
{video?.height && <meta property="og:video:height" content={String(video.height)} />}
|
|
72
|
+
|
|
73
|
+
{audio?.url && <meta property="og:audio" content={audio.url} />}
|
|
74
|
+
{audio?.secureUrl && <meta property="og:audio:secure_url" content={audio.secureUrl} />}
|
|
75
|
+
{audio?.type && <meta property="og:audio:type" content={audio.type} />}
|
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
---
|
|
2
2
|
export type Props = {
|
|
3
|
-
index
|
|
4
|
-
follow
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
extra
|
|
3
|
+
index?: boolean
|
|
4
|
+
follow?: boolean
|
|
5
|
+
archive?: boolean
|
|
6
|
+
snippet?: boolean
|
|
7
|
+
extra?: string
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const {
|
|
11
|
-
index = true,
|
|
12
|
-
follow = true,
|
|
13
|
-
noArchive,
|
|
14
|
-
noSnippet,
|
|
15
|
-
extra,
|
|
16
|
-
} = Astro.props
|
|
10
|
+
const { index = true, follow = true, archive = true, snippet = true, extra } = Astro.props
|
|
17
11
|
|
|
18
12
|
// Combine the directives into a comma separated string,
|
|
19
13
|
// for example: "index, follow, noarchive, nosnippet"
|
|
20
14
|
const directives = [
|
|
21
15
|
index ? "index" : "noindex",
|
|
22
16
|
follow ? "follow" : "nofollow",
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
!archive ? "noarchive" : null,
|
|
18
|
+
!snippet ? "nosnippet" : null,
|
|
25
19
|
extra,
|
|
26
|
-
]
|
|
20
|
+
]
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.join(", ")
|
|
27
23
|
---
|
|
28
24
|
|
|
29
|
-
<meta name="robots" content={directives} />
|
|
25
|
+
<meta name="robots" content={directives} />
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
export type Props = {
|
|
4
3
|
schema: Record<string, unknown>
|
|
5
4
|
}
|
|
6
5
|
|
|
7
|
-
const {
|
|
8
|
-
schema
|
|
9
|
-
} = Astro.props
|
|
10
|
-
|
|
6
|
+
const { schema } = Astro.props
|
|
11
7
|
---
|
|
12
8
|
|
|
13
|
-
<script is:inline type="application/ld+json" set:html={JSON.stringify(schema)} />
|
|
9
|
+
<script is:inline type="application/ld+json" set:html={JSON.stringify(schema)} />
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
export type Props = {
|
|
4
|
-
value
|
|
5
|
-
template
|
|
3
|
+
value: string
|
|
4
|
+
template?: `${string}%s${string}`
|
|
6
5
|
}
|
|
7
6
|
|
|
8
|
-
const {
|
|
9
|
-
value,
|
|
10
|
-
template = "%s"
|
|
11
|
-
} = Astro.props
|
|
12
|
-
|
|
7
|
+
const { value, template = "%s" } = Astro.props
|
|
13
8
|
---
|
|
14
9
|
|
|
15
|
-
<title>{template.replace("%s", value)}</title>
|
|
10
|
+
<title>{template.replace("%s", value)}</title>
|
|
@@ -1,32 +1,25 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
export type Props = {
|
|
4
|
-
title
|
|
5
|
-
description
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
title?: string
|
|
4
|
+
description?: string
|
|
5
|
+
url?: string
|
|
6
|
+
card?: "summary" | "summary_large_image" | "player" | "app"
|
|
7
|
+
site?: string
|
|
8
|
+
creator?: string
|
|
9
|
+
image?: {
|
|
10
|
+
url: string
|
|
11
|
+
alt?: string
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const {
|
|
16
|
-
title,
|
|
17
|
-
description,
|
|
18
|
-
image,
|
|
19
|
-
card = "summary_large_image",
|
|
20
|
-
site,
|
|
21
|
-
creator,
|
|
22
|
-
} = Astro.props
|
|
23
|
-
|
|
15
|
+
const { title, description, url, card = "summary_large_image", site, creator, image } = Astro.props
|
|
24
16
|
---
|
|
25
17
|
|
|
26
|
-
{card
|
|
27
|
-
{title
|
|
18
|
+
{card && <meta name="twitter:card" content={card} />}
|
|
19
|
+
{title && <meta name="twitter:title" content={title} />}
|
|
28
20
|
{description && <meta name="twitter:description" content={description} />}
|
|
29
|
-
{
|
|
30
|
-
{image?.
|
|
31
|
-
{
|
|
32
|
-
{
|
|
21
|
+
{url && <meta name="twitter:url" content={url} />}
|
|
22
|
+
{image?.url && <meta name="twitter:image" content={image.url} />}
|
|
23
|
+
{image?.alt && <meta name="twitter:image:alt" content={image.alt} />}
|
|
24
|
+
{site && <meta name="twitter:site" content={site} />}
|
|
25
|
+
{creator && <meta name="twitter:creator" content={creator} />}
|