sanity-plugin-seofields 1.0.1 → 1.0.2

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/index.js CHANGED
@@ -17,7 +17,7 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
17
17
  if (!title) return !1;
18
18
  const firstWord = title.trim().split(" ")[0].toLowerCase();
19
19
  return stopWords.includes(firstWord);
20
- }, truncate = (text, maxLength) => text.length > maxLength ? text.slice(0, maxLength) + "\u2026" : text, hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title), getMetaTitleValidationMessages = (title, keywords) => {
20
+ }, truncate = (text, maxLength) => text.length > maxLength ? text.slice(0, maxLength) + "\u2026" : text, hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title), getMetaTitleValidationMessages = (title, keywords, isParentseoField) => {
21
21
  const feedback = [], charCount = title?.length || 0;
22
22
  if (!title?.trim())
23
23
  return feedback.push({ text: "Meta Title is empty. Add content to improve SEO.", color: "red" }), feedback;
@@ -27,22 +27,23 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
27
27
  }) : charCount > 60 ? feedback.push({
28
28
  text: `Title is ${charCount} characters \u2014 exceeds recommended 60.`,
29
29
  color: "red"
30
- }) : feedback.push({ text: `Title length (${charCount}) looks good for SEO.`, color: "green" }), keywords.length > 0) {
31
- const hasKeyword = hasMatchingKeyword(title, keywords);
32
- feedback.push({
33
- text: hasKeyword ? "Keyword found in title \u2014 good job!" : "Keywords defined but missing in title.",
34
- color: hasKeyword ? "green" : "red"
35
- }), hasKeywordOveruse(title, keywords) && feedback.push({
36
- text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
37
- color: "orange"
38
- });
39
- } else
40
- feedback.push({
41
- text: "No keywords defined. Consider adding relevant keywords.",
42
- color: "orange"
43
- });
30
+ }) : feedback.push({ text: `Title length (${charCount}) looks good for SEO.`, color: "green" }), isParentseoField)
31
+ if (keywords.length > 0) {
32
+ const hasKeyword = hasMatchingKeyword(title, keywords);
33
+ feedback.push({
34
+ text: hasKeyword ? "Keyword found in title \u2014 good job!" : "Keywords defined but missing in title.",
35
+ color: hasKeyword ? "green" : "red"
36
+ }), hasKeywordOveruse(title, keywords) && feedback.push({
37
+ text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
38
+ color: "orange"
39
+ });
40
+ } else
41
+ feedback.push({
42
+ text: "No keywords defined. Consider adding relevant keywords.",
43
+ color: "orange"
44
+ });
44
45
  return startsWithStopWord(title) && feedback.push({ text: "Title starts with a stop word \u2014 consider rephrasing.", color: "orange" }), hasExcessivePunctuation(title) && feedback.push({ text: "Title contains excessive punctuation \u2014 simplify it.", color: "orange" }), feedback;
45
- }, getMetaDescriptionValidationMessages = (description, keywords) => {
46
+ }, getMetaDescriptionValidationMessages = (description, keywords, isParentseoField) => {
46
47
  const feedback = [], charCount = description?.length || 0;
47
48
  if (!description?.trim())
48
49
  return feedback.push({ text: "Meta description is empty. Add content to improve SEO.", color: "red" }), feedback;
@@ -52,20 +53,21 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
52
53
  }) : charCount > 160 ? feedback.push({
53
54
  text: `Description is ${charCount} chars \u2014 exceeds recommended 160.`,
54
55
  color: "red"
55
- }) : feedback.push({ text: `Description length (${charCount}) looks good for SEO.`, color: "green" }), keywords.length > 0) {
56
- const hasKeyword = hasMatchingKeyword(description, keywords);
57
- feedback.push({
58
- text: hasKeyword ? "Keyword found in description \u2014 good job!" : "Keywords defined but missing in description.",
59
- color: hasKeyword ? "green" : "red"
60
- }), hasKeywordOveruse(description, keywords) && feedback.push({
61
- text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
62
- color: "orange"
63
- });
64
- } else
65
- feedback.push({
66
- text: "No keywords defined. Consider adding relevant keywords.",
67
- color: "orange"
68
- });
56
+ }) : feedback.push({ text: `Description length (${charCount}) looks good for SEO.`, color: "green" }), isParentseoField)
57
+ if (keywords.length > 0) {
58
+ const hasKeyword = hasMatchingKeyword(description, keywords);
59
+ feedback.push({
60
+ text: hasKeyword ? "Keyword found in description \u2014 good job!" : "Keywords defined but missing in description.",
61
+ color: hasKeyword ? "green" : "red"
62
+ }), hasKeywordOveruse(description, keywords) && feedback.push({
63
+ text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
64
+ color: "orange"
65
+ });
66
+ } else
67
+ feedback.push({
68
+ text: "No keywords defined. Consider adding relevant keywords.",
69
+ color: "orange"
70
+ });
69
71
  return startsWithStopWord(description) && feedback.push({
70
72
  text: "Description starts with a stop word \u2014 consider rephrasing.",
71
73
  color: "orange"
@@ -73,32 +75,33 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
73
75
  text: "Description contains excessive punctuation \u2014 simplify it.",
74
76
  color: "orange"
75
77
  }), feedback;
76
- }, getOgTitleValidation = (title, keywords = []) => {
78
+ }, getOgTitleValidation = (title, keywords = [], isParentseoField) => {
77
79
  const feedback = [], count = title?.length || 0;
78
80
  if (!title?.trim())
79
81
  return feedback.push({ text: "OG Title is empty. Add content for better social preview.", color: "red" }), feedback;
80
82
  if (count < 40 ? feedback.push({
81
83
  text: `OG Title is ${count} chars \u2014 shorter than recommended 40.`,
82
84
  color: "orange"
83
- }) : count > 60 ? feedback.push({ text: `OG Title is ${count} chars \u2014 exceeds recommended 60.`, color: "red" }) : feedback.push({ text: `OG Title length (${count}) looks good.`, color: "green" }), keywords.length > 0) {
84
- const hasKeyword = hasMatchingKeyword(title, keywords);
85
- feedback.push({
86
- text: hasKeyword ? "Keyword found in OG title \u2014 good job!" : "Keywords defined but missing in OG title.",
87
- color: hasKeyword ? "green" : "red"
88
- }), hasKeywordOveruse(title, keywords) && feedback.push({
89
- text: "Keyword appears too many times in OG title \u2014 avoid keyword stuffing.",
90
- color: "orange"
91
- });
92
- } else
93
- feedback.push({
94
- text: "No keywords defined. Consider adding relevant keywords.",
95
- color: "orange"
96
- });
85
+ }) : count > 60 ? feedback.push({ text: `OG Title is ${count} chars \u2014 exceeds recommended 60.`, color: "red" }) : feedback.push({ text: `OG Title length (${count}) looks good.`, color: "green" }), isParentseoField)
86
+ if (keywords.length > 0) {
87
+ const hasKeyword = hasMatchingKeyword(title, keywords);
88
+ feedback.push({
89
+ text: hasKeyword ? "Keyword found in OG title \u2014 good job!" : "Keywords defined but missing in OG title.",
90
+ color: hasKeyword ? "green" : "red"
91
+ }), hasKeywordOveruse(title, keywords) && feedback.push({
92
+ text: "Keyword appears too many times in OG title \u2014 avoid keyword stuffing.",
93
+ color: "orange"
94
+ });
95
+ } else
96
+ feedback.push({
97
+ text: "No keywords defined. Consider adding relevant keywords.",
98
+ color: "orange"
99
+ });
97
100
  return startsWithStopWord(title) && feedback.push({
98
101
  text: "OG Title starts with a stop word \u2014 consider rephrasing.",
99
102
  color: "orange"
100
103
  }), hasExcessivePunctuation(title) && feedback.push({ text: "OG Title contains excessive punctuation \u2014 simplify it.", color: "orange" }), feedback;
101
- }, getOgDescriptionValidation = (desc, keywords = []) => {
104
+ }, getOgDescriptionValidation = (desc, keywords = [], isParentseoField) => {
102
105
  const feedback = [], count = desc?.length || 0;
103
106
  if (!desc?.trim())
104
107
  return feedback.push({
@@ -111,20 +114,21 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
111
114
  }) : count > 120 ? feedback.push({
112
115
  text: `OG Description is ${count} chars \u2014 exceeds recommended 120.`,
113
116
  color: "red"
114
- }) : feedback.push({ text: `OG Description length (${count}) looks good.`, color: "green" }), keywords.length > 0) {
115
- const hasKeyword = hasMatchingKeyword(desc, keywords);
116
- feedback.push({
117
- text: hasKeyword ? "Keyword found in OG description \u2014 good job!" : "Keywords defined but missing in OG description.",
118
- color: hasKeyword ? "green" : "red"
119
- }), hasKeywordOveruse(desc, keywords) && feedback.push({
120
- text: "Keyword appears too many times in OG description \u2014 avoid keyword stuffing.",
121
- color: "orange"
122
- });
123
- } else
124
- feedback.push({
125
- text: "No keywords defined. Consider adding relevant keywords.",
126
- color: "orange"
127
- });
117
+ }) : feedback.push({ text: `OG Description length (${count}) looks good.`, color: "green" }), isParentseoField)
118
+ if (keywords.length > 0) {
119
+ const hasKeyword = hasMatchingKeyword(desc, keywords);
120
+ feedback.push({
121
+ text: hasKeyword ? "Keyword found in OG description \u2014 good job!" : "Keywords defined but missing in OG description.",
122
+ color: hasKeyword ? "green" : "red"
123
+ }), hasKeywordOveruse(desc, keywords) && feedback.push({
124
+ text: "Keyword appears too many times in OG description \u2014 avoid keyword stuffing.",
125
+ color: "orange"
126
+ });
127
+ } else
128
+ feedback.push({
129
+ text: "No keywords defined. Consider adding relevant keywords.",
130
+ color: "orange"
131
+ });
128
132
  return startsWithStopWord(desc) && feedback.push({
129
133
  text: "OG Description starts with a stop word \u2014 consider rephrasing.",
130
134
  color: "orange"
@@ -132,7 +136,7 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
132
136
  text: "OG Description contains excessive punctuation \u2014 simplify it.",
133
137
  color: "orange"
134
138
  }), feedback;
135
- }, getTwitterTitleValidation = (title, keywords = []) => {
139
+ }, getTwitterTitleValidation = (title, keywords = [], isParentseoField) => {
136
140
  const feedback = [], count = title?.length || 0;
137
141
  if (!title?.trim())
138
142
  return feedback.push({ text: "Twitter Title is empty. Add content for better SEO.", color: "red" }), feedback;
@@ -142,19 +146,20 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
142
146
  }) : count > 70 ? feedback.push({
143
147
  text: `Twitter Title is ${count} chars \u2014 exceeds recommended 70.`,
144
148
  color: "red"
145
- }) : feedback.push({ text: `Twitter Title length (${count}) looks good.`, color: "green" }), keywords.length > 0) {
146
- const hasKeyword = hasMatchingKeyword(title, keywords);
147
- feedback.push({
148
- text: hasKeyword ? "Keyword found in Twitter title \u2014 good job!" : "Keywords defined but missing in Twitter title.",
149
- color: hasKeyword ? "green" : "red"
150
- });
151
- } else
152
- feedback.push({
153
- text: "No keywords defined. Consider adding relevant keywords.",
154
- color: "orange"
155
- });
149
+ }) : feedback.push({ text: `Twitter Title length (${count}) looks good.`, color: "green" }), isParentseoField)
150
+ if (keywords.length > 0) {
151
+ const hasKeyword = hasMatchingKeyword(title, keywords);
152
+ feedback.push({
153
+ text: hasKeyword ? "Keyword found in Twitter title \u2014 good job!" : "Keywords defined but missing in Twitter title.",
154
+ color: hasKeyword ? "green" : "red"
155
+ });
156
+ } else
157
+ feedback.push({
158
+ text: "No keywords defined. Consider adding relevant keywords.",
159
+ color: "orange"
160
+ });
156
161
  return /[!@#$%^&*]{2,}/.test(title) && feedback.push({ text: "Twitter Title has excessive punctuation \u2014 simplify it.", color: "orange" }), feedback;
157
- }, getTwitterDescriptionValidation = (desc, keywords = []) => {
162
+ }, getTwitterDescriptionValidation = (desc, keywords = [], isParentseoField) => {
158
163
  const feedback = [], count = desc?.length || 0;
159
164
  if (!desc?.trim())
160
165
  return feedback.push({ text: "Twitter Description is empty. Add content for better SEO.", color: "red" }), feedback;
@@ -164,32 +169,27 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
164
169
  }) : count > 200 ? feedback.push({
165
170
  text: `Twitter Description is ${count} chars \u2014 exceeds recommended 200.`,
166
171
  color: "red"
167
- }) : feedback.push({ text: `Twitter Description length (${count}) looks good.`, color: "green" }), keywords.length > 0) {
168
- const hasKeyword = hasMatchingKeyword(desc, keywords);
169
- feedback.push({
170
- text: hasKeyword ? "Keyword found in Twitter description \u2014 good job!" : "Keywords defined but missing in Twitter description.",
171
- color: hasKeyword ? "green" : "red"
172
- });
173
- } else
174
- feedback.push({
175
- text: "No keywords defined. Consider adding relevant keywords.",
176
- color: "orange"
177
- });
172
+ }) : feedback.push({ text: `Twitter Description length (${count}) looks good.`, color: "green" }), isParentseoField)
173
+ if (keywords.length > 0) {
174
+ const hasKeyword = hasMatchingKeyword(desc, keywords);
175
+ feedback.push({
176
+ text: hasKeyword ? "Keyword found in Twitter description \u2014 good job!" : "Keywords defined but missing in Twitter description.",
177
+ color: hasKeyword ? "green" : "red"
178
+ });
179
+ } else
180
+ feedback.push({
181
+ text: "No keywords defined. Consider adding relevant keywords.",
182
+ color: "orange"
183
+ });
178
184
  return /[!@#$%^&*]{2,}/.test(desc) && feedback.push({
179
185
  text: "Twitter Description has excessive punctuation \u2014 simplify it.",
180
186
  color: "orange"
181
187
  }), feedback;
182
188
  }, MetaTitle = (props) => {
183
- const client = sanity.useClient({ apiVersion: "2024-05-05" }), { value, onChange, renderDefault, path } = props, keywords = sanity.useFormValue([path[0]])?.keywords || [];
184
- react.useEffect(() => {
185
- value || (async () => {
186
- const data = await client.fetch("*[_type=='homePage'][0]{'title':seo.title}");
187
- data?.title && !value && onChange(sanity.set(data.title));
188
- })();
189
- }, [client, onChange, value]);
190
- const feedbackItems = react.useMemo(
191
- () => getMetaTitleValidationMessages(value || "", keywords),
192
- [value, keywords]
189
+ sanity.useClient({ apiVersion: "2024-05-05" });
190
+ const { value, renderDefault, path } = props, parent = sanity.useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = react.useMemo(
191
+ () => getMetaTitleValidationMessages(value || "", keywords, isParentseoField),
192
+ [value, keywords, isParentseoField]
193
193
  );
194
194
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
195
195
  renderDefault(props),
@@ -209,16 +209,10 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
209
209
  ] }, item.text)) })
210
210
  ] });
211
211
  }, MetaDescription = (props) => {
212
- const client = sanity.useClient({ apiVersion: "2024-05-05" }), { value, onChange, renderDefault, path } = props, keywords = sanity.useFormValue([path[0]])?.keywords || [];
213
- react.useEffect(() => {
214
- value || (async () => {
215
- const data = await client.fetch("*[_type=='homePage'][0]{'description':seo.description}");
216
- data?.description && !value && onChange(sanity.set(data.description));
217
- })();
218
- }, [client, onChange, value]);
219
- const feedbackItems = react.useMemo(
220
- () => getMetaDescriptionValidationMessages(value || "", keywords),
221
- [value, keywords]
212
+ sanity.useClient({ apiVersion: "2024-05-05" });
213
+ const { value, renderDefault, path } = props, parent = sanity.useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = react.useMemo(
214
+ () => getMetaDescriptionValidationMessages(value || "", keywords, isParentseoField),
215
+ [value, keywords, isParentseoField]
222
216
  );
223
217
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
224
218
  renderDefault(props),
@@ -283,91 +277,133 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
283
277
  ] })
284
278
  }
285
279
  );
280
+ }, DEFAULT_FIELD_INFO = {
281
+ title: {
282
+ title: "Meta Title",
283
+ description: "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."
284
+ },
285
+ description: {
286
+ title: "Meta Description",
287
+ description: "Provide a concise summary of the page content. This description may be used by search engines in search results."
288
+ },
289
+ metaImage: {
290
+ title: "Meta Image",
291
+ description: "Upload an image that represents the content of the page. This image may be used in social media previews and search engine results."
292
+ },
293
+ keywords: {
294
+ title: "Keywords",
295
+ description: "Add relevant keywords for this page. These keywords will be used for SEO purposes."
296
+ },
297
+ canonicalUrl: {
298
+ title: "Canonical URL",
299
+ description: "Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page."
300
+ },
301
+ robots: {
302
+ title: "Robots Settings",
303
+ description: "Configure how search engine crawlers should index and follow links on this page."
304
+ },
305
+ metaAttributes: {
306
+ title: "Additional Meta Attributes",
307
+ description: "Add custom meta attributes to the head of the document for additional SEO and social media integration."
308
+ }
309
+ }, getFieldInfo = (fieldName, fieldOverrides) => {
310
+ const fieldInfo = fieldOverrides && fieldOverrides[fieldName] || DEFAULT_FIELD_INFO[fieldName];
311
+ return fieldInfo ? { title: fieldInfo.title, description: fieldInfo.description } : { title: "", description: "" };
286
312
  };
287
- var seoFields = sanity.defineType({
288
- name: "seoFields",
289
- title: "SEO Fields",
290
- type: "object",
291
- fields: [
292
- sanity.defineField({
293
- name: "robots",
294
- title: "Robots Settings",
295
- type: "robots"
296
- // Use the separate robots type here
297
- }),
298
- sanity.defineField({
299
- name: "preview",
300
- title: "SEO Preview",
301
- type: "string",
302
- components: {
303
- input: SeoPreview
304
- // custom React component
305
- },
306
- // you can mark it read-only if you don't want editors to change it
307
- readOnly: !0
308
- }),
309
- sanity.defineField({
310
- name: "title",
311
- title: "Meta Title",
312
- type: "string",
313
- description: "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.",
314
- components: {
315
- input: MetaTitle
316
- }
317
- // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),
318
- }),
319
- sanity.defineField({
320
- name: "description",
321
- title: "Meta Description",
322
- type: "text",
323
- rows: 3,
324
- description: "Provide a concise summary of the page content. This description may be used by search engines in search results.",
325
- components: {
326
- input: MetaDescription
327
- }
328
- // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),
329
- }),
330
- sanity.defineField({
331
- name: "metaImage",
332
- title: "Meta Image",
333
- type: "image",
334
- options: {
335
- hotspot: !0
336
- },
337
- description: "Upload an image that represents the content of the page. This image may be used in social media previews and search engine results."
338
- }),
339
- sanity.defineField({
340
- name: "metaAttributes",
341
- title: "Additional Meta Attributes",
342
- type: "array",
343
- of: [{ type: "metaAttribute" }],
344
- description: "Add custom meta attributes to the head of the document for additional SEO and social media integration."
345
- }),
346
- sanity.defineField({
347
- name: "keywords",
348
- title: "Keywords",
349
- type: "array",
350
- of: [{ type: "string" }],
351
- description: "Add relevant keywords for this page. These keywords will be used for SEO purposes."
352
- }),
353
- sanity.defineField({
354
- name: "canonicalUrl",
355
- title: "Canonical URL",
356
- type: "url",
357
- description: "Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page."
358
- }),
359
- sanity.defineField({
360
- name: "openGraph",
361
- title: "Open Graph Settings",
362
- type: "openGraph"
363
- }),
364
- sanity.defineField({
365
- name: "twitter",
366
- title: "Twitter Card Settings",
367
- type: "twitter"
368
- })
369
- ]
370
- }), metaAttribute = sanity.defineType({
313
+ function seoFieldsSchema(config = {}) {
314
+ return sanity.defineType({
315
+ name: "seoFields",
316
+ title: "SEO Fields",
317
+ type: "object",
318
+ fields: [
319
+ sanity.defineField({
320
+ name: "robots",
321
+ title: "Robots Settings",
322
+ type: "robots"
323
+ // Use the separate robots type here
324
+ }),
325
+ // 👇 conditionally spread preview field
326
+ ...config.seoPreview ? [] : [
327
+ sanity.defineField({
328
+ name: "preview",
329
+ title: "SEO Preview",
330
+ type: "string",
331
+ components: { input: SeoPreview },
332
+ readOnly: !0
333
+ })
334
+ ],
335
+ sanity.defineField({
336
+ name: "title",
337
+ ...getFieldInfo("title", config.fieldOverrides),
338
+ // title: 'Meta Title',
339
+ type: "string",
340
+ // description:
341
+ // '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.',
342
+ components: {
343
+ input: MetaTitle
344
+ }
345
+ // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),
346
+ }),
347
+ sanity.defineField({
348
+ name: "description",
349
+ ...getFieldInfo("description", config.fieldOverrides),
350
+ // title: 'Meta Description',
351
+ // description:
352
+ // 'Provide a concise summary of the page content. This description may be used by search engines in search results.',
353
+ type: "text",
354
+ rows: 3,
355
+ components: {
356
+ input: MetaDescription
357
+ }
358
+ // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),
359
+ }),
360
+ sanity.defineField({
361
+ name: "metaImage",
362
+ ...getFieldInfo("metaImage", config.fieldOverrides),
363
+ // title: 'Meta Image',
364
+ // description:
365
+ // 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',
366
+ type: "image",
367
+ options: {
368
+ hotspot: !0
369
+ }
370
+ }),
371
+ sanity.defineField({
372
+ name: "metaAttributes",
373
+ title: "Additional Meta Attributes",
374
+ type: "array",
375
+ of: [{ type: "metaAttribute" }],
376
+ description: "Add custom meta attributes to the head of the document for additional SEO and social media integration."
377
+ }),
378
+ sanity.defineField({
379
+ name: "keywords",
380
+ ...getFieldInfo("keywords", config.fieldOverrides),
381
+ title: "Keywords",
382
+ type: "array",
383
+ of: [{ type: "string" }],
384
+ description: "Add relevant keywords for this page. These keywords will be used for SEO purposes."
385
+ }),
386
+ sanity.defineField({
387
+ name: "canonicalUrl",
388
+ ...getFieldInfo("canonicalUrl", config.fieldOverrides),
389
+ title: "Canonical URL",
390
+ type: "url",
391
+ description: "Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page."
392
+ }),
393
+ sanity.defineField({
394
+ name: "openGraph",
395
+ title: "Open Graph Settings",
396
+ type: "openGraph"
397
+ }),
398
+ sanity.defineField({
399
+ name: "twitter",
400
+ title: "Twitter Card Settings",
401
+ type: "twitter"
402
+ })
403
+ ]
404
+ });
405
+ }
406
+ var metaAttribute = sanity.defineType({
371
407
  name: "metaAttribute",
372
408
  title: "Meta Attribute",
373
409
  type: "object",
@@ -422,8 +458,8 @@ var seoFields = sanity.defineType({
422
458
  }
423
459
  });
424
460
  const OgTitle = (props) => {
425
- const { value, renderDefault, path } = props, keywords = sanity.useFormValue([path[0]])?.keywords || [], feedbackItems = react.useMemo(
426
- () => getOgTitleValidation(value || "", keywords),
461
+ const { value, renderDefault, path } = props, parent = sanity.useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = react.useMemo(
462
+ () => getOgTitleValidation(value || "", keywords, isParentseoField),
427
463
  [value, keywords]
428
464
  );
429
465
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
@@ -444,9 +480,9 @@ const OgTitle = (props) => {
444
480
  ] }, item.text)) })
445
481
  ] });
446
482
  }, OgDescription = (props) => {
447
- const { value, renderDefault, path } = props, keywords = sanity.useFormValue([path[0]])?.keywords || [], feedbackItems = react.useMemo(
448
- () => getOgDescriptionValidation(value || "", keywords),
449
- [value, keywords]
483
+ const { value, renderDefault, path } = props, parent = sanity.useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = react.useMemo(
484
+ () => getOgDescriptionValidation(value || "", keywords, isParentseoField),
485
+ [value, keywords, isParentseoField]
450
486
  );
451
487
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
452
488
  renderDefault(props),
@@ -548,8 +584,8 @@ var openGraph = sanity.defineType({
548
584
  ]
549
585
  });
550
586
  const TwitterTitle = (props) => {
551
- const { value, renderDefault, path } = props, keywords = sanity.useFormValue([path[0]])?.keywords || [], feedbackItems = react.useMemo(
552
- () => getTwitterTitleValidation(value || "", keywords),
587
+ const { value, renderDefault, path } = props, parent = sanity.useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = react.useMemo(
588
+ () => getTwitterTitleValidation(value || "", keywords, isParentseoField),
553
589
  [value, keywords]
554
590
  );
555
591
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
@@ -570,8 +606,8 @@ const TwitterTitle = (props) => {
570
606
  ] }, item.text)) })
571
607
  ] });
572
608
  }, TwitterDescription = (props) => {
573
- const { value, renderDefault, path } = props, keywords = sanity.useFormValue([path[0]])?.keywords || [], feedbackItems = react.useMemo(
574
- () => getTwitterDescriptionValidation(value || "", keywords),
609
+ const { value, renderDefault, path } = props, parent = sanity.useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = react.useMemo(
610
+ () => getTwitterDescriptionValidation(value || "", keywords, isParentseoField),
575
611
  [value, keywords]
576
612
  );
577
613
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
@@ -689,10 +725,24 @@ var twitter = sanity.defineType({
689
725
  description: "Add custom meta attributes to the head of the document for additional SEO and social media integration."
690
726
  }
691
727
  ]
692
- }), types = [seoFields, openGraph, metaAttribute, twitter, robots, metaTag];
728
+ });
729
+ function types(config = {}) {
730
+ return [
731
+ seoFieldsSchema(config),
732
+ // pass config here
733
+ openGraph,
734
+ twitter,
735
+ metaAttribute,
736
+ metaTag,
737
+ robots
738
+ ];
739
+ }
693
740
  const seofields = sanity.definePlugin((config = {}) => ({
694
741
  name: "sanity-plugin-seofields",
695
- schema: { types }
742
+ schema: {
743
+ types: types(config)
744
+ // pass config down to schemas
745
+ }
696
746
  })), isSeoFields = (obj) => obj && obj._type === "seoFields", isOpenGraphSettings = (obj) => obj && obj._type === "openGraph", isTwitterCardSettings = (obj) => obj && obj._type === "twitter", isMetaAttribute = (obj) => obj && obj._type === "metaAttribute", defaultSeoValidationRules = {
697
747
  title: {
698
748
  maxLength: 70,
@@ -716,6 +766,7 @@ const seofields = sanity.definePlugin((config = {}) => ({
716
766
  }
717
767
  };
718
768
  exports.allSchemas = types;
769
+ exports.default = seofields;
719
770
  exports.defaultSeoValidationRules = defaultSeoValidationRules;
720
771
  exports.isMetaAttribute = isMetaAttribute;
721
772
  exports.isOpenGraphSettings = isOpenGraphSettings;
@@ -724,7 +775,7 @@ exports.isTwitterCardSettings = isTwitterCardSettings;
724
775
  exports.metaAttributeSchema = metaAttribute;
725
776
  exports.metaTagSchema = metaTag;
726
777
  exports.openGraphSchema = openGraph;
727
- exports.seoFieldsSchema = seoFields;
728
- exports.seofields = seofields;
778
+ exports.robotsSchema = robots;
779
+ exports.seoFieldsSchema = seoFieldsSchema;
729
780
  exports.twitterSchema = twitter;
730
781
  //# sourceMappingURL=index.js.map