@indaco/sveo 1.0.0 → 1.0.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/dist/components/metadata/README.md +89 -0
- package/dist/components/metadata/opengraph/OpenGraph.svelte +7 -0
- package/dist/components/metadata/opengraph/README.md +58 -0
- package/dist/components/metadata/opengraph/article.svelte +2 -2
- package/dist/components/metadata/opengraph/business.svelte +1 -1
- package/dist/components/metadata/opengraph/video-episode.svelte +2 -3
- package/dist/components/metadata/opengraph/video-movie.svelte +1 -1
- package/dist/components/metadata/twittercard/README.md +81 -0
- package/dist/components/metadata/twittercard/TwitterCard.svelte +13 -13
- package/dist/components/schemaorg/breadcrumbs/JsonLdBreadcrumbs.svelte +24 -22
- package/dist/components/schemaorg/breadcrumbs/README.md +66 -0
- package/dist/components/schemaorg/sitenavigationelements/JsonLdSiteNavigationElements.svelte +8 -13
- package/dist/components/schemaorg/sitenavigationelements/README.md +64 -0
- package/dist/components/schemaorg/webpage/JsonLdWebPage.svelte +12 -9
- package/dist/components/schemaorg/webpage/README.md +51 -0
- package/dist/components/schemaorg/website/JsonLdWebSite.svelte +43 -40
- package/dist/components/schemaorg/website/README.md +85 -0
- package/dist/types.d.ts +5 -2
- package/dist/utils.js +4 -2
- package/package.json +236 -242
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# PageMetaTags
|
|
2
|
+
|
|
3
|
+
Easily add metadata (title, canonical url, description, keywords) to your pages as well as attach [OpenGraph] and [TwitterCard] to let pages become rich objects.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### Article Example
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import type { SEOWebPage } from '@indaco/sveo/types';
|
|
12
|
+
import { OpenGraphType, TwitterCardType } from '@indaco/sveo/types';
|
|
13
|
+
import { PageMetaTags } from '@indaco/sveo/metadata';
|
|
14
|
+
|
|
15
|
+
const samplePage: SEOWebPage = {
|
|
16
|
+
url: 'https://example.com/posts/getting-started',
|
|
17
|
+
title: 'Getting Started Article',
|
|
18
|
+
description: 'This is the description for the Getting Started Article',
|
|
19
|
+
author: 'Your Name',
|
|
20
|
+
keywords: ['sveltekit', 'components', 'tests', 'vitest'],
|
|
21
|
+
opengraph: {
|
|
22
|
+
type: OpenGraphType.Article,
|
|
23
|
+
article: {
|
|
24
|
+
published_time: '23-01-2022',
|
|
25
|
+
modified_time: '24-01-2022'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
twitter: {
|
|
29
|
+
type: TwitterCardType.Large,
|
|
30
|
+
site: '@username'
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
<script>
|
|
34
|
+
|
|
35
|
+
<PageMetaTags data={samplePage} />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Music Album Example
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<script>
|
|
42
|
+
import type { SEOWebPageMetadata } from '@indaco/sveo/types';
|
|
43
|
+
import { OpenGraphType } from '@indaco/sveo/types';
|
|
44
|
+
import { PageMetaTags } from '@indaco/sveo/metadata';
|
|
45
|
+
|
|
46
|
+
const sampleMusicAlbum: SEOWebPageMetadata = {
|
|
47
|
+
url: 'https://www.dgmlive.com/',
|
|
48
|
+
title: 'In the Court of the Crimson King',
|
|
49
|
+
description: 'Description for the album',
|
|
50
|
+
keywords: 'progressive rock, jazz rock, rock',
|
|
51
|
+
opengraph: {
|
|
52
|
+
type: OpenGraphType.MusicAlbum,
|
|
53
|
+
album: {
|
|
54
|
+
url: 'https://open.spotify.com/album/6tVg2Wl9hVKMpHYcAl2V2M?si=dJtzXM7ATvmLOn9NfdDnbg',
|
|
55
|
+
musicians: [
|
|
56
|
+
{
|
|
57
|
+
url: 'https://open.spotify.com/artist/7M1FPw29m5FbicYzS2xdpi?si=w9MGJ88-S3O7tiG5IheXAw'
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
songs: [
|
|
61
|
+
{
|
|
62
|
+
url: 'https://open.spotify.com/track/5L7VBYoosmkmiiDlzumdCe?si=aa49699b95604f8d'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
url: 'https://open.spotify.com/track/4QbpagjMCqSECj6IimTL2n?si=65e9458fe8454eea'
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
release_date: new Date('10-10-1969')
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
<script>
|
|
73
|
+
|
|
74
|
+
<PageMetaTags data={sampleMusicAlbum} />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Properties
|
|
78
|
+
|
|
79
|
+
### PageMetags
|
|
80
|
+
|
|
81
|
+
| Prop | Type | Required | Description |
|
|
82
|
+
| :----- | :----------: | :------: | :----------------------------------------- |
|
|
83
|
+
| `data` | [SEOWebPage] | yes | The SEO object containing Page metadata |
|
|
84
|
+
|
|
85
|
+
<!-- Resource Links -->
|
|
86
|
+
|
|
87
|
+
[SEOWebPage]: https://github.com/indaco/sveo/blob/913f83920f7f76183fc7d6ea58eebbceeb82f452/src/lib/types.ts#L34-L43
|
|
88
|
+
[OpenGraph]: https://ogp.me/
|
|
89
|
+
[TwitterCard]: https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards
|
|
@@ -21,6 +21,13 @@ let { data } = $props();
|
|
|
21
21
|
<meta property="og:description" content={data.description} />
|
|
22
22
|
{/if}
|
|
23
23
|
|
|
24
|
+
{#if data.opengraph.locale}
|
|
25
|
+
<meta property="og:locale" content={data.opengraph.locale} />
|
|
26
|
+
{/if}
|
|
27
|
+
{#if data.opengraph.site_name}
|
|
28
|
+
<meta property="og:site_name" content={data.opengraph.site_name} />
|
|
29
|
+
{/if}
|
|
30
|
+
|
|
24
31
|
{#if data.image?.url}
|
|
25
32
|
<meta property="og:image" content={data.image.url} />
|
|
26
33
|
{#if data.image.alt}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# OpenGraph
|
|
2
|
+
|
|
3
|
+
The `OpenGraph` component provides a comprehensive and type-safe way to generate Open Graph `<meta>` tags for your Svelte or SvelteKit application.
|
|
4
|
+
|
|
5
|
+
It is the **only exported component** for Open Graph, and it **automatically handles conditional rendering** of all supported subtypes (e.g. article, book, video, music) internally.
|
|
6
|
+
|
|
7
|
+
## Supported Open Graph Types
|
|
8
|
+
|
|
9
|
+
The component supports official Open Graph object types, including:
|
|
10
|
+
|
|
11
|
+
| Type | Description |
|
|
12
|
+
| :----------------------------- | :---------------------------------------- |
|
|
13
|
+
| `website` | Basic OG meta (title, description, image) |
|
|
14
|
+
| `article` | Adds `article:*` tags like author, tags |
|
|
15
|
+
| `book` | ISBN, release date, and tags |
|
|
16
|
+
| `profile` | Personal metadata |
|
|
17
|
+
| `business.business` | Contact information |
|
|
18
|
+
| `product` | Product-specific metadata |
|
|
19
|
+
| `music.song` | Song metadata |
|
|
20
|
+
| `music.album` | Album-level metadata |
|
|
21
|
+
| `music.playlist` | Playlist metadata |
|
|
22
|
+
| `music.radio_station` | Radio metadata |
|
|
23
|
+
| `video.movie` | Movie metadata |
|
|
24
|
+
| `video.episode` | Episode metadata (also includes movie) |
|
|
25
|
+
| `video.tv_show`, `video.other` | Handled like movie |
|
|
26
|
+
|
|
27
|
+
## Usage Example (Article)
|
|
28
|
+
|
|
29
|
+
To use the `OpenGraph` component, pass a typed `SEOWebPage` object with an `opengraph` field and `type`.
|
|
30
|
+
|
|
31
|
+
```svelte
|
|
32
|
+
<script lang="ts">
|
|
33
|
+
import { OpenGraphType, TwitterCardType } from '@indaco/sveo/types';
|
|
34
|
+
import type { SEOWebPage } from '@indaco/sveo/types';
|
|
35
|
+
|
|
36
|
+
const sampleArticle: SEOWebPage = {
|
|
37
|
+
url: 'https://example.com/posts/getting-started',
|
|
38
|
+
title: 'Getting Started Article',
|
|
39
|
+
description: 'This is the description for the Getting Started Article',
|
|
40
|
+
author: 'Mirco Veltri',
|
|
41
|
+
keywords: ['sveltekit, components, tests, vitest'],
|
|
42
|
+
opengraph: {
|
|
43
|
+
type: OpenGraphType.Article,
|
|
44
|
+
article: {
|
|
45
|
+
tags: ['sveltekit'],
|
|
46
|
+
published_time: '23-01-2022',
|
|
47
|
+
modified_time: '24-01-2022'
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
twitter: {
|
|
51
|
+
type: TwitterCardType.Large,
|
|
52
|
+
site: '@indaco'
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<OpenGraph data={sampleArticle} />
|
|
58
|
+
```
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">import { toISODateString } from '../../../utils.js';
|
|
2
2
|
let { data } = $props();
|
|
3
|
-
const times = [
|
|
3
|
+
const times = $derived([
|
|
4
4
|
['published_time', data.opengraph?.article?.published_time],
|
|
5
5
|
['modified_time', data.opengraph?.article?.modified_time],
|
|
6
6
|
['expiration_time', data.opengraph?.article?.expiration_time]
|
|
7
|
-
];
|
|
7
|
+
]);
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
10
|
{#each times as [key, value] (key)}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">import { toISODateString } from '../../../utils.js';
|
|
2
|
-
import VideoMovie from './video-movie.svelte';
|
|
3
2
|
let { data } = $props();
|
|
4
3
|
</script>
|
|
5
4
|
|
|
@@ -42,6 +41,6 @@ let { data } = $props();
|
|
|
42
41
|
{/each}
|
|
43
42
|
{/if}
|
|
44
43
|
|
|
45
|
-
{#if data.opengraph?.episode?.series}
|
|
46
|
-
<
|
|
44
|
+
{#if data.opengraph?.episode?.series?.url}
|
|
45
|
+
<meta property="video:series" content={data.opengraph.episode.series.url} />
|
|
47
46
|
{/if}
|
|
@@ -36,7 +36,7 @@ let { data } = $props();
|
|
|
36
36
|
{/if}
|
|
37
37
|
|
|
38
38
|
{#if Array.isArray(data.opengraph?.movie?.tags)}
|
|
39
|
-
{@const _tags = data.opengraph
|
|
39
|
+
{@const _tags = data.opengraph.movie.tags}
|
|
40
40
|
{#each _tags as tag (tag)}
|
|
41
41
|
<meta property="video:tag" content={tag} />
|
|
42
42
|
{/each}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# TwitterCard
|
|
2
|
+
|
|
3
|
+
Generate [Twitter Card](https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards) meta tags using a single, type-safe Svelte component.
|
|
4
|
+
|
|
5
|
+
It supports all official Twitter Card types:
|
|
6
|
+
|
|
7
|
+
- `summary`
|
|
8
|
+
- `summary_large_image`
|
|
9
|
+
- `player`
|
|
10
|
+
- `app`
|
|
11
|
+
|
|
12
|
+
The component automatically renders conditional tags based on the selected card type and the fields present in `data.twitter`.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```svelte
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import TwitterCard from '@indaco/sveo/metadata/twittercard/TwitterCard.svelte';
|
|
19
|
+
import type { SEOWebPage } from '@indaco/sveo/types';
|
|
20
|
+
|
|
21
|
+
const seo: SEOWebPage = {
|
|
22
|
+
title: 'Your Page Title',
|
|
23
|
+
description: 'Page description for Twitter card.',
|
|
24
|
+
image: {
|
|
25
|
+
url: 'https://example.com/og-image.jpg',
|
|
26
|
+
alt: 'An image for Twitter'
|
|
27
|
+
},
|
|
28
|
+
twitter: {
|
|
29
|
+
type: 'summary_large_image',
|
|
30
|
+
site: '@yourhandle'
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<TwitterCard data={seo} />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Renders the following tags (based on your data):
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<meta property="twitter:card" content="summary_large_image" />
|
|
42
|
+
<meta property="twitter:title" content="Your Page Title" />
|
|
43
|
+
<meta property="twitter:site" content="@yourhandle" />
|
|
44
|
+
<meta property="twitter:description" content="Page description for Twitter card." />
|
|
45
|
+
<meta property="twitter:image" content="https://example.com/og-image.jpg" />
|
|
46
|
+
<meta property="twitter:image:alt" content="An image for Twitter" />
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Example (Player Card)
|
|
50
|
+
|
|
51
|
+
```svelte
|
|
52
|
+
<TwitterCard
|
|
53
|
+
data={{
|
|
54
|
+
title: 'Watch this video!',
|
|
55
|
+
description: 'Best video ever.',
|
|
56
|
+
image: {
|
|
57
|
+
url: 'https://example.com/video-thumbnail.jpg',
|
|
58
|
+
alt: 'Video thumbnail'
|
|
59
|
+
},
|
|
60
|
+
twitter: {
|
|
61
|
+
type: 'player',
|
|
62
|
+
site: '@myapp',
|
|
63
|
+
player: {
|
|
64
|
+
url: 'https://example.com/embed/video',
|
|
65
|
+
width: 1280,
|
|
66
|
+
height: 720
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Props
|
|
74
|
+
|
|
75
|
+
| Prop | Type | Required | Description |
|
|
76
|
+
| :----- | :----------: | :------: | :----------------------------------------- |
|
|
77
|
+
| `data` | [SEOWebPage] | yes | The SEO object containing Twitter metadata |
|
|
78
|
+
|
|
79
|
+
<!-- Resource Links -->
|
|
80
|
+
|
|
81
|
+
[SEOWebPage]: https://github.com/indaco/sveo/blob/913f83920f7f76183fc7d6ea58eebbceeb82f452/src/lib/types.ts#L34-L43
|
|
@@ -4,34 +4,34 @@ let { data } = $props();
|
|
|
4
4
|
|
|
5
5
|
{#if data.twitter}
|
|
6
6
|
{#if data.twitter.type}
|
|
7
|
-
<meta
|
|
8
|
-
<meta
|
|
7
|
+
<meta name="twitter:card" content={data.twitter.type} />
|
|
8
|
+
<meta name="twitter:title" content={data.title} />
|
|
9
9
|
{#if data.twitter.site}
|
|
10
|
-
<meta
|
|
10
|
+
<meta name="twitter:site" content={data.twitter.site} />
|
|
11
11
|
{/if}
|
|
12
12
|
{#if data.description}
|
|
13
|
-
<meta
|
|
13
|
+
<meta name="twitter:description" content={data.description} />
|
|
14
14
|
{/if}
|
|
15
15
|
{#if data.image?.url}
|
|
16
|
-
<meta
|
|
16
|
+
<meta name="twitter:image" content={data.image.url} />
|
|
17
17
|
{#if data.image.alt}
|
|
18
|
-
<meta
|
|
18
|
+
<meta name="twitter:image:alt" content={data.image.alt} />
|
|
19
19
|
{/if}
|
|
20
20
|
{/if}
|
|
21
21
|
|
|
22
22
|
{#if data.twitter.type === TwitterCardType.Player && data.twitter.player}
|
|
23
|
-
<meta
|
|
24
|
-
<meta
|
|
25
|
-
<meta
|
|
23
|
+
<meta name="twitter:player" content={data.twitter.player.url} />
|
|
24
|
+
<meta name="twitter:player:width" content={data.twitter.player.width.toString()} />
|
|
25
|
+
<meta name="twitter:player:height" content={data.twitter.player.height.toString()} />
|
|
26
26
|
{/if}
|
|
27
27
|
|
|
28
28
|
{#if data.twitter.type === TwitterCardType.App && data.twitter.app}
|
|
29
29
|
{#if data.twitter.app.country}
|
|
30
|
-
<meta
|
|
30
|
+
<meta name="twitter:app:country" content={data.twitter.app.country} />
|
|
31
31
|
{/if}
|
|
32
|
-
<meta
|
|
33
|
-
<meta
|
|
34
|
-
<meta
|
|
32
|
+
<meta name="twitter:app:id:iphone" content={data.twitter.app.idIPhone} />
|
|
33
|
+
<meta name="twitter:app:id:ipad" content={data.twitter.app.idIPad} />
|
|
34
|
+
<meta name="twitter:app:id:googleplay" content={data.twitter.app.idGooglePlay} />
|
|
35
35
|
{/if}
|
|
36
36
|
{/if}
|
|
37
37
|
{/if}
|
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
<script lang="ts">import { serializeJSONLdSchema, pathSegments } from '../../../utils.js';
|
|
2
2
|
let { url } = $props();
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
position: 1,
|
|
9
|
-
name: 'Home',
|
|
10
|
-
url: baseURL
|
|
11
|
-
},
|
|
12
|
-
...segments.map((segment, index) => {
|
|
13
|
-
return {
|
|
3
|
+
const schemaOrgBreadcrumbList = $derived.by(() => {
|
|
4
|
+
const baseURL = new URL(url).origin;
|
|
5
|
+
const segments = pathSegments(url);
|
|
6
|
+
const itemListElement = [
|
|
7
|
+
{
|
|
14
8
|
'@type': 'ListItem',
|
|
15
|
-
position:
|
|
16
|
-
name:
|
|
17
|
-
url:
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
9
|
+
position: 1,
|
|
10
|
+
name: 'Home',
|
|
11
|
+
url: baseURL
|
|
12
|
+
},
|
|
13
|
+
...segments.map((segment, index) => {
|
|
14
|
+
return {
|
|
15
|
+
'@type': 'ListItem',
|
|
16
|
+
position: index + 2,
|
|
17
|
+
name: segment,
|
|
18
|
+
url: `${baseURL}/${segments.slice(0, index + 1).join('/')}`
|
|
19
|
+
};
|
|
20
|
+
})
|
|
21
|
+
];
|
|
22
|
+
return {
|
|
23
|
+
'@context': 'https://schema.org',
|
|
24
|
+
'@type': 'BreadcrumbList',
|
|
25
|
+
itemListElement
|
|
26
|
+
};
|
|
27
|
+
});
|
|
26
28
|
</script>
|
|
27
29
|
|
|
28
30
|
<svelte:head>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# JsonLdBreadcrumbs
|
|
2
|
+
|
|
3
|
+
The `JsonLdBreadcrumbs` component adds a [BreadcrumbList] to the page header as [structured data]. This helps search engines understand the page's position within the site hierarchy and enhances rich results in search listings.
|
|
4
|
+
|
|
5
|
+
A `BreadcrumbList` is an [ItemList] consisting of a chain of linked web pages, typically ending with the current page.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<script>
|
|
11
|
+
import { JsonLdBreadcrumbs } from '@indaco/sveo/schemaorg';
|
|
12
|
+
|
|
13
|
+
<JsonLdBreadcrumbs url="https://example.com/blog/welcome" />
|
|
14
|
+
</script>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Output
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<script type="application/ld+json">{
|
|
21
|
+
"@context": "https://schema.org",
|
|
22
|
+
"@type": "BreadcrumbList",
|
|
23
|
+
"itemListElement": [
|
|
24
|
+
{
|
|
25
|
+
"@type": "ListItem",
|
|
26
|
+
"position": 1,
|
|
27
|
+
"name": "Home",
|
|
28
|
+
"url": "https://example.com"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"@type": "ListItem",
|
|
32
|
+
"position": 2,
|
|
33
|
+
"name": "blog",
|
|
34
|
+
"url": "https://example.com/blog"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"@type": "ListItem",
|
|
38
|
+
"position": 3,
|
|
39
|
+
"name": "welcome",
|
|
40
|
+
"url": "https://example.com/blog/welcome"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}</script>
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### With SvelteKit
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<script>
|
|
51
|
+
import { page } from '$app/stores';
|
|
52
|
+
import { JsonLdBreadcrumbs } from '@indaco/sveo/schemaorg';
|
|
53
|
+
|
|
54
|
+
<JsonLdBreadcrumbs url={$page.url.href} />
|
|
55
|
+
</script>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Properties
|
|
59
|
+
|
|
60
|
+
| Property | Type | Required | Description |
|
|
61
|
+
| :-------- | :------: | :------: | :------------------------------- |
|
|
62
|
+
| url | `string` | yes | Absolute URL of the current page |
|
|
63
|
+
|
|
64
|
+
[structured data]: https://developers.google.com/search/docs/appearance/structured-data/breadcrumb
|
|
65
|
+
[BreadcrumbList]: https://schema.org/BreadcrumbList
|
|
66
|
+
[ItemList]: https://schema.org/ItemList
|
package/dist/components/schemaorg/sitenavigationelements/JsonLdSiteNavigationElements.svelte
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
<script lang="ts">import { serializeJSONLdSchema } from '../../../utils.js';
|
|
2
2
|
let { baseURL, data } = $props();
|
|
3
|
-
|
|
4
|
-
return Array.isArray(data)
|
|
5
|
-
? data.map((elem) => ({
|
|
6
|
-
'@type': 'SiteNavigationElement',
|
|
7
|
-
position: elem.weight,
|
|
8
|
-
name: elem.name,
|
|
9
|
-
url: elem.external ? elem.url : `${baseURL}${elem.url}`
|
|
10
|
-
}))
|
|
11
|
-
: [];
|
|
12
|
-
}
|
|
13
|
-
const schemaOrgSiteNavigationElement = {
|
|
3
|
+
const schemaOrgSiteNavigationElement = $derived.by(() => ({
|
|
14
4
|
'@context': 'https://schema.org',
|
|
15
5
|
'@type': 'ItemList',
|
|
16
|
-
itemListElement:
|
|
17
|
-
|
|
6
|
+
itemListElement: data.map((elem) => ({
|
|
7
|
+
'@type': 'SiteNavigationElement',
|
|
8
|
+
position: elem.weight,
|
|
9
|
+
name: elem.name,
|
|
10
|
+
url: elem.external ? elem.url : `${baseURL}${elem.url}`
|
|
11
|
+
}))
|
|
12
|
+
}));
|
|
18
13
|
</script>
|
|
19
14
|
|
|
20
15
|
<svelte:head>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# JsonLdSiteNavigationElement
|
|
2
|
+
|
|
3
|
+
The `JsonLdSiteNavigationElement` component injects a [SiteNavigationElement] from Schema.org into the page as a `<script type="application/ld+json">` tag, describing the structure of the site's main navigation menu for search engines.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import type { SEOMenuItem } from '@indaco/sveo/types.js';
|
|
10
|
+
import { JsonLdSiteNavigationElement } from '@indaco/sveo/schemaorg';
|
|
11
|
+
|
|
12
|
+
const menu: Array<SEOMenuItem> = [
|
|
13
|
+
{
|
|
14
|
+
identifier: 'home',
|
|
15
|
+
name: 'Home',
|
|
16
|
+
url: '/',
|
|
17
|
+
weight: 1
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
identifier: 'about',
|
|
21
|
+
name: 'About',
|
|
22
|
+
url: '/about',
|
|
23
|
+
weight: 2
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
<JsonLdSiteNavigationElement baseURL="https://example.com" data={menu} />
|
|
28
|
+
</script>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Output
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<script type="application/ld+json">{
|
|
35
|
+
"@context": "https://schema.org",
|
|
36
|
+
"@type": "ItemList",
|
|
37
|
+
"itemListElement": [
|
|
38
|
+
{
|
|
39
|
+
"@type": "SiteNavigationElement",
|
|
40
|
+
"position": 1,
|
|
41
|
+
"name": "home",
|
|
42
|
+
"url": "https://example.com/"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"@type": "SiteNavigationElement",
|
|
46
|
+
"position": 2,
|
|
47
|
+
"name": "about",
|
|
48
|
+
"url": "https://example.com/about"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}</script>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Properties
|
|
55
|
+
|
|
56
|
+
| Property | Type | Required | Description |
|
|
57
|
+
| :------- | :---------------: | :------: | :------------------------------------------- |
|
|
58
|
+
| baseURL | `string` | yes | Base URL to prefix relative paths |
|
|
59
|
+
| data | [SEOMenuItem]`[]` | yes | Array of menu items to include in the schema |
|
|
60
|
+
|
|
61
|
+
<!-- Resource Links -->
|
|
62
|
+
|
|
63
|
+
[SEOMenuItem]: https://github.com/indaco/sveo/blob/06de4d7c79a27f0474981cce3ebc2cf922484b09/src/lib/types.ts#L20-L27
|
|
64
|
+
[SiteNavigationElement]: https://schema.org/SiteNavigationElement
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
<script lang="ts">import { serializeJSONLdSchema } from '../../../utils.js';
|
|
2
2
|
let { data } = $props();
|
|
3
|
-
const schemaOrgWebPage = $
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const schemaOrgWebPage = $derived.by(() => {
|
|
4
|
+
const page = {
|
|
5
|
+
'@context': 'https://schema.org',
|
|
6
|
+
'@type': 'WebPage',
|
|
7
|
+
name: data.title,
|
|
8
|
+
description: data.description || ''
|
|
9
|
+
};
|
|
10
|
+
if (data.author)
|
|
11
|
+
page.author = data.author;
|
|
12
|
+
if (data.keywords?.length)
|
|
13
|
+
page.keywords = data.keywords;
|
|
14
|
+
return page;
|
|
8
15
|
});
|
|
9
|
-
if (data.author)
|
|
10
|
-
schemaOrgWebPage.author = data.author;
|
|
11
|
-
if (data.keywords?.length)
|
|
12
|
-
schemaOrgWebPage.keywords = data.keywords;
|
|
13
16
|
</script>
|
|
14
17
|
|
|
15
18
|
<svelte:head>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# JsonLdWebPage
|
|
2
|
+
|
|
3
|
+
The `JsonLdWebPage` component adds a [WebPage] Schema.org type to the page as structured data via a `<script type="application/ld+json">` tag. This helps search engines understand the page's content and metadata such as title, description, and keywords.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import type { SEOWebPage } from '@indaco/sveo/types.js';
|
|
10
|
+
import { JsonLdWebPage } from '@indaco/sveo/schemaorg';
|
|
11
|
+
|
|
12
|
+
const pageData: SEOWebPage = {
|
|
13
|
+
url: website.baseURL,
|
|
14
|
+
title: 'Home Page',
|
|
15
|
+
description: 'This is the description for the Home Page',
|
|
16
|
+
keywords: 'sveltekit, components, tests, vitest',
|
|
17
|
+
opengraph: {
|
|
18
|
+
type: OpenGraphType.Website
|
|
19
|
+
},
|
|
20
|
+
twitter: {
|
|
21
|
+
type: TwitterCardType.Summary
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
<JsonLdWebPage data={pageData} />
|
|
26
|
+
</script>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Output
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<script type="application/ld+json">{
|
|
33
|
+
"@context": "https://schema.org",
|
|
34
|
+
"@type": "WebPage",
|
|
35
|
+
"name": "Home Page",
|
|
36
|
+
"description": "This is the description for the Home Page",
|
|
37
|
+
"keywords": "sveltekit, components, tests, jest"
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Properties
|
|
43
|
+
|
|
44
|
+
| Property | Type | Required | Description |
|
|
45
|
+
| :------- | :----------: | :------: | :-------------------------------------- |
|
|
46
|
+
| data | [SEOWebPage] | yes | Web page metadata to convert to JSON-LD |
|
|
47
|
+
|
|
48
|
+
<!-- Resource Links -->
|
|
49
|
+
|
|
50
|
+
[SEOWebPage]: https://github.com/indaco/sveo/blob/913f83920f7f76183fc7d6ea58eebbceeb82f452/src/lib/types.ts#L34-L43
|
|
51
|
+
[WebPage]: https://schema.org/WebPage
|