sanity-plugin-seofields 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.
@@ -0,0 +1,93 @@
1
+ import {defineField, defineType} from 'sanity'
2
+ import MetaTitle from '../components/meta/MetaTitle'
3
+ import MetaDescription from '../components/meta/MetaDescription'
4
+ import SeoPreview from '../components/SeoPreview'
5
+
6
+ export default defineType({
7
+ name: 'seoFields',
8
+ title: 'SEO Fields',
9
+ type: 'object',
10
+ fields: [
11
+ defineField({
12
+ name: 'robots',
13
+ title: 'Robots Settings',
14
+ type: 'robots', // Use the separate robots type here
15
+ }),
16
+ defineField({
17
+ name: 'preview',
18
+ title: 'SEO Preview',
19
+ type: 'string',
20
+ components: {
21
+ input: SeoPreview, // custom React component
22
+ },
23
+ // you can mark it read-only if you don't want editors to change it
24
+ readOnly: true,
25
+ }),
26
+ defineField({
27
+ name: 'title',
28
+ title: 'Meta Title',
29
+ type: 'string',
30
+ description:
31
+ 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',
32
+ components: {
33
+ input: MetaTitle,
34
+ },
35
+ // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),
36
+ }),
37
+ defineField({
38
+ name: 'description',
39
+ title: 'Meta Description',
40
+ type: 'text',
41
+ rows: 3,
42
+ description:
43
+ 'Provide a concise summary of the page content. This description may be used by search engines in search results.',
44
+ components: {
45
+ input: MetaDescription,
46
+ },
47
+ // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),
48
+ }),
49
+ defineField({
50
+ name: 'metaImage',
51
+ title: 'Meta Image',
52
+ type: 'image',
53
+ options: {
54
+ hotspot: true,
55
+ },
56
+ description:
57
+ 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',
58
+ }),
59
+ defineField({
60
+ name: 'metaAttributes',
61
+ title: 'Additional Meta Attributes',
62
+ type: 'array',
63
+ of: [{type: 'metaAttribute'}],
64
+ description:
65
+ 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
66
+ }),
67
+ defineField({
68
+ name: 'keywords',
69
+ title: 'Keywords',
70
+ type: 'array',
71
+ of: [{type: 'string'}],
72
+ description:
73
+ 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',
74
+ }),
75
+ defineField({
76
+ name: 'canonicalUrl',
77
+ title: 'Canonical URL',
78
+ type: 'url',
79
+ description:
80
+ 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',
81
+ }),
82
+ defineField({
83
+ name: 'openGraph',
84
+ title: 'Open Graph Settings',
85
+ type: 'openGraph',
86
+ }),
87
+ defineField({
88
+ name: 'twitter',
89
+ title: 'Twitter Card Settings',
90
+ type: 'twitter',
91
+ }),
92
+ ],
93
+ })
@@ -0,0 +1,7 @@
1
+ import seoFields from '..'
2
+ import metaAttribute from './metaAttribute'
3
+ import openGraph from './openGraph'
4
+ import twitter from './twitter'
5
+ import robots from './robots'
6
+ import metaTag from './metaTag'
7
+ export default [seoFields, openGraph, metaAttribute, twitter, robots, metaTag]
@@ -0,0 +1,64 @@
1
+ import {defineField, defineType} from 'sanity'
2
+
3
+ export default defineType({
4
+ name: 'metaAttribute',
5
+ title: 'Meta Attribute',
6
+ type: 'object',
7
+ fields: [
8
+ defineField({
9
+ name: 'key',
10
+ title: 'Attribute Name',
11
+ type: 'string',
12
+ }),
13
+ defineField({
14
+ name: 'type',
15
+ title: 'Attribute Value Type',
16
+ type: 'string',
17
+ options: {
18
+ list: [
19
+ {title: 'String', value: 'string'},
20
+ {title: 'Image', value: 'image'},
21
+ ],
22
+ },
23
+ initialValue: 'string',
24
+ }),
25
+ defineField({
26
+ name: 'value',
27
+ title: 'Attribute Value',
28
+ type: 'string',
29
+ hidden: ({parent}) => parent?.type === 'image',
30
+ }),
31
+ defineField({
32
+ name: 'image',
33
+ title: 'Attribute Image Value',
34
+ type: 'image',
35
+ hidden: ({parent}) => parent?.type === 'string',
36
+ }),
37
+ ],
38
+ preview: {
39
+ select: {
40
+ attributeName: 'key',
41
+ attributeValueString: 'value',
42
+ attributeValueImage: 'image',
43
+ },
44
+ prepare({
45
+ attributeName,
46
+ attributeValueString,
47
+ attributeValueImage,
48
+ }: {
49
+ attributeName: string
50
+ attributeValueString: string
51
+ attributeValueImage: any
52
+ }) {
53
+ return {
54
+ title: attributeName || 'No Attribute Name',
55
+ subtitle: attributeValueString
56
+ ? `Value: ${attributeValueString}`
57
+ : attributeValueImage
58
+ ? 'Value: [Image]'
59
+ : 'No Attribute Value',
60
+ media: attributeValueImage,
61
+ }
62
+ },
63
+ },
64
+ })
@@ -0,0 +1,16 @@
1
+ import { defineType } from "sanity";
2
+
3
+ export default defineType({
4
+ name: "metaTag",
5
+ title: "Meta Tag",
6
+ type: "object",
7
+ fields: [
8
+ {
9
+ name: "metaAttributes",
10
+ title: "Meta Attributes",
11
+ type: "array",
12
+ of: [{ type: "metaAttribute" }],
13
+ description: "Add custom meta attributes to the head of the document for additional SEO and social media integration.",
14
+ },
15
+ ],
16
+ })
@@ -0,0 +1,86 @@
1
+ import {defineField, defineType} from 'sanity'
2
+ import OgTitle from '../../../components/openGraph/OgTitle'
3
+ import OgDescription from '../../../components/openGraph/OgDescription'
4
+
5
+ export default defineType({
6
+ name: 'openGraph',
7
+ title: 'Open Graph Settings',
8
+ type: 'object',
9
+ fields: [
10
+ defineField({
11
+ name: 'title',
12
+ title: 'Open Graph Title',
13
+ type: 'string',
14
+ components: {
15
+ input: OgTitle, // Can also wrap with a string input + preview
16
+ },
17
+ }),
18
+ defineField({
19
+ name: 'description',
20
+ title: 'Open Graph Description',
21
+ type: 'text',
22
+ rows: 3,
23
+ components: {
24
+ input: OgDescription, // Can also wrap with a text area + preview
25
+ },
26
+ }),
27
+ defineField({
28
+ name: 'siteName',
29
+ title: 'Open Graph Site Name',
30
+ type: 'string',
31
+ description: 'The name of your website. This is often the site title.',
32
+ }),
33
+ defineField({
34
+ name: 'type',
35
+ title: 'Open Graph Type',
36
+ type: 'string',
37
+ options: {
38
+ list: [
39
+ {title: 'Website', value: 'website'},
40
+ {title: 'Article', value: 'article'},
41
+ {title: 'Profile', value: 'profile'},
42
+ {title: 'Book', value: 'book'},
43
+ {title: 'Music', value: 'music'},
44
+ {title: 'Video', value: 'video'},
45
+ {title: 'Product', value: 'product'},
46
+ ],
47
+ // layout: 'radio', // Display as radio buttons
48
+ },
49
+ initialValue: 'website',
50
+ description: 'Select the type of content for Open Graph.',
51
+ }),
52
+
53
+ // upload image or ask for url
54
+ defineField({
55
+ name: 'imageType',
56
+ title: 'Image Type',
57
+ type: 'string',
58
+ options: {
59
+ list: [
60
+ {title: 'Upload Image', value: 'upload'},
61
+ {title: 'Image URL', value: 'url'},
62
+ ],
63
+ },
64
+ initialValue: 'upload',
65
+ }),
66
+ defineField({
67
+ name: 'image',
68
+ title: 'Open Graph Image',
69
+ type: 'image',
70
+ options: {
71
+ hotspot: true,
72
+ },
73
+ hidden: ({parent}) => parent?.imageType !== 'upload',
74
+ description:
75
+ 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',
76
+ }),
77
+ defineField({
78
+ name: 'imageUrl',
79
+ title: 'Open Graph Image URL',
80
+ type: 'url',
81
+ hidden: ({parent}) => parent?.imageType !== 'url',
82
+ description:
83
+ 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',
84
+ }),
85
+ ],
86
+ })
@@ -0,0 +1,26 @@
1
+ import {defineField, defineType} from 'sanity'
2
+
3
+ export default defineType({
4
+ name: 'robots',
5
+ title: 'Robots Settings',
6
+ type: 'object',
7
+ fields: [
8
+ defineField({
9
+ name: 'noIndex',
10
+ title: 'No Index',
11
+ type: 'boolean',
12
+ initialValue: false,
13
+ description:
14
+ 'Enable this to prevent search engines from indexing this page. The page will not appear in search results.',
15
+ }),
16
+ defineField({
17
+ name: 'noFollow',
18
+ title: 'No Follow',
19
+ type: 'boolean',
20
+ initialValue: false,
21
+ description:
22
+ 'Enable this to prevent search engines from following links on this page. Links will not pass SEO value.',
23
+ }),
24
+ ],
25
+ description: 'Select how search engines should index and follow links on this page.',
26
+ })
@@ -0,0 +1,68 @@
1
+ import {defineField, defineType} from 'sanity'
2
+ import TwitterTitle from '../../../components/twitter/twitterTitle'
3
+ import TwitterDescription from '../../../components/twitter/twitterDescription'
4
+
5
+ export default defineType({
6
+ name: 'twitter',
7
+ title: 'Twitter',
8
+ type: 'object',
9
+ fields: [
10
+ defineField({
11
+ name: 'card',
12
+ title: 'Card Type',
13
+ type: 'string',
14
+ options: {
15
+ list: [
16
+ {title: 'Summary', value: 'summary'},
17
+ {title: 'Summary with Large Image', value: 'summary_large_image'},
18
+ {title: 'App', value: 'app'},
19
+ {title: 'Player', value: 'player'},
20
+ ],
21
+ },
22
+ initialValue: 'summary_large_image', // good default
23
+ }),
24
+ defineField({
25
+ name: 'site',
26
+ title: 'Site Twitter Handle',
27
+ type: 'string',
28
+ description: 'The Twitter handle of the website (e.g., @example)',
29
+ }),
30
+ defineField({
31
+ name: 'title',
32
+ title: 'Twitter Title',
33
+ type: 'string',
34
+ description: 'The title of the content as it should appear on Twitter.',
35
+ components: {
36
+ input: TwitterTitle,
37
+ },
38
+ }),
39
+ defineField({
40
+ name: 'description',
41
+ title: 'Twitter Description',
42
+ type: 'text',
43
+ rows: 3,
44
+ description: 'A brief description of the content for Twitter.',
45
+ components: {
46
+ input: TwitterDescription,
47
+ },
48
+ }),
49
+ defineField({
50
+ name: 'image',
51
+ title: 'Twitter Image',
52
+ type: 'image',
53
+ description:
54
+ 'An image URL which should be at least 120x120px for "summary" card and 280x150px for "summary_large_image" card.',
55
+ options: {
56
+ hotspot: true,
57
+ },
58
+ fields: [
59
+ defineField({
60
+ name: 'alt',
61
+ title: 'Image Alt Text',
62
+ type: 'string',
63
+ description: 'Short alt text describing the image',
64
+ }),
65
+ ],
66
+ }),
67
+ ],
68
+ })
@@ -0,0 +1,239 @@
1
+ export type MetaAttribute = {
2
+ _type: 'metaAttribute'
3
+ key?: string
4
+ type?: 'string' | 'image'
5
+ value?: string
6
+ image?: {
7
+ asset?: {
8
+ _ref: string
9
+ _type: 'reference'
10
+ _weak?: boolean
11
+ [internalGroqTypeReferenceTo]?: 'sanity.imageAsset'
12
+ }
13
+ media?: unknown
14
+ hotspot?: SanityImageHotspot
15
+ crop?: SanityImageCrop
16
+ _type: 'image'
17
+ }
18
+ }
19
+
20
+ export type MetaTag = {
21
+ _type: 'metaTag'
22
+ metaAttributes?: Array<
23
+ {
24
+ _key: string
25
+ } & MetaAttribute
26
+ >
27
+ }
28
+
29
+ export type SeoFields = {
30
+ _type: 'seoFields'
31
+ robots?: Robots
32
+ preview?: string
33
+ title?: string
34
+ description?: string
35
+ metaImage?: {
36
+ asset?: {
37
+ _ref: string
38
+ _type: 'reference'
39
+ _weak?: boolean
40
+ [internalGroqTypeReferenceTo]?: 'sanity.imageAsset'
41
+ }
42
+ media?: unknown
43
+ hotspot?: SanityImageHotspot
44
+ crop?: SanityImageCrop
45
+ _type: 'image'
46
+ }
47
+ metaAttributes?: Array<MetaAttribute>
48
+ keywords?: Array<string>
49
+ canonicalUrl?: string
50
+ openGraph?: OpenGraph
51
+ twitter?: Twitter
52
+ }
53
+
54
+ export type Twitter = {
55
+ _type: 'twitter'
56
+ card?: 'summary' | 'summary_large_image' | 'app' | 'player'
57
+ site?: string
58
+ title?: string
59
+ description?: string
60
+ image?: {
61
+ asset?: {
62
+ _ref: string
63
+ _type: 'reference'
64
+ _weak?: boolean
65
+ [internalGroqTypeReferenceTo]?: 'sanity.imageAsset'
66
+ }
67
+ media?: unknown
68
+ hotspot?: SanityImageHotspot
69
+ crop?: SanityImageCrop
70
+ alt?: string
71
+ _type: 'image'
72
+ }
73
+ }
74
+
75
+ export type OpenGraph = {
76
+ _type: 'openGraph'
77
+ title?: string
78
+ description?: string
79
+ siteName?: string
80
+ type?: 'website' | 'article' | 'profile' | 'book' | 'music' | 'video' | 'product'
81
+ imageType?: 'upload' | 'url'
82
+ image?: {
83
+ asset?: {
84
+ _ref: string
85
+ _type: 'reference'
86
+ _weak?: boolean
87
+ [internalGroqTypeReferenceTo]?: 'sanity.imageAsset'
88
+ }
89
+ media?: unknown
90
+ hotspot?: SanityImageHotspot
91
+ crop?: SanityImageCrop
92
+ _type: 'image'
93
+ }
94
+ imageUrl?: string
95
+ }
96
+
97
+ export type Robots = {
98
+ _type: 'robots'
99
+ noIndex?: boolean
100
+ noFollow?: boolean
101
+ }
102
+
103
+ export type SanityImagePaletteSwatch = {
104
+ _type: 'sanity.imagePaletteSwatch'
105
+ background?: string
106
+ foreground?: string
107
+ population?: number
108
+ title?: string
109
+ }
110
+
111
+ export type SanityImagePalette = {
112
+ _type: 'sanity.imagePalette'
113
+ darkMuted?: SanityImagePaletteSwatch
114
+ lightVibrant?: SanityImagePaletteSwatch
115
+ darkVibrant?: SanityImagePaletteSwatch
116
+ vibrant?: SanityImagePaletteSwatch
117
+ dominant?: SanityImagePaletteSwatch
118
+ lightMuted?: SanityImagePaletteSwatch
119
+ muted?: SanityImagePaletteSwatch
120
+ }
121
+
122
+ export type SanityImageDimensions = {
123
+ _type: 'sanity.imageDimensions'
124
+ height: number
125
+ width: number
126
+ aspectRatio: number
127
+ }
128
+
129
+ export type SanityImageHotspot = {
130
+ _type: 'sanity.imageHotspot'
131
+ x: number
132
+ y: number
133
+ height: number
134
+ width: number
135
+ }
136
+
137
+ export type SanityImageCrop = {
138
+ _type: 'sanity.imageCrop'
139
+ top: number
140
+ bottom: number
141
+ left: number
142
+ right: number
143
+ }
144
+
145
+ export type SanityFileAsset = {
146
+ _id: string
147
+ _type: 'sanity.fileAsset'
148
+ _createdAt: string
149
+ _updatedAt: string
150
+ _rev: string
151
+ originalFilename?: string
152
+ label?: string
153
+ title?: string
154
+ description?: string
155
+ altText?: string
156
+ sha1hash?: string
157
+ extension?: string
158
+ mimeType?: string
159
+ size?: number
160
+ assetId?: string
161
+ uploadId?: string
162
+ path?: string
163
+ url?: string
164
+ source?: SanityAssetSourceData
165
+ }
166
+
167
+ export type SanityImageAsset = {
168
+ _id: string
169
+ _type: 'sanity.imageAsset'
170
+ _createdAt: string
171
+ _updatedAt: string
172
+ _rev: string
173
+ originalFilename?: string
174
+ label?: string
175
+ title?: string
176
+ description?: string
177
+ altText?: string
178
+ sha1hash?: string
179
+ extension?: string
180
+ mimeType?: string
181
+ size?: number
182
+ assetId?: string
183
+ uploadId?: string
184
+ path?: string
185
+ url?: string
186
+ metadata?: SanityImageMetadata
187
+ source?: SanityAssetSourceData
188
+ }
189
+
190
+ export type SanityImageMetadata = {
191
+ _type: 'sanity.imageMetadata'
192
+ location?: Geopoint
193
+ dimensions?: SanityImageDimensions
194
+ palette?: SanityImagePalette
195
+ lqip?: string
196
+ blurHash?: string
197
+ hasAlpha?: boolean
198
+ isOpaque?: boolean
199
+ }
200
+
201
+ export type Geopoint = {
202
+ _type: 'geopoint'
203
+ lat?: number
204
+ lng?: number
205
+ alt?: number
206
+ }
207
+
208
+ export type Slug = {
209
+ _type: 'slug'
210
+ current: string
211
+ source?: string
212
+ }
213
+
214
+ export type SanityAssetSourceData = {
215
+ _type: 'sanity.assetSourceData'
216
+ name?: string
217
+ id?: string
218
+ url?: string
219
+ }
220
+
221
+ export type AllSanitySchemaTypes =
222
+ | MetaAttribute
223
+ | MetaTag
224
+ | SeoFields
225
+ | Twitter
226
+ | OpenGraph
227
+ | Robots
228
+ | SanityImagePaletteSwatch
229
+ | SanityImagePalette
230
+ | SanityImageDimensions
231
+ | SanityImageHotspot
232
+ | SanityImageCrop
233
+ | SanityFileAsset
234
+ | SanityImageAsset
235
+ | SanityImageMetadata
236
+ | Geopoint
237
+ | Slug
238
+ | SanityAssetSourceData
239
+ export declare const internalGroqTypeReferenceTo: unique symbol