sanity-plugin-seofields 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/types.ts ADDED
@@ -0,0 +1,144 @@
1
+ // TypeScript interfaces for SEO Fields Plugin
2
+
3
+ // Base Sanity types
4
+ export interface SanityImage {
5
+ _type: 'image'
6
+ asset: {
7
+ _ref: string
8
+ _type: 'reference'
9
+ }
10
+ hotspot?: {
11
+ x: number
12
+ y: number
13
+ height: number
14
+ width: number
15
+ }
16
+ crop?: {
17
+ top: number
18
+ bottom: number
19
+ left: number
20
+ right: number
21
+ }
22
+ alt?: string
23
+ }
24
+
25
+ export interface SanityImageWithAlt extends SanityImage {
26
+ alt: string
27
+ }
28
+
29
+ // Robots settings
30
+ export interface RobotsSettings {
31
+ noIndex?: boolean
32
+ noFollow?: boolean
33
+ }
34
+
35
+ // Meta Attribute
36
+ export interface MetaAttribute {
37
+ _type: 'metaAttribute'
38
+ key: string
39
+ type: 'string' | 'image'
40
+ value?: string
41
+ image?: SanityImage
42
+ }
43
+
44
+ // Open Graph settings
45
+ export interface OpenGraphSettings {
46
+ _type: 'openGraph'
47
+ title?: string
48
+ description?: string
49
+ siteName?: string
50
+ type?: 'website' | 'article' | 'profile' | 'book' | 'music' | 'video' | 'product'
51
+ imageType?: 'upload' | 'url'
52
+ image?: SanityImage
53
+ imageUrl?: string
54
+ }
55
+
56
+ // Twitter Card settings
57
+ export interface TwitterCardSettings {
58
+ _type: 'twitter'
59
+ card?: 'summary' | 'summary_large_image' | 'app' | 'player'
60
+ site?: string
61
+ title?: string
62
+ description?: string
63
+ image?: SanityImageWithAlt
64
+ }
65
+
66
+ // Main SEO Fields interface
67
+ export interface SeoFields {
68
+ _type: 'seoFields'
69
+ robots?: RobotsSettings
70
+ preview?: string
71
+ title?: string
72
+ description?: string
73
+ metaImage?: SanityImage
74
+ keywords?: string[]
75
+ canonicalUrl?: string
76
+ openGraph?: OpenGraphSettings
77
+ twitter?: TwitterCardSettings
78
+ }
79
+
80
+ // Type guards
81
+ export const isSeoFields = (obj: any): obj is SeoFields => {
82
+ return obj && obj._type === 'seoFields'
83
+ }
84
+
85
+ export const isOpenGraphSettings = (obj: any): obj is OpenGraphSettings => {
86
+ return obj && obj._type === 'openGraph'
87
+ }
88
+
89
+ export const isTwitterCardSettings = (obj: any): obj is TwitterCardSettings => {
90
+ return obj && obj._type === 'twitter'
91
+ }
92
+
93
+ export const isMetaAttribute = (obj: any): obj is MetaAttribute => {
94
+ return obj && obj._type === 'metaAttribute'
95
+ }
96
+
97
+ // Utility types for form validation
98
+ export interface SeoValidationRules {
99
+ title: {
100
+ maxLength: number
101
+ warningLength: number
102
+ }
103
+ description: {
104
+ maxLength: number
105
+ warningLength: number
106
+ }
107
+ openGraphTitle: {
108
+ maxLength: number
109
+ }
110
+ openGraphDescription: {
111
+ maxLength: number
112
+ }
113
+ twitterTitle: {
114
+ maxLength: number
115
+ }
116
+ twitterDescription: {
117
+ maxLength: number
118
+ }
119
+ }
120
+
121
+ export const defaultSeoValidationRules: SeoValidationRules = {
122
+ title: {
123
+ maxLength: 70,
124
+ warningLength: 60,
125
+ },
126
+ description: {
127
+ maxLength: 160,
128
+ warningLength: 150,
129
+ },
130
+ openGraphTitle: {
131
+ maxLength: 95,
132
+ },
133
+ openGraphDescription: {
134
+ maxLength: 200,
135
+ },
136
+ twitterTitle: {
137
+ maxLength: 70,
138
+ },
139
+ twitterDescription: {
140
+ maxLength: 200,
141
+ },
142
+ }
143
+
144
+ // All types are already exported above with individual export statements
@@ -0,0 +1,295 @@
1
+ import {SchemaTypeDefinition} from 'sanity'
2
+ // Import all schema definitions
3
+ import allSchemas from '../schemas/types'
4
+
5
+ interface JsonSchemaProperty {
6
+ type?: string | string[]
7
+ items?: JsonSchemaProperty
8
+ properties?: Record<string, JsonSchemaProperty>
9
+ required?: string[]
10
+ enum?: string[]
11
+ const?: any
12
+ default?: any
13
+ description?: string
14
+ maxLength?: number
15
+ minLength?: number
16
+ pattern?: string
17
+ format?: string
18
+ $ref?: string
19
+ allOf?: JsonSchemaProperty[]
20
+ additionalProperties?: boolean
21
+ }
22
+
23
+ interface JsonSchema {
24
+ $schema: string
25
+ title: string
26
+ description: string
27
+ type: string
28
+ definitions: Record<string, JsonSchemaProperty>
29
+ properties: Record<string, JsonSchemaProperty>
30
+ additionalProperties: boolean
31
+ }
32
+
33
+ /**
34
+ * Converts Sanity field type to JSON Schema type
35
+ */
36
+ function mapSanityTypeToJsonSchema(sanityType: string): string | string[] {
37
+ const typeMap: Record<string, string | string[]> = {
38
+ string: 'string',
39
+ text: 'string',
40
+ number: 'number',
41
+ boolean: 'boolean',
42
+ array: 'array',
43
+ object: 'object',
44
+ image: 'object',
45
+ url: 'string',
46
+ date: 'string',
47
+ datetime: 'string',
48
+ reference: 'object',
49
+ }
50
+
51
+ return typeMap[sanityType] || 'string'
52
+ }
53
+
54
+ /**
55
+ * Converts Sanity field definition to JSON Schema property
56
+ */
57
+ function convertSanityFieldToJsonSchema(field: any): JsonSchemaProperty {
58
+ const jsonSchemaProperty: JsonSchemaProperty = {
59
+ type: mapSanityTypeToJsonSchema(field.type),
60
+ }
61
+
62
+ // Add description
63
+ if (field.description) {
64
+ jsonSchemaProperty.description = field.description
65
+ }
66
+
67
+ // Handle validation rules
68
+ if (field.validation) {
69
+ // This would need to be expanded based on your validation rules
70
+ // For now, we'll handle common cases
71
+ }
72
+
73
+ // Handle different field types
74
+ switch (field.type) {
75
+ case 'string':
76
+ if (field.options?.list) {
77
+ jsonSchemaProperty.enum = field.options.list.map((item: any) =>
78
+ typeof item === 'string' ? item : item.value,
79
+ )
80
+ }
81
+ if (field.validation) {
82
+ // Add length constraints if available
83
+ }
84
+ break
85
+
86
+ case 'text':
87
+ jsonSchemaProperty.type = 'string'
88
+ break
89
+
90
+ case 'url':
91
+ jsonSchemaProperty.type = 'string'
92
+ jsonSchemaProperty.format = 'uri'
93
+ break
94
+
95
+ case 'array':
96
+ jsonSchemaProperty.type = 'array'
97
+ if (field.of && field.of[0]) {
98
+ if (typeof field.of[0] === 'string') {
99
+ jsonSchemaProperty.items = {type: 'string'}
100
+ } else if (field.of[0].type) {
101
+ jsonSchemaProperty.items = convertSanityFieldToJsonSchema(field.of[0])
102
+ }
103
+ }
104
+ break
105
+
106
+ case 'object':
107
+ jsonSchemaProperty.type = 'object'
108
+ if (field.fields) {
109
+ jsonSchemaProperty.properties = {}
110
+ const required: string[] = []
111
+
112
+ field.fields.forEach((subField: any) => {
113
+ if (subField.name) {
114
+ jsonSchemaProperty.properties![subField.name] = convertSanityFieldToJsonSchema(subField)
115
+ if (subField.required) {
116
+ required.push(subField.name)
117
+ }
118
+ }
119
+ })
120
+
121
+ if (required.length > 0) {
122
+ jsonSchemaProperty.required = required
123
+ }
124
+ }
125
+ jsonSchemaProperty.additionalProperties = false
126
+ break
127
+
128
+ case 'image':
129
+ jsonSchemaProperty.$ref = '#/definitions/SanityImage'
130
+ break
131
+
132
+ case 'reference':
133
+ jsonSchemaProperty.type = 'object'
134
+ jsonSchemaProperty.properties = {
135
+ _ref: {type: 'string'},
136
+ _type: {type: 'string', const: 'reference'},
137
+ }
138
+ jsonSchemaProperty.required = ['_ref', '_type']
139
+ break
140
+ }
141
+
142
+ // Handle initial values as defaults
143
+ if (field.initialValue !== undefined) {
144
+ jsonSchemaProperty.default = field.initialValue
145
+ }
146
+
147
+ // Handle hidden fields (not really applicable to JSON Schema but we can note it)
148
+ if (field.hidden) {
149
+ jsonSchemaProperty.description = (jsonSchemaProperty.description || '') + ' (Hidden field)'
150
+ }
151
+
152
+ // Handle read-only fields
153
+ if (field.readOnly) {
154
+ jsonSchemaProperty.description = (jsonSchemaProperty.description || '') + ' (Read-only)'
155
+ }
156
+
157
+ return jsonSchemaProperty
158
+ }
159
+
160
+ /**
161
+ * Converts a Sanity schema definition to JSON Schema definition
162
+ */
163
+ function convertSanitySchemaToJsonSchema(schemaDefinition: any): JsonSchemaProperty {
164
+ const jsonSchemaDefinition: JsonSchemaProperty = {
165
+ type: 'object',
166
+ properties: {},
167
+ additionalProperties: false,
168
+ }
169
+
170
+ // Add _type property
171
+ if (schemaDefinition.name) {
172
+ jsonSchemaDefinition.properties!._type = {
173
+ type: 'string',
174
+ const: schemaDefinition.name,
175
+ }
176
+ }
177
+
178
+ // Add title and description
179
+ if (schemaDefinition.title) {
180
+ jsonSchemaDefinition.description = schemaDefinition.title
181
+ }
182
+
183
+ // Convert fields
184
+ if ('fields' in schemaDefinition && schemaDefinition.fields) {
185
+ const required: string[] = ['_type']
186
+
187
+ schemaDefinition.fields.forEach((field: any) => {
188
+ if (field.name) {
189
+ jsonSchemaDefinition.properties![field.name] = convertSanityFieldToJsonSchema(field)
190
+ if (field.required) {
191
+ required.push(field.name)
192
+ }
193
+ }
194
+ })
195
+
196
+ if (required.length > 1) {
197
+ // More than just _type
198
+ jsonSchemaDefinition.required = required
199
+ } else {
200
+ jsonSchemaDefinition.required = ['_type']
201
+ }
202
+ }
203
+
204
+ return jsonSchemaDefinition
205
+ }
206
+
207
+ /**
208
+ * Generates a complete JSON Schema from Sanity schema definitions
209
+ */
210
+ function generateSchemaJson(): JsonSchema {
211
+ const schemas = allSchemas
212
+
213
+ const jsonSchema: JsonSchema = {
214
+ $schema: 'http://json-schema.org/draft-07/schema#',
215
+ title: 'Sanity SEO Fields Plugin Schema',
216
+ description:
217
+ 'Schema definitions for SEO fields including meta tags, Open Graph, and Twitter Card settings',
218
+ type: 'object',
219
+ definitions: {},
220
+ properties: {},
221
+ additionalProperties: false,
222
+ }
223
+
224
+ // Add SanityImage definition
225
+ jsonSchema.definitions.SanityImage = {
226
+ type: 'object',
227
+ properties: {
228
+ _type: {type: 'string', const: 'image'},
229
+ asset: {
230
+ type: 'object',
231
+ properties: {
232
+ _ref: {type: 'string'},
233
+ _type: {type: 'string', const: 'reference'},
234
+ },
235
+ required: ['_ref', '_type'],
236
+ },
237
+ hotspot: {
238
+ type: 'object',
239
+ properties: {
240
+ x: {type: 'number'},
241
+ y: {type: 'number'},
242
+ height: {type: 'number'},
243
+ width: {type: 'number'},
244
+ },
245
+ },
246
+ crop: {
247
+ type: 'object',
248
+ properties: {
249
+ top: {type: 'number'},
250
+ bottom: {type: 'number'},
251
+ left: {type: 'number'},
252
+ right: {type: 'number'},
253
+ },
254
+ },
255
+ alt: {type: 'string'},
256
+ },
257
+ required: ['_type', 'asset'],
258
+ }
259
+
260
+ // Convert each schema
261
+ schemas.forEach((schema) => {
262
+ if (schema.name) {
263
+ const jsonSchemaDefinition = convertSanitySchemaToJsonSchema(schema)
264
+ jsonSchema.definitions[schema.name] = jsonSchemaDefinition
265
+ jsonSchema.properties[schema.name] = {$ref: `#/definitions/${schema.name}`}
266
+ }
267
+ })
268
+
269
+ return jsonSchema
270
+ }
271
+
272
+ /**
273
+ * Writes the generated schema to a file
274
+ */
275
+ export function writeSchemaJson(outputPath: string = './schema.json'): void {
276
+ const schema = generateSchemaJson()
277
+ const fs = require('fs')
278
+
279
+ try {
280
+ fs.writeFileSync(outputPath, JSON.stringify(schema, null, 2))
281
+ console.log(`✅ Schema.json generated successfully at: ${outputPath}`)
282
+ } catch (error) {
283
+ console.error('❌ Error writing schema.json:', error)
284
+ throw error
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Returns the generated schema as an object
290
+ */
291
+ export function getSchemaJson(): JsonSchema {
292
+ return generateSchemaJson()
293
+ }
294
+
295
+ export default generateSchemaJson