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/README.md +81 -6
- package/dist/index.d.mts +31 -19
- package/dist/index.d.ts +31 -19
- package/dist/index.js +252 -201
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +254 -203
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/meta/MetaDescription.tsx +12 -11
- package/src/components/meta/MetaTitle.tsx +12 -11
- package/src/components/openGraph/OgDescription.tsx +5 -4
- package/src/components/openGraph/OgTitle.tsx +3 -2
- package/src/components/twitter/twitterDescription.tsx +3 -2
- package/src/components/twitter/twitterTitle.tsx +3 -2
- package/src/index.ts +5 -2
- package/src/plugin.ts +18 -18
- package/src/schemas/index.ts +100 -88
- package/src/schemas/types/index.ts +12 -1
- package/src/utils/fieldsUtils.ts +137 -0
- package/src/utils/generaeDynamicJsonLd.ts +1 -1
- package/src/utils/seoUtils.ts +133 -96
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" }),
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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" }),
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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" }),
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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" }),
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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" }),
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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" }),
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
value ||
|
|
186
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
value ||
|
|
215
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
})
|
|
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: {
|
|
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.
|
|
728
|
-
exports.
|
|
778
|
+
exports.robotsSchema = robots;
|
|
779
|
+
exports.seoFieldsSchema = seoFieldsSchema;
|
|
729
780
|
exports.twitterSchema = twitter;
|
|
730
781
|
//# sourceMappingURL=index.js.map
|