sanity-plugin-seofields 1.0.8 → 1.0.10
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 +92 -171
- package/dist/index.d.mts +15 -189
- package/dist/index.d.ts +15 -189
- package/dist/index.js +230 -249
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +231 -250
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/SeoPreview.tsx +20 -19
- package/src/components/meta/MetaDescription.tsx +8 -17
- package/src/components/meta/MetaTitle.tsx +8 -17
- package/src/components/openGraph/OgDescription.tsx +6 -4
- package/src/components/openGraph/OgTitle.tsx +8 -6
- package/src/components/twitter/twitterDescription.tsx +9 -7
- package/src/components/twitter/twitterTitle.tsx +7 -5
- package/src/index.ts +3 -6
- package/src/plugin.ts +10 -9
- package/src/schemas/index.ts +10 -15
- package/src/schemas/types/index.ts +7 -5
- package/src/schemas/types/metaAttribute/index.ts +10 -14
- package/src/schemas/types/metaTag/index.ts +15 -14
- package/src/schemas/types/openGraph/index.ts +16 -5
- package/src/schemas/types/twitter/index.ts +16 -4
- package/src/types.ts +4 -65
- package/src/utils/fieldsUtils.ts +17 -96
- package/src/utils/seoUtils.ts +15 -15
- package/src/utils/utils.ts +2 -2
- package/src/types/index.ts +0 -241
- package/src/utils/generaeDynamicJsonLd.ts +0 -295
|
@@ -1,26 +1,28 @@
|
|
|
1
|
+
import {Stack, Text} from '@sanity/ui'
|
|
1
2
|
import React, {useMemo} from 'react'
|
|
2
3
|
import {StringInputProps, useFormValue} from 'sanity'
|
|
3
|
-
|
|
4
|
+
|
|
5
|
+
import {FeedbackType} from '../../types'
|
|
4
6
|
import {getTwitterDescriptionValidation} from '../../utils/seoUtils'
|
|
5
7
|
|
|
6
|
-
const TwitterDescription = (props: StringInputProps) => {
|
|
8
|
+
const TwitterDescription = (props: StringInputProps): React.ReactElement => {
|
|
7
9
|
const {value, renderDefault, path} = props
|
|
8
10
|
|
|
9
11
|
// Access parent object to get keywords
|
|
10
12
|
const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}
|
|
11
|
-
|
|
12
|
-
const keywords = parent?.keywords || []
|
|
13
|
+
const isParentseoField = parent && parent?._type === 'seoFields'
|
|
14
|
+
const keywords = useMemo(() => parent?.keywords || [], [parent?.keywords])
|
|
13
15
|
|
|
14
16
|
const feedbackItems = useMemo(
|
|
15
|
-
() => getTwitterDescriptionValidation(value || '', keywords,isParentseoField),
|
|
16
|
-
[value, keywords],
|
|
17
|
+
() => getTwitterDescriptionValidation(value || '', keywords, isParentseoField),
|
|
18
|
+
[value, keywords, isParentseoField],
|
|
17
19
|
)
|
|
18
20
|
|
|
19
21
|
return (
|
|
20
22
|
<Stack space={3}>
|
|
21
23
|
{renderDefault(props)}
|
|
22
24
|
<Stack space={2}>
|
|
23
|
-
{feedbackItems.map((item:
|
|
25
|
+
{feedbackItems.map((item: FeedbackType) => (
|
|
24
26
|
<div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>
|
|
25
27
|
<div
|
|
26
28
|
style={{
|
|
@@ -1,26 +1,28 @@
|
|
|
1
|
+
import {Stack, Text} from '@sanity/ui'
|
|
1
2
|
import React, {useMemo} from 'react'
|
|
2
3
|
import {StringInputProps, useFormValue} from 'sanity'
|
|
3
|
-
|
|
4
|
+
|
|
5
|
+
import {FeedbackType} from '../../types'
|
|
4
6
|
import {getTwitterTitleValidation} from '../../utils/seoUtils'
|
|
5
7
|
|
|
6
|
-
const TwitterTitle = (props: StringInputProps) => {
|
|
8
|
+
const TwitterTitle = (props: StringInputProps): React.ReactElement => {
|
|
7
9
|
const {value, renderDefault, path} = props
|
|
8
10
|
|
|
9
11
|
// Access parent object to get keywords
|
|
10
12
|
const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}
|
|
11
13
|
const isParentseoField = parent && parent?._type === 'seoFields'
|
|
12
|
-
const keywords = parent?.keywords || []
|
|
14
|
+
const keywords = useMemo(() => parent?.keywords || [], [parent?.keywords])
|
|
13
15
|
|
|
14
16
|
const feedbackItems = useMemo(
|
|
15
17
|
() => getTwitterTitleValidation(value || '', keywords, isParentseoField),
|
|
16
|
-
[value, keywords],
|
|
18
|
+
[value, keywords, isParentseoField],
|
|
17
19
|
)
|
|
18
20
|
|
|
19
21
|
return (
|
|
20
22
|
<Stack space={3}>
|
|
21
23
|
{renderDefault(props)}
|
|
22
24
|
<Stack space={2}>
|
|
23
|
-
{feedbackItems.map((item:
|
|
25
|
+
{feedbackItems.map((item: FeedbackType) => (
|
|
24
26
|
<div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>
|
|
25
27
|
<div
|
|
26
28
|
style={{
|
package/src/index.ts
CHANGED
|
@@ -7,14 +7,11 @@ export default seofields
|
|
|
7
7
|
// Re-export everything from plugin.ts
|
|
8
8
|
export * from './plugin'
|
|
9
9
|
|
|
10
|
-
// Export all TypeScript types
|
|
11
|
-
export * from './types'
|
|
12
|
-
|
|
13
10
|
// Export schema types for external use
|
|
14
11
|
export {default as seoFieldsSchema} from './schemas'
|
|
15
|
-
export {default as
|
|
16
|
-
export {default as twitterSchema} from './schemas/types/twitter'
|
|
12
|
+
export {default as allSchemas} from './schemas/types'
|
|
17
13
|
export {default as metaAttributeSchema} from './schemas/types/metaAttribute'
|
|
18
14
|
export {default as metaTagSchema} from './schemas/types/metaTag'
|
|
19
|
-
export {default as
|
|
15
|
+
export {default as openGraphSchema} from './schemas/types/openGraph'
|
|
20
16
|
export {default as robotsSchema} from './schemas/types/robots'
|
|
17
|
+
export {default as twitterSchema} from './schemas/types/twitter'
|
package/src/plugin.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// plugin.ts
|
|
2
2
|
import {definePlugin} from 'sanity'
|
|
3
|
+
|
|
3
4
|
import types from './schemas/types'
|
|
4
5
|
|
|
5
6
|
export interface SeoFieldConfig {
|
|
@@ -38,9 +39,13 @@ export type twitterFieldKeys =
|
|
|
38
39
|
|
|
39
40
|
export type AllFieldKeys = SeoFieldKeys | openGraphFieldKeys | twitterFieldKeys
|
|
40
41
|
|
|
42
|
+
export type ValidHiddenFieldKeys = Exclude<
|
|
43
|
+
AllFieldKeys,
|
|
44
|
+
'openGraphImageUrl' | 'twitterImageUrl' | 'openGraphImageType' | 'twitterImageType'
|
|
45
|
+
>
|
|
46
|
+
|
|
41
47
|
export interface FieldVisibilityConfig {
|
|
42
|
-
hiddenFields?:
|
|
43
|
-
postType?: string
|
|
48
|
+
hiddenFields?: ValidHiddenFieldKeys[]
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
export interface SeoFieldsPluginConfig {
|
|
@@ -67,22 +72,18 @@ export interface SeoFieldsPluginConfig {
|
|
|
67
72
|
* This allows customization of field titles and descriptions.
|
|
68
73
|
* For example, to change the title of the 'title' field:
|
|
69
74
|
*/
|
|
70
|
-
fieldOverrides?:
|
|
71
|
-
[key in AllFieldKeys]?: SeoFieldConfig
|
|
72
|
-
}
|
|
75
|
+
fieldOverrides?: Partial<Record<AllFieldKeys, SeoFieldConfig>>
|
|
73
76
|
/**
|
|
74
77
|
* A mapping of document types to field visibility configurations.
|
|
75
78
|
* This allows you to specify which fields should be hidden for specific document types.
|
|
76
79
|
*/
|
|
77
|
-
fieldVisibility?:
|
|
78
|
-
[postType: string]: FieldVisibilityConfig
|
|
79
|
-
}
|
|
80
|
+
fieldVisibility?: Record<string, FieldVisibilityConfig>
|
|
80
81
|
|
|
81
82
|
/**
|
|
82
83
|
* A list of fields that should be hidden by default in all document types.
|
|
83
84
|
* This can be overridden by specific document type settings in `fieldVisibility`.
|
|
84
85
|
*/
|
|
85
|
-
defaultHiddenFields?:
|
|
86
|
+
defaultHiddenFields?: ValidHiddenFieldKeys[]
|
|
86
87
|
/**
|
|
87
88
|
* The base URL of your website, used for generating full URLs in the SEO preview.
|
|
88
89
|
* Defaults to 'https://www.example.com' if not provided.
|
package/src/schemas/index.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import {defineField, defineType} from 'sanity'
|
|
2
|
-
|
|
1
|
+
import {defineField, defineType, SchemaTypeDefinition} from 'sanity'
|
|
2
|
+
|
|
3
3
|
import MetaDescription from '../components/meta/MetaDescription'
|
|
4
|
+
import MetaTitle from '../components/meta/MetaTitle'
|
|
4
5
|
import SeoPreview from '../components/SeoPreview'
|
|
5
6
|
import {SeoFieldsPluginConfig} from '../plugin'
|
|
6
|
-
import {
|
|
7
|
+
import {getFieldHiddenFunction, getFieldInfo} from '../utils/fieldsUtils'
|
|
7
8
|
import {isEmpty} from '../utils/utils'
|
|
9
|
+
import openGraph from './types/openGraph'
|
|
10
|
+
import twitter from './types/twitter'
|
|
8
11
|
|
|
9
|
-
export default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}) {
|
|
12
|
+
export default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {
|
|
10
13
|
return defineType({
|
|
11
14
|
name: 'seoFields',
|
|
12
15
|
title: 'SEO Fields',
|
|
@@ -34,7 +37,7 @@ export default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}) {
|
|
|
34
37
|
config.seoPreview.prefix
|
|
35
38
|
? {prefix: config.seoPreview.prefix}
|
|
36
39
|
: {}),
|
|
37
|
-
} as
|
|
40
|
+
} as Record<string, unknown>,
|
|
38
41
|
// Use a readOnly field to prevent editing
|
|
39
42
|
// This field is just for preview purposes
|
|
40
43
|
initialValue: '' as string, // Set an initial value
|
|
@@ -111,16 +114,8 @@ export default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}) {
|
|
|
111
114
|
'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',
|
|
112
115
|
hidden: getFieldHiddenFunction('canonicalUrl', config),
|
|
113
116
|
}),
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
title: 'Open Graph Settings',
|
|
117
|
-
type: 'openGraph',
|
|
118
|
-
}),
|
|
119
|
-
defineField({
|
|
120
|
-
name: 'twitter',
|
|
121
|
-
title: 'X (Formerly Twitter) Card Settings',
|
|
122
|
-
type: 'twitter',
|
|
123
|
-
}),
|
|
117
|
+
openGraph(config) as any,
|
|
118
|
+
twitter(config) as any,
|
|
124
119
|
],
|
|
125
120
|
})
|
|
126
121
|
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
+
import {SchemaTypeDefinition} from 'sanity'
|
|
2
|
+
|
|
3
|
+
import {SeoFieldsPluginConfig} from '../../plugin'
|
|
1
4
|
import seoFields from '..'
|
|
2
5
|
import metaAttribute from './metaAttribute'
|
|
6
|
+
import metaTag from './metaTag'
|
|
3
7
|
import openGraph from './openGraph'
|
|
4
|
-
import twitter from './twitter'
|
|
5
8
|
import robots from './robots'
|
|
6
|
-
import
|
|
7
|
-
import {SeoFieldsPluginConfig} from '../../plugin'
|
|
9
|
+
import twitter from './twitter'
|
|
8
10
|
|
|
9
|
-
export default function types(config: SeoFieldsPluginConfig = {}) {
|
|
11
|
+
export default function types(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition[] {
|
|
10
12
|
return [
|
|
11
13
|
seoFields(config), // pass config here
|
|
12
14
|
openGraph(config), // pass config here
|
|
13
15
|
twitter(config), // pass config here
|
|
14
|
-
metaAttribute,
|
|
16
|
+
metaAttribute as SchemaTypeDefinition,
|
|
15
17
|
metaTag,
|
|
16
18
|
robots,
|
|
17
19
|
]
|
|
@@ -41,22 +41,18 @@ export default defineType({
|
|
|
41
41
|
attributeValueString: 'value',
|
|
42
42
|
attributeValueImage: 'image',
|
|
43
43
|
},
|
|
44
|
-
prepare({
|
|
45
|
-
|
|
46
|
-
attributeValueString
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
prepare({attributeName, attributeValueString, attributeValueImage}) {
|
|
45
|
+
let subtitle = ''
|
|
46
|
+
if (attributeValueString) {
|
|
47
|
+
subtitle = `Value: ${attributeValueString}`
|
|
48
|
+
} else if (attributeValueImage) {
|
|
49
|
+
subtitle = 'Value: [Image]'
|
|
50
|
+
} else {
|
|
51
|
+
subtitle = 'No Attribute Value'
|
|
52
|
+
}
|
|
53
53
|
return {
|
|
54
54
|
title: attributeName || 'No Attribute Name',
|
|
55
|
-
subtitle:
|
|
56
|
-
? `Value: ${attributeValueString}`
|
|
57
|
-
: attributeValueImage
|
|
58
|
-
? 'Value: [Image]'
|
|
59
|
-
: 'No Attribute Value',
|
|
55
|
+
subtitle: subtitle,
|
|
60
56
|
media: attributeValueImage,
|
|
61
57
|
}
|
|
62
58
|
},
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {defineType} from 'sanity'
|
|
2
2
|
|
|
3
3
|
export default defineType({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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:
|
|
14
|
+
'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
})
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {defineField, defineType} from 'sanity'
|
|
2
|
-
|
|
1
|
+
import {defineField, defineType, SchemaTypeDefinition} from 'sanity'
|
|
2
|
+
|
|
3
3
|
import OgDescription from '../../../components/openGraph/OgDescription'
|
|
4
|
+
import OgTitle from '../../../components/openGraph/OgTitle'
|
|
4
5
|
import {SeoFieldsPluginConfig} from '../../../plugin'
|
|
5
6
|
import {getFieldHiddenFunction, getFieldInfo} from '../../../utils/fieldsUtils'
|
|
6
7
|
|
|
7
|
-
export default function openGraph(config: SeoFieldsPluginConfig = {}) {
|
|
8
|
+
export default function openGraph(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {
|
|
8
9
|
return defineType({
|
|
9
10
|
name: 'openGraph',
|
|
10
11
|
title: 'Open Graph Settings',
|
|
@@ -90,13 +91,23 @@ export default function openGraph(config: SeoFieldsPluginConfig = {}) {
|
|
|
90
91
|
description: 'A description of the image for accessibility purposes.',
|
|
91
92
|
}),
|
|
92
93
|
],
|
|
93
|
-
hidden: (
|
|
94
|
+
hidden: (context) => {
|
|
95
|
+
const {parent} = context
|
|
96
|
+
if (parent?.imageType !== 'upload') return true
|
|
97
|
+
const hiddenFn = getFieldHiddenFunction('openGraphImage', config)
|
|
98
|
+
return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn
|
|
99
|
+
},
|
|
94
100
|
}),
|
|
95
101
|
defineField({
|
|
96
102
|
name: 'imageUrl',
|
|
97
103
|
...getFieldInfo('openGraphImageUrl', config.fieldOverrides),
|
|
98
104
|
type: 'url',
|
|
99
|
-
hidden: (
|
|
105
|
+
hidden: (context) => {
|
|
106
|
+
const {parent} = context
|
|
107
|
+
if (parent?.imageType !== 'url') return true
|
|
108
|
+
const hiddenFn = getFieldHiddenFunction('openGraphImage', config)
|
|
109
|
+
return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn
|
|
110
|
+
},
|
|
100
111
|
}),
|
|
101
112
|
],
|
|
102
113
|
})
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {defineField, defineType} from 'sanity'
|
|
2
|
-
|
|
1
|
+
import {defineField, defineType, SchemaTypeDefinition} from 'sanity'
|
|
2
|
+
|
|
3
3
|
import TwitterDescription from '../../../components/twitter/twitterDescription'
|
|
4
|
+
import TwitterTitle from '../../../components/twitter/twitterTitle'
|
|
4
5
|
import {SeoFieldsPluginConfig} from '../../../plugin'
|
|
5
6
|
import {getFieldHiddenFunction, getFieldInfo} from '../../../utils/fieldsUtils'
|
|
6
7
|
|
|
7
|
-
export default function twitter(config: SeoFieldsPluginConfig = {}) {
|
|
8
|
+
export default function twitter(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {
|
|
8
9
|
return defineType({
|
|
9
10
|
name: 'twitter',
|
|
10
11
|
title: 'X (Formerly Twitter)',
|
|
@@ -84,12 +85,23 @@ export default function twitter(config: SeoFieldsPluginConfig = {}) {
|
|
|
84
85
|
description: 'A description of the image for accessibility purposes.',
|
|
85
86
|
}),
|
|
86
87
|
],
|
|
88
|
+
hidden: (context) => {
|
|
89
|
+
const {parent} = context
|
|
90
|
+
if (parent?.imageType !== 'upload') return true
|
|
91
|
+
const hiddenFn = getFieldHiddenFunction('twitterImage', config)
|
|
92
|
+
return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn
|
|
93
|
+
},
|
|
87
94
|
}),
|
|
88
95
|
defineField({
|
|
89
96
|
name: 'imageUrl',
|
|
90
97
|
...getFieldInfo('twitterImageUrl', config.fieldOverrides),
|
|
91
98
|
type: 'url',
|
|
92
|
-
hidden: (
|
|
99
|
+
hidden: (context) => {
|
|
100
|
+
const {parent} = context
|
|
101
|
+
if (parent?.imageType !== 'url') return true
|
|
102
|
+
const hiddenFn = getFieldHiddenFunction('twitterImage', config)
|
|
103
|
+
return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn
|
|
104
|
+
},
|
|
93
105
|
}),
|
|
94
106
|
],
|
|
95
107
|
})
|
package/src/types.ts
CHANGED
|
@@ -78,69 +78,8 @@ export interface SeoFields {
|
|
|
78
78
|
openGraph?: OpenGraphSettings
|
|
79
79
|
twitter?: TwitterCardSettings
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export const isOpenGraphSettings = (obj: any): obj is OpenGraphSettings => {
|
|
88
|
-
return obj && obj._type === 'openGraph'
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export const isTwitterCardSettings = (obj: any): obj is TwitterCardSettings => {
|
|
92
|
-
return obj && obj._type === 'twitter'
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const isMetaAttribute = (obj: any): obj is MetaAttribute => {
|
|
96
|
-
return obj && obj._type === 'metaAttribute'
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Utility types for form validation
|
|
100
|
-
export interface SeoValidationRules {
|
|
101
|
-
title: {
|
|
102
|
-
maxLength: number
|
|
103
|
-
warningLength: number
|
|
104
|
-
}
|
|
105
|
-
description: {
|
|
106
|
-
maxLength: number
|
|
107
|
-
warningLength: number
|
|
108
|
-
}
|
|
109
|
-
openGraphTitle: {
|
|
110
|
-
maxLength: number
|
|
111
|
-
}
|
|
112
|
-
openGraphDescription: {
|
|
113
|
-
maxLength: number
|
|
114
|
-
}
|
|
115
|
-
twitterTitle: {
|
|
116
|
-
maxLength: number
|
|
117
|
-
}
|
|
118
|
-
twitterDescription: {
|
|
119
|
-
maxLength: number
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export const defaultSeoValidationRules: SeoValidationRules = {
|
|
124
|
-
title: {
|
|
125
|
-
maxLength: 70,
|
|
126
|
-
warningLength: 60,
|
|
127
|
-
},
|
|
128
|
-
description: {
|
|
129
|
-
maxLength: 160,
|
|
130
|
-
warningLength: 150,
|
|
131
|
-
},
|
|
132
|
-
openGraphTitle: {
|
|
133
|
-
maxLength: 95,
|
|
134
|
-
},
|
|
135
|
-
openGraphDescription: {
|
|
136
|
-
maxLength: 200,
|
|
137
|
-
},
|
|
138
|
-
twitterTitle: {
|
|
139
|
-
maxLength: 70,
|
|
140
|
-
},
|
|
141
|
-
twitterDescription: {
|
|
142
|
-
maxLength: 200,
|
|
143
|
-
},
|
|
81
|
+
export type FeedbackTypeColors = 'green' | 'orange' | 'red'
|
|
82
|
+
export type FeedbackType = {
|
|
83
|
+
text: string
|
|
84
|
+
color: FeedbackTypeColors
|
|
144
85
|
}
|
|
145
|
-
|
|
146
|
-
// All types are already exported above with individual export statements
|
package/src/utils/fieldsUtils.ts
CHANGED
|
@@ -1,94 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
// import OgTitle from '../../../components/openGraph/OgTitle'
|
|
3
|
-
// import OgDescription from '../../../components/openGraph/OgDescription'
|
|
1
|
+
import {AllFieldKeys, SeoFieldsPluginConfig, ValidHiddenFieldKeys} from '../plugin'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
import twitter from '../schemas/types/twitter'
|
|
7
|
-
|
|
8
|
-
// export default defineType({
|
|
9
|
-
// name: 'openGraph',
|
|
10
|
-
// title: 'Open Graph Settings',
|
|
11
|
-
// type: 'object',
|
|
12
|
-
// fields: [
|
|
13
|
-
// defineField({
|
|
14
|
-
// name: 'title',
|
|
15
|
-
// title: 'Open Graph Title',
|
|
16
|
-
// type: 'string',
|
|
17
|
-
// components: {
|
|
18
|
-
// input: OgTitle, // Can also wrap with a string input + preview
|
|
19
|
-
// },
|
|
20
|
-
// }),
|
|
21
|
-
// defineField({
|
|
22
|
-
// name: 'description',
|
|
23
|
-
// title: 'Open Graph Description',
|
|
24
|
-
// type: 'text',
|
|
25
|
-
// rows: 3,
|
|
26
|
-
// components: {
|
|
27
|
-
// input: OgDescription, // Can also wrap with a text area + preview
|
|
28
|
-
// },
|
|
29
|
-
// }),
|
|
30
|
-
// defineField({
|
|
31
|
-
// name: 'siteName',
|
|
32
|
-
// title: 'Open Graph Site Name',
|
|
33
|
-
// type: 'string',
|
|
34
|
-
// description: 'The name of your website. This is often the site title.',
|
|
35
|
-
// }),
|
|
36
|
-
// defineField({
|
|
37
|
-
// name: 'type',
|
|
38
|
-
// title: 'Open Graph Type',
|
|
39
|
-
// type: 'string',
|
|
40
|
-
// options: {
|
|
41
|
-
// list: [
|
|
42
|
-
// {title: 'Website', value: 'website'},
|
|
43
|
-
// {title: 'Article', value: 'article'},
|
|
44
|
-
// {title: 'Profile', value: 'profile'},
|
|
45
|
-
// {title: 'Book', value: 'book'},
|
|
46
|
-
// {title: 'Music', value: 'music'},
|
|
47
|
-
// {title: 'Video', value: 'video'},
|
|
48
|
-
// {title: 'Product', value: 'product'},
|
|
49
|
-
// ],
|
|
50
|
-
// // layout: 'radio', // Display as radio buttons
|
|
51
|
-
// },
|
|
52
|
-
// initialValue: 'website',
|
|
53
|
-
// description: 'Select the type of content for Open Graph.',
|
|
54
|
-
// }),
|
|
55
|
-
|
|
56
|
-
// // upload image or ask for url
|
|
57
|
-
// defineField({
|
|
58
|
-
// name: 'imageType',
|
|
59
|
-
// title: 'Image Type',
|
|
60
|
-
// type: 'string',
|
|
61
|
-
// options: {
|
|
62
|
-
// list: [
|
|
63
|
-
// {title: 'Upload Image', value: 'upload'},
|
|
64
|
-
// {title: 'Image URL', value: 'url'},
|
|
65
|
-
// ],
|
|
66
|
-
// },
|
|
67
|
-
// initialValue: 'upload',
|
|
68
|
-
// }),
|
|
69
|
-
// defineField({
|
|
70
|
-
// name: 'image',
|
|
71
|
-
// title: 'Open Graph Image',
|
|
72
|
-
// type: 'image',
|
|
73
|
-
// options: {
|
|
74
|
-
// hotspot: true,
|
|
75
|
-
// },
|
|
76
|
-
// hidden: ({parent}) => parent?.imageType !== 'upload',
|
|
77
|
-
// description:
|
|
78
|
-
// 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',
|
|
79
|
-
// }),
|
|
80
|
-
// defineField({
|
|
81
|
-
// name: 'imageUrl',
|
|
82
|
-
// title: 'Open Graph Image URL',
|
|
83
|
-
// type: 'url',
|
|
84
|
-
// hidden: ({parent}) => parent?.imageType !== 'url',
|
|
85
|
-
// description:
|
|
86
|
-
// 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',
|
|
87
|
-
// }),
|
|
88
|
-
// ],
|
|
89
|
-
// })
|
|
90
|
-
|
|
91
|
-
const DEFAULT_FIELD_INFO = {
|
|
3
|
+
const DEFAULT_FIELD_INFO: Record<AllFieldKeys, {title: string; description: string}> = {
|
|
92
4
|
title: {
|
|
93
5
|
title: 'Meta Title',
|
|
94
6
|
description:
|
|
@@ -159,7 +71,7 @@ const DEFAULT_FIELD_INFO = {
|
|
|
159
71
|
'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',
|
|
160
72
|
},
|
|
161
73
|
twitterCard: {
|
|
162
|
-
title: 'Card Type',
|
|
74
|
+
title: 'X Card Type',
|
|
163
75
|
description: '',
|
|
164
76
|
},
|
|
165
77
|
twitterSite: {
|
|
@@ -198,12 +110,12 @@ const DEFAULT_FIELD_INFO = {
|
|
|
198
110
|
export const getFieldInfo = (
|
|
199
111
|
fieldName: string,
|
|
200
112
|
fieldOverrides: SeoFieldsPluginConfig['fieldOverrides'],
|
|
201
|
-
) => {
|
|
113
|
+
): {title: string; description: string} => {
|
|
202
114
|
const fieldInfo =
|
|
203
115
|
(fieldOverrides && fieldOverrides[fieldName as keyof typeof fieldOverrides]) ||
|
|
204
116
|
DEFAULT_FIELD_INFO[fieldName as keyof typeof DEFAULT_FIELD_INFO]
|
|
205
117
|
return fieldInfo
|
|
206
|
-
? {title: fieldInfo.title, description: fieldInfo.description}
|
|
118
|
+
? {title: fieldInfo.title || '', description: fieldInfo.description || ''}
|
|
207
119
|
: {title: '', description: ''}
|
|
208
120
|
}
|
|
209
121
|
|
|
@@ -211,7 +123,7 @@ export const getFieldInfo = (
|
|
|
211
123
|
* Check if a field should be hidden based on the current document type
|
|
212
124
|
*/
|
|
213
125
|
export const isFieldHidden = (
|
|
214
|
-
fieldName:
|
|
126
|
+
fieldName: ValidHiddenFieldKeys,
|
|
215
127
|
config: SeoFieldsPluginConfig,
|
|
216
128
|
documentType?: string,
|
|
217
129
|
): boolean => {
|
|
@@ -231,8 +143,17 @@ export const isFieldHidden = (
|
|
|
231
143
|
/**
|
|
232
144
|
* Get the hidden function for any field
|
|
233
145
|
*/
|
|
234
|
-
export const getFieldHiddenFunction = (
|
|
235
|
-
|
|
146
|
+
export const getFieldHiddenFunction = (
|
|
147
|
+
fieldName: ValidHiddenFieldKeys,
|
|
148
|
+
config: SeoFieldsPluginConfig,
|
|
149
|
+
) => {
|
|
150
|
+
return ({
|
|
151
|
+
document,
|
|
152
|
+
}: {
|
|
153
|
+
document?: {
|
|
154
|
+
_type?: string
|
|
155
|
+
}
|
|
156
|
+
}): boolean => {
|
|
236
157
|
const documentType = document?._type
|
|
237
158
|
return isFieldHidden(fieldName, config, documentType)
|
|
238
159
|
}
|
package/src/utils/seoUtils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {FeedbackType} from '../types'
|
|
2
2
|
|
|
3
3
|
export const stopWords = ['the', 'a', 'an', 'and', 'or', 'but']
|
|
4
4
|
|
|
@@ -33,8 +33,8 @@ export const primaryKeywordAtStart = (title: string, keywords: string[]): boolea
|
|
|
33
33
|
return title.toLowerCase().startsWith(keywords[0].toLowerCase())
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export const truncate = (text: string, maxLength: number) =>
|
|
37
|
-
text.length > maxLength ? text.slice(0, maxLength)
|
|
36
|
+
export const truncate = (text: string, maxLength: number): string =>
|
|
37
|
+
text.length > maxLength ? `${text.slice(0, maxLength)}…` : text
|
|
38
38
|
|
|
39
39
|
export const hasExcessivePunctuation = (title: string): boolean => /[!@#$%^&*]{2,}/.test(title)
|
|
40
40
|
|
|
@@ -42,8 +42,8 @@ export const getMetaTitleValidationMessages = (
|
|
|
42
42
|
title: string,
|
|
43
43
|
keywords: string[],
|
|
44
44
|
isParentseoField: boolean,
|
|
45
|
-
) => {
|
|
46
|
-
const feedback:
|
|
45
|
+
): FeedbackType[] => {
|
|
46
|
+
const feedback: FeedbackType[] = []
|
|
47
47
|
|
|
48
48
|
const minChar = 50
|
|
49
49
|
const maxChar = 60
|
|
@@ -108,8 +108,8 @@ export const getMetaDescriptionValidationMessages = (
|
|
|
108
108
|
description: string,
|
|
109
109
|
keywords: string[],
|
|
110
110
|
isParentseoField: boolean,
|
|
111
|
-
) => {
|
|
112
|
-
const feedback:
|
|
111
|
+
): FeedbackType[] => {
|
|
112
|
+
const feedback: FeedbackType[] = []
|
|
113
113
|
|
|
114
114
|
const minChar = 150
|
|
115
115
|
const maxChar = 160
|
|
@@ -180,8 +180,8 @@ export const getOgTitleValidation = (
|
|
|
180
180
|
title: string,
|
|
181
181
|
keywords: string[] = [],
|
|
182
182
|
isParentseoField: boolean,
|
|
183
|
-
) => {
|
|
184
|
-
const feedback:
|
|
183
|
+
): FeedbackType[] => {
|
|
184
|
+
const feedback: FeedbackType[] = []
|
|
185
185
|
const min = 40
|
|
186
186
|
const max = 60
|
|
187
187
|
const count = title?.length || 0
|
|
@@ -244,8 +244,8 @@ export const getOgDescriptionValidation = (
|
|
|
244
244
|
desc: string,
|
|
245
245
|
keywords: string[] = [],
|
|
246
246
|
isParentseoField: boolean,
|
|
247
|
-
) => {
|
|
248
|
-
const feedback:
|
|
247
|
+
): FeedbackType[] => {
|
|
248
|
+
const feedback: FeedbackType[] = []
|
|
249
249
|
const min = 90
|
|
250
250
|
const max = 120
|
|
251
251
|
const count = desc?.length || 0
|
|
@@ -317,8 +317,8 @@ export const getTwitterTitleValidation = (
|
|
|
317
317
|
title: string,
|
|
318
318
|
keywords: string[] = [],
|
|
319
319
|
isParentseoField: boolean,
|
|
320
|
-
) => {
|
|
321
|
-
const feedback:
|
|
320
|
+
): FeedbackType[] => {
|
|
321
|
+
const feedback: FeedbackType[] = []
|
|
322
322
|
const min = 30
|
|
323
323
|
const max = 70
|
|
324
324
|
const count = title?.length || 0
|
|
@@ -370,8 +370,8 @@ export const getTwitterDescriptionValidation = (
|
|
|
370
370
|
desc: string,
|
|
371
371
|
keywords: string[] = [],
|
|
372
372
|
isParentseoField: boolean,
|
|
373
|
-
) => {
|
|
374
|
-
const feedback:
|
|
373
|
+
): FeedbackType[] => {
|
|
374
|
+
const feedback: FeedbackType[] = []
|
|
375
375
|
const min = 50
|
|
376
376
|
const max = 200
|
|
377
377
|
const count = desc?.length || 0
|