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/src/utils/seoUtils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import {PathSegment, useFormValue} from 'sanity'
|
|
2
|
+
|
|
2
3
|
export const stopWords = ['the', 'a', 'an', 'and', 'or', 'but']
|
|
3
4
|
|
|
4
5
|
export const hasMatchingKeyword = (title: string, keywordList: string[]): boolean => {
|
|
@@ -37,7 +38,11 @@ export const truncate = (text: string, maxLength: number) =>
|
|
|
37
38
|
|
|
38
39
|
export const hasExcessivePunctuation = (title: string): boolean => /[!@#$%^&*]{2,}/.test(title)
|
|
39
40
|
|
|
40
|
-
export const getMetaTitleValidationMessages = (
|
|
41
|
+
export const getMetaTitleValidationMessages = (
|
|
42
|
+
title: string,
|
|
43
|
+
keywords: string[],
|
|
44
|
+
isParentseoField: boolean,
|
|
45
|
+
) => {
|
|
41
46
|
const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []
|
|
42
47
|
|
|
43
48
|
const minChar = 50
|
|
@@ -64,26 +69,28 @@ export const getMetaTitleValidationMessages = (title: string, keywords: string[]
|
|
|
64
69
|
else feedback.push({text: `Title length (${charCount}) looks good for SEO.`, color: 'green'})
|
|
65
70
|
|
|
66
71
|
// Keyword checks
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
if (isParentseoField) {
|
|
73
|
+
if (keywords.length > 0) {
|
|
74
|
+
const hasKeyword = hasMatchingKeyword(title, keywords)
|
|
75
|
+
feedback.push({
|
|
76
|
+
text: hasKeyword
|
|
77
|
+
? 'Keyword found in title — good job!'
|
|
78
|
+
: 'Keywords defined but missing in title.',
|
|
79
|
+
color: hasKeyword ? 'green' : 'red',
|
|
80
|
+
})
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
if (hasKeywordOveruse(title, keywords)) {
|
|
83
|
+
feedback.push({
|
|
84
|
+
text: 'Keyword appears too many times — avoid keyword stuffing.',
|
|
85
|
+
color: 'orange',
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
77
89
|
feedback.push({
|
|
78
|
-
text: '
|
|
90
|
+
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
79
91
|
color: 'orange',
|
|
80
92
|
})
|
|
81
93
|
}
|
|
82
|
-
} else {
|
|
83
|
-
feedback.push({
|
|
84
|
-
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
85
|
-
color: 'orange',
|
|
86
|
-
})
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
// Stop word check
|
|
@@ -97,7 +104,11 @@ export const getMetaTitleValidationMessages = (title: string, keywords: string[]
|
|
|
97
104
|
return feedback
|
|
98
105
|
}
|
|
99
106
|
|
|
100
|
-
export const getMetaDescriptionValidationMessages = (
|
|
107
|
+
export const getMetaDescriptionValidationMessages = (
|
|
108
|
+
description: string,
|
|
109
|
+
keywords: string[],
|
|
110
|
+
isParentseoField: boolean,
|
|
111
|
+
) => {
|
|
101
112
|
const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []
|
|
102
113
|
|
|
103
114
|
const minChar = 150
|
|
@@ -124,26 +135,28 @@ export const getMetaDescriptionValidationMessages = (description: string, keywor
|
|
|
124
135
|
feedback.push({text: `Description length (${charCount}) looks good for SEO.`, color: 'green'})
|
|
125
136
|
|
|
126
137
|
// Keyword checks
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
if (isParentseoField) {
|
|
139
|
+
if (keywords.length > 0) {
|
|
140
|
+
const hasKeyword = hasMatchingKeyword(description, keywords)
|
|
141
|
+
feedback.push({
|
|
142
|
+
text: hasKeyword
|
|
143
|
+
? 'Keyword found in description — good job!'
|
|
144
|
+
: 'Keywords defined but missing in description.',
|
|
145
|
+
color: hasKeyword ? 'green' : 'red',
|
|
146
|
+
})
|
|
135
147
|
|
|
136
|
-
|
|
148
|
+
if (hasKeywordOveruse(description, keywords)) {
|
|
149
|
+
feedback.push({
|
|
150
|
+
text: 'Keyword appears too many times — avoid keyword stuffing.',
|
|
151
|
+
color: 'orange',
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
137
155
|
feedback.push({
|
|
138
|
-
text: '
|
|
156
|
+
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
139
157
|
color: 'orange',
|
|
140
158
|
})
|
|
141
159
|
}
|
|
142
|
-
} else {
|
|
143
|
-
feedback.push({
|
|
144
|
-
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
145
|
-
color: 'orange',
|
|
146
|
-
})
|
|
147
160
|
}
|
|
148
161
|
|
|
149
162
|
// Stop word / filler check
|
|
@@ -163,7 +176,11 @@ export const getMetaDescriptionValidationMessages = (description: string, keywor
|
|
|
163
176
|
return feedback
|
|
164
177
|
}
|
|
165
178
|
|
|
166
|
-
export const getOgTitleValidation = (
|
|
179
|
+
export const getOgTitleValidation = (
|
|
180
|
+
title: string,
|
|
181
|
+
keywords: string[] = [],
|
|
182
|
+
isParentseoField: boolean,
|
|
183
|
+
) => {
|
|
167
184
|
const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []
|
|
168
185
|
const min = 40
|
|
169
186
|
const max = 60
|
|
@@ -185,27 +202,29 @@ export const getOgTitleValidation = (title: string, keywords: string[] = []) =>
|
|
|
185
202
|
feedback.push({text: `OG Title is ${count} chars — exceeds recommended ${max}.`, color: 'red'})
|
|
186
203
|
else feedback.push({text: `OG Title length (${count}) looks good.`, color: 'green'})
|
|
187
204
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
205
|
+
if (isParentseoField) {
|
|
206
|
+
// Keyword checks
|
|
207
|
+
if (keywords.length > 0) {
|
|
208
|
+
const hasKeyword = hasMatchingKeyword(title, keywords)
|
|
209
|
+
feedback.push({
|
|
210
|
+
text: hasKeyword
|
|
211
|
+
? 'Keyword found in OG title — good job!'
|
|
212
|
+
: 'Keywords defined but missing in OG title.',
|
|
213
|
+
color: hasKeyword ? 'green' : 'red',
|
|
214
|
+
})
|
|
197
215
|
|
|
198
|
-
|
|
216
|
+
if (hasKeywordOveruse(title, keywords)) {
|
|
217
|
+
feedback.push({
|
|
218
|
+
text: 'Keyword appears too many times in OG title — avoid keyword stuffing.',
|
|
219
|
+
color: 'orange',
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
199
223
|
feedback.push({
|
|
200
|
-
text: '
|
|
224
|
+
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
201
225
|
color: 'orange',
|
|
202
226
|
})
|
|
203
227
|
}
|
|
204
|
-
} else {
|
|
205
|
-
feedback.push({
|
|
206
|
-
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
207
|
-
color: 'orange',
|
|
208
|
-
})
|
|
209
228
|
}
|
|
210
229
|
|
|
211
230
|
// Additional OG-specific checks
|
|
@@ -221,7 +240,11 @@ export const getOgTitleValidation = (title: string, keywords: string[] = []) =>
|
|
|
221
240
|
return feedback
|
|
222
241
|
}
|
|
223
242
|
|
|
224
|
-
export const getOgDescriptionValidation = (
|
|
243
|
+
export const getOgDescriptionValidation = (
|
|
244
|
+
desc: string,
|
|
245
|
+
keywords: string[] = [],
|
|
246
|
+
isParentseoField: boolean,
|
|
247
|
+
) => {
|
|
225
248
|
const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []
|
|
226
249
|
const min = 90
|
|
227
250
|
const max = 120
|
|
@@ -250,26 +273,28 @@ export const getOgDescriptionValidation = (desc: string, keywords: string[] = []
|
|
|
250
273
|
else feedback.push({text: `OG Description length (${count}) looks good.`, color: 'green'})
|
|
251
274
|
|
|
252
275
|
// Keyword checks
|
|
253
|
-
if (
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
276
|
+
if (isParentseoField) {
|
|
277
|
+
if (keywords.length > 0) {
|
|
278
|
+
const hasKeyword = hasMatchingKeyword(desc, keywords)
|
|
279
|
+
feedback.push({
|
|
280
|
+
text: hasKeyword
|
|
281
|
+
? 'Keyword found in OG description — good job!'
|
|
282
|
+
: 'Keywords defined but missing in OG description.',
|
|
283
|
+
color: hasKeyword ? 'green' : 'red',
|
|
284
|
+
})
|
|
261
285
|
|
|
262
|
-
|
|
286
|
+
if (hasKeywordOveruse(desc, keywords)) {
|
|
287
|
+
feedback.push({
|
|
288
|
+
text: 'Keyword appears too many times in OG description — avoid keyword stuffing.',
|
|
289
|
+
color: 'orange',
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
263
293
|
feedback.push({
|
|
264
|
-
text: '
|
|
294
|
+
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
265
295
|
color: 'orange',
|
|
266
296
|
})
|
|
267
297
|
}
|
|
268
|
-
} else {
|
|
269
|
-
feedback.push({
|
|
270
|
-
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
271
|
-
color: 'orange',
|
|
272
|
-
})
|
|
273
298
|
}
|
|
274
299
|
|
|
275
300
|
// Additional OG-specific checks
|
|
@@ -288,7 +313,11 @@ export const getOgDescriptionValidation = (desc: string, keywords: string[] = []
|
|
|
288
313
|
return feedback
|
|
289
314
|
}
|
|
290
315
|
|
|
291
|
-
export const getTwitterTitleValidation = (
|
|
316
|
+
export const getTwitterTitleValidation = (
|
|
317
|
+
title: string,
|
|
318
|
+
keywords: string[] = [],
|
|
319
|
+
isParentseoField: boolean,
|
|
320
|
+
) => {
|
|
292
321
|
const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []
|
|
293
322
|
const min = 30
|
|
294
323
|
const max = 70
|
|
@@ -312,20 +341,22 @@ export const getTwitterTitleValidation = (title: string, keywords: string[] = []
|
|
|
312
341
|
})
|
|
313
342
|
else feedback.push({text: `Twitter Title length (${count}) looks good.`, color: 'green'})
|
|
314
343
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
344
|
+
if (isParentseoField) {
|
|
345
|
+
// Keyword checks
|
|
346
|
+
if (keywords.length > 0) {
|
|
347
|
+
const hasKeyword = hasMatchingKeyword(title, keywords)
|
|
348
|
+
feedback.push({
|
|
349
|
+
text: hasKeyword
|
|
350
|
+
? 'Keyword found in Twitter title — good job!'
|
|
351
|
+
: 'Keywords defined but missing in Twitter title.',
|
|
352
|
+
color: hasKeyword ? 'green' : 'red',
|
|
353
|
+
})
|
|
354
|
+
} else {
|
|
355
|
+
feedback.push({
|
|
356
|
+
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
357
|
+
color: 'orange',
|
|
358
|
+
})
|
|
359
|
+
}
|
|
329
360
|
}
|
|
330
361
|
|
|
331
362
|
// Punctuation check
|
|
@@ -335,7 +366,11 @@ export const getTwitterTitleValidation = (title: string, keywords: string[] = []
|
|
|
335
366
|
return feedback
|
|
336
367
|
}
|
|
337
368
|
|
|
338
|
-
export const getTwitterDescriptionValidation = (
|
|
369
|
+
export const getTwitterDescriptionValidation = (
|
|
370
|
+
desc: string,
|
|
371
|
+
keywords: string[] = [],
|
|
372
|
+
isParentseoField: boolean,
|
|
373
|
+
) => {
|
|
339
374
|
const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []
|
|
340
375
|
const min = 50
|
|
341
376
|
const max = 200
|
|
@@ -359,20 +394,22 @@ export const getTwitterDescriptionValidation = (desc: string, keywords: string[]
|
|
|
359
394
|
})
|
|
360
395
|
else feedback.push({text: `Twitter Description length (${count}) looks good.`, color: 'green'})
|
|
361
396
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
397
|
+
if (isParentseoField) {
|
|
398
|
+
// Keyword checks
|
|
399
|
+
if (keywords.length > 0) {
|
|
400
|
+
const hasKeyword = hasMatchingKeyword(desc, keywords)
|
|
401
|
+
feedback.push({
|
|
402
|
+
text: hasKeyword
|
|
403
|
+
? 'Keyword found in Twitter description — good job!'
|
|
404
|
+
: 'Keywords defined but missing in Twitter description.',
|
|
405
|
+
color: hasKeyword ? 'green' : 'red',
|
|
406
|
+
})
|
|
407
|
+
} else {
|
|
408
|
+
feedback.push({
|
|
409
|
+
text: 'No keywords defined. Consider adding relevant keywords.',
|
|
410
|
+
color: 'orange',
|
|
411
|
+
})
|
|
412
|
+
}
|
|
376
413
|
}
|
|
377
414
|
|
|
378
415
|
// Punctuation check
|